From 0a7b5fa00f5d45b5151e4a3f182b6e2d8c4ffda7 Mon Sep 17 00:00:00 2001 From: yohlo Date: Sat, 8 Aug 2026 15:29:40 -0700 Subject: [PATCH] i18n --- .claude/skills/add-language/SKILL.md | 56 + .claude/skills/i18n/SKILL.md | 101 + .gitea/workflows/ci-cd.yaml | 25 + .gitignore | 4 +- CLAUDE.md | 23 + bun.lock | 509 ++-- lingui.config.ts | 13 + package.json | 9 +- src/app/routes/__root.tsx | 147 +- src/app/routes/_authed/admin.tsx | 14 +- src/app/routes/_authed/admin/activities.tsx | 8 +- src/app/routes/_authed/admin/index.tsx | 3 +- src/app/routes/_authed/admin/preview.tsx | 7 +- .../admin/tournaments/$id/assign-partners.tsx | 19 +- .../_authed/admin/tournaments/$id/index.tsx | 4 +- .../_authed/admin/tournaments/$id/teams.tsx | 4 +- .../_authed/admin/tournaments/index.tsx | 3 +- .../_authed/admin/tournaments/run.$id.tsx | 7 +- src/app/routes/_authed/badges.tsx | 3 +- src/app/routes/_authed/settings.tsx | 11 +- src/app/routes/_authed/stats.tsx | 14 +- .../_authed/tournaments/$id.predictions.tsx | 3 +- .../$id.predictions_.$playerId.tsx | 9 +- src/app/routes/_authed/tournaments/index.tsx | 3 +- src/app/routes/api/push/test.ts | 3 +- src/app/routes/api/spotify/capture.ts | 10 +- src/app/routes/api/spotify/playback.ts | 40 +- src/app/routes/api/spotify/resume.ts | 12 +- src/app/routes/api/teams/upload-logo.ts | 19 +- src/app/routes/api/tournaments/upload-logo.ts | 19 +- src/components/DefaultCatchBoundary.tsx | 28 +- src/components/avatar.tsx | 4 +- src/components/countdown.tsx | 4 +- src/components/date-time-picker.tsx | 4 +- src/components/ios-install-prompt.tsx | 8 +- src/components/phone-number-input.tsx | 6 +- src/components/sheet/modal.tsx | 55 +- .../sheet/slide-panel/slide-panel-field.tsx | 8 +- .../sheet/slide-panel/slide-panel.tsx | 20 +- src/components/stats-overview.tsx | 42 +- src/components/swipeable-tabs.tsx | 8 +- src/components/typeahead.tsx | 9 +- src/contexts/auth-context.tsx | 6 +- src/contexts/spotify-context.tsx | 120 +- .../components/activities-table.tsx | 58 +- src/features/admin/components/admin-page.tsx | 14 +- .../admin/components/award-badges.tsx | 20 +- .../badges/components/badge-showcase.tsx | 3 +- .../badges/components/badge-stats-table.tsx | 31 +- .../bracket/components/bracket-view.tsx | 5 +- .../bracket/components/match-card.tsx | 40 +- .../bracket/components/match-dock.tsx | 17 +- .../bracket/components/match-form.tsx | 42 +- .../bracket/components/match-report.tsx | 72 +- .../bracket/components/match-slot.tsx | 9 +- src/features/bracket/components/seed-list.tsx | 4 +- src/features/bracket/utils/group-label.ts | 26 +- src/features/core/components/back-button.tsx | 4 +- src/features/core/components/header.tsx | 11 +- src/features/core/components/providers.tsx | 7 +- .../core/components/settings-button.tsx | 4 +- src/features/core/hooks/use-links.ts | 20 +- src/features/core/hooks/use-router-config.ts | 4 +- src/features/core/types/header-config.ts | 18 +- src/features/login/components/code-prompt.tsx | 10 +- src/features/login/components/layout.tsx | 3 +- src/features/login/components/name-prompt.tsx | 18 +- .../login/components/phone-prompt.tsx | 10 +- .../player-prompt/existing-player-button.tsx | 3 +- .../login/components/player-prompt/index.tsx | 24 +- .../player-prompt/new-player-button.tsx | 3 +- src/features/login/hooks/use-consume-code.ts | 14 +- src/features/login/hooks/use-create-code.ts | 8 +- src/features/login/hooks/use-create-user.ts | 6 +- .../matches/components/match-card.tsx | 12 +- .../matches/components/match-list.tsx | 3 +- .../components/team-head-to-head-sheet.tsx | 35 +- .../components/league-head-to-head.tsx | 20 +- .../components/player-head-to-head-sheet.tsx | 33 +- .../players/components/player-stats-table.tsx | 85 +- .../components/players-activity-table.tsx | 23 +- .../players/components/profile/header.tsx | 6 +- .../players/components/profile/index.tsx | 23 +- .../players/components/profile/name-form.tsx | 21 +- .../players/components/profile/skeleton.tsx | 11 +- src/features/players/server.ts | 11 +- .../predictions/components/matchup-sheet.tsx | 13 +- .../components/prediction-editor.tsx | 14 +- .../components/prediction-leaderboard.tsx | 66 +- .../components/prediction-match-card.tsx | 3 +- .../components/winner-selector.tsx | 5 +- src/features/predictions/queries.ts | 4 +- src/features/predictions/utils.ts | 20 +- .../reactions/components/emoji-bar.tsx | 4 +- .../reactions/components/emoji-picker.tsx | 31 +- .../components/accent-color-picker.tsx | 3 +- .../components/color-scheme-picker.tsx | 12 +- .../settings/components/locale-picker.tsx | 44 + .../components/notifications-section.tsx | 28 +- src/features/settings/server.ts | 26 + .../components/spotify-controls-bar.tsx | 20 +- .../spotify/components/spotify-sheet.tsx | 26 +- .../teams/components/manage-teams.tsx | 15 +- .../teams/components/regional-team-card.tsx | 3 +- src/features/teams/components/team-card.tsx | 3 +- .../components/team-form/color-picker.tsx | 11 +- .../components/team-form/duration-picker.tsx | 19 +- .../teams/components/team-form/index.tsx | 36 +- .../components/team-form/players-picker.tsx | 6 +- .../components/team-form/song-picker.tsx | 15 +- .../components/team-form/song-search.tsx | 7 +- .../components/team-form/song-summary.tsx | 3 +- .../teams/components/team-profile/index.tsx | 17 +- .../components/team-profile/skeleton.tsx | 11 +- .../teams/hooks/use-available-players.ts | 6 +- src/features/teams/hooks/use-create-team.ts | 6 +- src/features/teams/hooks/use-update-team.ts | 7 +- .../components/edit-enrolled-players.tsx | 12 +- .../components/edit-enrolled-teams.tsx | 12 +- .../tournaments/components/edit-rules.tsx | 5 +- .../components/group-match-card.tsx | 14 +- .../tournaments/components/group-preview.tsx | 5 +- .../components/group-stage-view.tsx | 26 +- .../components/manage-tournament.tsx | 20 +- .../tournaments/components/podium.tsx | 14 +- .../tournaments/components/profile/index.tsx | 13 +- .../components/profile/skeleton.tsx | 11 +- .../components/seed-tournament.tsx | 16 +- .../components/setup-group-stage.tsx | 85 +- .../components/started-tournament/header.tsx | 24 +- .../components/started-tournament/index.tsx | 10 +- .../started-tournament/sections/nav-grid.tsx | 32 +- .../sections/recent-results.tsx | 6 +- .../sections/score-report-cta.tsx | 15 +- .../components/team-assignment-preview.tsx | 5 +- .../components/tournament-card-list.tsx | 10 +- .../components/tournament-form.tsx | 62 +- .../components/tournament-list.tsx | 6 +- .../components/tournament-stats.tsx | 30 +- .../upcoming-tournament/enroll-free-agent.tsx | 18 +- .../upcoming-tournament/enroll-team/index.tsx | 8 +- .../enroll-team/team-selection-view.tsx | 10 +- .../enrolled-free-agent.tsx | 22 +- .../enrolled-players-list-button.tsx | 16 +- .../components/upcoming-tournament/index.tsx | 12 +- .../upcoming-tournament/rules-list-button.tsx | 8 +- .../upcoming-tournament/team-list-button.tsx | 14 +- .../unenroll-free-agent.tsx | 14 +- .../upcoming-tournament/unenroll-team.tsx | 12 +- .../upcoming-tournament/update-team.tsx | 6 +- .../hooks/use-admin-enroll-player.ts | 4 +- .../hooks/use-admin-unenroll-player.ts | 4 +- .../hooks/use-confirm-team-assignments.ts | 5 +- .../hooks/use-create-tournament.ts | 6 +- .../hooks/use-enroll-free-agent.ts | 6 +- .../tournaments/hooks/use-enroll-team.ts | 6 +- .../hooks/use-unenroll-free-agent.ts | 6 +- .../tournaments/hooks/use-unenroll-team.ts | 6 +- .../tournaments/utils/group-config.ts | 26 +- src/lib/i18n/error-messages.ts | 23 + src/lib/i18n/index.ts | 73 + src/lib/i18n/meta.ts | 26 + src/lib/i18n/provider.tsx | 61 + src/lib/i18n/server-messages.ts | 12 + src/lib/mantine/mantine-provider.tsx | 24 +- src/lib/push/index.ts | 47 +- .../recipes/passwordless-twilio-verify.ts | 24 +- .../hooks/use-server-mutation.ts | 20 +- .../tanstack-query/hooks/use-server-query.ts | 12 +- .../hooks/use-server-suspense-query.ts | 12 +- src/lib/twilio/index.ts | 15 +- src/locales/de/messages.po | 2670 +++++++++++++++++ src/locales/en/messages.po | 2670 +++++++++++++++++ src/locales/es/messages.po | 2670 +++++++++++++++++ src/locales/ja/messages.po | 2670 +++++++++++++++++ src/types/po.d.ts | 4 + src/types/vite-env.d.ts | 1 + vite.config.ts | 9 +- 178 files changed, 13099 insertions(+), 1285 deletions(-) create mode 100644 .claude/skills/add-language/SKILL.md create mode 100644 .claude/skills/i18n/SKILL.md create mode 100644 CLAUDE.md create mode 100644 lingui.config.ts create mode 100644 src/features/settings/components/locale-picker.tsx create mode 100644 src/lib/i18n/error-messages.ts create mode 100644 src/lib/i18n/index.ts create mode 100644 src/lib/i18n/meta.ts create mode 100644 src/lib/i18n/provider.tsx create mode 100644 src/lib/i18n/server-messages.ts create mode 100644 src/locales/de/messages.po create mode 100644 src/locales/en/messages.po create mode 100644 src/locales/es/messages.po create mode 100644 src/locales/ja/messages.po create mode 100644 src/types/po.d.ts create mode 100644 src/types/vite-env.d.ts diff --git a/.claude/skills/add-language/SKILL.md b/.claude/skills/add-language/SKILL.md new file mode 100644 index 0000000..b19d2fa --- /dev/null +++ b/.claude/skills/add-language/SKILL.md @@ -0,0 +1,56 @@ +--- +name: add-language +description: Add or remove a UI language/locale — config, catalog generation, translation agents, SMS/date/meta integration, and verification. Use when asked to add, enable, support, or remove a language. +--- + +# Adding a language + +Example: adding French (`fr`). Five places name the locale set — update all: + +1. `src/lib/i18n/index.ts` — `SUPPORTED_LOCALES` + `LOCALE_LABELS` (label in + the language itself, no region clarifier: `fr: "Français"`). +2. `lingui.config.ts` — `locales`. +3. `src/lib/mantine/mantine-provider.tsx` — `DAYJS_LOCALE` map + the matching + `import 'dayjs/locale/fr'` (calendar month/weekday names come from dayjs; + without this, calendars stay English). +4. `src/lib/i18n/meta.ts` — `OG_LOCALE_BY_LOCALE` (fr → fr_FR). +5. `src/lib/twilio/index.ts` — `TWILIO_VERIFY_LOCALES` ONLY if Twilio Verify + supports the code (https://www.twilio.com/docs/verify/supported-languages). + Unsupported codes must stay out — the allowlist silently falls back to the + service-default English SMS instead of failing the login send. + +Then generate + translate: + +6. `bun run extract` creates `src/locales/fr/messages.po` (every msgid, empty + msgstr). +7. Translate via a chunked agent workflow (this repo's proven recipe): + - Split en.po entry blocks (split on blank lines, skip header) into ~60-entry + chunks; one sonnet agent per chunk RETURNS translated entries (never let + parallel agents edit one .po). Validate each returned chunk by msgid count; + retry failures once. + - Merge by msgid back into the locale .po. Match msgids by exact bytes — + watch non-breaking spaces (\xa0) in msgids, which agents normalize away; + patch those few by hand. + - Agent rules: msgstr single-line, ICU placeholders/plural keywords and + `<0>` tags preserved (translate only human words inside branches; plural + categories follow the target language's CLDR set), FLXN/Flexxon/Spotify + untranslated, casual sporty tone, register decided up front (tú/du/です・ます). +8. `bun run i18n:check` must pass (0 missing). Optionally run per-locale + native-reviewer agents over the full .po (fidelity, register, glossary + consistency, ICU integrity) and apply their FIX lines. +9. `bunx tsc --noEmit && bun run build`; confirm the new locale appears as its + own lazy `messages-*.js` chunk in dist/client/assets. +10. Manual check: switch language in Settings (persists to SuperTokens + metadata), confirm UI + calendar + SMS behavior. + +# Removing a language + +Reverse of the above: delete from the five locale-set sites (skip Twilio if it +was never listed), `rm -rf src/locales/`, drop the dayjs import, then +`bun run i18n:check` + tsc. Users with the removed locale saved in metadata +fall back to English automatically via `resolveLocale`. + +Fallback semantics: untranslated entries render English (msgid); unauth +visitors get Accept-Language detection (SSR first paint on public routes is +English, corrected after hydration); authed users get their saved locale +server-rendered. diff --git a/.claude/skills/i18n/SKILL.md b/.claude/skills/i18n/SKILL.md new file mode 100644 index 0000000..619c98f --- /dev/null +++ b/.claude/skills/i18n/SKILL.md @@ -0,0 +1,101 @@ +--- +name: i18n +description: Rules and workflow for user-facing strings — wrapping new text in Lingui macros, updating catalogs, and translating new entries. Use whenever adding, editing, or removing any user-visible text (components, hooks, toasts, route titles, server errors). +--- + +# i18n workflow (Lingui v6) + +Every user-facing string must go through Lingui. English source text IS the +translation key (msgid); catalogs live in `src/locales//messages.po` +(en, es, de, ja — es is Mexican Spanish). msgstr mirrors msgid in en.po; that +is normal for the source locale. + +## Wrapping rules + +Allowed APIs — context-bound only (SSR-safe): + +- `import { Trans, Plural, useLingui } from "@lingui/react/macro"` +- `import { msg } from "@lingui/core/macro"` (descriptor only) +- NEVER `t`/`plural`/`select` from `@lingui/core/macro` — they bind a global + i18n and bleed locale across SSR requests. +- Carve-out: a component that only needs `i18n._()` (no `t`` macro) may use + `useLingui` from plain `@lingui/react` (see header.tsx, mantine-provider) — + it's the same context-bound hook, just without macro sugar. + +Patterns: + +| Context | Pattern | +|---|---| +| JSX text | `Match starts soon` | +| String props / expressions in a component or hook | `const { t } = useLingui()` at TOP LEVEL (unconditional — React Compiler), then `t`Saved!`` | +| Countable text in JSX | `` | +| Countable text in a string expression | `i18n._({ ...msg`{n, plural, one {# team} other {# teams}}`, values: { n } })` — a bare `t`Teams (${n})`` gives translators no plural branch | +| Route header titles (loader/beforeLoad) | `title: msg`Manage {name}`, titleValues: { name }` — resolved by `Header` | +| Plain .ts utils | take an `i18n: I18n` param and use msg descriptors (see `src/features/predictions/utils.ts`) | +| Server code (API routes, push, meta) | `localizedFor(context.metadata)` from `src/lib/i18n/server-messages.ts` (or `createI18n(resolveLocale(...))`) + msg descriptors | +| Server-fn error toasts | localized centrally via the ErrorType map in `src/lib/i18n/error-messages.ts` — add new ErrorTypes there, not per-call-site | + +v6 signature: values ride inside the descriptor — `i18n._({ ...msg`Hi {name}`, +values: { name } })`. `i18n._(descriptor, values)` does NOT exist in v6. + +Homographs: when the same English word means different things in different +places (e.g. "Home" = nav screen vs. home team), give one of them a context so +translators get separate entries: `t({ message: "Home", context: "match team" })` +— Spanish needs "Inicio" vs "Local" there. Same-spelling msgids silently share +one translation otherwise. + +Never wrap: logger/console output, query keys, route paths, URLs, CSS values, +PB collection/field names, ids/slugs, `===`-compared values, object keys, +brand names FLXN/Flexxon/Spotify, data-derived names, thrown errors that are +only logged (mapKnownError discards their text — only the ErrorType map's +strings reach users). + +## Architecture invariants (violating these breaks the app, not just a string) + +- **Provider order**: `LinguiProvider` must stay ABOVE `AuthProvider` + (`src/features/core/components/providers.tsx`). The localized query hooks + (`useServerQuery`/`useMe`/etc.) call `useLingui`, so anything using them — + including AuthProvider — must render inside it. LinguiProvider therefore + reads the auth query with raw `useQuery`, never `useAuth`/`useMe`. +- **`__root` head() must not read `match`/context** — doing so creates + circular route-type inference that breaks `beforeLoad` typing in child + routes. Meta stays DEFAULT_LOCALE (crawlers have no session anyway). +- **Babel order** in vite.config.ts: lingui macro plugin BEFORE + react-compiler. Reversed order fails the build on ``. +- Catalog loading: `__root` beforeLoad preloads the active locale on authed + routes; `LinguiProvider` self-loads a missing catalog client-side (public + routes). Unauthenticated non-en visitors get an English SSR first paint on + /login that corrects after hydration — known, accepted. +- `useMemo`/`useCallback` bodies that CACHE a computed string from `t`/`i18n` + must list them in deps. (Callbacks that merely *call* `t` when invoked read + the live locale and are safe.) +- Tab labels: `SwipeableTabs` items need a stable `value` slug; `label` is + the translated display string. + +## Catalog workflow — run after any string change + +1. `bun run extract` — regenerates ALL locale .po files from source, adds new + msgids, removes stale ones. Never hand-edit msgids or en.po. +2. Translate the new empty `msgstr ""` entries in es/de/ja. Spawn one + translation agent per locale (batch ~60 entries; have them RETURN the + translated entries and merge by msgid — don't let parallel agents edit one + file). Translation rules for agents: + - msgid byte-identical; translate msgstr only, single line. + - Preserve ICU placeholders/plural structure (translate only the words + inside branches) and `<0>`/`` tags exactly. + - Watch invisible characters: msgids can contain non-breaking spaces + (\xa0) that agents silently normalize — verify by byte comparison, + not visual. + - Registers: es = informal tú (Mexican), de = informal du, ja = + polite-casual です/ます. FLXN/Flexxon/Spotify untranslated. +3. `bun run i18n:check` — fails if any locale has missing translations. CI + (`.gitea/workflows/ci-cd.yaml`, `i18n-check` job) enforces this AND fails + if committed catalogs are stale vs. source — always commit `src/locales` + with string changes. +4. `bun run build` runs extract automatically, so a forgotten extract can't + ship hash-ids — but only step 3 catches untranslated entries. + +Production strips English defaults from components; text lives only in the +hash-keyed compiled catalog, so a msgid missing from the catalog renders as +its hash. Non-en catalogs are separate lazy chunks — English users download +nothing extra. diff --git a/.gitea/workflows/ci-cd.yaml b/.gitea/workflows/ci-cd.yaml index 955bfcb..5346ad6 100644 --- a/.gitea/workflows/ci-cd.yaml +++ b/.gitea/workflows/ci-cd.yaml @@ -7,9 +7,34 @@ on: - main jobs: + i18n-check: + name: i18n Catalog Check + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Bun + uses: oven-sh/setup-bun@v2 + + - name: Install dependencies + run: bun install --frozen-lockfile + + - name: Catalogs in sync with source + run: | + bunx lingui extract --clean + git diff --exit-code src/locales || { + echo "::error::Locale catalogs are stale — run 'bun run extract' and commit src/locales" + exit 1 + } + + - name: No missing translations + run: bunx lingui compile --strict + build-app: name: Build and Push App Docker Image runs-on: ubuntu-latest + needs: i18n-check steps: - name: Checkout code uses: actions/checkout@v4 diff --git a/.gitignore b/.gitignore index 7618350..ee34e6c 100644 --- a/.gitignore +++ b/.gitignore @@ -20,4 +20,6 @@ yarn.lock /_scripts/ /pb_data/ /.tanstack/ -/dist/ \ No newline at end of file +/dist/ +# lingui compile output (i18n:check); runtime uses .po via vite plugin +src/locales/*/messages.js diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..00968b1 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,23 @@ +# FLXN + +## i18n (Lingui v6) — applies to every user-facing string + +Any change that adds, edits, or removes user-visible text must follow the +`i18n` skill (`.claude/skills/i18n/SKILL.md`): wrap with context-bound Lingui +macros, then `bun run extract`, translate new entries in every non-`en` +locale, and `bun run i18n:check`. Adding a language: use the `add-language` +skill. + +Hard rules (full detail in the skill): + +- Only `Trans`/`Plural`/`useLingui` from `@lingui/react/macro` and `msg` from + `@lingui/core/macro`. Never global `t`/`plural`/`select` from core/macro + (SSR locale bleed). +- `useLingui()` at top level of components/hooks only; add `t`/`i18n` to memo + dep arrays. +- Never hand-edit msgids or `src/locales/en/messages.po` (generated). +- vite.config.ts babel plugin order (lingui before react-compiler) is + load-bearing. +- `LinguiProvider` stays ABOVE `AuthProvider` in providers.tsx (localized + query hooks call useLingui), and `__root`'s `head()` must never read + `match`/context (circular route-type inference breaks beforeLoad typing). diff --git a/bun.lock b/bun.lock index 8bff1b0..231b5ea 100644 --- a/bun.lock +++ b/bun.lock @@ -5,6 +5,8 @@ "name": "tanstack-start-example-basic-react-query", "dependencies": { "@hello-pangea/dnd": "^18.0.1", + "@lingui/core": "^6.5.0", + "@lingui/react": "^6.5.0", "@mantine/carousel": "^8.2.4", "@mantine/core": "^8.2.4", "@mantine/dates": "^8.2.4", @@ -42,6 +44,9 @@ "zod": "^4.0.15", }, "devDependencies": { + "@lingui/babel-plugin-lingui-macro": "^6.5.0", + "@lingui/cli": "^6.5.0", + "@lingui/vite-plugin": "^6.5.0", "@types/node": "^22.5.4", "@types/react": "^19.2.17", "@types/react-dom": "^19.2.3", @@ -70,7 +75,7 @@ "@babel/core": ["@babel/core@7.29.0", "", { "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", "@babel/helper-compilation-targets": "^7.28.6", "@babel/helper-module-transforms": "^7.28.6", "@babel/helpers": "^7.28.6", "@babel/parser": "^7.29.0", "@babel/template": "^7.28.6", "@babel/traverse": "^7.29.0", "@babel/types": "^7.29.0", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA=="], - "@babel/generator": ["@babel/generator@7.29.1", "", { "dependencies": { "@babel/parser": "^7.29.0", "@babel/types": "^7.29.0", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw=="], + "@babel/generator": ["@babel/generator@7.29.7", "", { "dependencies": { "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ=="], "@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.29.7", "", { "dependencies": { "@babel/types": "^7.29.7" } }, "sha512-OoK6239jHPuSQOoS0kfTVKn0b/rVTk0seKq4Gd2UMLtmOVLjDC0ki3e+c90Trqv2gMfvJFqkiljrr568+qddiw=="], @@ -100,9 +105,9 @@ "@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.29.7", "", { "dependencies": { "@babel/traverse": "^7.29.7", "@babel/types": "^7.29.7" } }, "sha512-brcMGQaVzIeUb+6/bs1Av0f8YuNNjKY2JyvfRCsFuFsdKccEQ5Ges2y74D74NZ1Rz8lKJ9ksJkfqwQFJ/iNEyQ=="], - "@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="], + "@babel/helper-string-parser": ["@babel/helper-string-parser@7.29.7", "", {}, "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw=="], - "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="], + "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="], "@babel/helper-validator-option": ["@babel/helper-validator-option@7.29.7", "", {}, "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw=="], @@ -110,7 +115,7 @@ "@babel/helpers": ["@babel/helpers@7.28.6", "", { "dependencies": { "@babel/template": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw=="], - "@babel/parser": ["@babel/parser@7.29.0", "", { "dependencies": { "@babel/types": "^7.29.0" }, "bin": "./bin/babel-parser.js" }, "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww=="], + "@babel/parser": ["@babel/parser@7.29.7", "", { "dependencies": { "@babel/types": "^7.29.7" }, "bin": "./bin/babel-parser.js" }, "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg=="], "@babel/plugin-bugfix-firefox-class-in-computed-class-key": ["@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.29.7", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.29.7", "@babel/traverse": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-j8SrR0zLZrRsC09DlszEx8FpMiwukKffYXMK0d5LmOglO7vGG6sz/BR/20yHqWH+Lnn31JTt2PE3hIWNgM2J6w=="], @@ -250,7 +255,9 @@ "@babel/traverse": ["@babel/traverse@7.29.0", "", { "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.29.0", "@babel/template": "^7.28.6", "@babel/types": "^7.29.0", "debug": "^4.3.1" } }, "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA=="], - "@babel/types": ["@babel/types@7.29.0", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A=="], + "@babel/types": ["@babel/types@7.29.7", "", { "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" } }, "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA=="], + + "@colors/colors": ["@colors/colors@1.5.0", "", {}, "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ=="], "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.3", "", { "os": "aix", "cpu": "ppc64" }, "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg=="], @@ -318,6 +325,10 @@ "@isaacs/cliui": ["@isaacs/cliui@9.0.0", "", {}, "sha512-AokJm4tuBHillT+FpMtxQ60n8ObyXBatq7jD2/JA9dxbDDokKQm8KMht5ibGzLVU9IJDIKK4TPKgMHEYMn3lMg=="], + "@jest/schemas": ["@jest/schemas@29.6.3", "", { "dependencies": { "@sinclair/typebox": "^0.27.8" } }, "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA=="], + + "@jest/types": ["@jest/types@29.6.3", "", { "dependencies": { "@jest/schemas": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", "@types/istanbul-reports": "^3.0.0", "@types/node": "*", "@types/yargs": "^17.0.8", "chalk": "^4.0.0" } }, "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw=="], + "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="], "@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="], @@ -330,6 +341,24 @@ "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], + "@lingui/babel-plugin-extract-messages": ["@lingui/babel-plugin-extract-messages@6.5.0", "", { "dependencies": { "@lingui/conf": "6.5.0" } }, "sha512-ZvHG9eekHvfr+qErKgfPG/twfpXfMIiNSQk7iHKr/eSaeUP/c+Syuysx7s5I4WbkZlnzYjovp1K1iS7VHdX93Q=="], + + "@lingui/babel-plugin-lingui-macro": ["@lingui/babel-plugin-lingui-macro@6.5.0", "", { "dependencies": { "@babel/core": "^7.20.12", "@babel/types": "^7.20.7", "@lingui/conf": "6.5.0", "@lingui/message-utils": "6.5.0" } }, "sha512-p80TT6WMGXpFnAe9nP9ad/BJsADBxBvTa1QFy07tZqpg/5G5eOxUn641koQjHHr/EQ3yNstLohPV9aAVoKKsiA=="], + + "@lingui/cli": ["@lingui/cli@6.5.0", "", { "dependencies": { "@babel/core": "^7.21.0", "@babel/generator": "^7.28.5", "@babel/parser": "^7.22.0", "@babel/types": "^7.21.2", "@lingui/babel-plugin-extract-messages": "6.5.0", "@lingui/babel-plugin-lingui-macro": "6.5.0", "@lingui/conf": "6.5.0", "@lingui/core": "6.5.0", "@lingui/format-po": "6.5.0", "@lingui/message-utils": "6.5.0", "chokidar": "5.0.0", "cli-table3": "^0.6.5", "commander": "^14.0.2", "jiti": "^2.6.1", "micromatch": "^4.0.7", "ms": "^2.1.3", "normalize-path": "^3.0.0", "ora": "^9.1.0", "pseudolocale": "^2.2.0", "source-map": "^0.7.6", "tinypool": "^2.1.0" }, "peerDependencies": { "esbuild": "^0.28.1", "rolldown": "^1.0.0" }, "optionalPeers": ["esbuild", "rolldown"], "bin": { "lingui": "dist/lingui.js" } }, "sha512-aTvggRa8yUHT6tNmkWuJfNehO/gkShiQA0UTn02hNSTIqqfh3y4avsHiJGFBAEmFx/zpvJ+XcHtZDhTtNMojhA=="], + + "@lingui/conf": ["@lingui/conf@6.5.0", "", { "dependencies": { "jest-validate": "^29.4.3", "jiti": "^2.5.1", "lilconfig": "^3.1.3", "normalize-path": "^3.0.0" } }, "sha512-zfR4uuzev2mz9ayVB2AwEQlzodDKgMcq/OR3pdHiJFCl80UQRsFA5Oqe67TmxA7SSbTygK+U0nS0qrotmXjyrA=="], + + "@lingui/core": ["@lingui/core@6.5.0", "", { "dependencies": { "@lingui/babel-plugin-lingui-macro": "6.5.0", "@lingui/message-utils": "6.5.0" }, "peerDependencies": { "babel-plugin-macros": "2 || 3" }, "optionalPeers": ["babel-plugin-macros"] }, "sha512-zAoD/fIMNqtgvH15BJBtyHSMRssnMSumCEggj4c4kGYU0hh5rWn1JoOw/xLbA0Xe2iH4oBG9/SDnvVjJ4VvWrA=="], + + "@lingui/format-po": ["@lingui/format-po@6.5.0", "", { "dependencies": { "@lingui/conf": "6.5.0", "@lingui/message-utils": "6.5.0", "pofile-ts": "^4.0.3" } }, "sha512-jK2JhYpKuDMG3VP8T0W6X5SL0GYRkeaTOlX3EiRggDWNJqc6wFxJP0vTZScMlyBXHC+v0OUxy+4Ja+9kVJwKsQ=="], + + "@lingui/message-utils": ["@lingui/message-utils@6.5.0", "", { "dependencies": { "@messageformat/date-skeleton": "^1.1.0", "@messageformat/parser": "^5.0.0", "js-sha256": "^0.10.1" } }, "sha512-qZZijYERMADeWVJbpQGyFxicW3F17CjtR2hnzo0VgT4cICPE27e0zBieQHxuUD/pj4dmyOOldVhWhHs5CN6ABw=="], + + "@lingui/react": ["@lingui/react@6.5.0", "", { "dependencies": { "@lingui/babel-plugin-lingui-macro": "6.5.0", "@lingui/core": "6.5.0" }, "peerDependencies": { "babel-plugin-macros": "2 || 3", "react": "^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" }, "optionalPeers": ["babel-plugin-macros"] }, "sha512-Q2jd82chqbCrQAPu8MNl+/PwSNumehWbH2AxXGFNq1/w1v0T3dgbfOXOtQ2sNd0Fq+a5gkuyul8RWbW1CDYw7Q=="], + + "@lingui/vite-plugin": ["@lingui/vite-plugin@6.5.0", "", { "dependencies": { "@lingui/cli": "6.5.0", "@lingui/conf": "6.5.0" }, "peerDependencies": { "@babel/core": "^7.29.0 || ^8.0.0-rc.1", "@lingui/babel-plugin-lingui-macro": "^5 || ^6", "@rolldown/plugin-babel": "^0.1.7 || ^0.2.0", "rolldown": "^1.0.0-rc.5", "vite": "^6.3.0 || ^7 || ^8" }, "optionalPeers": ["@babel/core", "@lingui/babel-plugin-lingui-macro", "@rolldown/plugin-babel", "rolldown"] }, "sha512-mWiMnG0dTXofzRDbi5KrAMQRQQCk70YtKebYSmkA1rmVaLLdmBuGKvvh1kJYCFyM8hxtpLvqaFDebJvG9MUjXQ=="], + "@mantine/carousel": ["@mantine/carousel@8.3.14", "", { "peerDependencies": { "@mantine/core": "8.3.14", "@mantine/hooks": "8.3.14", "embla-carousel": ">=8.0.0", "embla-carousel-react": ">=8.0.0", "react": "^18.x || ^19.x", "react-dom": "^18.x || ^19.x" } }, "sha512-1RAgUkeRFhuPnbwOXnF2pEEqD7iYCgkUDpFDsGzBNuX2SQt2MkXolCn/sdcGg4nWGhl7iqaWzR/YcZeg/TlXIQ=="], "@mantine/core": ["@mantine/core@8.3.14", "", { "dependencies": { "@floating-ui/react": "^0.27.16", "clsx": "^2.1.1", "react-number-format": "^5.4.4", "react-remove-scroll": "^2.7.1", "react-textarea-autosize": "8.5.9", "type-fest": "^4.41.0" }, "peerDependencies": { "@mantine/hooks": "8.3.14", "react": "^18.x || ^19.x", "react-dom": "^18.x || ^19.x" } }, "sha512-ZOxggx65Av1Ii1NrckCuqzluRpmmG+8DyEw24wDom3rmwsPg9UV+0le2QTyI5Eo60LzPfUju1KuEPiUzNABIPg=="], @@ -342,6 +371,10 @@ "@mantine/tiptap": ["@mantine/tiptap@8.3.14", "", { "peerDependencies": { "@mantine/core": "8.3.14", "@mantine/hooks": "8.3.14", "@tiptap/extension-link": ">=2.1.12", "@tiptap/react": ">=2.1.12", "react": "^18.x || ^19.x", "react-dom": "^18.x || ^19.x" } }, "sha512-M7z5Jeyt5uT1TKq8UB40zJAQx9whzmDSmI4iUhBuBioV/XKq/QmXTrNe9v/shFO6fixqU3f37NGJ3YdZP17Iog=="], + "@messageformat/date-skeleton": ["@messageformat/date-skeleton@1.1.0", "", {}, "sha512-rmGAfB1tIPER+gh3p/RgA+PVeRE/gxuQ2w4snFWPF5xtb5mbWR7Cbw7wCOftcUypbD6HVoxrVdyyghPm3WzP5A=="], + + "@messageformat/parser": ["@messageformat/parser@5.1.1", "", { "dependencies": { "moo": "^0.5.1" } }, "sha512-3p0YRGCcTUCYvBKLIxtDDyrJ0YijGIwrTRu1DT8gIviIDZru8H23+FkY6MJBzM1n9n20CiM4VeDYuBsrrwnLjg=="], + "@oozcitak/dom": ["@oozcitak/dom@2.0.2", "", { "dependencies": { "@oozcitak/infra": "^2.0.2", "@oozcitak/url": "^3.0.0", "@oozcitak/util": "^10.0.0" } }, "sha512-GjpKhkSYC3Mj4+lfwEyI1dqnsKTgwGy48ytZEhm4A/xnH/8z9M3ZVXKr/YGQi3uCLs1AEBS+x5T2JPiueEDW8w=="], "@oozcitak/infra": ["@oozcitak/infra@2.0.2", "", { "dependencies": { "@oozcitak/util": "^10.0.0" } }, "sha512-2g+E7hoE2dgCz/APPOEK5s3rMhJvNxSMBrP+U+j1OWsIbtSpWxxlUjq1lU8RIsFJNYv7NMlnVsCuHcUzJW+8vA=="], @@ -452,6 +485,8 @@ "@simplewebauthn/browser": ["@simplewebauthn/browser@13.2.2", "", {}, "sha512-FNW1oLQpTJyqG5kkDg5ZsotvWgmBaC6jCHR7Ej0qUNep36Wl9tj2eZu7J5rP+uhXgHaLk+QQ3lqcw2vS5MX1IA=="], + "@sinclair/typebox": ["@sinclair/typebox@0.27.12", "", {}, "sha512-hhyNJ+nbR6ZR7pToHvllEFun9TL0sbL+tk/ON75lo+Xas054uez98qRbsuNt7MBCyZKK4+8Yli/OAGZhmfBZ/g=="], + "@solid-primitives/event-listener": ["@solid-primitives/event-listener@2.4.3", "", { "dependencies": { "@solid-primitives/utils": "^6.3.2" }, "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-h4VqkYFv6Gf+L7SQj+Y6puigL/5DIi7x5q07VZET7AWcS+9/G3WfIE9WheniHWJs51OEkRB43w6lDys5YeFceg=="], "@solid-primitives/keyboard": ["@solid-primitives/keyboard@1.3.3", "", { "dependencies": { "@solid-primitives/event-listener": "^2.4.3", "@solid-primitives/rootless": "^1.5.2", "@solid-primitives/utils": "^6.3.2" }, "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-9dQHTTgLBqyAI7aavtO+HnpTVJgWQA1ghBSrmLtMu1SMxLPDuLfuNr+Tk5udb4AL4Ojg7h9JrKOGEEDqsJXWJA=="], @@ -598,6 +633,12 @@ "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], + "@types/istanbul-lib-coverage": ["@types/istanbul-lib-coverage@2.0.6", "", {}, "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w=="], + + "@types/istanbul-lib-report": ["@types/istanbul-lib-report@3.0.3", "", { "dependencies": { "@types/istanbul-lib-coverage": "*" } }, "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA=="], + + "@types/istanbul-reports": ["@types/istanbul-reports@3.0.4", "", { "dependencies": { "@types/istanbul-lib-report": "*" } }, "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ=="], + "@types/linkify-it": ["@types/linkify-it@5.0.0", "", {}, "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q=="], "@types/markdown-it": ["@types/markdown-it@14.1.2", "", { "dependencies": { "@types/linkify-it": "^5", "@types/mdurl": "^2" } }, "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog=="], @@ -618,6 +659,10 @@ "@types/web-push": ["@types/web-push@3.6.4", "", { "dependencies": { "@types/node": "*" } }, "sha512-GnJmSr40H3RAnj0s34FNTcJi1hmWFV5KXugE0mYWnYhgTAHLJ/dJKAwDmvPJYMke0RplY2XE9LnM4hqSqKIjhQ=="], + "@types/yargs": ["@types/yargs@17.0.35", "", { "dependencies": { "@types/yargs-parser": "*" } }, "sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg=="], + + "@types/yargs-parser": ["@types/yargs-parser@21.0.3", "", {}, "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ=="], + "@vitejs/plugin-react": ["@vitejs/plugin-react@5.1.3", "", { "dependencies": { "@babel/core": "^7.29.0", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-rc.2", "@types/babel__core": "^7.20.5", "react-refresh": "^0.18.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-NVUnA6gQCl8jfoYqKqQU5Clv0aPw14KkZYCsX6T9Lfu9slI0LOU10OTwFHS/WmptsMMpshNd/1tuWsHQ2Uk+cg=="], "acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="], @@ -626,6 +671,10 @@ "ajv": ["ajv@8.20.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA=="], + "ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + + "ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], + "ansis": ["ansis@4.2.0", "", {}, "sha512-HqZ5rWlFjGiV0tDm3UxxgNRqsOTniqoKZu0pIAfh7TZQMGuZK+hH0drySty0si0QXj1ieop4+SkSfPZBPPkHig=="], "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], @@ -672,6 +721,8 @@ "brace-expansion": ["brace-expansion@5.0.7", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA=="], + "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], + "browser-image-compression": ["browser-image-compression@2.0.2", "", { "dependencies": { "uzip": "0.20201231.0" } }, "sha512-pBLlQyUf6yB8SmmngrcOw3EoS4RpQ1BcylI3T9Yqn7+4nrQTXJD4sJDe5ODnJdrvNMaio5OicFo75rDyJD2Ucw=="], "browser-tabs-lock": ["browser-tabs-lock@1.3.0", "", { "dependencies": { "lodash": ">=4.17.21" } }, "sha512-g6nHaobTiT0eMZ7jh16YpD2kcjAp+PInbiVq3M1x6KKaEIVhT4v9oURNIpZLOZ3LQbQ3XYfNhMAb/9hzNLIWrw=="], @@ -692,17 +743,31 @@ "call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="], + "camelcase": ["camelcase@6.3.0", "", {}, "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA=="], + "camelcase-css": ["camelcase-css@2.0.1", "", {}, "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA=="], "caniuse-lite": ["caniuse-lite@1.0.30001769", "", {}, "sha512-BCfFL1sHijQlBGWBMuJyhZUhzo7wer5sVj9hqekB/7xn0Ypy+pER/edCYQm4exbXj4WiySGp40P8UuTh6w1srg=="], + "chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="], + "chokidar": ["chokidar@5.0.0", "", { "dependencies": { "readdirp": "^5.0.0" } }, "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw=="], + "cli-cursor": ["cli-cursor@5.0.0", "", { "dependencies": { "restore-cursor": "^5.0.0" } }, "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw=="], + + "cli-spinners": ["cli-spinners@3.4.0", "", {}, "sha512-bXfOC4QcT1tKXGorxL3wbJm6XJPDqEnij2gQ2m7ESQuE+/z9YFIWnl/5RpTiKWbMq3EVKR4fRLJGn6DVfu0mpw=="], + + "cli-table3": ["cli-table3@0.6.5", "", { "dependencies": { "string-width": "^4.2.0" }, "optionalDependencies": { "@colors/colors": "1.5.0" } }, "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ=="], + "clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="], + "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], + + "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], + "combined-stream": ["combined-stream@1.0.8", "", { "dependencies": { "delayed-stream": "~1.0.0" } }, "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg=="], - "commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="], + "commander": ["commander@14.0.3", "", {}, "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw=="], "common-tags": ["common-tags@1.8.2", "", {}, "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA=="], @@ -778,6 +843,8 @@ "embla-carousel-reactive-utils": ["embla-carousel-reactive-utils@8.6.0", "", { "peerDependencies": { "embla-carousel": "8.6.0" } }, "sha512-fMVUDUEx0/uIEDM0Mz3dHznDhfX+znCCDCeIophYb1QGVM7YThSWX+wz11zlYwWFOr74b4QLGg0hrGPJeG2s4A=="], + "emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], + "entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="], "es-abstract": ["es-abstract@1.24.2", "", { "dependencies": { "array-buffer-byte-length": "^1.0.2", "arraybuffer.prototype.slice": "^1.0.4", "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "call-bound": "^1.0.4", "data-view-buffer": "^1.0.2", "data-view-byte-length": "^1.0.2", "data-view-byte-offset": "^1.0.1", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "es-set-tostringtag": "^2.1.0", "es-to-primitive": "^1.3.0", "function.prototype.name": "^1.1.8", "get-intrinsic": "^1.3.0", "get-proto": "^1.0.1", "get-symbol-description": "^1.1.0", "globalthis": "^1.0.4", "gopd": "^1.2.0", "has-property-descriptors": "^1.0.2", "has-proto": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "internal-slot": "^1.1.0", "is-array-buffer": "^3.0.5", "is-callable": "^1.2.7", "is-data-view": "^1.0.2", "is-negative-zero": "^2.0.3", "is-regex": "^1.2.1", "is-set": "^2.0.3", "is-shared-array-buffer": "^1.0.4", "is-string": "^1.1.1", "is-typed-array": "^1.1.15", "is-weakref": "^1.1.1", "math-intrinsics": "^1.1.0", "object-inspect": "^1.13.4", "object-keys": "^1.1.1", "object.assign": "^4.1.7", "own-keys": "^1.0.1", "regexp.prototype.flags": "^1.5.4", "safe-array-concat": "^1.1.3", "safe-push-apply": "^1.0.0", "safe-regex-test": "^1.1.0", "set-proto": "^1.0.0", "stop-iteration-iterator": "^1.1.0", "string.prototype.trim": "^1.2.10", "string.prototype.trimend": "^1.0.9", "string.prototype.trimstart": "^1.0.8", "typed-array-buffer": "^1.0.3", "typed-array-byte-length": "^1.0.3", "typed-array-byte-offset": "^1.0.4", "typed-array-length": "^1.0.7", "unbox-primitive": "^1.1.0", "which-typed-array": "^1.1.19" } }, "sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg=="], @@ -824,6 +891,8 @@ "filelist": ["filelist@1.0.6", "", { "dependencies": { "minimatch": "^5.0.1" } }, "sha512-5giy2PkLYY1cP39p17Ech+2xlpTRL9HLspOfEgm0L6CwBXBTgsK5ou0JtzYuepxkaQ/tvhCFIJ5uXo0OrM2DxA=="], + "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="], + "follow-redirects": ["follow-redirects@1.15.11", "", {}, "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ=="], "for-each": ["for-each@0.3.5", "", { "dependencies": { "is-callable": "^1.2.7" } }, "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg=="], @@ -850,6 +919,8 @@ "gensync": ["gensync@1.0.0-beta.2", "", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="], + "get-east-asian-width": ["get-east-asian-width@1.6.0", "", {}, "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA=="], + "get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="], "get-nonce": ["get-nonce@1.0.1", "", {}, "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q=="], @@ -878,6 +949,8 @@ "has-bigints": ["has-bigints@1.1.0", "", {}, "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg=="], + "has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="], + "has-property-descriptors": ["has-property-descriptors@1.0.2", "", { "dependencies": { "es-define-property": "^1.0.0" } }, "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg=="], "has-proto": ["has-proto@1.2.0", "", { "dependencies": { "dunder-proto": "^1.0.0" } }, "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ=="], @@ -922,14 +995,20 @@ "is-finalizationregistry": ["is-finalizationregistry@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3" } }, "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg=="], + "is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="], + "is-generator-function": ["is-generator-function@1.1.2", "", { "dependencies": { "call-bound": "^1.0.4", "generator-function": "^2.0.0", "get-proto": "^1.0.1", "has-tostringtag": "^1.0.2", "safe-regex-test": "^1.1.0" } }, "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA=="], + "is-interactive": ["is-interactive@2.0.0", "", {}, "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ=="], + "is-map": ["is-map@2.0.3", "", {}, "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw=="], "is-module": ["is-module@1.0.0", "", {}, "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g=="], "is-negative-zero": ["is-negative-zero@2.0.3", "", {}, "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw=="], + "is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="], + "is-number-object": ["is-number-object@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" } }, "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw=="], "is-obj": ["is-obj@1.0.1", "", {}, "sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg=="], @@ -950,6 +1029,8 @@ "is-typed-array": ["is-typed-array@1.1.15", "", { "dependencies": { "which-typed-array": "^1.1.16" } }, "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ=="], + "is-unicode-supported": ["is-unicode-supported@2.1.0", "", {}, "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ=="], + "is-weakmap": ["is-weakmap@2.0.2", "", {}, "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w=="], "is-weakref": ["is-weakref@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3" } }, "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew=="], @@ -966,10 +1047,16 @@ "jake": ["jake@10.9.4", "", { "dependencies": { "async": "^3.2.6", "filelist": "^1.0.4", "picocolors": "^1.1.1" }, "bin": { "jake": "bin/cli.js" } }, "sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA=="], + "jest-get-type": ["jest-get-type@29.6.3", "", {}, "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw=="], + + "jest-validate": ["jest-validate@29.7.0", "", { "dependencies": { "@jest/types": "^29.6.3", "camelcase": "^6.2.0", "chalk": "^4.0.0", "jest-get-type": "^29.6.3", "leven": "^3.1.0", "pretty-format": "^29.7.0" } }, "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw=="], + "jiti": ["jiti@2.7.0", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ=="], "jose": ["jose@4.15.9", "", {}, "sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA=="], + "js-sha256": ["js-sha256@0.10.1", "", {}, "sha512-5obBtsz9301ULlsgggLg542s/jqtddfOpV5KJc4hajc9JV8GeY2gZHSVpYBn4nWqAUTJ9v+xwtbJ1mIBgIH5Vw=="], + "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], "js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="], @@ -1020,6 +1107,8 @@ "lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.32.0", "", { "os": "win32", "cpu": "x64" }, "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q=="], + "lilconfig": ["lilconfig@3.1.3", "", {}, "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw=="], + "linkify-it": ["linkify-it@5.0.0", "", { "dependencies": { "uc.micro": "^2.0.0" } }, "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ=="], "linkifyjs": ["linkifyjs@4.3.2", "", {}, "sha512-NT1CJtq3hHIreOianA8aSXn6Cw0JzYOuDQbOrSPe7gqFnCpKP++MQe3ODgO3oh2GJFORkAAdqredOa60z63GbA=="], @@ -1044,6 +1133,8 @@ "lodash.sortby": ["lodash.sortby@4.7.0", "", {}, "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA=="], + "log-symbols": ["log-symbols@7.0.1", "", { "dependencies": { "is-unicode-supported": "^2.0.0", "yoctocolors": "^2.1.1" } }, "sha512-ja1E3yCr9i/0hmBVaM0bfwDjnGy8I/s6PP4DFp+yP+a+mrHO4Rm7DtmnqROTUkHIkqffC84YY7AeqX6oFk0WFg=="], + "loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": { "loose-envify": "cli.js" } }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="], "lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="], @@ -1056,10 +1147,14 @@ "mdurl": ["mdurl@2.0.0", "", {}, "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w=="], + "micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="], + "mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], "mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], + "mimic-function": ["mimic-function@5.0.1", "", {}, "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA=="], + "minimalistic-assert": ["minimalistic-assert@1.0.1", "", {}, "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A=="], "minimatch": ["minimatch@10.2.5", "", { "dependencies": { "brace-expansion": "^5.0.5" } }, "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg=="], @@ -1068,6 +1163,8 @@ "minipass": ["minipass@7.1.3", "", {}, "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A=="], + "moo": ["moo@0.5.3", "", {}, "sha512-m2fmM2dDm7GZQsY7KK2cme8agi+AAljILjQnof7p1ZMDe6dQ4bdnSMx0cPppudoeNv5hEFQirN6u+O4fDE0IWA=="], + "motion-dom": ["motion-dom@12.33.0", "", { "dependencies": { "motion-utils": "^12.29.2" } }, "sha512-XRPebVypsl0UM+7v0Hr8o9UAj0S2djsQWRdHBd5iVouVpMrQqAI0C/rDAT3QaYnXnHuC5hMcwDHCboNeyYjPoQ=="], "motion-utils": ["motion-utils@12.29.2", "", {}, "sha512-G3kc34H2cX2gI63RqU+cZq+zWRRPSsNIOjpdl9TN4AQwC4sgwYPl/Q/Obf/d53nOm569T0fYK+tcoSV50BWx8A=="], @@ -1082,6 +1179,8 @@ "nodemailer": ["nodemailer@6.10.1", "", {}, "sha512-Z+iLaBGVaSjbIzQ4pX6XV41HrooLsQ10ZWPUehGmuantvzWoDVBnmsdUcOIDM1t+yPor5pDhVlDESgOMEGxhHA=="], + "normalize-path": ["normalize-path@3.0.0", "", {}, "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="], + "object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="], "object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="], @@ -1090,6 +1189,10 @@ "object.assign": ["object.assign@4.1.7", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0", "has-symbols": "^1.1.0", "object-keys": "^1.1.1" } }, "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw=="], + "onetime": ["onetime@7.0.0", "", { "dependencies": { "mimic-function": "^5.0.0" } }, "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ=="], + + "ora": ["ora@9.4.1", "", { "dependencies": { "chalk": "^5.6.2", "cli-cursor": "^5.0.0", "cli-spinners": "^3.2.0", "is-interactive": "^2.0.0", "is-unicode-supported": "^2.1.0", "log-symbols": "^7.0.1", "stdin-discarder": "^0.3.2", "string-width": "^8.1.0" } }, "sha512-6VlU9MLXbjVQD04AZCMX28hVtA5bUoadvUqO76MUCVA0ilwJbMiHsITRPfyVm6p/BC0Av/BXMujx39WCe1LEqw=="], + "orderedmap": ["orderedmap@2.1.1", "", {}, "sha512-TvAWxi0nDe1j/rtMcWcIj94+Ffe6n7zhow33h40SKxmsmozs6dz/e+EajymfoFcHd7sxNn8yHM8839uixMOV6g=="], "own-keys": ["own-keys@1.0.1", "", { "dependencies": { "get-intrinsic": "^1.2.6", "object-keys": "^1.1.1", "safe-push-apply": "^1.0.0" } }, "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg=="], @@ -1114,6 +1217,8 @@ "pocketbase": ["pocketbase@0.26.8", "", {}, "sha512-aQ/ewvS7ncvAE8wxoW10iAZu6ElgbeFpBhKPnCfvRovNzm2gW8u/sQNPGN6vNgVEagz44kK//C61oKjfa+7Low=="], + "pofile-ts": ["pofile-ts@4.0.3", "", {}, "sha512-sz1pnjgEfPyZ+QvaeX3NtCmbYnEvG01LZRLoN/uXoLtPZtxCIH5IctL7yXXc0fFyk/fqV6K8g3hlNfr6IJwupA=="], + "possible-typed-array-names": ["possible-typed-array-names@1.1.0", "", {}, "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg=="], "postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="], @@ -1136,6 +1241,8 @@ "pretty-bytes": ["pretty-bytes@5.6.0", "", {}, "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg=="], + "pretty-format": ["pretty-format@29.7.0", "", { "dependencies": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", "react-is": "^18.0.0" } }, "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ=="], + "process": ["process@0.11.10", "", {}, "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A=="], "prop-types": ["prop-types@15.8.1", "", { "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", "react-is": "^16.13.1" } }, "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg=="], @@ -1178,6 +1285,8 @@ "proxy-from-env": ["proxy-from-env@1.1.0", "", {}, "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="], + "pseudolocale": ["pseudolocale@2.2.0", "", { "dependencies": { "commander": "^10.0.0" }, "bin": { "pseudolocale": "dist/cli.mjs" } }, "sha512-O+D2eU7fO9wVLqrohvt9V/9fwMadnJQ4jxwiK+LeNEqhMx8JYx4xQHkArDCJFAdPPOp/pQq6z5L37eBvAoc8jw=="], + "punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="], "punycode.js": ["punycode.js@2.3.1", "", {}, "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA=="], @@ -1238,6 +1347,8 @@ "resolve-pkg-maps": ["resolve-pkg-maps@1.0.0", "", {}, "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="], + "restore-cursor": ["restore-cursor@5.1.0", "", { "dependencies": { "onetime": "^7.0.0", "signal-exit": "^4.1.0" } }, "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA=="], + "rollup": ["rollup@4.57.1", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.57.1", "@rollup/rollup-android-arm64": "4.57.1", "@rollup/rollup-darwin-arm64": "4.57.1", "@rollup/rollup-darwin-x64": "4.57.1", "@rollup/rollup-freebsd-arm64": "4.57.1", "@rollup/rollup-freebsd-x64": "4.57.1", "@rollup/rollup-linux-arm-gnueabihf": "4.57.1", "@rollup/rollup-linux-arm-musleabihf": "4.57.1", "@rollup/rollup-linux-arm64-gnu": "4.57.1", "@rollup/rollup-linux-arm64-musl": "4.57.1", "@rollup/rollup-linux-loong64-gnu": "4.57.1", "@rollup/rollup-linux-loong64-musl": "4.57.1", "@rollup/rollup-linux-ppc64-gnu": "4.57.1", "@rollup/rollup-linux-ppc64-musl": "4.57.1", "@rollup/rollup-linux-riscv64-gnu": "4.57.1", "@rollup/rollup-linux-riscv64-musl": "4.57.1", "@rollup/rollup-linux-s390x-gnu": "4.57.1", "@rollup/rollup-linux-x64-gnu": "4.57.1", "@rollup/rollup-linux-x64-musl": "4.57.1", "@rollup/rollup-openbsd-x64": "4.57.1", "@rollup/rollup-openharmony-arm64": "4.57.1", "@rollup/rollup-win32-arm64-msvc": "4.57.1", "@rollup/rollup-win32-ia32-msvc": "4.57.1", "@rollup/rollup-win32-x64-gnu": "4.57.1", "@rollup/rollup-win32-x64-msvc": "4.57.1", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-oQL6lgK3e2QZeQ7gcgIkS2YZPg5slw37hYufJ3edKlfQSGGm8ICoxswK15ntSzF/a8+h7ekRy7k7oWc3BQ7y8A=="], "rope-sequence": ["rope-sequence@1.3.4", "", {}, "sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ=="], @@ -1294,7 +1405,7 @@ "sonner": ["sonner@2.0.7", "", { "peerDependencies": { "react": "^18.0.0 || ^19.0.0 || ^19.0.0-rc", "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-rc" } }, "sha512-W6ZN4p58k8aDKA4XPcx2hpIQXBRAgyiWVkYhT7CvK6D3iAu7xjvVyhQHg2/iaKJZ1XVJ4r7XuwGL+WGEK37i9w=="], - "source-map": ["source-map@0.8.0-beta.0", "", { "dependencies": { "whatwg-url": "^7.0.0" } }, "sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA=="], + "source-map": ["source-map@0.7.6", "", {}, "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ=="], "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], @@ -1302,8 +1413,12 @@ "srvx": ["srvx@0.11.22", "", { "bin": { "srvx": "bin/srvx.mjs" } }, "sha512-LqZxxBDMKuMAZzFzJnDCkFOrs9MZQZr0LvHiO/SuSZVdQaXD7xQ5UWTUxheJrQPve1qk9MG2B/yttUvJxw8egQ=="], + "stdin-discarder": ["stdin-discarder@0.3.2", "", {}, "sha512-eCPu1qRxPVkl5605OTWF8Wz40b4Mf45NY5LQmVPQ599knfs5QhASUm9GbJ5BDMDOXgrnh0wyEdvzmL//YMlw0A=="], + "stop-iteration-iterator": ["stop-iteration-iterator@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "internal-slot": "^1.1.0" } }, "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ=="], + "string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], + "string.prototype.matchall": ["string.prototype.matchall@4.0.12", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-abstract": "^1.23.6", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", "get-intrinsic": "^1.2.6", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "internal-slot": "^1.1.0", "regexp.prototype.flags": "^1.5.3", "set-function-name": "^2.0.2", "side-channel": "^1.1.0" } }, "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA=="], "string.prototype.trim": ["string.prototype.trim@1.2.11", "", { "dependencies": { "call-bind": "^1.0.9", "call-bound": "^1.0.4", "define-data-property": "^1.1.4", "define-properties": "^1.2.1", "es-abstract": "^1.24.2", "es-object-atoms": "^1.1.2", "has-property-descriptors": "^1.0.2", "safe-regex-test": "^1.1.0" } }, "sha512-PwvK7BU+CMTJGYQCTZb5RWXIML92lftJLhQz1tBzgKiqGxJaMlBAa48POXaNAC2s4y8jr3EFqrkF9+44neS46w=="], @@ -1314,6 +1429,8 @@ "stringify-object": ["stringify-object@3.3.0", "", { "dependencies": { "get-own-enumerable-property-symbols": "^3.0.0", "is-obj": "^1.0.1", "is-regexp": "^1.0.0" } }, "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw=="], + "strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + "strip-comments": ["strip-comments@2.0.1", "", {}, "sha512-ZprKx+bBLXv067WTCALv8SSz5l2+XhpYCsVtSqlMnkAXMWDq+/ekVbl1ghqP9rUHTzv6sm/DwCOiYutU/yp1fw=="], "sugarss": ["sugarss@5.0.1", "", { "peerDependencies": { "postcss": "^8.3.3" } }, "sha512-ctS5RYCBVvPoZAnzIaX5QSShK8ZiZxD5HUqSxlusvEMC+QZQIPCPOIJg6aceFX+K2rf4+SH89eu++h1Zmsr2nw=="], @@ -1326,6 +1443,8 @@ "supertokens-website": ["supertokens-website@20.1.6", "", { "dependencies": { "browser-tabs-lock": "^1.3.0", "supertokens-js-override": "^0.0.4" } }, "sha512-WSehco2PsrFp4WY7h6tDutYyi2nPgJS8lahUadcL/cpBqgEuZ3pjnvN1NDASSNSTfXeO1smm3HrvvLZG5C7qHA=="], + "supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], + "supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="], "tabbable": ["tabbable@6.4.0", "", {}, "sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg=="], @@ -1340,10 +1459,14 @@ "tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], + "tinypool": ["tinypool@2.1.0", "", {}, "sha512-Pugqs6M0m7Lv1I7FtxN4aoyToKg1C4tu+/381vH35y8oENM/Ai7f7C4StcoK4/+BSw9ebcS8jRiVrORFKCALLw=="], + "tldts": ["tldts@6.1.86", "", { "dependencies": { "tldts-core": "^6.1.86" }, "bin": { "tldts": "bin/cli.js" } }, "sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ=="], "tldts-core": ["tldts-core@6.1.86", "", {}, "sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA=="], + "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], + "tr46": ["tr46@1.0.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA=="], "tsconfck": ["tsconfck@3.1.6", "", { "peerDependencies": { "typescript": "^5.0.0" }, "optionalPeers": ["typescript"], "bin": { "tsconfck": "bin/tsconfck.js" } }, "sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w=="], @@ -1478,9 +1601,17 @@ "yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="], + "yoctocolors": ["yoctocolors@2.1.2", "", {}, "sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug=="], + "zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], - "@babel/helper-annotate-as-pure/@babel/types": ["@babel/types@7.29.7", "", { "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" } }, "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA=="], + "@babel/code-frame/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="], + + "@babel/core/@babel/generator": ["@babel/generator@7.29.1", "", { "dependencies": { "@babel/parser": "^7.29.0", "@babel/types": "^7.29.0", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw=="], + + "@babel/core/@babel/parser": ["@babel/parser@7.29.0", "", { "dependencies": { "@babel/types": "^7.29.0" }, "bin": "./bin/babel-parser.js" }, "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww=="], + + "@babel/core/@babel/types": ["@babel/types@7.29.0", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A=="], "@babel/helper-compilation-targets/@babel/compat-data": ["@babel/compat-data@7.29.0", "", {}, "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg=="], @@ -1494,9 +1625,9 @@ "@babel/helper-member-expression-to-functions/@babel/traverse": ["@babel/traverse@7.29.7", "", { "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/generator": "^7.29.7", "@babel/helper-globals": "^7.29.7", "@babel/parser": "^7.29.7", "@babel/template": "^7.29.7", "@babel/types": "^7.29.7", "debug": "^4.3.1" } }, "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw=="], - "@babel/helper-member-expression-to-functions/@babel/types": ["@babel/types@7.29.7", "", { "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" } }, "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA=="], + "@babel/helper-module-imports/@babel/types": ["@babel/types@7.29.0", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A=="], - "@babel/helper-optimise-call-expression/@babel/types": ["@babel/types@7.29.7", "", { "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" } }, "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA=="], + "@babel/helper-module-transforms/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="], "@babel/helper-remap-async-to-generator/@babel/traverse": ["@babel/traverse@7.29.7", "", { "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/generator": "^7.29.7", "@babel/helper-globals": "^7.29.7", "@babel/parser": "^7.29.7", "@babel/template": "^7.29.7", "@babel/types": "^7.29.7", "debug": "^4.3.1" } }, "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw=="], @@ -1504,13 +1635,11 @@ "@babel/helper-skip-transparent-expression-wrappers/@babel/traverse": ["@babel/traverse@7.29.7", "", { "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/generator": "^7.29.7", "@babel/helper-globals": "^7.29.7", "@babel/parser": "^7.29.7", "@babel/template": "^7.29.7", "@babel/types": "^7.29.7", "debug": "^4.3.1" } }, "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw=="], - "@babel/helper-skip-transparent-expression-wrappers/@babel/types": ["@babel/types@7.29.7", "", { "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" } }, "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA=="], - "@babel/helper-wrap-function/@babel/template": ["@babel/template@7.29.7", "", { "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7" } }, "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg=="], "@babel/helper-wrap-function/@babel/traverse": ["@babel/traverse@7.29.7", "", { "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/generator": "^7.29.7", "@babel/helper-globals": "^7.29.7", "@babel/parser": "^7.29.7", "@babel/template": "^7.29.7", "@babel/types": "^7.29.7", "debug": "^4.3.1" } }, "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw=="], - "@babel/helper-wrap-function/@babel/types": ["@babel/types@7.29.7", "", { "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" } }, "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA=="], + "@babel/helpers/@babel/types": ["@babel/types@7.29.0", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A=="], "@babel/plugin-bugfix-firefox-class-in-computed-class-key/@babel/helper-plugin-utils": ["@babel/helper-plugin-utils@7.29.7", "", {}, "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw=="], @@ -1610,8 +1739,6 @@ "@babel/plugin-transform-modules-systemjs/@babel/helper-plugin-utils": ["@babel/helper-plugin-utils@7.29.7", "", {}, "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw=="], - "@babel/plugin-transform-modules-systemjs/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="], - "@babel/plugin-transform-modules-systemjs/@babel/traverse": ["@babel/traverse@7.29.7", "", { "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/generator": "^7.29.7", "@babel/helper-globals": "^7.29.7", "@babel/parser": "^7.29.7", "@babel/template": "^7.29.7", "@babel/types": "^7.29.7", "debug": "^4.3.1" } }, "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw=="], "@babel/plugin-transform-modules-umd/@babel/helper-module-transforms": ["@babel/helper-module-transforms@7.29.7", "", { "dependencies": { "@babel/helper-module-imports": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7", "@babel/traverse": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg=="], @@ -1676,18 +1803,60 @@ "@babel/preset-modules/@babel/helper-plugin-utils": ["@babel/helper-plugin-utils@7.29.7", "", {}, "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw=="], + "@babel/preset-modules/@babel/types": ["@babel/types@7.29.0", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A=="], + + "@babel/template/@babel/parser": ["@babel/parser@7.29.0", "", { "dependencies": { "@babel/types": "^7.29.0" }, "bin": "./bin/babel-parser.js" }, "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww=="], + + "@babel/template/@babel/types": ["@babel/types@7.29.0", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A=="], + + "@babel/traverse/@babel/generator": ["@babel/generator@7.29.1", "", { "dependencies": { "@babel/parser": "^7.29.0", "@babel/types": "^7.29.0", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw=="], + + "@babel/traverse/@babel/parser": ["@babel/parser@7.29.0", "", { "dependencies": { "@babel/types": "^7.29.0" }, "bin": "./bin/babel-parser.js" }, "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww=="], + + "@babel/traverse/@babel/types": ["@babel/types@7.29.0", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A=="], + + "@jest/types/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], + "@rollup/pluginutils/picomatch": ["picomatch@4.0.5", "", {}, "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A=="], + "@tanstack/router-generator/@babel/types": ["@babel/types@7.29.0", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A=="], + "@tanstack/router-generator/zod": ["zod@4.4.3", "", {}, "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ=="], + "@tanstack/router-plugin/@babel/types": ["@babel/types@7.29.0", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A=="], + "@tanstack/router-plugin/zod": ["zod@4.4.3", "", {}, "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ=="], + "@tanstack/router-utils/@babel/generator": ["@babel/generator@7.29.1", "", { "dependencies": { "@babel/parser": "^7.29.0", "@babel/types": "^7.29.0", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw=="], + + "@tanstack/router-utils/@babel/parser": ["@babel/parser@7.29.0", "", { "dependencies": { "@babel/types": "^7.29.0" }, "bin": "./bin/babel-parser.js" }, "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww=="], + + "@tanstack/router-utils/@babel/types": ["@babel/types@7.29.0", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A=="], + "@tanstack/start-plugin-core/@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="], - "@tanstack/start-plugin-core/source-map": ["source-map@0.7.6", "", {}, "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ=="], + "@tanstack/start-plugin-core/@babel/types": ["@babel/types@7.29.0", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A=="], "@tanstack/start-plugin-core/zod": ["zod@4.4.3", "", {}, "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ=="], + "@types/babel__core/@babel/parser": ["@babel/parser@7.29.0", "", { "dependencies": { "@babel/types": "^7.29.0" }, "bin": "./bin/babel-parser.js" }, "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww=="], + + "@types/babel__core/@babel/types": ["@babel/types@7.29.0", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A=="], + + "@types/babel__generator/@babel/types": ["@babel/types@7.29.0", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A=="], + + "@types/babel__template/@babel/parser": ["@babel/parser@7.29.0", "", { "dependencies": { "@babel/types": "^7.29.0" }, "bin": "./bin/babel-parser.js" }, "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww=="], + + "@types/babel__template/@babel/types": ["@babel/types@7.29.0", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A=="], + + "@types/babel__traverse/@babel/types": ["@babel/types@7.29.0", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A=="], + + "babel-dead-code-elimination/@babel/parser": ["@babel/parser@7.29.0", "", { "dependencies": { "@babel/types": "^7.29.0" }, "bin": "./bin/babel-parser.js" }, "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww=="], + + "babel-dead-code-elimination/@babel/types": ["@babel/types@7.29.0", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A=="], + + "babel-plugin-react-compiler/@babel/types": ["@babel/types@7.29.0", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A=="], + "dotenv-expand/dotenv": ["dotenv@16.6.1", "", {}, "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow=="], "es-abstract-get/es-object-atoms": ["es-object-atoms@1.1.2", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw=="], @@ -1698,14 +1867,26 @@ "is-core-module/hasown": ["hasown@2.0.4", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A=="], + "jest-validate/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], + "jsonwebtoken/semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="], + "micromatch/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="], + "node-fetch/whatwg-url": ["whatwg-url@5.0.0", "", { "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" } }, "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw=="], "object.assign/es-object-atoms": ["es-object-atoms@1.1.2", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw=="], + "ora/string-width": ["string-width@8.2.2", "", { "dependencies": { "get-east-asian-width": "^1.5.0", "strip-ansi": "^7.1.2" } }, "sha512-GaPUh5gfdrYzqeVNZvUfT23vYYxXzKYidUcnMtJg/3rxRV63EFZy3k6xfKlmfeJD0176lnUV/Usr3XcwSvFzpg=="], + "path-scurry/lru-cache": ["lru-cache@11.5.2", "", {}, "sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g=="], + "pretty-format/ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="], + + "pretty-format/react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="], + + "pseudolocale/commander": ["commander@10.0.1", "", {}, "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug=="], + "reflect.getprototypeof/es-object-atoms": ["es-object-atoms@1.1.2", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw=="], "solid-js/seroval": ["seroval@1.5.0", "", {}, "sha512-OE4cvmJ1uSPrKorFIH9/w/Qwuvi/IMcGbv5RKgcJ/zjA/IohDLU6SVaxFN9FwajbP7nsX0dQqMDes1whk3y+yw=="], @@ -1722,221 +1903,179 @@ "tempy/type-fest": ["type-fest@0.16.0", "", {}, "sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg=="], + "terser/commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="], + "unplugin/picomatch": ["picomatch@4.0.5", "", {}, "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A=="], "web-push/https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="], - "@babel/helper-annotate-as-pure/@babel/types/@babel/helper-string-parser": ["@babel/helper-string-parser@7.29.7", "", {}, "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw=="], + "workbox-build/source-map": ["source-map@0.8.0-beta.0", "", { "dependencies": { "whatwg-url": "^7.0.0" } }, "sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA=="], - "@babel/helper-annotate-as-pure/@babel/types/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="], + "@babel/core/@babel/types/@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="], + + "@babel/core/@babel/types/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="], "@babel/helper-create-class-features-plugin/@babel/traverse/@babel/code-frame": ["@babel/code-frame@7.29.7", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw=="], - "@babel/helper-create-class-features-plugin/@babel/traverse/@babel/generator": ["@babel/generator@7.29.7", "", { "dependencies": { "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ=="], - "@babel/helper-create-class-features-plugin/@babel/traverse/@babel/helper-globals": ["@babel/helper-globals@7.29.7", "", {}, "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA=="], - "@babel/helper-create-class-features-plugin/@babel/traverse/@babel/parser": ["@babel/parser@7.29.7", "", { "dependencies": { "@babel/types": "^7.29.7" }, "bin": "./bin/babel-parser.js" }, "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg=="], - "@babel/helper-create-class-features-plugin/@babel/traverse/@babel/template": ["@babel/template@7.29.7", "", { "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7" } }, "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg=="], - "@babel/helper-create-class-features-plugin/@babel/traverse/@babel/types": ["@babel/types@7.29.7", "", { "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" } }, "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA=="], - "@babel/helper-member-expression-to-functions/@babel/traverse/@babel/code-frame": ["@babel/code-frame@7.29.7", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw=="], - "@babel/helper-member-expression-to-functions/@babel/traverse/@babel/generator": ["@babel/generator@7.29.7", "", { "dependencies": { "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ=="], - "@babel/helper-member-expression-to-functions/@babel/traverse/@babel/helper-globals": ["@babel/helper-globals@7.29.7", "", {}, "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA=="], - "@babel/helper-member-expression-to-functions/@babel/traverse/@babel/parser": ["@babel/parser@7.29.7", "", { "dependencies": { "@babel/types": "^7.29.7" }, "bin": "./bin/babel-parser.js" }, "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg=="], - "@babel/helper-member-expression-to-functions/@babel/traverse/@babel/template": ["@babel/template@7.29.7", "", { "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7" } }, "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg=="], - "@babel/helper-member-expression-to-functions/@babel/types/@babel/helper-string-parser": ["@babel/helper-string-parser@7.29.7", "", {}, "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw=="], + "@babel/helper-module-imports/@babel/types/@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="], - "@babel/helper-member-expression-to-functions/@babel/types/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="], - - "@babel/helper-optimise-call-expression/@babel/types/@babel/helper-string-parser": ["@babel/helper-string-parser@7.29.7", "", {}, "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw=="], - - "@babel/helper-optimise-call-expression/@babel/types/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="], + "@babel/helper-module-imports/@babel/types/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="], "@babel/helper-remap-async-to-generator/@babel/traverse/@babel/code-frame": ["@babel/code-frame@7.29.7", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw=="], - "@babel/helper-remap-async-to-generator/@babel/traverse/@babel/generator": ["@babel/generator@7.29.7", "", { "dependencies": { "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ=="], - "@babel/helper-remap-async-to-generator/@babel/traverse/@babel/helper-globals": ["@babel/helper-globals@7.29.7", "", {}, "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA=="], - "@babel/helper-remap-async-to-generator/@babel/traverse/@babel/parser": ["@babel/parser@7.29.7", "", { "dependencies": { "@babel/types": "^7.29.7" }, "bin": "./bin/babel-parser.js" }, "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg=="], - "@babel/helper-remap-async-to-generator/@babel/traverse/@babel/template": ["@babel/template@7.29.7", "", { "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7" } }, "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg=="], - "@babel/helper-remap-async-to-generator/@babel/traverse/@babel/types": ["@babel/types@7.29.7", "", { "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" } }, "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA=="], - "@babel/helper-replace-supers/@babel/traverse/@babel/code-frame": ["@babel/code-frame@7.29.7", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw=="], - "@babel/helper-replace-supers/@babel/traverse/@babel/generator": ["@babel/generator@7.29.7", "", { "dependencies": { "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ=="], - "@babel/helper-replace-supers/@babel/traverse/@babel/helper-globals": ["@babel/helper-globals@7.29.7", "", {}, "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA=="], - "@babel/helper-replace-supers/@babel/traverse/@babel/parser": ["@babel/parser@7.29.7", "", { "dependencies": { "@babel/types": "^7.29.7" }, "bin": "./bin/babel-parser.js" }, "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg=="], - "@babel/helper-replace-supers/@babel/traverse/@babel/template": ["@babel/template@7.29.7", "", { "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7" } }, "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg=="], - "@babel/helper-replace-supers/@babel/traverse/@babel/types": ["@babel/types@7.29.7", "", { "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" } }, "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA=="], - "@babel/helper-skip-transparent-expression-wrappers/@babel/traverse/@babel/code-frame": ["@babel/code-frame@7.29.7", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw=="], - "@babel/helper-skip-transparent-expression-wrappers/@babel/traverse/@babel/generator": ["@babel/generator@7.29.7", "", { "dependencies": { "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ=="], - "@babel/helper-skip-transparent-expression-wrappers/@babel/traverse/@babel/helper-globals": ["@babel/helper-globals@7.29.7", "", {}, "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA=="], - "@babel/helper-skip-transparent-expression-wrappers/@babel/traverse/@babel/parser": ["@babel/parser@7.29.7", "", { "dependencies": { "@babel/types": "^7.29.7" }, "bin": "./bin/babel-parser.js" }, "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg=="], - "@babel/helper-skip-transparent-expression-wrappers/@babel/traverse/@babel/template": ["@babel/template@7.29.7", "", { "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7" } }, "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg=="], - "@babel/helper-skip-transparent-expression-wrappers/@babel/types/@babel/helper-string-parser": ["@babel/helper-string-parser@7.29.7", "", {}, "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw=="], - - "@babel/helper-skip-transparent-expression-wrappers/@babel/types/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="], - "@babel/helper-wrap-function/@babel/template/@babel/code-frame": ["@babel/code-frame@7.29.7", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw=="], - "@babel/helper-wrap-function/@babel/template/@babel/parser": ["@babel/parser@7.29.7", "", { "dependencies": { "@babel/types": "^7.29.7" }, "bin": "./bin/babel-parser.js" }, "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg=="], - "@babel/helper-wrap-function/@babel/traverse/@babel/code-frame": ["@babel/code-frame@7.29.7", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw=="], - "@babel/helper-wrap-function/@babel/traverse/@babel/generator": ["@babel/generator@7.29.7", "", { "dependencies": { "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ=="], - "@babel/helper-wrap-function/@babel/traverse/@babel/helper-globals": ["@babel/helper-globals@7.29.7", "", {}, "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA=="], - "@babel/helper-wrap-function/@babel/traverse/@babel/parser": ["@babel/parser@7.29.7", "", { "dependencies": { "@babel/types": "^7.29.7" }, "bin": "./bin/babel-parser.js" }, "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg=="], + "@babel/helpers/@babel/types/@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="], - "@babel/helper-wrap-function/@babel/types/@babel/helper-string-parser": ["@babel/helper-string-parser@7.29.7", "", {}, "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw=="], - - "@babel/helper-wrap-function/@babel/types/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="], + "@babel/helpers/@babel/types/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="], "@babel/plugin-bugfix-firefox-class-in-computed-class-key/@babel/traverse/@babel/code-frame": ["@babel/code-frame@7.29.7", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw=="], - "@babel/plugin-bugfix-firefox-class-in-computed-class-key/@babel/traverse/@babel/generator": ["@babel/generator@7.29.7", "", { "dependencies": { "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ=="], - "@babel/plugin-bugfix-firefox-class-in-computed-class-key/@babel/traverse/@babel/helper-globals": ["@babel/helper-globals@7.29.7", "", {}, "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA=="], - "@babel/plugin-bugfix-firefox-class-in-computed-class-key/@babel/traverse/@babel/parser": ["@babel/parser@7.29.7", "", { "dependencies": { "@babel/types": "^7.29.7" }, "bin": "./bin/babel-parser.js" }, "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg=="], - "@babel/plugin-bugfix-firefox-class-in-computed-class-key/@babel/traverse/@babel/template": ["@babel/template@7.29.7", "", { "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7" } }, "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg=="], - "@babel/plugin-bugfix-firefox-class-in-computed-class-key/@babel/traverse/@babel/types": ["@babel/types@7.29.7", "", { "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" } }, "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA=="], - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/@babel/traverse/@babel/code-frame": ["@babel/code-frame@7.29.7", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw=="], - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/@babel/traverse/@babel/generator": ["@babel/generator@7.29.7", "", { "dependencies": { "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ=="], - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/@babel/traverse/@babel/helper-globals": ["@babel/helper-globals@7.29.7", "", {}, "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA=="], - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/@babel/traverse/@babel/parser": ["@babel/parser@7.29.7", "", { "dependencies": { "@babel/types": "^7.29.7" }, "bin": "./bin/babel-parser.js" }, "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg=="], - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/@babel/traverse/@babel/template": ["@babel/template@7.29.7", "", { "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7" } }, "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg=="], - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/@babel/traverse/@babel/types": ["@babel/types@7.29.7", "", { "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" } }, "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA=="], - "@babel/plugin-transform-async-generator-functions/@babel/traverse/@babel/code-frame": ["@babel/code-frame@7.29.7", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw=="], - "@babel/plugin-transform-async-generator-functions/@babel/traverse/@babel/generator": ["@babel/generator@7.29.7", "", { "dependencies": { "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ=="], - "@babel/plugin-transform-async-generator-functions/@babel/traverse/@babel/helper-globals": ["@babel/helper-globals@7.29.7", "", {}, "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA=="], - "@babel/plugin-transform-async-generator-functions/@babel/traverse/@babel/parser": ["@babel/parser@7.29.7", "", { "dependencies": { "@babel/types": "^7.29.7" }, "bin": "./bin/babel-parser.js" }, "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg=="], - "@babel/plugin-transform-async-generator-functions/@babel/traverse/@babel/template": ["@babel/template@7.29.7", "", { "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7" } }, "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg=="], - "@babel/plugin-transform-async-generator-functions/@babel/traverse/@babel/types": ["@babel/types@7.29.7", "", { "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" } }, "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA=="], - "@babel/plugin-transform-async-to-generator/@babel/helper-module-imports/@babel/traverse": ["@babel/traverse@7.29.7", "", { "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/generator": "^7.29.7", "@babel/helper-globals": "^7.29.7", "@babel/parser": "^7.29.7", "@babel/template": "^7.29.7", "@babel/types": "^7.29.7", "debug": "^4.3.1" } }, "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw=="], - "@babel/plugin-transform-async-to-generator/@babel/helper-module-imports/@babel/types": ["@babel/types@7.29.7", "", { "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" } }, "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA=="], - "@babel/plugin-transform-classes/@babel/traverse/@babel/code-frame": ["@babel/code-frame@7.29.7", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw=="], - "@babel/plugin-transform-classes/@babel/traverse/@babel/generator": ["@babel/generator@7.29.7", "", { "dependencies": { "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ=="], - - "@babel/plugin-transform-classes/@babel/traverse/@babel/parser": ["@babel/parser@7.29.7", "", { "dependencies": { "@babel/types": "^7.29.7" }, "bin": "./bin/babel-parser.js" }, "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg=="], - "@babel/plugin-transform-classes/@babel/traverse/@babel/template": ["@babel/template@7.29.7", "", { "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7" } }, "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg=="], - "@babel/plugin-transform-classes/@babel/traverse/@babel/types": ["@babel/types@7.29.7", "", { "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" } }, "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA=="], - "@babel/plugin-transform-computed-properties/@babel/template/@babel/code-frame": ["@babel/code-frame@7.29.7", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw=="], - "@babel/plugin-transform-computed-properties/@babel/template/@babel/parser": ["@babel/parser@7.29.7", "", { "dependencies": { "@babel/types": "^7.29.7" }, "bin": "./bin/babel-parser.js" }, "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg=="], - - "@babel/plugin-transform-computed-properties/@babel/template/@babel/types": ["@babel/types@7.29.7", "", { "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" } }, "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA=="], - "@babel/plugin-transform-destructuring/@babel/traverse/@babel/code-frame": ["@babel/code-frame@7.29.7", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw=="], - "@babel/plugin-transform-destructuring/@babel/traverse/@babel/generator": ["@babel/generator@7.29.7", "", { "dependencies": { "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ=="], - "@babel/plugin-transform-destructuring/@babel/traverse/@babel/helper-globals": ["@babel/helper-globals@7.29.7", "", {}, "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA=="], - "@babel/plugin-transform-destructuring/@babel/traverse/@babel/parser": ["@babel/parser@7.29.7", "", { "dependencies": { "@babel/types": "^7.29.7" }, "bin": "./bin/babel-parser.js" }, "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg=="], - "@babel/plugin-transform-destructuring/@babel/traverse/@babel/template": ["@babel/template@7.29.7", "", { "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7" } }, "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg=="], - "@babel/plugin-transform-destructuring/@babel/traverse/@babel/types": ["@babel/types@7.29.7", "", { "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" } }, "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA=="], - "@babel/plugin-transform-function-name/@babel/traverse/@babel/code-frame": ["@babel/code-frame@7.29.7", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw=="], - "@babel/plugin-transform-function-name/@babel/traverse/@babel/generator": ["@babel/generator@7.29.7", "", { "dependencies": { "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ=="], - "@babel/plugin-transform-function-name/@babel/traverse/@babel/helper-globals": ["@babel/helper-globals@7.29.7", "", {}, "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA=="], - "@babel/plugin-transform-function-name/@babel/traverse/@babel/parser": ["@babel/parser@7.29.7", "", { "dependencies": { "@babel/types": "^7.29.7" }, "bin": "./bin/babel-parser.js" }, "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg=="], - "@babel/plugin-transform-function-name/@babel/traverse/@babel/template": ["@babel/template@7.29.7", "", { "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7" } }, "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg=="], - "@babel/plugin-transform-function-name/@babel/traverse/@babel/types": ["@babel/types@7.29.7", "", { "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" } }, "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA=="], - "@babel/plugin-transform-modules-amd/@babel/helper-module-transforms/@babel/helper-module-imports": ["@babel/helper-module-imports@7.29.7", "", { "dependencies": { "@babel/traverse": "^7.29.7", "@babel/types": "^7.29.7" } }, "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g=="], - "@babel/plugin-transform-modules-amd/@babel/helper-module-transforms/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="], - "@babel/plugin-transform-modules-amd/@babel/helper-module-transforms/@babel/traverse": ["@babel/traverse@7.29.7", "", { "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/generator": "^7.29.7", "@babel/helper-globals": "^7.29.7", "@babel/parser": "^7.29.7", "@babel/template": "^7.29.7", "@babel/types": "^7.29.7", "debug": "^4.3.1" } }, "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw=="], "@babel/plugin-transform-modules-commonjs/@babel/helper-module-transforms/@babel/helper-module-imports": ["@babel/helper-module-imports@7.29.7", "", { "dependencies": { "@babel/traverse": "^7.29.7", "@babel/types": "^7.29.7" } }, "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g=="], - "@babel/plugin-transform-modules-commonjs/@babel/helper-module-transforms/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="], - "@babel/plugin-transform-modules-commonjs/@babel/helper-module-transforms/@babel/traverse": ["@babel/traverse@7.29.7", "", { "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/generator": "^7.29.7", "@babel/helper-globals": "^7.29.7", "@babel/parser": "^7.29.7", "@babel/template": "^7.29.7", "@babel/types": "^7.29.7", "debug": "^4.3.1" } }, "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw=="], "@babel/plugin-transform-modules-systemjs/@babel/helper-module-transforms/@babel/helper-module-imports": ["@babel/helper-module-imports@7.29.7", "", { "dependencies": { "@babel/traverse": "^7.29.7", "@babel/types": "^7.29.7" } }, "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g=="], "@babel/plugin-transform-modules-systemjs/@babel/traverse/@babel/code-frame": ["@babel/code-frame@7.29.7", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw=="], - "@babel/plugin-transform-modules-systemjs/@babel/traverse/@babel/generator": ["@babel/generator@7.29.7", "", { "dependencies": { "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ=="], - "@babel/plugin-transform-modules-systemjs/@babel/traverse/@babel/helper-globals": ["@babel/helper-globals@7.29.7", "", {}, "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA=="], - "@babel/plugin-transform-modules-systemjs/@babel/traverse/@babel/parser": ["@babel/parser@7.29.7", "", { "dependencies": { "@babel/types": "^7.29.7" }, "bin": "./bin/babel-parser.js" }, "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg=="], - "@babel/plugin-transform-modules-systemjs/@babel/traverse/@babel/template": ["@babel/template@7.29.7", "", { "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7" } }, "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg=="], - "@babel/plugin-transform-modules-systemjs/@babel/traverse/@babel/types": ["@babel/types@7.29.7", "", { "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" } }, "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA=="], - "@babel/plugin-transform-modules-umd/@babel/helper-module-transforms/@babel/helper-module-imports": ["@babel/helper-module-imports@7.29.7", "", { "dependencies": { "@babel/traverse": "^7.29.7", "@babel/types": "^7.29.7" } }, "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g=="], - "@babel/plugin-transform-modules-umd/@babel/helper-module-transforms/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="], - "@babel/plugin-transform-modules-umd/@babel/helper-module-transforms/@babel/traverse": ["@babel/traverse@7.29.7", "", { "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/generator": "^7.29.7", "@babel/helper-globals": "^7.29.7", "@babel/parser": "^7.29.7", "@babel/template": "^7.29.7", "@babel/types": "^7.29.7", "debug": "^4.3.1" } }, "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw=="], "@babel/plugin-transform-object-rest-spread/@babel/traverse/@babel/code-frame": ["@babel/code-frame@7.29.7", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw=="], - "@babel/plugin-transform-object-rest-spread/@babel/traverse/@babel/generator": ["@babel/generator@7.29.7", "", { "dependencies": { "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ=="], - "@babel/plugin-transform-object-rest-spread/@babel/traverse/@babel/helper-globals": ["@babel/helper-globals@7.29.7", "", {}, "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA=="], - "@babel/plugin-transform-object-rest-spread/@babel/traverse/@babel/parser": ["@babel/parser@7.29.7", "", { "dependencies": { "@babel/types": "^7.29.7" }, "bin": "./bin/babel-parser.js" }, "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg=="], - "@babel/plugin-transform-object-rest-spread/@babel/traverse/@babel/template": ["@babel/template@7.29.7", "", { "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7" } }, "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg=="], - "@babel/plugin-transform-object-rest-spread/@babel/traverse/@babel/types": ["@babel/types@7.29.7", "", { "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" } }, "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA=="], + "@babel/preset-modules/@babel/types/@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="], + + "@babel/preset-modules/@babel/types/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="], + + "@babel/template/@babel/types/@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="], + + "@babel/template/@babel/types/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="], + + "@babel/traverse/@babel/types/@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="], + + "@babel/traverse/@babel/types/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="], + + "@tanstack/router-generator/@babel/types/@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="], + + "@tanstack/router-generator/@babel/types/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="], + + "@tanstack/router-plugin/@babel/types/@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="], + + "@tanstack/router-plugin/@babel/types/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="], + + "@tanstack/router-utils/@babel/types/@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="], + + "@tanstack/router-utils/@babel/types/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="], + + "@tanstack/start-plugin-core/@babel/code-frame/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="], + + "@tanstack/start-plugin-core/@babel/types/@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="], + + "@tanstack/start-plugin-core/@babel/types/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="], + + "@types/babel__core/@babel/types/@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="], + + "@types/babel__core/@babel/types/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="], + + "@types/babel__generator/@babel/types/@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="], + + "@types/babel__generator/@babel/types/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="], + + "@types/babel__template/@babel/types/@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="], + + "@types/babel__template/@babel/types/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="], + + "@types/babel__traverse/@babel/types/@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="], + + "@types/babel__traverse/@babel/types/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="], + + "babel-dead-code-elimination/@babel/types/@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="], + + "babel-dead-code-elimination/@babel/types/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="], + + "babel-plugin-react-compiler/@babel/types/@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="], + + "babel-plugin-react-compiler/@babel/types/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="], "filelist/minimatch/brace-expansion": ["brace-expansion@2.1.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-w5JZcKgdhDOgOwm8H+KgbosopHMuGcl6qbulwjtz3SM7I7P3yW1eAjzMPLrIE+NQ9vjgANKHWeMHnrT0OXW1oA=="], @@ -1944,158 +2083,36 @@ "node-fetch/whatwg-url/webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="], + "ora/string-width/strip-ansi": ["strip-ansi@7.2.0", "", { "dependencies": { "ansi-regex": "^6.2.2" } }, "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w=="], + "web-push/https-proxy-agent/agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="], - "@babel/helper-create-class-features-plugin/@babel/traverse/@babel/code-frame/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="], - - "@babel/helper-create-class-features-plugin/@babel/traverse/@babel/types/@babel/helper-string-parser": ["@babel/helper-string-parser@7.29.7", "", {}, "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw=="], - - "@babel/helper-create-class-features-plugin/@babel/traverse/@babel/types/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="], - - "@babel/helper-member-expression-to-functions/@babel/traverse/@babel/code-frame/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="], - - "@babel/helper-remap-async-to-generator/@babel/traverse/@babel/code-frame/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="], - - "@babel/helper-remap-async-to-generator/@babel/traverse/@babel/types/@babel/helper-string-parser": ["@babel/helper-string-parser@7.29.7", "", {}, "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw=="], - - "@babel/helper-remap-async-to-generator/@babel/traverse/@babel/types/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="], - - "@babel/helper-replace-supers/@babel/traverse/@babel/code-frame/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="], - - "@babel/helper-replace-supers/@babel/traverse/@babel/types/@babel/helper-string-parser": ["@babel/helper-string-parser@7.29.7", "", {}, "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw=="], - - "@babel/helper-replace-supers/@babel/traverse/@babel/types/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="], - - "@babel/helper-skip-transparent-expression-wrappers/@babel/traverse/@babel/code-frame/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="], - - "@babel/helper-wrap-function/@babel/template/@babel/code-frame/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="], - - "@babel/helper-wrap-function/@babel/traverse/@babel/code-frame/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="], - - "@babel/plugin-bugfix-firefox-class-in-computed-class-key/@babel/traverse/@babel/code-frame/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="], - - "@babel/plugin-bugfix-firefox-class-in-computed-class-key/@babel/traverse/@babel/types/@babel/helper-string-parser": ["@babel/helper-string-parser@7.29.7", "", {}, "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw=="], - - "@babel/plugin-bugfix-firefox-class-in-computed-class-key/@babel/traverse/@babel/types/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="], - - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/@babel/traverse/@babel/code-frame/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="], - - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/@babel/traverse/@babel/types/@babel/helper-string-parser": ["@babel/helper-string-parser@7.29.7", "", {}, "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw=="], - - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/@babel/traverse/@babel/types/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="], - - "@babel/plugin-transform-async-generator-functions/@babel/traverse/@babel/code-frame/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="], - - "@babel/plugin-transform-async-generator-functions/@babel/traverse/@babel/types/@babel/helper-string-parser": ["@babel/helper-string-parser@7.29.7", "", {}, "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw=="], - - "@babel/plugin-transform-async-generator-functions/@babel/traverse/@babel/types/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="], - "@babel/plugin-transform-async-to-generator/@babel/helper-module-imports/@babel/traverse/@babel/code-frame": ["@babel/code-frame@7.29.7", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw=="], - "@babel/plugin-transform-async-to-generator/@babel/helper-module-imports/@babel/traverse/@babel/generator": ["@babel/generator@7.29.7", "", { "dependencies": { "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ=="], - "@babel/plugin-transform-async-to-generator/@babel/helper-module-imports/@babel/traverse/@babel/helper-globals": ["@babel/helper-globals@7.29.7", "", {}, "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA=="], - "@babel/plugin-transform-async-to-generator/@babel/helper-module-imports/@babel/traverse/@babel/parser": ["@babel/parser@7.29.7", "", { "dependencies": { "@babel/types": "^7.29.7" }, "bin": "./bin/babel-parser.js" }, "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg=="], - "@babel/plugin-transform-async-to-generator/@babel/helper-module-imports/@babel/traverse/@babel/template": ["@babel/template@7.29.7", "", { "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7" } }, "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg=="], - "@babel/plugin-transform-async-to-generator/@babel/helper-module-imports/@babel/types/@babel/helper-string-parser": ["@babel/helper-string-parser@7.29.7", "", {}, "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw=="], - - "@babel/plugin-transform-async-to-generator/@babel/helper-module-imports/@babel/types/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="], - - "@babel/plugin-transform-classes/@babel/traverse/@babel/code-frame/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="], - - "@babel/plugin-transform-classes/@babel/traverse/@babel/types/@babel/helper-string-parser": ["@babel/helper-string-parser@7.29.7", "", {}, "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw=="], - - "@babel/plugin-transform-classes/@babel/traverse/@babel/types/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="], - - "@babel/plugin-transform-computed-properties/@babel/template/@babel/code-frame/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="], - - "@babel/plugin-transform-computed-properties/@babel/template/@babel/types/@babel/helper-string-parser": ["@babel/helper-string-parser@7.29.7", "", {}, "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw=="], - - "@babel/plugin-transform-computed-properties/@babel/template/@babel/types/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="], - - "@babel/plugin-transform-destructuring/@babel/traverse/@babel/code-frame/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="], - - "@babel/plugin-transform-destructuring/@babel/traverse/@babel/types/@babel/helper-string-parser": ["@babel/helper-string-parser@7.29.7", "", {}, "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw=="], - - "@babel/plugin-transform-destructuring/@babel/traverse/@babel/types/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="], - - "@babel/plugin-transform-function-name/@babel/traverse/@babel/code-frame/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="], - - "@babel/plugin-transform-function-name/@babel/traverse/@babel/types/@babel/helper-string-parser": ["@babel/helper-string-parser@7.29.7", "", {}, "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw=="], - - "@babel/plugin-transform-function-name/@babel/traverse/@babel/types/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="], - - "@babel/plugin-transform-modules-amd/@babel/helper-module-transforms/@babel/helper-module-imports/@babel/types": ["@babel/types@7.29.7", "", { "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" } }, "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA=="], - "@babel/plugin-transform-modules-amd/@babel/helper-module-transforms/@babel/traverse/@babel/code-frame": ["@babel/code-frame@7.29.7", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw=="], - "@babel/plugin-transform-modules-amd/@babel/helper-module-transforms/@babel/traverse/@babel/generator": ["@babel/generator@7.29.7", "", { "dependencies": { "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ=="], - "@babel/plugin-transform-modules-amd/@babel/helper-module-transforms/@babel/traverse/@babel/helper-globals": ["@babel/helper-globals@7.29.7", "", {}, "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA=="], - "@babel/plugin-transform-modules-amd/@babel/helper-module-transforms/@babel/traverse/@babel/parser": ["@babel/parser@7.29.7", "", { "dependencies": { "@babel/types": "^7.29.7" }, "bin": "./bin/babel-parser.js" }, "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg=="], - "@babel/plugin-transform-modules-amd/@babel/helper-module-transforms/@babel/traverse/@babel/template": ["@babel/template@7.29.7", "", { "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7" } }, "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg=="], - "@babel/plugin-transform-modules-amd/@babel/helper-module-transforms/@babel/traverse/@babel/types": ["@babel/types@7.29.7", "", { "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" } }, "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA=="], - - "@babel/plugin-transform-modules-commonjs/@babel/helper-module-transforms/@babel/helper-module-imports/@babel/types": ["@babel/types@7.29.7", "", { "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" } }, "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA=="], - "@babel/plugin-transform-modules-commonjs/@babel/helper-module-transforms/@babel/traverse/@babel/code-frame": ["@babel/code-frame@7.29.7", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw=="], - "@babel/plugin-transform-modules-commonjs/@babel/helper-module-transforms/@babel/traverse/@babel/generator": ["@babel/generator@7.29.7", "", { "dependencies": { "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ=="], - "@babel/plugin-transform-modules-commonjs/@babel/helper-module-transforms/@babel/traverse/@babel/helper-globals": ["@babel/helper-globals@7.29.7", "", {}, "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA=="], - "@babel/plugin-transform-modules-commonjs/@babel/helper-module-transforms/@babel/traverse/@babel/parser": ["@babel/parser@7.29.7", "", { "dependencies": { "@babel/types": "^7.29.7" }, "bin": "./bin/babel-parser.js" }, "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg=="], - "@babel/plugin-transform-modules-commonjs/@babel/helper-module-transforms/@babel/traverse/@babel/template": ["@babel/template@7.29.7", "", { "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7" } }, "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg=="], - "@babel/plugin-transform-modules-commonjs/@babel/helper-module-transforms/@babel/traverse/@babel/types": ["@babel/types@7.29.7", "", { "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" } }, "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA=="], - - "@babel/plugin-transform-modules-systemjs/@babel/helper-module-transforms/@babel/helper-module-imports/@babel/types": ["@babel/types@7.29.7", "", { "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" } }, "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA=="], - - "@babel/plugin-transform-modules-systemjs/@babel/traverse/@babel/types/@babel/helper-string-parser": ["@babel/helper-string-parser@7.29.7", "", {}, "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw=="], - - "@babel/plugin-transform-modules-umd/@babel/helper-module-transforms/@babel/helper-module-imports/@babel/types": ["@babel/types@7.29.7", "", { "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" } }, "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA=="], - "@babel/plugin-transform-modules-umd/@babel/helper-module-transforms/@babel/traverse/@babel/code-frame": ["@babel/code-frame@7.29.7", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw=="], - "@babel/plugin-transform-modules-umd/@babel/helper-module-transforms/@babel/traverse/@babel/generator": ["@babel/generator@7.29.7", "", { "dependencies": { "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ=="], - "@babel/plugin-transform-modules-umd/@babel/helper-module-transforms/@babel/traverse/@babel/helper-globals": ["@babel/helper-globals@7.29.7", "", {}, "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA=="], - "@babel/plugin-transform-modules-umd/@babel/helper-module-transforms/@babel/traverse/@babel/parser": ["@babel/parser@7.29.7", "", { "dependencies": { "@babel/types": "^7.29.7" }, "bin": "./bin/babel-parser.js" }, "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg=="], - "@babel/plugin-transform-modules-umd/@babel/helper-module-transforms/@babel/traverse/@babel/template": ["@babel/template@7.29.7", "", { "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7" } }, "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg=="], - "@babel/plugin-transform-modules-umd/@babel/helper-module-transforms/@babel/traverse/@babel/types": ["@babel/types@7.29.7", "", { "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" } }, "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA=="], - - "@babel/plugin-transform-object-rest-spread/@babel/traverse/@babel/code-frame/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="], - - "@babel/plugin-transform-object-rest-spread/@babel/traverse/@babel/types/@babel/helper-string-parser": ["@babel/helper-string-parser@7.29.7", "", {}, "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw=="], - - "@babel/plugin-transform-object-rest-spread/@babel/traverse/@babel/types/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="], - "filelist/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], - "@babel/plugin-transform-async-to-generator/@babel/helper-module-imports/@babel/traverse/@babel/code-frame/@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="], - - "@babel/plugin-transform-modules-amd/@babel/helper-module-transforms/@babel/helper-module-imports/@babel/types/@babel/helper-string-parser": ["@babel/helper-string-parser@7.29.7", "", {}, "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw=="], - - "@babel/plugin-transform-modules-amd/@babel/helper-module-transforms/@babel/traverse/@babel/types/@babel/helper-string-parser": ["@babel/helper-string-parser@7.29.7", "", {}, "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw=="], - - "@babel/plugin-transform-modules-commonjs/@babel/helper-module-transforms/@babel/helper-module-imports/@babel/types/@babel/helper-string-parser": ["@babel/helper-string-parser@7.29.7", "", {}, "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw=="], - - "@babel/plugin-transform-modules-commonjs/@babel/helper-module-transforms/@babel/traverse/@babel/types/@babel/helper-string-parser": ["@babel/helper-string-parser@7.29.7", "", {}, "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw=="], - - "@babel/plugin-transform-modules-systemjs/@babel/helper-module-transforms/@babel/helper-module-imports/@babel/types/@babel/helper-string-parser": ["@babel/helper-string-parser@7.29.7", "", {}, "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw=="], - - "@babel/plugin-transform-modules-umd/@babel/helper-module-transforms/@babel/helper-module-imports/@babel/types/@babel/helper-string-parser": ["@babel/helper-string-parser@7.29.7", "", {}, "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw=="], - - "@babel/plugin-transform-modules-umd/@babel/helper-module-transforms/@babel/traverse/@babel/types/@babel/helper-string-parser": ["@babel/helper-string-parser@7.29.7", "", {}, "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw=="], + "ora/string-width/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], } } diff --git a/lingui.config.ts b/lingui.config.ts new file mode 100644 index 0000000..4553277 --- /dev/null +++ b/lingui.config.ts @@ -0,0 +1,13 @@ +import { defineConfig } from "@lingui/conf"; + +export default defineConfig({ + sourceLocale: "en", + locales: ["en", "es", "de", "ja"], + catalogs: [ + { + path: "/src/locales/{locale}/messages", + include: ["src"], + exclude: ["**/node_modules/**", "**/*.d.ts"], + }, + ], +}); diff --git a/package.json b/package.json index 681d1b2..cc84311 100644 --- a/package.json +++ b/package.json @@ -5,11 +5,15 @@ "type": "module", "scripts": { "dev": "vite dev --host 0.0.0.0", - "build": "vite build && tsc --noEmit && bun scripts/generate-sw.mjs", + "extract": "lingui extract --clean", + "i18n:check": "lingui extract --clean && lingui compile --strict", + "build": "lingui extract --clean && vite build && tsc --noEmit && bun scripts/generate-sw.mjs", "start": "bun run server.ts" }, "dependencies": { "@hello-pangea/dnd": "^18.0.1", + "@lingui/core": "^6.5.0", + "@lingui/react": "^6.5.0", "@mantine/carousel": "^8.2.4", "@mantine/core": "^8.2.4", "@mantine/dates": "^8.2.4", @@ -47,6 +51,9 @@ "zod": "^4.0.15" }, "devDependencies": { + "@lingui/babel-plugin-lingui-macro": "^6.5.0", + "@lingui/cli": "^6.5.0", + "@lingui/vite-plugin": "^6.5.0", "@types/node": "^22.5.4", "@types/react": "^19.2.17", "@types/react-dom": "^19.2.3", diff --git a/src/app/routes/__root.tsx b/src/app/routes/__root.tsx index 5d1f121..217155c 100644 --- a/src/app/routes/__root.tsx +++ b/src/app/routes/__root.tsx @@ -18,6 +18,8 @@ import { ColorSchemeScript, mantineHtmlProps } from "@mantine/core"; import { HeaderConfig } from "@/features/core/types/header-config"; import { playerQueries } from "@/features/players/queries"; import { ensureServerQueryData } from "@/lib/tanstack-query/utils/ensure"; +import { DEFAULT_LOCALE, ensureMessages, resolveLocale } from "@/lib/i18n"; +import { getRootMeta } from "@/lib/i18n/meta"; import FullScreenLoader from "@/components/full-screen-loader"; import { CHROME_COLORS } from "@/lib/mantine/theme-colors"; import mantineCssUrl from '@mantine/core/styles.css?url' @@ -33,74 +35,81 @@ export const Route = createRootRouteWithContext<{ withPadding: boolean; fullWidth: boolean; }>()({ - head: () => ({ - title: "FLXN IX", - meta: [ - { - charSet: "utf-8", - }, - { - name: "viewport", - content: - "width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, interactive-widget=resizes-content, viewport-fit=cover", - }, - { name: 'description', content: 'FLXN — beer pong tournaments, brackets, and stats.' }, - { name: 'keywords', content: 'FLXN, beer pong, tournament, sports, statistics, pong' }, - { property: 'og:title', content: 'FLXN' }, - { property: 'og:description', content: 'FLXN — beer pong tournaments, brackets, and stats.' }, - { property: 'og:url', content: 'https://flexxon.app' }, - { property: 'og:type', content: 'website' }, - { property: 'og:site_name', content: 'FLXN' }, - { property: 'og:image', content: 'https://flexxon.app/favicon.png' }, - { property: 'og:image:width', content: '512' }, - { property: 'og:image:height', content: '512' }, - { property: 'og:image:alt', content: 'FLXN logo' }, - { property: 'og:locale', content: 'en_US' }, - { name: 'twitter:card', content: 'summary' }, - { name: 'twitter:title', content: 'FLXN' }, - { name: 'twitter:description', content: 'FLXN — beer pong tournaments, brackets, and stats.' }, - { name: 'twitter:image', content: 'https://flexxon.app/favicon.png' }, - { name: 'mobile-web-app-capable', content: 'yes' }, - { name: 'apple-mobile-web-app-capable', content: 'yes' }, - { name: 'apple-mobile-web-app-status-bar-style', content: 'default' }, - { name: 'apple-mobile-web-app-title', content: 'FLXN' }, - ], - links: [ - { - rel: "apple-touch-icon", - sizes: "180x180", - href: "/apple-touch-icon.png", - }, - { - rel: "icon", - type: "image/png", - sizes: "32x32", - href: "/favicon-32x32.png", - }, - { - rel: "icon", - type: "image/png", - sizes: "16x16", - href: "/favicon-16x16.png", - }, - { rel: "manifest", href: "/site.webmanifest" }, - { rel: "icon", href: "/favicon.ico" }, - { rel: 'stylesheet', href: mantineCssUrl }, - { rel: 'stylesheet', href: mantineCarouselCssUrl }, - { rel: 'stylesheet', href: mantineDatesCssUrl }, - { rel: 'stylesheet', href: mantineTiptapCssUrl }, - { rel: "preconnect", href: "https://fonts.googleapis.com" }, - { - rel: "preconnect", - href: "https://fonts.gstatic.com", - crossOrigin: "anonymous", - }, - { - rel: "stylesheet", - href: "https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&family=League+Spartan:wght@100..900&display=swap", - } - ], - }), + head: () => { + // Meta stays DEFAULT_LOCALE: crawlers/unfurlers have no session, and + // reading match.context here creates circular route-type inference that + // breaks beforeLoad typing in child routes. + const rootMeta = getRootMeta(DEFAULT_LOCALE); + + return { + title: "FLXN IX", + meta: [ + { + charSet: "utf-8", + }, + { + name: "viewport", + content: + "width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, interactive-widget=resizes-content, viewport-fit=cover", + }, + { name: 'description', content: rootMeta.description }, + { name: 'keywords', content: 'FLXN, beer pong, tournament, sports, statistics, pong' }, + { property: 'og:title', content: 'FLXN' }, + { property: 'og:description', content: rootMeta.description }, + { property: 'og:url', content: 'https://flexxon.app' }, + { property: 'og:type', content: 'website' }, + { property: 'og:site_name', content: 'FLXN' }, + { property: 'og:image', content: 'https://flexxon.app/favicon.png' }, + { property: 'og:image:width', content: '512' }, + { property: 'og:image:height', content: '512' }, + { property: 'og:image:alt', content: rootMeta.ogImageAlt }, + { property: 'og:locale', content: rootMeta.ogLocale }, + { name: 'twitter:card', content: 'summary' }, + { name: 'twitter:title', content: 'FLXN' }, + { name: 'twitter:description', content: rootMeta.description }, + { name: 'twitter:image', content: 'https://flexxon.app/favicon.png' }, + { name: 'mobile-web-app-capable', content: 'yes' }, + { name: 'apple-mobile-web-app-capable', content: 'yes' }, + { name: 'apple-mobile-web-app-status-bar-style', content: 'default' }, + { name: 'apple-mobile-web-app-title', content: 'FLXN' }, + ], + links: [ + { + rel: "apple-touch-icon", + sizes: "180x180", + href: "/apple-touch-icon.png", + }, + { + rel: "icon", + type: "image/png", + sizes: "32x32", + href: "/favicon-32x32.png", + }, + { + rel: "icon", + type: "image/png", + sizes: "16x16", + href: "/favicon-16x16.png", + }, + { rel: "manifest", href: "/site.webmanifest" }, + { rel: "icon", href: "/favicon.ico" }, + { rel: 'stylesheet', href: mantineCssUrl }, + { rel: 'stylesheet', href: mantineCarouselCssUrl }, + { rel: 'stylesheet', href: mantineDatesCssUrl }, + { rel: 'stylesheet', href: mantineTiptapCssUrl }, + { rel: "preconnect", href: "https://fonts.googleapis.com" }, + { + rel: "preconnect", + href: "https://fonts.gstatic.com", + crossOrigin: "anonymous", + }, + { + rel: "stylesheet", + href: "https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&family=League+Spartan:wght@100..900&display=swap", + } + ], + }; + }, errorComponent: (props) => { return ( @@ -123,6 +132,7 @@ export const Route = createRootRouteWithContext<{ context.queryClient, playerQueries.auth() ); + await ensureMessages(resolveLocale(auth?.metadata?.locale)); return { auth }; } catch (error: any) { if (isRedirect(error) || error instanceof Response) throw error; @@ -138,6 +148,7 @@ export const Route = createRootRouteWithContext<{ context.queryClient, playerQueries.auth() ); + await ensureMessages(resolveLocale(auth?.metadata?.locale)); return { auth }; } catch { return {}; diff --git a/src/app/routes/_authed/admin.tsx b/src/app/routes/_authed/admin.tsx index 09755e1..37869b2 100644 --- a/src/app/routes/_authed/admin.tsx +++ b/src/app/routes/_authed/admin.tsx @@ -1,4 +1,6 @@ import { Outlet, redirect, createFileRoute } from "@tanstack/react-router"; +import { msg } from "@lingui/core/macro"; +import type { HeaderConfig } from "@/features/core/types/header-config"; export const Route = createFileRoute("/_authed/admin")({ component: Outlet, @@ -7,12 +9,12 @@ export const Route = createFileRoute("/_authed/admin")({ throw redirect({ to: "/" }); } - return { - header: { - ...context.header, - title: "Admin", - withBackButton: true, - }, + const header: HeaderConfig = { + ...context.header, + title: msg`Admin`, + withBackButton: true, }; + + return { header }; }, }); diff --git a/src/app/routes/_authed/admin/activities.tsx b/src/app/routes/_authed/admin/activities.tsx index 86337ca..0cc963a 100644 --- a/src/app/routes/_authed/admin/activities.tsx +++ b/src/app/routes/_authed/admin/activities.tsx @@ -4,6 +4,8 @@ import { ActivitiesTable, activityQueries } from "@/features/activities"; import { PlayersActivityTable, playerQueries } from "@/features/players"; import { Box, Divider, Group, Skeleton, Stack, Tabs } from "@mantine/core"; import { Suspense, useState } from "react"; +import { Trans } from "@lingui/react/macro"; +import { msg } from "@lingui/core/macro"; export const Route = createFileRoute("/_authed/admin/activities")({ component: Stats, @@ -16,7 +18,7 @@ export const Route = createFileRoute("/_authed/admin/activities")({ withPadding: false, fullWidth: true, header: { - title: "Activities", + title: msg`Activities`, withBackButton: true, }, refresh: [activityQueries.search().queryKey, playerQueries.activity().queryKey], @@ -53,8 +55,8 @@ function Stats() { return ( - Server Functions - Player Activity + Server Functions + Player Activity diff --git a/src/app/routes/_authed/admin/index.tsx b/src/app/routes/_authed/admin/index.tsx index 7468ff0..64934ec 100644 --- a/src/app/routes/_authed/admin/index.tsx +++ b/src/app/routes/_authed/admin/index.tsx @@ -1,11 +1,12 @@ import { createFileRoute } from "@tanstack/react-router"; import { AdminPage } from "@/features/admin"; +import { msg } from "@lingui/core/macro"; export const Route = createFileRoute("/_authed/admin/")({ loader: () => ({ header: { withBackButton: true, - title: "Admin", + title: msg`Admin`, }, withPadding: false, }), diff --git a/src/app/routes/_authed/admin/preview.tsx b/src/app/routes/_authed/admin/preview.tsx index b13c1d2..ca6870f 100644 --- a/src/app/routes/_authed/admin/preview.tsx +++ b/src/app/routes/_authed/admin/preview.tsx @@ -2,13 +2,15 @@ import BracketPreview from "@/features/admin/components/preview"; import { NumberInput } from "@mantine/core"; import { createFileRoute } from "@tanstack/react-router"; import { useState } from "react"; +import { useLingui } from "@lingui/react/macro"; +import { msg } from "@lingui/core/macro"; export const Route = createFileRoute("/_authed/admin/preview")({ component: RouteComponent, loader: () => ({ header: { withBackButton: true, - title: "Bracket Preview", + title: msg`Bracket Preview`, }, withPadding: false, fullWidth: true, @@ -16,13 +18,14 @@ export const Route = createFileRoute("/_authed/admin/preview")({ }); function RouteComponent() { + const { t } = useLingui(); const [n, setN] = useState(16); return ( <> setN(value as number)} w={150} diff --git a/src/app/routes/_authed/admin/tournaments/$id/assign-partners.tsx b/src/app/routes/_authed/admin/tournaments/$id/assign-partners.tsx index 79b9bd1..f49fb71 100644 --- a/src/app/routes/_authed/admin/tournaments/$id/assign-partners.tsx +++ b/src/app/routes/_authed/admin/tournaments/$id/assign-partners.tsx @@ -9,6 +9,8 @@ import TeamAssignmentPreview from "@/features/tournaments/components/team-assign import { WarningCircleIcon, ShuffleIcon, CheckCircleIcon } from "@phosphor-icons/react"; import { PlayerInfo } from "@/features/players/types"; import { useQueryClient } from "@tanstack/react-query"; +import { Trans, Plural } from "@lingui/react/macro"; +import { msg } from "@lingui/core/macro"; export const Route = createFileRoute("/_authed/admin/tournaments/$id/assign-partners")({ beforeLoad: async ({ context, params }) => { @@ -23,7 +25,8 @@ export const Route = createFileRoute("/_authed/admin/tournaments/$id/assign-part loader: ({ context }) => ({ header: { withBackButton: true, - title: `Manage ${context.tournament.name}`, + title: msg`Manage {name}`, + titleValues: { name: context.tournament.name }, }, }), component: RouteComponent, @@ -120,19 +123,19 @@ function RouteComponent() { {freeAgents.length} - {freeAgents.length === 1 ? "player enrolled" : "players enrolled"} + {!hasEnoughPlayers && ( }> - Need at least 2 players to create teams + Need at least 2 players to create teams )} {hasOddPlayers && ( }> - Cannot create teams with an odd number of players. Please have one player unenroll. + Cannot create teams with an odd number of players. Please have one player unenroll. )} @@ -142,7 +145,7 @@ function RouteComponent() { onClick={handleGenerate} loading={generateMutation.isPending} > - Generate Random Pairings + Generate Random Pairings )} @@ -151,7 +154,7 @@ function RouteComponent() { - Partner Assignments + Partner Assignments diff --git a/src/app/routes/_authed/admin/tournaments/$id/index.tsx b/src/app/routes/_authed/admin/tournaments/$id/index.tsx index 3c23e98..ba24ea8 100644 --- a/src/app/routes/_authed/admin/tournaments/$id/index.tsx +++ b/src/app/routes/_authed/admin/tournaments/$id/index.tsx @@ -3,6 +3,7 @@ import { tournamentQueries } from "@/features/tournaments/queries"; import ManageTournament from "@/features/tournaments/components/manage-tournament"; import { ensureServerQueryData } from "@/lib/tanstack-query/utils/ensure"; import { Divider, Group, Skeleton, Stack } from "@mantine/core"; +import { msg } from "@lingui/core/macro"; export const Route = createFileRoute("/_authed/admin/tournaments/$id/")({ beforeLoad: async ({ context, params }) => { @@ -19,7 +20,8 @@ export const Route = createFileRoute("/_authed/admin/tournaments/$id/")({ loader: ({ context }) => ({ header: { withBackButton: true, - title: `Manage ${context.tournament.name}`, + title: msg`Manage {name}`, + titleValues: { name: context.tournament.name }, }, withPadding: false, }), diff --git a/src/app/routes/_authed/admin/tournaments/$id/teams.tsx b/src/app/routes/_authed/admin/tournaments/$id/teams.tsx index ced398a..fa96f99 100644 --- a/src/app/routes/_authed/admin/tournaments/$id/teams.tsx +++ b/src/app/routes/_authed/admin/tournaments/$id/teams.tsx @@ -3,6 +3,7 @@ import { tournamentQueries } from "@/features/tournaments/queries"; import ManageTeams from "@/features/teams/components/manage-teams"; import { ensureServerQueryData } from "@/lib/tanstack-query/utils/ensure"; import { Box, Divider, Group, Skeleton, Stack } from "@mantine/core"; +import { msg } from "@lingui/core/macro"; export const Route = createFileRoute("/_authed/admin/tournaments/$id/teams")({ beforeLoad: async ({ context, params }) => { @@ -19,7 +20,8 @@ export const Route = createFileRoute("/_authed/admin/tournaments/$id/teams")({ loader: ({ context }) => ({ header: { withBackButton: true, - title: `${context.tournament.name} Teams`, + title: msg`{name} Teams`, + titleValues: { name: context.tournament.name }, }, withPadding: false, }), diff --git a/src/app/routes/_authed/admin/tournaments/index.tsx b/src/app/routes/_authed/admin/tournaments/index.tsx index 1210da2..7210102 100644 --- a/src/app/routes/_authed/admin/tournaments/index.tsx +++ b/src/app/routes/_authed/admin/tournaments/index.tsx @@ -4,6 +4,7 @@ import { prefetchServerQuery } from "@/lib/tanstack-query/utils/prefetch"; import { Divider, Group, Skeleton, Stack } from "@mantine/core"; import { createFileRoute } from "@tanstack/react-router"; import { Suspense } from "react"; +import { msg } from "@lingui/core/macro"; export const Route = createFileRoute("/_authed/admin/tournaments/")({ beforeLoad: ({ context }) => { @@ -13,7 +14,7 @@ export const Route = createFileRoute("/_authed/admin/tournaments/")({ loader: () => ({ header: { withBackButton: true, - title: "Manage Tournaments", + title: msg`Manage Tournaments`, }, refresh: tournamentQueries.list().queryKey, withPadding: false, diff --git a/src/app/routes/_authed/admin/tournaments/run.$id.tsx b/src/app/routes/_authed/admin/tournaments/run.$id.tsx index 6b8200a..ad68e03 100644 --- a/src/app/routes/_authed/admin/tournaments/run.$id.tsx +++ b/src/app/routes/_authed/admin/tournaments/run.$id.tsx @@ -14,6 +14,8 @@ import { Match } from "@/features/matches/types"; import BracketView from "@/features/bracket/components/bracket-view"; import { SpotifyControlsBar } from "@/features/spotify/components"; import { useAuth } from "@/contexts/auth-context"; +import { Trans } from "@lingui/react/macro"; +import { msg } from "@lingui/core/macro"; export const Route = createFileRoute("/_authed/admin/tournaments/run/$id")({ beforeLoad: async ({ context, params }) => { @@ -33,7 +35,8 @@ export const Route = createFileRoute("/_authed/admin/tournaments/run/$id")({ showSpotifyPanel: true, header: { withBackButton: true, - title: `Run ${context.tournament.name}`, + title: msg`Run {name}`, + titleValues: { name: context.tournament.name }, }, }), component: RouteComponent, @@ -160,7 +163,7 @@ function RouteComponent() { />
- Knockout Bracket + <Trans>Knockout Bracket</Trans>
diff --git a/src/app/routes/_authed/badges.tsx b/src/app/routes/_authed/badges.tsx index c16290f..72111f7 100644 --- a/src/app/routes/_authed/badges.tsx +++ b/src/app/routes/_authed/badges.tsx @@ -5,6 +5,7 @@ import PlayerStatsTableSkeleton from '@/features/players/components/player-stats import { prefetchServerQuery } from '@/lib/tanstack-query/utils/prefetch'; import { createFileRoute } from '@tanstack/react-router'; import { Suspense } from 'react'; +import { msg } from '@lingui/core/macro'; export const Route = createFileRoute('/_authed/badges')({ component: Badges, @@ -16,7 +17,7 @@ export const Route = createFileRoute('/_authed/badges')({ withPadding: false, fullWidth: true, header: { - title: 'All Badges', + title: msg`All Badges`, withBackButton: true, }, refresh: [badgeQueries.allBadges().queryKey], diff --git a/src/app/routes/_authed/settings.tsx b/src/app/routes/_authed/settings.tsx index 1725d2b..4860ec6 100644 --- a/src/app/routes/_authed/settings.tsx +++ b/src/app/routes/_authed/settings.tsx @@ -2,14 +2,17 @@ import { createFileRoute } from "@tanstack/react-router"; import { Box, Title, Stack } from "@mantine/core"; import { ColorSchemePicker } from "@/features/settings/components/color-scheme-picker"; import AccentColorPicker from "@/features/settings/components/accent-color-picker"; +import LocalePicker from "@/features/settings/components/locale-picker"; import { NotificationsSection } from "@/features/settings/components/notifications-section"; import { SignOutIcon } from "@phosphor-icons/react"; import ListLink from "@/components/list-link"; +import { Trans, useLingui } from "@lingui/react/macro"; +import { msg } from "@lingui/core/macro"; export const Route = createFileRoute("/_authed/settings")({ loader: () => ({ header: { - title: "Settings", + title: msg`Settings`, withBackButton: true, }, withPadding: false, @@ -18,6 +21,7 @@ export const Route = createFileRoute("/_authed/settings")({ }); function RouteComponent() { + const { t } = useLingui(); return ( <> - Appearance + <Trans>Appearance</Trans> + - + ); } diff --git a/src/app/routes/_authed/stats.tsx b/src/app/routes/_authed/stats.tsx index 5533770..2d8c0e0 100644 --- a/src/app/routes/_authed/stats.tsx +++ b/src/app/routes/_authed/stats.tsx @@ -7,6 +7,8 @@ import { prefetchServerQuery } from "@/lib/tanstack-query/utils/prefetch"; import LeagueHeadToHead from "@/features/players/components/league-head-to-head"; import { Box, Loader, Tabs, Button, Group, Container, Stack } from "@mantine/core"; import { useQueryClient } from "@tanstack/react-query"; +import { Trans } from "@lingui/react/macro"; +import { msg } from "@lingui/core/macro"; export const Route = createFileRoute("/_authed/stats")({ component: Stats, @@ -18,7 +20,7 @@ export const Route = createFileRoute("/_authed/stats")({ withPadding: false, fullWidth: true, header: { - title: "Player Stats" + title: msg`Player Stats` }, refresh: [playerQueries.allStats().queryKey], }), @@ -41,8 +43,8 @@ function Stats() { return ( - Stats - Head to Head + Stats + Head to Head @@ -54,21 +56,21 @@ function Stats() { size="compact-xs" onClick={() => setViewType('all')} > - All + All diff --git a/src/app/routes/_authed/tournaments/$id.predictions.tsx b/src/app/routes/_authed/tournaments/$id.predictions.tsx index 5a72491..6fffa48 100644 --- a/src/app/routes/_authed/tournaments/$id.predictions.tsx +++ b/src/app/routes/_authed/tournaments/$id.predictions.tsx @@ -3,6 +3,7 @@ import { tournamentQueries, useTournament } from "@/features/tournaments/queries import { ensureServerQueryData } from "@/lib/tanstack-query/utils/ensure"; import { Container } from "@mantine/core"; import { PredictionLeaderboard } from "@/features/predictions/components/prediction-leaderboard"; +import { msg } from "@lingui/core/macro"; export const Route = createFileRoute("/_authed/tournaments/$id/predictions")({ beforeLoad: async ({ context, params }) => { @@ -19,7 +20,7 @@ export const Route = createFileRoute("/_authed/tournaments/$id/predictions")({ loader: () => ({ header: { withBackButton: true, - title: "Predictions", + title: msg`Predictions`, }, }), component: RouteComponent, diff --git a/src/app/routes/_authed/tournaments/$id.predictions_.$playerId.tsx b/src/app/routes/_authed/tournaments/$id.predictions_.$playerId.tsx index 15141f1..51b031a 100644 --- a/src/app/routes/_authed/tournaments/$id.predictions_.$playerId.tsx +++ b/src/app/routes/_authed/tournaments/$id.predictions_.$playerId.tsx @@ -1,4 +1,6 @@ import { createFileRoute, redirect } from "@tanstack/react-router"; +import { Trans } from "@lingui/react/macro"; +import { msg } from "@lingui/core/macro"; import { useMemo } from "react"; import { Box, Container, Group, Paper, Stack, Text } from "@mantine/core"; import { tournamentQueries, useTournament } from "@/features/tournaments/queries"; @@ -41,7 +43,8 @@ export const Route = createFileRoute( withPadding: false, header: { withBackButton: true, - title: `${context.prediction.player.first_name}'s Bracket`, + title: msg`{name}'s Bracket`, + titleValues: { name: context.prediction.player.first_name }, }, }), component: RouteComponent, @@ -100,7 +103,7 @@ function RouteComponent() { - PTS + PTS {score.points} @@ -108,7 +111,7 @@ function RouteComponent() { - PICKS + PICKS {score.correct}/{score.total} diff --git a/src/app/routes/_authed/tournaments/index.tsx b/src/app/routes/_authed/tournaments/index.tsx index 94cb0b0..2d6e4a8 100644 --- a/src/app/routes/_authed/tournaments/index.tsx +++ b/src/app/routes/_authed/tournaments/index.tsx @@ -4,6 +4,7 @@ import { prefetchServerQuery } from '@/lib/tanstack-query/utils/prefetch' import { Suspense } from 'react' import TournamentCardList from '@/features/tournaments/components/tournament-card-list' import { Skeleton, Stack } from '@mantine/core' +import { msg } from '@lingui/core/macro' export const Route = createFileRoute('/_authed/tournaments/')({ beforeLoad: async ({ context }) => { @@ -13,7 +14,7 @@ export const Route = createFileRoute('/_authed/tournaments/')({ loader: () => ({ header: { withBackButton: true, - title: 'Tournaments', + title: msg`Tournaments`, }, refresh: tournamentQueries.list().queryKey }), diff --git a/src/app/routes/api/push/test.ts b/src/app/routes/api/push/test.ts index 718dedf..3f8f587 100644 --- a/src/app/routes/api/push/test.ts +++ b/src/app/routes/api/push/test.ts @@ -1,4 +1,5 @@ import { createFileRoute } from "@tanstack/react-router"; +import { msg } from "@lingui/core/macro"; import { superTokensRequestMiddleware } from "@/utils/supertokens"; import { sendPushToPlayer } from "@/lib/push"; import { isPushConfigured } from "@/lib/config"; @@ -25,7 +26,7 @@ export const Route = createFileRoute("/api/push/test")({ try { const result = await sendPushToPlayer(player.id, { title: "Flexxon", - body: "Test notification — push is working on this device.", + body: msg`Test notification — push is working on this device.`, url: "/settings", tag: "flexxon-test", }); diff --git a/src/app/routes/api/spotify/capture.ts b/src/app/routes/api/spotify/capture.ts index 4a8b85b..18949b1 100644 --- a/src/app/routes/api/spotify/capture.ts +++ b/src/app/routes/api/spotify/capture.ts @@ -1,11 +1,15 @@ import { createFileRoute } from "@tanstack/react-router"; +import { msg } from "@lingui/core/macro"; import { SpotifyWebApiClient } from "@/lib/spotify/client"; import type { SpotifyPlaybackSnapshot } from "@/lib/spotify/types"; +import { localizedFor } from "@/lib/i18n/server-messages"; export const Route = createFileRoute("/api/spotify/capture")({ server: { handlers: { POST: async ({ request }: { request: Request }) => { + // No session middleware on this route; localize with DEFAULT_LOCALE. + const i18n = localizedFor(null); try { const cookies = request.headers.get("Cookie") || ""; const accessTokenMatch = cookies.match( @@ -14,7 +18,7 @@ export const Route = createFileRoute("/api/spotify/capture")({ if (!accessTokenMatch) { return new Response( - JSON.stringify({ error: "No access token found" }), + JSON.stringify({ error: i18n._(msg`No access token found`) }), { status: 401, headers: { "Content-Type": "application/json" }, @@ -29,7 +33,7 @@ export const Route = createFileRoute("/api/spotify/capture")({ if (!snapshot) { return new Response( - JSON.stringify({ error: "No active playback to capture" }), + JSON.stringify({ error: i18n._(msg`No active playback to capture`) }), { status: 400, headers: { "Content-Type": "application/json" }, @@ -47,7 +51,7 @@ export const Route = createFileRoute("/api/spotify/capture")({ const errorMessage = error instanceof Error ? error.message - : "Failed to capture playback state"; + : i18n._(msg`Failed to capture playback state`); return new Response(JSON.stringify({ error: errorMessage }), { status: 500, diff --git a/src/app/routes/api/spotify/playback.ts b/src/app/routes/api/spotify/playback.ts index 2cccf34..4e9b504 100644 --- a/src/app/routes/api/spotify/playback.ts +++ b/src/app/routes/api/spotify/playback.ts @@ -1,5 +1,7 @@ import { createFileRoute } from "@tanstack/react-router"; +import { msg } from "@lingui/core/macro"; import { SpotifyWebApiClient } from "@/lib/spotify/client"; +import { localizedFor } from "@/lib/i18n/server-messages"; function getAccessTokenFromCookies(request: Request): string | null { const cookieHeader = request.headers.get("cookie"); @@ -16,11 +18,13 @@ export const Route = createFileRoute("/api/spotify/playback")({ server: { handlers: { POST: async ({ request }: { request: Request }) => { + // No session middleware on this route; localize with DEFAULT_LOCALE. + const i18n = localizedFor(null); try { const accessToken = getAccessTokenFromCookies(request); if (!accessToken) { return new Response( - JSON.stringify({ error: "No access token found" }), + JSON.stringify({ error: i18n._(msg`No access token found`) }), { status: 401, headers: { "Content-Type": "application/json" }, @@ -41,7 +45,7 @@ export const Route = createFileRoute("/api/spotify/playback")({ if (!trackId) { return new Response( JSON.stringify({ - error: "trackId is required for playTrack action", + error: i18n._(msg`trackId is required for playTrack action`), }), { status: 400, @@ -63,7 +67,7 @@ export const Route = createFileRoute("/api/spotify/playback")({ case "volume": if (typeof volumePercent !== "number") { return new Response( - JSON.stringify({ error: "volumePercent must be a number" }), + JSON.stringify({ error: i18n._(msg`volumePercent must be a number`) }), { status: 400, headers: { "Content-Type": "application/json" }, @@ -76,7 +80,7 @@ export const Route = createFileRoute("/api/spotify/playback")({ if (!deviceId) { return new Response( JSON.stringify({ - error: "deviceId is required for transfer action", + error: i18n._(msg`deviceId is required for transfer action`), }), { status: 400, @@ -87,10 +91,13 @@ export const Route = createFileRoute("/api/spotify/playback")({ await spotifyClient.transferPlayback(deviceId); break; default: - return new Response(JSON.stringify({ error: "Invalid action" }), { - status: 400, - headers: { "Content-Type": "application/json" }, - }); + return new Response( + JSON.stringify({ error: i18n._(msg`Invalid action`) }), + { + status: 400, + headers: { "Content-Type": "application/json" }, + } + ); } return new Response(JSON.stringify({ success: true }), { @@ -104,8 +111,9 @@ export const Route = createFileRoute("/api/spotify/playback")({ if (error.message.includes("NO_ACTIVE_DEVICE")) { return new Response( JSON.stringify({ - error: - "No active device found. Please select a device first.", + error: i18n._( + msg`No active device found. Please select a device first.` + ), }), { status: 400, @@ -117,7 +125,9 @@ export const Route = createFileRoute("/api/spotify/playback")({ if (error.message.includes("PREMIUM_REQUIRED")) { return new Response( JSON.stringify({ - error: "Spotify Premium is required for playback control.", + error: i18n._( + msg`Spotify Premium is required for playback control.` + ), }), { status: 403, @@ -135,7 +145,7 @@ export const Route = createFileRoute("/api/spotify/playback")({ return new Response( JSON.stringify({ - error: "Playback control failed", + error: i18n._(msg`Playback control failed`), details: error instanceof Error ? error.message : "Unknown error", }), { @@ -147,11 +157,13 @@ export const Route = createFileRoute("/api/spotify/playback")({ }, GET: async ({ request }: { request: Request }) => { + // No session middleware on this route; localize with DEFAULT_LOCALE. + const i18n = localizedFor(null); try { const accessToken = getAccessTokenFromCookies(request); if (!accessToken) { return new Response( - JSON.stringify({ error: "No access token found" }), + JSON.stringify({ error: i18n._(msg`No access token found`) }), { status: 401, headers: { "Content-Type": "application/json" }, @@ -190,7 +202,7 @@ export const Route = createFileRoute("/api/spotify/playback")({ } catch (error) { console.error("Get playback data error:", error); return new Response( - JSON.stringify({ error: "Failed to get playback data" }), + JSON.stringify({ error: i18n._(msg`Failed to get playback data`) }), { status: 500, headers: { "Content-Type": "application/json" }, diff --git a/src/app/routes/api/spotify/resume.ts b/src/app/routes/api/spotify/resume.ts index 046c4f8..2b98afc 100644 --- a/src/app/routes/api/spotify/resume.ts +++ b/src/app/routes/api/spotify/resume.ts @@ -1,11 +1,15 @@ import { createFileRoute } from "@tanstack/react-router"; +import { msg } from "@lingui/core/macro"; import { SpotifyWebApiClient } from "@/lib/spotify/client"; import type { SpotifyPlaybackSnapshot } from "@/lib/spotify/types"; +import { localizedFor } from "@/lib/i18n/server-messages"; export const Route = createFileRoute("/api/spotify/resume")({ server: { handlers: { POST: async ({ request }: { request: Request }) => { + // No session middleware on this route; localize with DEFAULT_LOCALE. + const i18n = localizedFor(null); try { const cookies = request.headers.get("Cookie") || ""; const accessTokenMatch = cookies.match( @@ -14,7 +18,7 @@ export const Route = createFileRoute("/api/spotify/resume")({ if (!accessTokenMatch) { return new Response( - JSON.stringify({ error: "No access token found" }), + JSON.stringify({ error: i18n._(msg`No access token found`) }), { status: 401, headers: { "Content-Type": "application/json" }, @@ -30,7 +34,7 @@ export const Route = createFileRoute("/api/spotify/resume")({ if (!snapshot) { return new Response( - JSON.stringify({ error: "No snapshot provided" }), + JSON.stringify({ error: i18n._(msg`No snapshot provided`) }), { status: 400, headers: { "Content-Type": "application/json" }, @@ -47,14 +51,14 @@ export const Route = createFileRoute("/api/spotify/resume")({ } catch (error) { console.error("Spotify resume error:", error); - let errorMessage = "Failed to resume playback state"; + let errorMessage = i18n._(msg`Failed to resume playback state`); if (error instanceof Error) { if ( error.message.includes("Premium") || error.message.includes("403") ) { - errorMessage = "Spotify premium required"; + errorMessage = i18n._(msg`Spotify premium required`); } else { errorMessage = error.message; } diff --git a/src/app/routes/api/teams/upload-logo.ts b/src/app/routes/api/teams/upload-logo.ts index 40a4b1a..1ea5723 100644 --- a/src/app/routes/api/teams/upload-logo.ts +++ b/src/app/routes/api/teams/upload-logo.ts @@ -1,7 +1,9 @@ import { createFileRoute } from "@tanstack/react-router"; +import { msg } from "@lingui/core/macro"; import { superTokensRequestMiddleware } from "@/utils/supertokens"; import { pbAdmin } from "@/lib/pocketbase/client"; import { logger } from "@/lib/logger"; +import { localizedFor } from "@/lib/i18n/server-messages"; import { z } from "zod"; const uploadSchema = z.object({ @@ -13,6 +15,7 @@ export const Route = createFileRoute("/api/teams/upload-logo")({ middleware: [superTokensRequestMiddleware], handlers: { POST: async ({ request, context }) => { + const i18n = localizedFor(context.metadata); try { const userId = context.userAuthId; const isAdmin = context.roles.includes("Admin"); @@ -27,7 +30,7 @@ export const Route = createFileRoute("/api/teams/upload-logo")({ if (!validationResult.success) { return new Response( JSON.stringify({ - error: "Invalid input", + error: i18n._(msg`Invalid input`), details: validationResult.error.issues, }), { @@ -40,7 +43,7 @@ export const Route = createFileRoute("/api/teams/upload-logo")({ if (!logoFile || logoFile.size === 0) { return new Response( JSON.stringify({ - error: "Logo file is required", + error: i18n._(msg`Logo file is required`), }), { status: 400, @@ -58,7 +61,9 @@ export const Route = createFileRoute("/api/teams/upload-logo")({ if (!allowedTypes.includes(logoFile.type)) { return new Response( JSON.stringify({ - error: "Invalid file type. Only JPEG, PNG and GIF are allowed.", + error: i18n._( + msg`Invalid file type. Only JPEG, PNG and GIF are allowed.` + ), }), { status: 400, @@ -71,7 +76,7 @@ export const Route = createFileRoute("/api/teams/upload-logo")({ if (logoFile.size > maxSize) { return new Response( JSON.stringify({ - error: "File too large. Maximum size is 10MB.", + error: i18n._(msg`File too large. Maximum size is 10MB.`), }), { status: 400, @@ -84,7 +89,7 @@ export const Route = createFileRoute("/api/teams/upload-logo")({ if (!team) { return new Response( JSON.stringify({ - error: "Team not found", + error: i18n._(msg`Team not found`), }), { status: 404, @@ -132,8 +137,8 @@ export const Route = createFileRoute("/api/teams/upload-logo")({ return new Response( JSON.stringify({ - error: "Failed to upload logo", - message: error.message || "Unknown error occurred", + error: i18n._(msg`Failed to upload logo`), + message: error.message || i18n._(msg`Unknown error occurred`), }), { status: 500, diff --git a/src/app/routes/api/tournaments/upload-logo.ts b/src/app/routes/api/tournaments/upload-logo.ts index b0f60dc..9b7f990 100644 --- a/src/app/routes/api/tournaments/upload-logo.ts +++ b/src/app/routes/api/tournaments/upload-logo.ts @@ -1,7 +1,9 @@ import { createFileRoute } from "@tanstack/react-router"; +import { msg } from "@lingui/core/macro"; import { superTokensRequestMiddleware } from "@/utils/supertokens"; import { pbAdmin } from "@/lib/pocketbase/client"; import { logger } from "@/lib/logger"; +import { localizedFor } from "@/lib/i18n/server-messages"; import { z } from "zod"; const uploadSchema = z.object({ @@ -13,6 +15,7 @@ export const Route = createFileRoute("/api/tournaments/upload-logo")({ middleware: [superTokensRequestMiddleware], handlers: { POST: async ({ request, context }) => { + const i18n = localizedFor(context.metadata); try { const userId = context.userAuthId; const isAdmin = context.roles.includes("Admin"); @@ -28,7 +31,7 @@ export const Route = createFileRoute("/api/tournaments/upload-logo")({ if (!validationResult.success) { return new Response( JSON.stringify({ - error: "Invalid input", + error: i18n._(msg`Invalid input`), details: validationResult.error.issues, }), { @@ -41,7 +44,7 @@ export const Route = createFileRoute("/api/tournaments/upload-logo")({ if (!logoFile || logoFile.size === 0) { return new Response( JSON.stringify({ - error: "Logo file is required", + error: i18n._(msg`Logo file is required`), }), { status: 400, @@ -59,7 +62,9 @@ export const Route = createFileRoute("/api/tournaments/upload-logo")({ if (!allowedTypes.includes(logoFile.type)) { return new Response( JSON.stringify({ - error: "Invalid file type. Only JPEG, PNG and GIF are allowed.", + error: i18n._( + msg`Invalid file type. Only JPEG, PNG and GIF are allowed.` + ), }), { status: 400, @@ -72,7 +77,7 @@ export const Route = createFileRoute("/api/tournaments/upload-logo")({ if (logoFile.size > maxSize) { return new Response( JSON.stringify({ - error: "File too large. Maximum size is 10MB.", + error: i18n._(msg`File too large. Maximum size is 10MB.`), }), { status: 400, @@ -85,7 +90,7 @@ export const Route = createFileRoute("/api/tournaments/upload-logo")({ if (!tournament) { return new Response( JSON.stringify({ - error: "Tournament not found", + error: i18n._(msg`Tournament not found`), }), { status: 404, @@ -130,8 +135,8 @@ export const Route = createFileRoute("/api/tournaments/upload-logo")({ return new Response( JSON.stringify({ - error: "Failed to upload logo", - message: error.message || "Unknown error occurred", + error: i18n._(msg`Failed to upload logo`), + message: error.message || i18n._(msg`Unknown error occurred`), }), { status: 500, diff --git a/src/components/DefaultCatchBoundary.tsx b/src/components/DefaultCatchBoundary.tsx index 828fd54..81f4f8a 100644 --- a/src/components/DefaultCatchBoundary.tsx +++ b/src/components/DefaultCatchBoundary.tsx @@ -23,25 +23,27 @@ import { useEffect } from 'react' import toast from '@/lib/sonner' import { logger } from '@/lib/logger' import { XCircleIcon, WarningIcon } from '@phosphor-icons/react' +import { Trans, useLingui } from '@lingui/react/macro' import Button from './button' export function DefaultCatchBoundary({ error }: ErrorComponentProps) { const router = useRouter() const navigate = useNavigate() + const { t } = useLingui() const isRoot = useMatch({ strict: false, select: (state) => state.id === rootRouteId, }) const [detailsOpened, { toggle: toggleDetails }] = useDisclosure(false) - const errorMessage = error?.message || 'Unknown error' + const errorMessage = error?.message || t`Unknown error` const errorStack = error?.stack || 'No stack trace available' useEffect(() => { logger.error('DefaultCatchBoundary | ', error) if (errorMessage.toLowerCase().includes('unauthenticated')) { - toast.error('You\'ve been logged out') + toast.error(t`You've been logged out`) router.history.push('/login') throw redirect({ to: '/login' }) } @@ -53,23 +55,23 @@ export function DefaultCatchBoundary({ error }: ErrorComponentProps) {
- Access Denied + Access Denied - You don't have permission to access this page. + You don't have permission to access this page. - Home + Home @@ -84,21 +86,21 @@ export function DefaultCatchBoundary({ error }: ErrorComponentProps) { - Something went wrong + Something went wrong - An error occurred while loading this page. + An error occurred while loading this page. - Error: {errorMessage} + Error: {errorMessage} @@ -112,7 +114,7 @@ export function DefaultCatchBoundary({ error }: ErrorComponentProps) { variant="light" onClick={() => router.invalidate()} > - Retry + Retry {isRoot ? ( - Home + Home ) : ( )} diff --git a/src/components/avatar.tsx b/src/components/avatar.tsx index a7b5ded..86c4bb8 100644 --- a/src/components/avatar.tsx +++ b/src/components/avatar.tsx @@ -10,6 +10,7 @@ import { } from "@mantine/core"; import { useState } from "react"; import { XIcon } from "@phosphor-icons/react"; +import { useLingui } from "@lingui/react/macro"; interface AvatarProps extends Omit { @@ -30,6 +31,7 @@ const Avatar = ({ contain = false, ...props }: AvatarProps) => { + const { t } = useLingui(); const [isFullscreenOpen, setIsFullscreenOpen] = useState(false); const hasImage = Boolean(props.src); @@ -102,7 +104,7 @@ const Avatar = ({ color="dark" size="lg" radius="xl" - aria-label="Close image preview" + aria-label={t`Close image preview`} style={{ position: 'absolute', top: -10, diff --git a/src/components/countdown.tsx b/src/components/countdown.tsx index 2a3f79e..c2dae28 100644 --- a/src/components/countdown.tsx +++ b/src/components/countdown.tsx @@ -1,6 +1,7 @@ import useNow from '@/hooks/use-now'; import { Text, Group } from '@mantine/core'; import { useMemo } from 'react'; +import { useLingui } from '@lingui/react/macro'; import classes from './countdown.module.css'; interface CountdownProps { @@ -34,6 +35,7 @@ function calculateTimeLeft(targetDate: Date, currentTime = new Date()): TimeLeft const pad = (num: number) => num.toString().padStart(2, '0'); export function Countdown({ date, label, color }: CountdownProps) { + const { t } = useLingui(); const now = useNow(); const timeLeft = useMemo(() => calculateTimeLeft(date, now), [date, now]); @@ -45,7 +47,7 @@ export function Countdown({ date, label, color }: CountdownProps) { const prefix = timeLeft.days > 0 - ? `${timeLeft.days}d ${pad(timeLeft.hours)}:${pad(timeLeft.minutes)}:` + ? `${t`${timeLeft.days}d`} ${pad(timeLeft.hours)}:${pad(timeLeft.minutes)}:` : `${pad(timeLeft.hours)}:${pad(timeLeft.minutes)}:`; return ( diff --git a/src/components/date-time-picker.tsx b/src/components/date-time-picker.tsx index 69abddd..71c6756 100644 --- a/src/components/date-time-picker.tsx +++ b/src/components/date-time-picker.tsx @@ -2,6 +2,7 @@ import { DatePicker, TimeInput } from "@mantine/dates"; import { ActionIcon, Stack } from "@mantine/core"; import { useRef } from "react"; import { ClockIcon } from "@phosphor-icons/react"; +import { useLingui } from "@lingui/react/macro"; interface DateTimePickerProps { value: Date | null; @@ -16,6 +17,7 @@ const DateTimePicker = ({ label, ...rest }: DateTimePickerProps) => { + const { t } = useLingui(); const timeRef = useRef(null); const currentDate = value ? new Date(value) : null; @@ -73,7 +75,7 @@ const DateTimePicker = ({ /> (null) @@ -31,8 +33,8 @@ export function IOSInstallPrompt() { if (!show || !platform) return null const instructions = platform === 'ios' - ? 'Tap Share → Add to Home Screen' - : 'Tap Menu (⋮) → Add to Home screen' + ? t`Tap Share → Add to Home Screen` + : t`Tap Menu (⋮) → Add to Home screen` return ( @@ -42,7 +44,7 @@ export function IOSInstallPrompt() { - Please install FLXN • This will save me Twilio credits as you won't be signed out! + Please install FLXN • This will save me Twilio credits as you won't be signed out! {instructions} diff --git a/src/components/phone-number-input.tsx b/src/components/phone-number-input.tsx index 60df2b6..45b1747 100644 --- a/src/components/phone-number-input.tsx +++ b/src/components/phone-number-input.tsx @@ -1,6 +1,7 @@ import { Input, InputProps, Group, Text } from "@mantine/core"; import { CheckFat, Phone } from "@phosphor-icons/react"; import { IMaskInput } from "react-imask"; +import { Trans, useLingui } from "@lingui/react/macro"; interface PhoneNumberInputProps extends InputProps { id: string; @@ -20,6 +21,7 @@ const PhoneNumberInput: React.FC = ({ error, ...props }) => { + const { t } = useLingui(); return ( = ({  {" "} - +1 + +1 } leftSectionWidth={50} leftSectionProps={{ style: { padding: 0 } }} - placeholder="(713) 867-5309" + placeholder={t`(713) 867-5309`} onAccept={(_, mask) => onChange(mask.unmaskedValue)} rightSection={ value?.length === 10 && ( diff --git a/src/components/sheet/modal.tsx b/src/components/sheet/modal.tsx index 091b6f6..6ce0fa9 100644 --- a/src/components/sheet/modal.tsx +++ b/src/components/sheet/modal.tsx @@ -1,5 +1,6 @@ import { Flex, Loader, Modal as MantineModal, Title } from "@mantine/core"; import { PropsWithChildren, Suspense } from "react"; +import { useLingui } from "@lingui/react/macro"; interface ModalProps extends PropsWithChildren { title?: string; @@ -15,31 +16,35 @@ const Modal: React.FC = ({ opened, onClose, onExited, -}) => ( - {title}} - radius={20} - transitionProps={{ - transition: "pop", - duration: 200, - timingFunction: "ease-out", - onExited, - }} - overlayProps={{ backgroundOpacity: 0.4 }} - closeButtonProps={{ "aria-label": "Close" }} - > - - - - } +}) => { + const { t } = useLingui(); + + return ( + {title}} + radius={20} + transitionProps={{ + transition: "pop", + duration: 200, + timingFunction: "ease-out", + onExited, + }} + overlayProps={{ backgroundOpacity: 0.4 }} + closeButtonProps={{ "aria-label": t`Close` }} > - {children} - - -); + + + + } + > + {children} + + + ); +}; export default Modal; diff --git a/src/components/sheet/slide-panel/slide-panel-field.tsx b/src/components/sheet/slide-panel/slide-panel-field.tsx index 176d926..01c528e 100644 --- a/src/components/sheet/slide-panel/slide-panel-field.tsx +++ b/src/components/sheet/slide-panel/slide-panel-field.tsx @@ -1,6 +1,7 @@ import { Box, Text, UnstyledButton, Flex, Stack } from "@mantine/core"; import { CaretRightIcon } from "@phosphor-icons/react"; import React, { ComponentType, useContext } from "react"; +import { useLingui } from "@lingui/react/macro"; import { SlidePanelContext } from "./slide-panel-context"; interface SlidePanelFieldProps { @@ -23,12 +24,13 @@ const SlidePanelField = ({ Component, title, label, - placeholder = "Select value", + placeholder, withAsterisk = false, formatValue, componentProps, error, }: SlidePanelFieldProps) => { + const { t, i18n } = useLingui(); const context = useContext(SlidePanelContext); if (!context) { @@ -53,11 +55,11 @@ const SlidePanelField = ({ } if (value != null) { if (value instanceof Date) { - return value.toLocaleDateString(); + return value.toLocaleDateString(i18n.locale); } return String(value); } - return placeholder; + return placeholder ?? t`Select value`; }; return ( diff --git a/src/components/sheet/slide-panel/slide-panel.tsx b/src/components/sheet/slide-panel/slide-panel.tsx index 7ccf2e4..730638e 100644 --- a/src/components/sheet/slide-panel/slide-panel.tsx +++ b/src/components/sheet/slide-panel/slide-panel.tsx @@ -9,6 +9,7 @@ import { } from "@mantine/core"; import { ArrowLeftIcon, CheckIcon } from "@phosphor-icons/react"; import { useState, ReactNode } from "react"; +import { Trans, useLingui } from "@lingui/react/macro"; import { SlidePanelContext, type PanelConfig } from "./slide-panel-context"; import Button from "@/components/button"; @@ -28,13 +29,16 @@ const SlidePanel = ({ children, onSubmit, onCancel, - submitText = "Submit", - cancelText = "Cancel", + submitText, + cancelText, cancelColor = "red", maxHeight = "70vh", formProps = {}, loading = false, }: SlidePanelProps) => { + const { t } = useLingui(); + const resolvedSubmitText = submitText ?? t`Submit`; + const resolvedCancelText = cancelText ?? t`Cancel`; const [isOpen, setIsOpen] = useState(false); const [panelConfig, setPanelConfig] = useState(null); const [tempValue, setTempValue] = useState(null); @@ -113,7 +117,7 @@ const SlidePanel = ({ loading={loading} disabled={loading} > - {submitText} + {resolvedSubmitText} {onCancel && ( )} @@ -153,7 +157,7 @@ const SlidePanel = ({ @@ -162,7 +166,7 @@ const SlidePanel = ({ variant="transparent" color="green" onClick={handleConfirm} - aria-label="Confirm" + aria-label={t`Confirm`} > @@ -182,8 +186,8 @@ const SlidePanel = ({ /> - - + + )} diff --git a/src/components/stats-overview.tsx b/src/components/stats-overview.tsx index 57f5bb3..2f27ad9 100644 --- a/src/components/stats-overview.tsx +++ b/src/components/stats-overview.tsx @@ -19,6 +19,7 @@ import { ArrowUpIcon, ArrowDownIcon, } from "@phosphor-icons/react"; +import { Trans, useLingui } from "@lingui/react/macro"; import { BaseStats } from "@/types/stats"; interface StatsOverviewProps { @@ -62,11 +63,13 @@ const StatItem = ({ }; const StatsOverview = ({ statsData, isLoading = false }: StatsOverviewProps) => { + const { t } = useLingui(); + if (!statsData && !isLoading) { return ( - No stats available yet + No stats available yet ); @@ -96,15 +99,15 @@ const StatsOverview = ({ statsData, isLoading = false }: StatsOverviewProps) => const avgMarginOfLoss = statsData.margin_of_loss ? parseFloat(statsData.margin_of_loss.toFixed(1)) : 0; const allStats = [ - { label: "Matches Played", value: overallStats.matches, Icon: BoxingGloveIcon }, - { label: "Wins", value: overallStats.wins, Icon: CrownIcon }, - { label: "Losses", value: overallStats.losses, Icon: XIcon }, - { label: "Cups Made", value: overallStats.total_cups_made, Icon: FireIcon }, - { label: "Cups Against", value: overallStats.total_cups_against, Icon: ShieldIcon }, - { label: "Avg Cups Per Match", value: avgCupsPerMatch >= 0 ? avgCupsPerMatch : null, Icon: ChartLineUpIcon }, - { label: "Avg Cups Against", value: avgCupsAgainstPerMatch >= 0 ? avgCupsAgainstPerMatch : null, Icon: ShieldCheckIcon }, - { label: "Avg Win Margin", value: avgMarginOfVictory >= 0 ? avgMarginOfVictory : null, Icon: ArrowUpIcon }, - { label: "Avg Loss Margin", value: avgMarginOfLoss >= 0 ? avgMarginOfLoss : null, Icon: ArrowDownIcon }, + { label: t`Matches Played`, value: overallStats.matches, Icon: BoxingGloveIcon }, + { label: t`Wins`, value: overallStats.wins, Icon: CrownIcon }, + { label: t`Losses`, value: overallStats.losses, Icon: XIcon }, + { label: t`Cups Made`, value: overallStats.total_cups_made, Icon: FireIcon }, + { label: t`Cups Against`, value: overallStats.total_cups_against, Icon: ShieldIcon }, + { label: t`Avg Cups Per Match`, value: avgCupsPerMatch >= 0 ? avgCupsPerMatch : null, Icon: ChartLineUpIcon }, + { label: t`Avg Cups Against`, value: avgCupsAgainstPerMatch >= 0 ? avgCupsAgainstPerMatch : null, Icon: ShieldCheckIcon }, + { label: t`Avg Win Margin`, value: avgMarginOfVictory >= 0 ? avgMarginOfVictory : null, Icon: ArrowUpIcon }, + { label: t`Avg Loss Margin`, value: avgMarginOfLoss >= 0 ? avgMarginOfLoss : null, Icon: ArrowDownIcon }, ]; return ( @@ -127,16 +130,17 @@ const StatsOverview = ({ statsData, isLoading = false }: StatsOverviewProps) => }; export const StatsSkeleton = () => { + const { t } = useLingui(); const skeletonStats = [ - { label: "Matches Played", Icon: BoxingGloveIcon }, - { label: "Wins", Icon: CrownIcon }, - { label: "Losses", Icon: XIcon }, - { label: "Cups Made", Icon: FireIcon }, - { label: "Cups Against", Icon: ShieldIcon }, - { label: "Avg Cups Per Match", Icon: ChartLineUpIcon }, - { label: "Avg Cups Against", Icon: ShieldCheckIcon }, - { label: "Avg Win Margin", Icon: ArrowUpIcon }, - { label: "Avg Loss Margin", Icon: ArrowDownIcon }, + { label: t`Matches Played`, Icon: BoxingGloveIcon }, + { label: t`Wins`, Icon: CrownIcon }, + { label: t`Losses`, Icon: XIcon }, + { label: t`Cups Made`, Icon: FireIcon }, + { label: t`Cups Against`, Icon: ShieldIcon }, + { label: t`Avg Cups Per Match`, Icon: ChartLineUpIcon }, + { label: t`Avg Cups Against`, Icon: ShieldCheckIcon }, + { label: t`Avg Win Margin`, Icon: ArrowUpIcon }, + { label: t`Avg Loss Margin`, Icon: ArrowDownIcon }, ]; return ( diff --git a/src/components/swipeable-tabs.tsx b/src/components/swipeable-tabs.tsx index 6404d19..64f2bac 100644 --- a/src/components/swipeable-tabs.tsx +++ b/src/components/swipeable-tabs.tsx @@ -12,9 +12,13 @@ import { useRouter } from "@tanstack/react-router"; interface TabItem { label: string; + /** Stable URL slug for the ?tab= param; defaults to label. Set this when label is translated. */ + value?: string; content: ReactNode; } +const tabValue = (tab: TabItem) => (tab.value ?? tab.label).toLowerCase(); + interface SwipeableTabsProps { tabs: TabItem[]; defaultTab?: number; @@ -36,7 +40,7 @@ function SwipeableTabs({ const urlTab = search?.tab; if (typeof urlTab === "string") { const tabIndex = tabs.findIndex( - (tab) => tab.label.toLowerCase() === urlTab.toLowerCase() + (tab) => tabValue(tab) === urlTab.toLowerCase() ); return tabIndex !== -1 ? tabIndex : defaultTab; } @@ -62,7 +66,7 @@ function SwipeableTabs({ ?.querySelector(".mantine-ScrollArea-viewport") ?.scrollTo({ top: 0 }); - const tabLabel = tabs[index].label.toLowerCase(); + const tabLabel = tabValue(tabs[index]); if (typeof window !== "undefined") { const url = new URL(window.location.href); url.searchParams.set("tab", tabLabel); diff --git a/src/components/typeahead.tsx b/src/components/typeahead.tsx index 5d096c6..a21743a 100644 --- a/src/components/typeahead.tsx +++ b/src/components/typeahead.tsx @@ -1,6 +1,7 @@ import { useState, useRef, useEffect, ReactNode } from "react"; import { TextInput, Loader, Paper, Stack, Box, Text } from "@mantine/core"; import { useDebouncedCallback } from "@mantine/hooks"; +import { useLingui } from "@lingui/react/macro"; export interface TypeaheadOption { id: string; @@ -24,12 +25,14 @@ const Typeahead = ({ searchFn, renderOption, format, - placeholder = "Search...", + placeholder, debounceMs = 300, disabled = false, initialValue = "", maxHeight = 200, }: TypeaheadProps) => { + const { t } = useLingui(); + const resolvedPlaceholder = placeholder ?? t`Search...`; const [searchQuery, setSearchQuery] = useState(initialValue); const [searchResults, setSearchResults] = useState[]>([]); const [isLoading, setIsLoading] = useState(false); @@ -119,7 +122,7 @@ const Typeahead = ({ } await performSearch(searchQuery); }} - placeholder={placeholder} + placeholder={resolvedPlaceholder} rightSection={isLoading ? : null} disabled={disabled} /> @@ -164,7 +167,7 @@ const Typeahead = ({ ) : ( - {searchQuery.trim() ? 'No results found' : 'Start typing to search...'} + {searchQuery.trim() ? t`No results found` : t`Start typing to search...`} )} diff --git a/src/contexts/auth-context.tsx b/src/contexts/auth-context.tsx index 1f13d16..340ecee 100644 --- a/src/contexts/auth-context.tsx +++ b/src/contexts/auth-context.tsx @@ -12,7 +12,11 @@ import { playerKeys, playerQueries, useMe } from "@/features/players/queries"; interface AuthData { user: Player | undefined; - metadata: { accentColor: MantineColor; colorScheme: MantineColorScheme }; + metadata: { + accentColor: MantineColor; + colorScheme: MantineColorScheme; + locale?: string; + }; roles: string[]; phone: string; } diff --git a/src/contexts/spotify-context.tsx b/src/contexts/spotify-context.tsx index f173621..c2ff000 100644 --- a/src/contexts/spotify-context.tsx +++ b/src/contexts/spotify-context.tsx @@ -1,4 +1,5 @@ import { createContext, useCallback, useEffect, useMemo, useState, PropsWithChildren } from 'react'; +import { useLingui } from '@lingui/react/macro'; import { SpotifyAuth } from '@/lib/spotify/auth'; import { useAuth } from './auth-context'; import { useConfig } from '@/hooks/use-config'; @@ -38,49 +39,51 @@ const deepEqual = (a: unknown, b: unknown): boolean => { ); }; -const makeSpotifyRequest = async (endpoint: string, options: RequestInit = {}) => { - const response = await fetch(`/api/spotify/${endpoint}`, { - ...options, - credentials: 'include', - headers: { - 'Content-Type': 'application/json', - ...options.headers, - }, - }); - - if (!response.ok) { - let errorMessage = 'Request failed'; - try { - const errorData = await response.json(); - errorMessage = errorData.error || errorMessage; - } catch { - errorMessage = `HTTP ${response.status}: ${response.statusText}`; - } - throw new Error(errorMessage); - } - - if (response.status === 204 || response.headers.get('content-length') === '0') { - return {}; - } - - const contentType = response.headers.get('content-type') || ''; - if (!contentType.includes('application/json')) { - console.warn(`Non-JSON response from ${endpoint}:`, contentType); - return {}; - } - - try { - return await response.json(); - } catch (error) { - console.warn(`Failed to parse JSON response from ${endpoint}:`, error); - return {}; - } -}; - export const SpotifyProvider: React.FC = ({ children }) => { const { roles } = useAuth(); const isAdmin = roles?.includes('Admin') || false; const config = useConfig(); + const { t } = useLingui(); + + // Defined inside the provider so error messages can be localized via `t`. + const makeSpotifyRequest = async (endpoint: string, options: RequestInit = {}) => { + const response = await fetch(`/api/spotify/${endpoint}`, { + ...options, + credentials: 'include', + headers: { + 'Content-Type': 'application/json', + ...options.headers, + }, + }); + + if (!response.ok) { + let errorMessage = t`Request failed`; + try { + const errorData = await response.json(); + errorMessage = errorData.error || errorMessage; + } catch { + errorMessage = t`HTTP ${response.status}: ${response.statusText}`; + } + throw new Error(errorMessage); + } + + if (response.status === 204 || response.headers.get('content-length') === '0') { + return {}; + } + + const contentType = response.headers.get('content-type') || ''; + if (!contentType.includes('application/json')) { + console.warn(`Non-JSON response from ${endpoint}:`, contentType); + return {}; + } + + try { + return await response.json(); + } catch (error) { + console.warn(`Failed to parse JSON response from ${endpoint}:`, error); + return {}; + } + }; const [authState, setAuthState] = useState(defaultSpotifyState); @@ -150,10 +153,9 @@ export const SpotifyProvider: React.FC = ({ children }) => { } if (error) { - let errorMessage = `Authentication failed: ${error}`; - if (details) { - errorMessage += ` - ${decodeURIComponent(details)}`; - } + const errorMessage = details + ? t`Authentication failed: ${error} - ${decodeURIComponent(details)}` + : t`Authentication failed: ${error}`; setError(errorMessage); console.error('Spotify OAuth Error:', { error, details }); @@ -203,7 +205,7 @@ export const SpotifyProvider: React.FC = ({ children }) => { } finally { setIsLoading(false); } - }, [authState.isAuthenticated]); + }, [authState.isAuthenticated, t]); const playTrack = useCallback(async (trackId: string, deviceId?: string, positionMs?: number) => { if (!authState.isAuthenticated) return; @@ -226,7 +228,7 @@ export const SpotifyProvider: React.FC = ({ children }) => { } finally { setIsLoading(false); } - }, [authState.isAuthenticated]); + }, [authState.isAuthenticated, t]); const pause = useCallback(async () => { if (!authState.isAuthenticated) return; @@ -249,7 +251,7 @@ export const SpotifyProvider: React.FC = ({ children }) => { } finally { setIsLoading(false); } - }, [authState.isAuthenticated]); + }, [authState.isAuthenticated, t]); const skipNext = useCallback(async () => { if (!authState.isAuthenticated) return; @@ -272,7 +274,7 @@ export const SpotifyProvider: React.FC = ({ children }) => { } finally { setIsLoading(false); } - }, [authState.isAuthenticated]); + }, [authState.isAuthenticated, t]); const skipPrevious = useCallback(async () => { if (!authState.isAuthenticated) return; @@ -295,7 +297,7 @@ export const SpotifyProvider: React.FC = ({ children }) => { } finally { setIsLoading(false); } - }, [authState.isAuthenticated]); + }, [authState.isAuthenticated, t]); const setVolume = useCallback(async (volumePercent: number) => { if (!authState.isAuthenticated) return; @@ -309,11 +311,11 @@ export const SpotifyProvider: React.FC = ({ children }) => { body: JSON.stringify({ action: 'volume', volumePercent }), }); } catch (error) { - setError(error instanceof Error ? error.message : 'Failed to set volume'); + setError(error instanceof Error ? error.message : t`Failed to set volume`); } finally { setIsLoading(false); } - }, [authState.isAuthenticated]); + }, [authState.isAuthenticated, t]); const getDevices = useCallback(async () => { if (!authState.isAuthenticated) return; @@ -330,11 +332,11 @@ export const SpotifyProvider: React.FC = ({ children }) => { setActiveDeviceState(active); } } catch (error) { - setError(error instanceof Error ? error.message : 'Failed to get devices'); + setError(error instanceof Error ? error.message : t`Failed to get devices`); } finally { setIsLoading(false); } - }, [authState.isAuthenticated]); + }, [authState.isAuthenticated, t]); const setActiveDevice = useCallback(async (deviceId: string) => { if (!authState.isAuthenticated) return; @@ -355,11 +357,11 @@ export const SpotifyProvider: React.FC = ({ children }) => { setTimeout(getDevices, 1000); } catch (error) { - setError(error instanceof Error ? error.message : 'Failed to set active device'); + setError(error instanceof Error ? error.message : t`Failed to set active device`); } finally { setIsLoading(false); } - }, [authState.isAuthenticated, devices]); + }, [authState.isAuthenticated, devices, t]); const refreshPlaybackState = useCallback(async () => { if (!authState.isAuthenticated) return; @@ -382,7 +384,7 @@ export const SpotifyProvider: React.FC = ({ children }) => { } catch (error) { console.warn('Failed to refresh playback state:', error); } - }, [authState.isAuthenticated]); + }, [authState.isAuthenticated, t]); useEffect(() => { if (!authState.isAuthenticated) return; @@ -422,11 +424,11 @@ export const SpotifyProvider: React.FC = ({ children }) => { setCapturedState(response.snapshot); } } catch (error) { - setError(error instanceof Error ? error.message : 'Failed to capture playback state'); + setError(error instanceof Error ? error.message : t`Failed to capture playback state`); } finally { setIsCaptureLoading(false); } - }, [authState.isAuthenticated]); + }, [authState.isAuthenticated, t]); const resumePlaybackState = useCallback(async () => { if (!authState.isAuthenticated || !capturedState) return; @@ -442,11 +444,11 @@ export const SpotifyProvider: React.FC = ({ children }) => { setTimeout(refreshPlaybackState, 1000); } catch (error) { - setError(error instanceof Error ? error.message : 'Failed to resume playback state'); + setError(error instanceof Error ? error.message : t`Failed to resume playback state`); } finally { setIsResumeLoading(false); } - }, [authState.isAuthenticated, capturedState, refreshPlaybackState]); + }, [authState.isAuthenticated, capturedState, refreshPlaybackState, t]); const clearCapturedState = useCallback(() => { setCapturedState(null); diff --git a/src/features/activities/components/activities-table.tsx b/src/features/activities/components/activities-table.tsx index 95ec56e..4bf97b7 100644 --- a/src/features/activities/components/activities-table.tsx +++ b/src/features/activities/components/activities-table.tsx @@ -1,4 +1,5 @@ import { useState, useMemo, memo } from "react"; +import { Trans, Plural, useLingui } from "@lingui/react/macro"; import { Text, TextInput, @@ -35,13 +36,14 @@ interface ActivityListItemProps { } const ActivityListItem = memo(({ activity, onClick }: ActivityListItemProps) => { + const { t, i18n } = useLingui(); const playerName = typeof activity.player === "object" && activity.player ? `${activity.player.first_name} ${activity.player.last_name}` - : "System"; + : t`System`; const formatDate = (dateStr: string) => { const date = new Date(dateStr); - return date.toLocaleString(); + return date.toLocaleString(i18n.locale); }; return ( @@ -78,7 +80,7 @@ const ActivityListItem = memo(({ activity, onClick }: ActivityListItemProps) => {playerName} - {activity.duration}ms + {activity.duration}ms {formatDate(activity.created)} @@ -102,44 +104,45 @@ interface ActivityDetailsSheetProps { } const ActivityDetailsSheet = memo(({ activity, isOpen, onClose }: ActivityDetailsSheetProps) => { + const { t, i18n } = useLingui(); if (!activity) return null; const playerName = typeof activity.player === "object" && activity.player ? `${activity.player.first_name} ${activity.player.last_name}` - : "System"; + : t`System`; const formatDate = (dateStr: string) => { const date = new Date(dateStr); - return date.toLocaleString(); + return date.toLocaleString(i18n.locale); }; return ( - + - Function Name + Function Name {activity.name} - Status + Status {activity.success ? ( <> - Success + Success ) : ( <> - Failed + Failed )} @@ -148,21 +151,21 @@ const ActivityDetailsSheet = memo(({ activity, isOpen, onClose }: ActivityDetail - Player + Player {playerName} - Duration + Duration - {activity.duration}ms + {activity.duration}ms - Created + Created {formatDate(activity.created)} @@ -170,7 +173,7 @@ const ActivityDetailsSheet = memo(({ activity, isOpen, onClose }: ActivityDetail {activity.user_agent && ( - User Agent + User Agent {activity.user_agent} @@ -181,7 +184,7 @@ const ActivityDetailsSheet = memo(({ activity, isOpen, onClose }: ActivityDetail {activity.error && ( - Error Message + Error Message @@ -194,7 +197,7 @@ const ActivityDetailsSheet = memo(({ activity, isOpen, onClose }: ActivityDetail {activity.arguments && ( - Arguments + Arguments {JSON.stringify(activity.arguments, null, 2)} @@ -228,7 +231,7 @@ const ActivitiesResults = ({ searchParams, page, setPage, onActivityClick }: any - No Activities Found + <Trans>No Activities Found</Trans> )} @@ -248,6 +251,7 @@ const ActivitiesResults = ({ searchParams, page, setPage, onActivityClick }: any }; export const ActivitiesTable = () => { + const { t } = useLingui(); const [page, setPage] = useState(1); const [search, setSearch] = useState(""); const [successFilter, setSuccessFilter] = useState(null); @@ -302,7 +306,7 @@ export const ActivitiesTable = () => { { setSearch(e.currentTarget.value); @@ -314,16 +318,16 @@ export const ActivitiesTable = () => { { ({ + value: locale, + label: LOCALE_LABELS[locale], + }))} + value={resolveLocale(metadata.locale)} + onChange={handleChange} + allowDeselect={false} + /> + + ); +}; + +export default LocalePicker; diff --git a/src/features/settings/components/notifications-section.tsx b/src/features/settings/components/notifications-section.tsx index 1e5bec5..2cd40f3 100644 --- a/src/features/settings/components/notifications-section.tsx +++ b/src/features/settings/components/notifications-section.tsx @@ -1,10 +1,12 @@ import { Box, Title, Stack, Switch, Button, Text, Group } from "@mantine/core"; +import { Trans, useLingui } from "@lingui/react/macro"; import { useWebPush } from "@/hooks/use-web-push"; import toast from "@/lib/sonner"; export function NotificationsSection() { const { supported, configured, permission, subscribed, busy, enable, disable, sendTest } = useWebPush(); + const { t } = useLingui(); if (!supported || !configured) { return ( @@ -13,11 +15,11 @@ export function NotificationsSection() { py="sm" style={{ borderBottom: "1px solid var(--mantine-color-default-border)" }} > - Notifications + <Trans>Notifications</Trans> {supported - ? "Notifications aren't available right now." - : "This device doesn't support push notifications. On iOS, install the app to your Home Screen first."} + ? Notifications aren't available right now. + : This device doesn't support push notifications. On iOS, install the app to your Home Screen first.} ); @@ -27,25 +29,25 @@ export function NotificationsSection() { if (checked) { const ok = await enable(); if (ok) { - toast.success("Notifications enabled on this device"); + toast.success(t`Notifications enabled on this device`); } else if (permission === "denied") { toast.error( - "Notifications are blocked. Enable them in your browser settings." + t`Notifications are blocked. Enable them in your browser settings.` ); } else { - toast.error("Couldn't enable notifications"); + toast.error(t`Couldn't enable notifications`); } } else { const ok = await disable(); - if (ok) toast.success("Notifications disabled on this device"); - else toast.error("Couldn't disable notifications"); + if (ok) toast.success(t`Notifications disabled on this device`); + else toast.error(t`Couldn't disable notifications`); } }; const handleTest = async () => { const ok = await sendTest(); - if (ok) toast.success("Test notification sent"); - else toast.error("Couldn't send test notification"); + if (ok) toast.success(t`Test notification sent`); + else toast.error(t`Couldn't send test notification`); }; return ( @@ -54,13 +56,13 @@ export function NotificationsSection() { py="sm" style={{ borderBottom: "1px solid var(--mantine-color-default-border)" }} > - Notifications + <Trans>Notifications</Trans> handleToggle(e.currentTarget.checked)} disabled={busy} - label="Enable notifications on this device" + label={t`Enable notifications on this device`} /> {subscribed && ( @@ -70,7 +72,7 @@ export function NotificationsSection() { onClick={handleTest} loading={busy} > - Send test notification + Send test notification )} diff --git a/src/features/settings/server.ts b/src/features/settings/server.ts index eaddaa8..4236096 100644 --- a/src/features/settings/server.ts +++ b/src/features/settings/server.ts @@ -25,6 +25,32 @@ export const updateUserColorScheme = createServerFn({ method: "POST" }) }; }); +export const updateUserLocale = createServerFn({ method: "POST" }) + .validator((data: string) => data) + .middleware([superTokensFunctionMiddleware, serverFnLoggingMiddleware]) + .handler(async ({ context, data }) => { + const { userAuthId, metadata } = context; + if (!userAuthId) return; + + const { SUPPORTED_LOCALES } = await import("@/lib/i18n"); + if (!(SUPPORTED_LOCALES as readonly string[]).includes(data)) return; + + const { updateUserMetadataFields } = await import( + "@/utils/supertokens-core.server" + ); + + await updateUserMetadataFields(userAuthId, { + locale: data, + }); + + return { + metadata: { + ...metadata, + locale: data, + }, + }; + }); + export const updateUserAccentColor = createServerFn({ method: "POST" }) .validator((data: string) => data) .middleware([superTokensFunctionMiddleware, serverFnLoggingMiddleware]) diff --git a/src/features/spotify/components/spotify-controls-bar.tsx b/src/features/spotify/components/spotify-controls-bar.tsx index 719e0ba..3aff698 100644 --- a/src/features/spotify/components/spotify-controls-bar.tsx +++ b/src/features/spotify/components/spotify-controls-bar.tsx @@ -10,6 +10,7 @@ import { FloppyDiskIcon, ClockCounterClockwiseIcon, } from '@phosphor-icons/react'; +import { Trans, useLingui } from '@lingui/react/macro'; import { useSpotify } from '@/lib/spotify/hooks'; import { useAuth } from '@/contexts/auth-context'; import SpotifySheet from './spotify-sheet'; @@ -18,6 +19,7 @@ const SpotifyControlsBar = () => { const { roles } = useAuth(); const isAdmin = roles?.includes('Admin') || false; const [sheetOpened, { open: openSheet, close: closeSheet }] = useDisclosure(false); + const { t } = useLingui(); const spotify = useSpotify(); @@ -47,7 +49,7 @@ const SpotifyControlsBar = () => { - Connect Spotify + Connect Spotify { )} - + { - + { - + { - + { - + { {activeDevice && ( - Playing on {activeDevice.name} + Playing on {activeDevice.name} )} - + { {isAuthenticated && !hasActiveDevice && !isLoading && ( - No active device. Please select a device in settings. + No active device. Please select a device in settings. )} diff --git a/src/features/spotify/components/spotify-sheet.tsx b/src/features/spotify/components/spotify-sheet.tsx index faae741..c001efc 100644 --- a/src/features/spotify/components/spotify-sheet.tsx +++ b/src/features/spotify/components/spotify-sheet.tsx @@ -15,6 +15,7 @@ import { SignOutIcon, ArrowsClockwiseIcon } from '@phosphor-icons/react'; +import { Trans, useLingui } from '@lingui/react/macro'; import { useSpotify } from '@/lib/spotify/hooks'; import { useAuth } from '@/contexts/auth-context'; import Sheet from '@/components/sheet/sheet'; @@ -28,6 +29,7 @@ const SpotifySheet: React.FC = ({ opened, onClose }) => { const { roles } = useAuth(); const isAdmin = roles?.includes('Admin') || false; const spotify = useSpotify(); + const { t } = useLingui(); if (!isAdmin || !spotify) return null; const { @@ -63,14 +65,14 @@ const SpotifySheet: React.FC = ({ opened, onClose }) => { {!isAuthenticated ? ( <> - Connect your Spotify account + Connect your Spotify account @@ -82,7 +84,7 @@ const SpotifySheet: React.FC = ({ opened, onClose }) => { loading={isLoading} fullWidth > - Connect with Spotify + Connect with Spotify ) : ( @@ -91,7 +93,7 @@ const SpotifySheet: React.FC = ({ opened, onClose }) => { {currentTrack && ( <> - Now Playing + <Trans>Now Playing</Trans> {currentTrack.album.images[2] && ( = ({ opened, onClose }) => { <Group gap="xs"> <DevicesIcon size={20} /> - Select Device + <Trans>Select Device</Trans> </Group> = ({ opened, onClose }) => { value={activeDevice?.id || ''} onChange={(event) => handleDeviceChange(event.currentTarget.value)} data={[ - { value: '', label: 'Select a device...' }, + { value: '', label: t`Select a device...` }, ...devices.map(device => ({ value: device.id, - label: `${device.name} ${device.is_active ? '(Active)' : ''} - ${device.type}`, + label: `${device.name} ${device.is_active ? t`(Active)` : ''} - ${device.type}`, })), ]} disabled={isLoading} /> ) : ( - No devices found. + No devices found. )} {activeDevice && ( - Active device: {activeDevice.name} - {activeDevice.volume_percent !== null && - ` (Volume: ${activeDevice.volume_percent}%)` + Active device: {activeDevice.name} + {activeDevice.volume_percent !== null && + t` (Volume: ${activeDevice.volume_percent}%)` } )} @@ -183,7 +185,7 @@ const SpotifySheet: React.FC = ({ opened, onClose }) => { onClick={logout} fullWidth > - Disconnect Spotify + Disconnect Spotify )} diff --git a/src/features/teams/components/manage-teams.tsx b/src/features/teams/components/manage-teams.tsx index b0abb03..238e62b 100644 --- a/src/features/teams/components/manage-teams.tsx +++ b/src/features/teams/components/manage-teams.tsx @@ -12,6 +12,7 @@ import { MagnifyingGlassIcon, UsersIcon, } from "@phosphor-icons/react"; +import { Trans, useLingui } from "@lingui/react/macro"; import { Tournament } from "@/features/tournaments/types"; import TeamList from "./team-list"; import Sheet from "@/components/sheet/sheet"; @@ -27,10 +28,11 @@ interface TeamEditSheetProps { const TeamEditSheet = ({ teamId, isOpen, onClose }: TeamEditSheetProps) => { const { data: team } = useTeam(teamId); + const { t } = useLingui(); return ( @@ -56,6 +58,7 @@ interface ManageTeamsProps { const ManageTeams = ({ tournament }: ManageTeamsProps) => { const [search, setSearch] = useState(""); const [selectedTeamId, setSelectedTeamId] = useState(null); + const { t } = useLingui(); const { isOpen: editTeamOpened, @@ -109,10 +112,10 @@ const ManageTeams = ({ tournament }: ManageTeamsProps) => { - No Teams Enrolled + <Trans>No Teams Enrolled</Trans> - This tournament has no enrolled teams yet. + This tournament has no enrolled teams yet. @@ -124,7 +127,7 @@ const ManageTeams = ({ tournament }: ManageTeamsProps) => { setSearch(e.currentTarget.value)} leftSection={} @@ -134,7 +137,7 @@ const ManageTeams = ({ tournament }: ManageTeamsProps) => { - {filteredTeams.length} of {teams.length} teams + {filteredTeams.length} of {teams.length} teams @@ -145,7 +148,7 @@ const ManageTeams = ({ tournament }: ManageTeamsProps) => { {filteredTeams.length === 0 && search && ( - No teams found matching "{search}" + No teams found matching "{search}" )} diff --git a/src/features/teams/components/regional-team-card.tsx b/src/features/teams/components/regional-team-card.tsx index 8ad7372..89573b5 100644 --- a/src/features/teams/components/regional-team-card.tsx +++ b/src/features/teams/components/regional-team-card.tsx @@ -5,6 +5,7 @@ import { Box, Title } from "@mantine/core"; +import { Trans } from "@lingui/react/macro"; import { useTeam } from "../queries"; import TeamAvatar from "@/components/team-avatar"; @@ -19,7 +20,7 @@ const RegionalTeamCard = ({ teamId }: RegionalTeamCardProps) => { return ( - Failed to load team + Failed to load team ); diff --git a/src/features/teams/components/team-card.tsx b/src/features/teams/components/team-card.tsx index 31c35cf..493776a 100644 --- a/src/features/teams/components/team-card.tsx +++ b/src/features/teams/components/team-card.tsx @@ -7,6 +7,7 @@ import { Divider, Title } from "@mantine/core"; +import { Trans } from "@lingui/react/macro"; import { useTeam } from "../queries"; import TeamAvatar from "@/components/team-avatar"; import SongSummary from "./team-form/song-summary"; @@ -22,7 +23,7 @@ const TeamCard = ({ teamId }: TeamCardProps) => { return ( - Failed to load team + Failed to load team ); diff --git a/src/features/teams/components/team-form/color-picker.tsx b/src/features/teams/components/team-form/color-picker.tsx index 2e243ba..5d29cb1 100644 --- a/src/features/teams/components/team-form/color-picker.tsx +++ b/src/features/teams/components/team-form/color-picker.tsx @@ -1,5 +1,6 @@ import { ColorPicker, TextInput, Stack, Group, ColorSwatch, Text } from '@mantine/core'; import React, { useState, useCallback, useMemo } from 'react'; +import { useLingui } from '@lingui/react/macro'; const presetColors = [ '#FF0000', '#00FF00', '#0000FF', '#FFFF00', @@ -16,8 +17,10 @@ interface TeamColorPickerProps { const TeamColorPicker: React.FC = ({ value, onChange, - label = "Select Color" + label }) => { + const { t } = useLingui(); + const displayLabel = label ?? t`Select Color`; const [customHex, setCustomHex] = useState(value || ''); const isValidHex = useMemo(() => { @@ -41,7 +44,7 @@ const TeamColorPicker: React.FC = ({ return ( - {label} + {displayLabel} = ({ diff --git a/src/features/teams/components/team-form/duration-picker.tsx b/src/features/teams/components/team-form/duration-picker.tsx index 16cec2a..4a818c2 100644 --- a/src/features/teams/components/team-form/duration-picker.tsx +++ b/src/features/teams/components/team-form/duration-picker.tsx @@ -1,5 +1,6 @@ import { useState, useEffect, useCallback } from "react"; import { Stack, Text, Group, TextInput, Button } from "@mantine/core"; +import { Trans, useLingui } from "@lingui/react/macro"; interface DurationPickerProps { songDurationMs: number; @@ -16,8 +17,9 @@ interface IncrementButtonsProps { } const IncrementButtons = ({ onAdjust, disabled, isPositive = true }: IncrementButtonsProps) => { + const { t } = useLingui(); const increments = [1, 5, 30, 60]; - const labels = ["1s", "5s", "30s", "1m"]; + const labels = [t`1s`, t`5s`, t`30s`, t`1m`]; return ( @@ -51,6 +53,7 @@ const DurationPicker = ({ onChange, disabled = false, }: DurationPickerProps) => { + const { t } = useLingui(); const songDurationSeconds = Math.floor(songDurationMs / 1000); const formatTime = useCallback((seconds: number) => { @@ -91,16 +94,16 @@ const DurationPicker = ({ const getValidationMessage = useCallback((start: number, end: number) => { const duration = end - start; if (start < 0 || end > songDurationSeconds) { - return "Segment must be within song duration"; + return t`Segment must be within song duration`; } if (duration < 10) { - return "Segment must be at least 10 seconds"; + return t`Segment must be at least 10 seconds`; } if (duration > 15) { - return "Segment must be no more than 15 seconds"; + return t`Segment must be no more than 15 seconds`; } return null; - }, [songDurationSeconds]); + }, [songDurationSeconds, t]); const updateTimes = useCallback((newStart: number, newEnd: number) => { const clampedStart = Math.max(0, Math.min(newStart, songDurationSeconds - 10)); @@ -166,14 +169,14 @@ const DurationPicker = ({ return ( - Walkout Segment ({segmentDuration}s) + Walkout Segment ({segmentDuration}s) - Start + Start - End + End void; @@ -31,6 +32,7 @@ const TeamForm = ({ onSubmit }: TeamFormProps) => { const isEditMode = !!teamId; + const { t } = useLingui(); const config: UseFormInput = { initialValues: { @@ -49,16 +51,16 @@ const TeamForm = ({ }, onSubmitPreventDefault: "always", validate: { - name: isNotEmpty("Name is required"), + name: isNotEmpty(t`Name is required`), players: (value: string[]) => value.length > 1 && value[1] !== "" ? undefined - : "Players are required", - song_name: isNotEmpty("Song is required"), + : t`Players are required`, + song_name: isNotEmpty(t`Song is required`), song_start: (value, values) => { if (values.song_name && values.song_id) { if (value === undefined || value === null) { - return "Song start time is required"; + return t`Song start time is required`; } } return undefined; @@ -66,15 +68,15 @@ const TeamForm = ({ song_end: (value, values) => { if (values.song_name && values.song_id) { if (value === undefined || value === null) { - return "Song end time is required"; + return t`Song end time is required`; } if (values.song_start !== undefined && value !== undefined) { const duration = value - values.song_start; if (duration < 10) { - return "Song segment must be at least 10 seconds"; + return t`Song segment must be at least 10 seconds`; } if (duration > 15) { - return "Song segment must be no more than 15 seconds"; + return t`Song segment must be no more than 15 seconds`; } } } @@ -100,8 +102,8 @@ const TeamForm = ({ const mutation = isEditMode ? updateTeam : createTeam; const errorMessage = isEditMode - ? "Failed to update team" - : "Failed to create team"; + ? t`Failed to update team` + : t`Failed to create team`; mutation(teamData, { onSuccess: async (team: any) => { @@ -143,7 +145,7 @@ const TeamForm = ({ if (!response.ok) { const error = await response.json(); - throw new Error(error.error || "Failed to upload logo"); + throw new Error(error.error || t`Failed to upload logo`); } const result = await response.json(); @@ -160,8 +162,8 @@ const TeamForm = ({ ); } catch (error: any) { const logoErrorMessage = isEditMode - ? `Team updated but logo upload failed: ${error.message}` - : `Team created but logo upload failed: ${error.message}`; + ? t`Team updated but logo upload failed: ${error.message}` + : t`Team created but logo upload failed: ${error.message}`; toast.error(logoErrorMessage); logger.error("Team logo upload error", error); } @@ -182,20 +184,20 @@ const TeamForm = ({ }, }); }, - [isEditMode, createTeam, updateTeam, queryClient] + [isEditMode, createTeam, updateTeam, queryClient, t] ); return ( } {...form.getInputProps("logo")} /> diff --git a/src/features/teams/components/team-form/players-picker.tsx b/src/features/teams/components/team-form/players-picker.tsx index c340b7e..459b160 100644 --- a/src/features/teams/components/team-form/players-picker.tsx +++ b/src/features/teams/components/team-form/players-picker.tsx @@ -3,6 +3,7 @@ import { useAuth } from "@/contexts/auth-context"; import { useUnenrolledPlayers } from "@/features/players/queries"; import { Autocomplete, Stack, Text, TextInput } from "@mantine/core"; import { useCallback, useEffect, useMemo, useState } from "react"; +import { useLingui } from "@lingui/react/macro"; interface PlayersPickerProps { tournamentId: string; @@ -13,6 +14,7 @@ interface PlayersPickerProps { } const PlayersPicker = ({ tournamentId, value = [], onChange, disabled, error }: PlayersPickerProps) => { + const { t } = useLingui(); const { roles, user } = useAuth(); const isAdmin = roles.includes("Admin"); @@ -40,7 +42,7 @@ const PlayersPicker = ({ tournamentId, value = [], onChange, disabled, error }: return ( { + const { t } = useLingui(); const currentSong = useMemo((): Song | undefined => { const values = form.getValues(); if (values.song_id && values.song_name) { @@ -63,9 +65,9 @@ const SongPicker = ({ form, error }: SongPickerProps) => { error={error} Component={SongPickerComponent} componentProps={{}} - title={"Select Song"} - label={"Walkout Song"} - placeholder={"Select your walkout song"} + title={t`Select Song`} + label={t`Walkout Song`} + placeholder={t`Select your walkout song`} /> ); }; @@ -76,6 +78,7 @@ interface SongPickerComponentProps { } const SongPickerComponent = ({ value: song, onChange }: SongPickerComponentProps) => { + const { t } = useLingui(); const handleSongSelect = (track: SpotifyTrack) => { const defaultStart = 0; const defaultEnd = Math.min(15, Math.floor(track.duration_ms / 1000)); @@ -106,7 +109,7 @@ const SongPickerComponent = ({ value: song, onChange }: SongPickerComponentProps return ( - Search for a Song + Search for a Song @@ -122,10 +125,10 @@ const SongPickerComponent = ({ value: song, onChange }: SongPickerComponentProps
- {song?.song_name || "No song selected"} + {song?.song_name || t`No song selected`} - {song?.song_artist || "Select a song to see details"} + {song?.song_artist || t`Select a song to see details`} {song?.song_album || ""} diff --git a/src/features/teams/components/team-form/song-search.tsx b/src/features/teams/components/team-form/song-search.tsx index 79ae4da..91b1b70 100644 --- a/src/features/teams/components/team-form/song-search.tsx +++ b/src/features/teams/components/team-form/song-search.tsx @@ -1,13 +1,16 @@ import { Text, Group, Avatar, Box } from "@mantine/core"; import { SpotifyTrack } from "@/lib/spotify/types"; import Typeahead, { TypeaheadOption } from "@/components/typeahead"; +import { useLingui } from "@lingui/react/macro"; interface SongSearchProps { onChange: (track: SpotifyTrack) => void; placeholder?: string; } -const SongSearch = ({ onChange, placeholder = "Search for songs..." }: SongSearchProps) => { +const SongSearch = ({ onChange, placeholder }: SongSearchProps) => { + const { t } = useLingui(); + const resolvedPlaceholder = placeholder ?? t`Search for songs...`; const searchSpotifyTracks = async (query: string): Promise[]> => { try { // Use a default search term when query is empty to show popular tracks @@ -72,7 +75,7 @@ const SongSearch = ({ onChange, placeholder = "Search for songs..." }: SongSearc searchFn={searchSpotifyTracks} renderOption={renderOption} format={formatTrack} - placeholder={placeholder} + placeholder={resolvedPlaceholder} /> ) }; diff --git a/src/features/teams/components/team-form/song-summary.tsx b/src/features/teams/components/team-form/song-summary.tsx index d01d472..d1763d1 100644 --- a/src/features/teams/components/team-form/song-summary.tsx +++ b/src/features/teams/components/team-form/song-summary.tsx @@ -1,5 +1,6 @@ import { Group, Avatar, Text, Box } from "@mantine/core"; import { MusicNoteIcon } from "@phosphor-icons/react"; +import { Trans } from "@lingui/react/macro"; interface Song { song_id: string; @@ -27,7 +28,7 @@ const SongSummary = ({ song }: SongSummaryProps) => { return ( - No song selected + No song selected ); } diff --git a/src/features/teams/components/team-profile/index.tsx b/src/features/teams/components/team-profile/index.tsx index 3a5e4a2..b8dedb7 100644 --- a/src/features/teams/components/team-profile/index.tsx +++ b/src/features/teams/components/team-profile/index.tsx @@ -7,28 +7,31 @@ import { useTeam, useTeamMatches, useTeamStats } from "../../queries"; import MatchList from "@/features/matches/components/match-list"; import PlayerList from "@/features/players/components/player-list"; import { MatchListSkeleton } from "./skeleton"; +import { Trans, useLingui } from "@lingui/react/macro"; interface ProfileProps { id: string; } const TeamProfile = ({ id }: ProfileProps) => { + const { t } = useLingui(); const { data: team } = useTeam(id); const { data: matches, isLoading: matchesLoading } = useTeamMatches(id); const { data: stats, isLoading: statsLoading, error: statsError } = useTeamStats(id); - if (!team) return Team not found; + if (!team) return Team not found; const tabs = [ { - label: "Overview", + label: t`Overview`, + value: "overview", content: <> - Players + Players - Statistics + Statistics {statsLoading ? ( ) : ( @@ -38,7 +41,8 @@ const TeamProfile = ({ id }: ProfileProps) => { , }, { - label: "Matches", + label: t`Matches`, + value: "matches", content: matchesLoading ? ( ) : ( @@ -46,7 +50,8 @@ const TeamProfile = ({ id }: ProfileProps) => { ), }, { - label: "Tournaments", + label: t`Tournaments`, + value: "tournaments", content: ( <> diff --git a/src/features/teams/components/team-profile/skeleton.tsx b/src/features/teams/components/team-profile/skeleton.tsx index af729e5..866579f 100644 --- a/src/features/teams/components/team-profile/skeleton.tsx +++ b/src/features/teams/components/team-profile/skeleton.tsx @@ -3,6 +3,7 @@ import SwipeableTabs from "@/components/swipeable-tabs"; import TournamentList from "@/features/tournaments/components/tournament-list"; import { StatsSkeleton } from "@/components/stats-overview"; import HeaderSkeleton from "./header-skeleton"; +import { useLingui } from "@lingui/react/macro"; const MatchCardSkeleton = ({ opacity = 1 }: { opacity?: number }) => ( @@ -63,17 +64,21 @@ export const OverviewSkeleton = () => ( ); const ProfileSkeleton = () => { + const { t } = useLingui(); const tabs = [ { - label: "Overview", + label: t`Overview`, + value: "overview", content: , }, { - label: "Matches", + label: t`Matches`, + value: "matches", content: , }, { - label: "Tournaments", + label: t`Tournaments`, + value: "tournaments", content: , }, ]; diff --git a/src/features/teams/hooks/use-available-players.ts b/src/features/teams/hooks/use-available-players.ts index 4061a91..135dcd1 100644 --- a/src/features/teams/hooks/use-available-players.ts +++ b/src/features/teams/hooks/use-available-players.ts @@ -1,4 +1,5 @@ import { useMemo } from 'react'; +import { useLingui } from '@lingui/react/macro'; import { listPlayers } from '@/features/players/server'; import { useServerSuspenseQuery } from '@/lib/tanstack-query/hooks'; @@ -15,6 +16,7 @@ export const playerQueries = { }; export const useAvailablePlayers = (excludedPlayerIds: string[] = []) => { + const { t } = useLingui(); const { data: allPlayers } = useServerSuspenseQuery(playerQueries.all()); const availablePlayers = useMemo(() => { @@ -30,9 +32,9 @@ export const useAvailablePlayers = (excludedPlayerIds: string[] = []) => { const playerOptions = useMemo(() => availablePlayers.map(player => ({ value: player.id, - label: `${player.first_name} ${player.last_name}`.trim() || 'Unnamed Player' + label: `${player.first_name} ${player.last_name}`.trim() || t`Unnamed Player` })), - [availablePlayers] + [availablePlayers, t] ); return { diff --git a/src/features/teams/hooks/use-create-team.ts b/src/features/teams/hooks/use-create-team.ts index 513b90a..eb221e4 100644 --- a/src/features/teams/hooks/use-create-team.ts +++ b/src/features/teams/hooks/use-create-team.ts @@ -1,4 +1,5 @@ import { useNavigate } from "@tanstack/react-router"; +import { useLingui } from "@lingui/react/macro"; import { createTeam } from "@/features/teams/server"; import { TeamInput } from "@/features/teams/types"; import { logger } from "@/lib/logger"; @@ -6,13 +7,14 @@ import { useServerMutation } from "@/lib/tanstack-query/hooks"; const useCreateTeam = () => { const navigate = useNavigate(); - + const { t } = useLingui(); + return useServerMutation({ mutationFn: (data: TeamInput) => createTeam({ data }), onMutate: (data) => { logger.info('Creating team', data); }, - successMessage: 'Team created successfully!', + successMessage: t`Team created successfully!`, }); }; diff --git a/src/features/teams/hooks/use-update-team.ts b/src/features/teams/hooks/use-update-team.ts index bf4df83..2c5c1b6 100644 --- a/src/features/teams/hooks/use-update-team.ts +++ b/src/features/teams/hooks/use-update-team.ts @@ -1,16 +1,19 @@ +import { useLingui } from "@lingui/react/macro"; import { updateTeam } from "@/features/teams/server"; import { TeamUpdateInput } from "@/features/teams/types"; import { useServerMutation } from "@/lib/tanstack-query/hooks"; import { logger } from "@/lib/logger"; const useUpdateTeam = (teamId: string) => { + const { t } = useLingui(); + return useServerMutation({ - mutationFn: (data: TeamUpdateInput) => + mutationFn: (data: TeamUpdateInput) => updateTeam({ data: { id: teamId, updates: data } }), onMutate: (data) => { logger.info('Updating team', { teamId, updates: Object.keys(data) }); }, - successMessage: 'Team updated successfully!', + successMessage: t`Team updated successfully!`, }); }; diff --git a/src/features/tournaments/components/edit-enrolled-players.tsx b/src/features/tournaments/components/edit-enrolled-players.tsx index 170f055..8dd8f1e 100644 --- a/src/features/tournaments/components/edit-enrolled-players.tsx +++ b/src/features/tournaments/components/edit-enrolled-players.tsx @@ -7,6 +7,7 @@ import { } from "@mantine/core"; import { TrashIcon } from "@phosphor-icons/react"; import { useCallback, memo } from "react"; +import { Trans, Plural, useLingui } from "@lingui/react/macro"; import { useFreeAgents } from "../queries"; import PlayerAvatar from "@/components/player-avatar"; import { PlayerInfo, Player } from "@/features/players/types"; @@ -51,6 +52,7 @@ const PlayerItem = memo(({ player, onRemove, disabled }: PlayerItemProps) => { }); const EditEnrolledPlayers = ({ tournamentId }: EditEnrolledPlayersProps) => { + const { t } = useLingui(); const { data: freeAgents = [], isLoading } = useFreeAgents(tournamentId); const { data: allPlayers = [] } = usePlayers(); @@ -112,10 +114,10 @@ const EditEnrolledPlayers = ({ tournamentId }: EditEnrolledPlayersProps) => { - Add Player + Add Player { - Enrolled Players + Enrolled Players - {enrolledPlayers.length} players + @@ -140,7 +142,7 @@ const EditEnrolledPlayers = ({ tournamentId }: EditEnrolledPlayersProps) => { ) : !hasEnrolledPlayers ? ( - No players enrolled yet + No players enrolled yet ) : ( diff --git a/src/features/tournaments/components/edit-enrolled-teams.tsx b/src/features/tournaments/components/edit-enrolled-teams.tsx index 1fd105e..40e5821 100644 --- a/src/features/tournaments/components/edit-enrolled-teams.tsx +++ b/src/features/tournaments/components/edit-enrolled-teams.tsx @@ -8,6 +8,7 @@ import { import Typeahead, { TypeaheadOption } from "@/components/typeahead"; import { TrashIcon } from "@phosphor-icons/react"; import { useState, useCallback, useMemo, memo } from "react"; +import { Trans, Plural, useLingui } from "@lingui/react/macro"; import { useTournament, useUnenrolledTeams } from "../queries"; import useEnrollTeam from "../hooks/use-enroll-team"; import useUnenrollTeam from "../hooks/use-unenroll-team"; @@ -65,6 +66,7 @@ const TeamItem = memo(({ team, onUnenroll, disabled, isRegional }: TeamItemProps }); const EditEnrolledTeams = ({ tournamentId }: EditEnrolledTeamsProps) => { + const { t } = useLingui(); const { data: tournament, isLoading: tournamentLoading } = useTournament(tournamentId); const { data: unenrolledTeams = [], isLoading: unenrolledLoading } = @@ -131,10 +133,10 @@ const EditEnrolledTeams = ({ tournamentId }: EditEnrolledTeamsProps) => { - Add Team + Add Team { - Enrolled Teams + Enrolled Teams - {enrolledTeams.length} teams + @@ -159,7 +161,7 @@ const EditEnrolledTeams = ({ tournamentId }: EditEnrolledTeamsProps) => { ) : !hasEnrolledTeams ? ( - No teams enrolled yet + No teams enrolled yet ) : ( diff --git a/src/features/tournaments/components/edit-rules.tsx b/src/features/tournaments/components/edit-rules.tsx index 51816d0..05e2593 100644 --- a/src/features/tournaments/components/edit-rules.tsx +++ b/src/features/tournaments/components/edit-rules.tsx @@ -6,6 +6,7 @@ import { useState, useCallback } from "react"; import { useTournament } from "../queries"; import useUpdateTournament from "../hooks/use-update-tournament"; import { RichTextEditor } from "@/components/rich-text-editor"; +import { Trans } from "@lingui/react/macro"; interface EditRulesProps { tournamentId: string; @@ -35,8 +36,8 @@ const EditRules = ({ tournamentId, onClose }: EditRulesProps) => { return ( - - + + ); }; diff --git a/src/features/tournaments/components/group-match-card.tsx b/src/features/tournaments/components/group-match-card.tsx index 42fb3b8..4a4fe62 100644 --- a/src/features/tournaments/components/group-match-card.tsx +++ b/src/features/tournaments/components/group-match-card.tsx @@ -11,6 +11,7 @@ import { useQueryClient } from "@tanstack/react-query"; import { MatchForm } from "@/features/bracket/components/match-form"; import TeamAvatar from "@/components/team-avatar"; import AnimatedScore from "@/features/matches/components/animated-score"; +import { useLingui } from "@lingui/react/macro"; interface GroupMatchCardProps { match: Match; @@ -19,6 +20,7 @@ interface GroupMatchCardProps { } const GroupMatchCard: React.FC = ({ match, showControls, nextUpMatchId }) => { + const { t } = useLingui(); const queryClient = useQueryClient(); const editSheet = useSheet(); @@ -32,7 +34,7 @@ const GroupMatchCard: React.FC = ({ match, showControls, ne const start = useServerMutation({ mutationFn: startMatch, - successMessage: "Match started!", + successMessage: t`Match started!`, onSuccess: () => { queryClient.invalidateQueries({ queryKey: tournamentKeys.details(match.tournament.id), @@ -113,7 +115,7 @@ const GroupMatchCard: React.FC = ({ match, showControls, ne style={{ flex: 1, minWidth: 0 }} lineClamp={1} > - {match.home?.name || "TBD"} + {match.home?.name || t`TBD`} {isEnded && match.home_cups !== undefined && ( @@ -143,7 +145,7 @@ const GroupMatchCard: React.FC = ({ match, showControls, ne style={{ flex: 1, minWidth: 0 }} lineClamp={1} > - {match.away?.name || "TBD"} + {match.away?.name || t`TBD`} {isEnded && match.away_cups !== undefined && ( @@ -163,7 +165,7 @@ const GroupMatchCard: React.FC = ({ match, showControls, ne {showStartButton && ( = ({ match, showControls, ne {showEditButton && ( = ({ match, showControls, ne {showControls && ( - + = ({ groups }) => { - Group {group.groupName} + Group {group.groupName} - ({group.teams.length} teams) + () diff --git a/src/features/tournaments/components/group-stage-view.tsx b/src/features/tournaments/components/group-stage-view.tsx index bad1c83..5572215 100644 --- a/src/features/tournaments/components/group-stage-view.tsx +++ b/src/features/tournaments/components/group-stage-view.tsx @@ -10,6 +10,7 @@ import { populateKnockoutBracket } from "@/features/matches/server"; import { useQueryClient } from "@tanstack/react-query"; import { tournamentKeys } from "../queries"; import { computeGroupStageOrder } from "../utils/match-queue"; +import { Trans, useLingui } from "@lingui/react/macro"; interface GroupStageViewProps { groups: Group[]; @@ -43,12 +44,13 @@ const GroupStageView: React.FC = ({ groupConfig, nextUpMatchId, }) => { + const { t } = useLingui(); const queryClient = useQueryClient(); const [expandedTeams, setExpandedTeams] = useState>({}); const populateKnockoutMutation = useServerMutation({ mutationFn: populateKnockoutBracket, - successMessage: "Knockout bracket populated successfully!", + successMessage: t`Knockout bracket populated successfully!`, onSuccess: () => { if (tournamentId) { queryClient.invalidateQueries({ queryKey: tournamentKeys.details(tournamentId) }); @@ -289,7 +291,7 @@ const GroupStageView: React.FC = ({ return ( - No groups have been created yet + No groups have been created yet ); @@ -301,15 +303,15 @@ const GroupStageView: React.FC = ({ {showGenerateKnockoutButton && ( - }> + }> - All group matches are finished! Populate the knockout bracket to advance qualified teams. + All group matches are finished! Populate the knockout bracket to advance qualified teams. @@ -330,7 +332,7 @@ const GroupStageView: React.FC = ({ }} > - Group {group.name} + Group {group.name} ({completedMatches}/{totalMatches}) @@ -358,12 +360,12 @@ const GroupStageView: React.FC = ({ onClick={() => toggleTeams(group.id)} > - Standings ({standings.length}) + Standings ({standings.length}) {expandedTeams[group.id] ? : } @@ -402,12 +404,12 @@ const GroupStageView: React.FC = ({ {isWildcard && ( - WC + WC )} {isQualified && ( - Q + Q )} @@ -431,7 +433,7 @@ const GroupStageView: React.FC = ({ }) ) : ( - No teams assigned + No teams assigned )} @@ -441,7 +443,7 @@ const GroupStageView: React.FC = ({ {groupMatches.length === 0 ? ( - No matches scheduled + No matches scheduled ) : ( diff --git a/src/features/tournaments/components/manage-tournament.tsx b/src/features/tournaments/components/manage-tournament.tsx index 4a601dd..4e08c46 100644 --- a/src/features/tournaments/components/manage-tournament.tsx +++ b/src/features/tournaments/components/manage-tournament.tsx @@ -17,12 +17,14 @@ import EditEnrolledPlayers from "./edit-enrolled-players"; import ListLink from "@/components/list-link"; import React from "react"; import EditRules from "./edit-rules"; +import { useLingui } from "@lingui/react/macro"; interface ManageTournamentProps { tournamentId: string; } const ManageTournament = ({ tournamentId }: ManageTournamentProps) => { + const { t } = useLingui(); const { data: tournament } = useTournament(tournamentId); if (!tournament) throw new Error("Tournament not found."); @@ -48,43 +50,43 @@ const ManageTournament = ({ tournamentId }: ManageTournamentProps) => { <> {tournament.regional && ( )} {!tournament.regional && ( )} @@ -104,7 +106,7 @@ const ManageTournament = ({ tournamentId }: ManageTournamentProps) => { @@ -112,7 +114,7 @@ const ManageTournament = ({ tournamentId }: ManageTournamentProps) => { diff --git a/src/features/tournaments/components/podium.tsx b/src/features/tournaments/components/podium.tsx index 6610b0b..2e18af7 100644 --- a/src/features/tournaments/components/podium.tsx +++ b/src/features/tournaments/components/podium.tsx @@ -2,6 +2,9 @@ import { Stack, Group, Text, ThemeIcon } from "@mantine/core"; import { CrownIcon, MedalIcon } from "@phosphor-icons/react"; import { TeamInfo } from "@/features/teams/types"; import { Tournament } from "../types"; +import { msg } from "@lingui/core/macro"; +import { useLingui } from "@lingui/react/macro"; +import type { MessageDescriptor } from "@lingui/core"; interface PodiumProps { tournament: Tournament; @@ -40,7 +43,7 @@ const tierConfig: Record< PodiumTier, { color: string; - label: string; + label: MessageDescriptor; labelColor: string; icon: React.ReactNode; iconSize: "xl" | "lg"; @@ -50,7 +53,7 @@ const tierConfig: Record< > = { first: { color: "yellow", - label: "Champions", + label: msg`Champions`, labelColor: "light-dark(var(--mantine-color-yellow-8), var(--mantine-color-yellow-4))", icon: , @@ -60,7 +63,7 @@ const tierConfig: Record< }, second: { color: "gray", - label: "2nd place", + label: msg`2nd place`, labelColor: "light-dark(var(--mantine-color-gray-6), var(--mantine-color-gray-4))", icon: , @@ -70,7 +73,7 @@ const tierConfig: Record< }, third: { color: "orange", - label: "3rd place", + label: msg`3rd place`, labelColor: "light-dark(var(--mantine-color-orange-8), var(--mantine-color-orange-4))", icon: , @@ -81,6 +84,7 @@ const tierConfig: Record< }; const PodiumRow = ({ team, tier }: { team: TeamInfo; tier: PodiumTier }) => { + const { t } = useLingui(); const config = tierConfig[tier]; return ( @@ -121,7 +125,7 @@ const PodiumRow = ({ team, tier }: { team: TeamInfo; tier: PodiumTier }) => { mt={4} mr={4} > - {config.label} + {t(config.label)} ); diff --git a/src/features/tournaments/components/profile/index.tsx b/src/features/tournaments/components/profile/index.tsx index ca38f40..c4a9c51 100644 --- a/src/features/tournaments/components/profile/index.tsx +++ b/src/features/tournaments/components/profile/index.tsx @@ -6,31 +6,36 @@ import SwipeableTabs from "@/components/swipeable-tabs"; import { useTournament } from "../../queries"; import MatchList from "@/features/matches/components/match-list"; import { TournamentStats } from "../tournament-stats"; +import { useLingui } from "@lingui/react/macro"; interface ProfileProps { id: string; } const Profile = ({ id }: ProfileProps) => { + const { t } = useLingui(); const { data: tournament } = useTournament(id); if (!tournament) return null; const tabs = useMemo(() => [ { - label: "Overview", + label: t`Overview`, + value: "overview", content: }, { - label: "Matches", + label: t`Matches`, + value: "matches", content: b.order - a.order) || []} /> }, { - label: "Teams", + label: t`Teams`, + value: "teams", content: <> } - ], [tournament]); + ], [tournament, t]); return <>
diff --git a/src/features/tournaments/components/profile/skeleton.tsx b/src/features/tournaments/components/profile/skeleton.tsx index 306fd67..a6d60ff 100644 --- a/src/features/tournaments/components/profile/skeleton.tsx +++ b/src/features/tournaments/components/profile/skeleton.tsx @@ -2,6 +2,7 @@ import { Box, Divider, Group, Paper, Skeleton, Stack } from "@mantine/core"; import SwipeableTabs from "@/components/swipeable-tabs"; import TeamList from "@/features/teams/components/team-list"; import HeaderSkeleton from "./header-skeleton"; +import { useLingui } from "@lingui/react/macro"; const MatchCardSkeleton = ({ opacity = 1 }: { opacity?: number }) => ( @@ -72,17 +73,21 @@ const OverviewSkeleton = () => ( ); const ProfileSkeleton = () => { + const { t } = useLingui(); const tabs = [ { - label: "Overview", + label: t`Overview`, + value: "overview", content: , }, { - label: "Matches", + label: t`Matches`, + value: "matches", content: , }, { - label: "Teams", + label: t`Teams`, + value: "teams", content: , }, ]; diff --git a/src/features/tournaments/components/seed-tournament.tsx b/src/features/tournaments/components/seed-tournament.tsx index f82a89c..7976823 100644 --- a/src/features/tournaments/components/seed-tournament.tsx +++ b/src/features/tournaments/components/seed-tournament.tsx @@ -19,6 +19,7 @@ import { BracketData } from "@/features/bracket/types"; import BracketView from "@/features/bracket/components/bracket-view"; import { useQueryClient } from "@tanstack/react-query"; import { tournamentKeys } from "../queries"; +import { Trans, Plural, useLingui } from "@lingui/react/macro"; interface SeedTournamentProps { tournamentId: string; @@ -31,6 +32,7 @@ const SeedTournament: React.FC = ({ teams, isRegional, }) => { + const { t } = useLingui(); const [orderedTeams, setOrderedTeams] = useState(teams); const { data: bracketPreview } = useBracketPreview(teams.length); const queryClient = useQueryClient(); @@ -57,7 +59,7 @@ const SeedTournament: React.FC = ({ const generateBracket = useServerMutation({ mutationFn: generateTournamentBracket, - successMessage: "Tournament bracket generated successfully!", + successMessage: t`Tournament bracket generated successfully!`, onSuccess: () => { queryClient.invalidateQueries({ queryKey: tournamentKeys.details(tournamentId), @@ -108,10 +110,14 @@ const SeedTournament: React.FC = ({ - Team Seeding + Team Seeding - {orderedTeams.length} teams + @@ -122,7 +128,7 @@ const SeedTournament: React.FC = ({ loading={generateBracket.isPending} disabled={orderedTeams.length === 0} > - Confirm Seeding + Confirm Seeding @@ -201,7 +207,7 @@ const SeedTournament: React.FC = ({ loading={generateBracket.isPending} disabled={orderedTeams.length === 0} > - Confirm Seeding + Confirm Seeding diff --git a/src/features/tournaments/components/setup-group-stage.tsx b/src/features/tournaments/components/setup-group-stage.tsx index f041e2b..4987d13 100644 --- a/src/features/tournaments/components/setup-group-stage.tsx +++ b/src/features/tournaments/components/setup-group-stage.tsx @@ -12,6 +12,8 @@ import { Box, } from "@mantine/core"; import { InfoIcon } from "@phosphor-icons/react"; +import { Trans, Plural, useLingui } from "@lingui/react/macro"; +import { msg } from "@lingui/core/macro"; import { useServerMutation } from "@/lib/tanstack-query/hooks/use-server-mutation"; import { generateGroupStage } from "../server"; import { TeamInfo } from "@/features/teams/types"; @@ -21,6 +23,10 @@ import { getGroupName, GroupConfigOption, } from "../utils/group-config"; +import { + formatGroupRankLabel, + formatWildcardLabel, +} from "@/features/bracket/utils/group-label"; import GroupPreview from "./group-preview"; import { useQueryClient } from "@tanstack/react-query"; import { tournamentKeys } from "../queries"; @@ -39,17 +45,18 @@ const SetupGroupStage: React.FC = ({ teams, }) => { const queryClient = useQueryClient(); + const { t, i18n } = useLingui(); const [selectedConfigIndex, setSelectedConfigIndex] = useState("0"); const [seed, setSeed] = useState(Date.now()); const configurations = useMemo(() => { try { - return calculateGroupConfigurations(teams.length); + return calculateGroupConfigurations(i18n, teams.length); } catch (error) { return []; } - }, [teams.length]); + }, [teams.length, i18n]); const selectedConfig: GroupConfigOption | null = useMemo(() => { const index = parseInt(selectedConfigIndex); @@ -105,20 +112,20 @@ const SetupGroupStage: React.FC = ({ const group1 = i % selectedConfig.num_groups; const rankIndex1 = Math.floor(i / selectedConfig.num_groups); const rank1 = rankIndex1 + 1; - const groupName1 = groupNames[group1] || `Group ${group1 + 1}`; - const rankSuffix1 = rank1 === 1 ? '1st' : rank1 === 2 ? '2nd' : rank1 === 3 ? '3rd' : `${rank1}th`; - seedLabels[seedIndex++] = `${groupName1} ${rankSuffix1}`; + const groupName1 = groupNames[group1] || + i18n._({ ...msg`Group {n}`, values: { n: group1 + 1 } }); + seedLabels[seedIndex++] = formatGroupRankLabel(i18n, groupName1, rank1); const group2 = (i + 1) % selectedConfig.num_groups; const rankIndex2 = selectedConfig.advance_per_group - 1 - rankIndex1; const rank2 = rankIndex2 + 1; - const groupName2 = groupNames[group2] || `Group ${group2 + 1}`; - const rankSuffix2 = rank2 === 1 ? '1st' : rank2 === 2 ? '2nd' : rank2 === 3 ? '3rd' : `${rank2}th`; - seedLabels[seedIndex++] = `${groupName2} ${rankSuffix2}`; + const groupName2 = groupNames[group2] || + i18n._({ ...msg`Group {n}`, values: { n: group2 + 1 } }); + seedLabels[seedIndex++] = formatGroupRankLabel(i18n, groupName2, rank2); } for (let i = 0; i < wildcardsNeeded; i++) { - seedLabels[seedIndex++] = `Wildcard ${i + 1}`; + seedLabels[seedIndex++] = formatWildcardLabel(i18n, i + 1); } const ordersMap: Record = {}; @@ -154,11 +161,11 @@ const SetupGroupStage: React.FC = ({ ); return { matches: placeholderMatches, orders: ordersMap }; - }, [knockoutTeamCount, selectedConfig]); + }, [knockoutTeamCount, selectedConfig, i18n]); const generateGroups = useServerMutation({ mutationFn: generateGroupStage, - successMessage: "Group stage generated successfully!", + successMessage: t`Group stage generated successfully!`, onSuccess: () => { queryClient.invalidateQueries({ queryKey: tournamentKeys.details(tournamentId), @@ -195,8 +202,10 @@ const SetupGroupStage: React.FC = ({ if (configurations.length === 0) { return ( - }> - Need at least 4 teams to create a group stage format. Current team count: {teams.length} + }> + + Need at least 4 teams to create a group stage format. Current team count: {teams.length} + ); } @@ -209,17 +218,17 @@ const SetupGroupStage: React.FC = ({ - Group Stage Setup + Group Stage Setup - {teams.length} teams + - Group Configuration + Group Configuration