build(deps): DEP-9 — i18n + formatting stack (i18next 26, react-i18next 17, @formatjs/intl 4) (WALLET-1334)#1386
Merged
Comp0te merged 11 commits intoJul 2, 2026
Conversation
…a 1.5 (WALLET-1333) Bump redux 4.2.1 -> 5.0.1, react-redux 8.1.3 -> 9.3.0, reselect 4.1.7 -> 5.2.0, and redux-saga 1.2.3 -> 1.5.0. Fix a dead deep-import (`react-redux/es/exports`) left over from react-redux 8, which no longer resolves under react-redux 9's ESM `exports` map. Wrap four previously-unmemoized selectors (selectVaultAccountsPublicKeys, selectVaultAccountsExceptLedgersAccounts, selectAllContactsNames, selectAllContactsPublicKeys) in createSelector, and split selectIsAccountConnected's tuple-returning input selector into two scalar input selectors — both were returning a new reference on every call, which reselect 5's new default inputStabilityCheck/react-redux 9's stabilityCheck now surface as dev-mode warnings.
…react-hook-form 7.80 (WALLET-1335) Atomic bump of 4 coupled packages: @hookform/resolvers@5 is typed against yup@1 and peers on react-hook-form@^7.55, so all three must move together; react-router-dom@7 tags along since it needs the same Node 20 baseline. Mechanical fallout fixed: 12 deep imports of @hookform/resolvers/yup/dist/yup -> @hookform/resolvers/yup (v5 dist layout changed), 2 deep imports of yup/es/types -> yup (v1 dropped es/), and use-typed-location.ts's Location import moved from the now-removed @remix-run/router package to react-router-dom's re-export. Type reconciliation for yup 1's stricter inference (yupResolver vs FormValues) is deliberately out of scope here and follows in later commits.
…-cspr forms (WALLET-1335) yup 1 infers Yup.string() without .required()/.defined() as an optional/undefinable output, which broke yupResolver's strict typing against useForm<FormValues>() wherever the *FormValues interface declared the field as a plain required string. - form-validation-rules.ts: add .required() to useCreatePasswordRule, useVerifyPasswordAgainstHashRule, useRepeatPasswordRule, and useValidSecretPhraseRule — all four back genuinely required fields (password, confirm password, secret phrase), so this is also a type fix and a validation-completeness fix. - buy-cspr.ts: casperAmount is an unvalidated derived/display field (set via setValue, never user-required), so relax BuyCSPRFormValues.casperAmount to string | undefined instead of forcing .required(). yup's inferred schema type is structurally asymmetric between Input and Output for non-required fields, so no single FormValues shape satisfies Resolver<F, any, F> here; cast the resolver to Resolver<BuyCSPRFormValues> to bridge that gap.
…on schema (WALLET-1335) useTransferAmountForm picks between two differently-shaped yup schemas (erc20AmountFormSchema has paymentAmount, csprAmountFormSchema doesn't), making amountFormSchema's type a TS union. keyof of a union is the intersection of keys, so the resolver's inferred `names` type silently dropped paymentAmount, breaking assignability against Resolver<TransferAmountFormValues, ...>. Add an unvalidated paymentAmount field to csprAmountFormSchema so both union branches share the same key set (paymentAmount stays ERC-20-only in practice — this is purely to align the inferred type, not a validation change for CSPR transfers). That alone doesn't fully close the gap because of the same Input/Output asymmetry yup produces for unvalidated fields (as in buy-cspr.ts), so cast the resolver to Resolver<TransferAmountFormValues> as well.
…gSchema cleanup (WALLET-1335) Yup.mixed() with no generic now infers its .test() callback argument as yup 1's narrow AnyPresentValue type, breaking .length/index access on secretKeyFile's file-type validators. Give it an explicit Yup.mixed<FileList>() — the field is bound to a native <input type="file"> and was always a FileList at runtime, so also fix ImportAccountFormValues.secretKeyFile from the pre-existing (wrong) string | undefined to FileList | undefined. Same Input/Output asymmetry as buy-cspr.ts/transfer.ts requires the same yupResolver(...) as Resolver<FormValues> cast. Also fix an unrelated pre-existing type bug surfaced during review: unlock-vault/index.tsx and password-protection-page/index.tsx typed their Worker message payload's isPasswordCorrect as Yup.StringSchema<...> instead of boolean, which is what verify-password-worker.ts actually posts back (verifyPasswordAgainstHash returns Promise<boolean>). The wrong type never caused a runtime issue (TS can't check cross-Worker message shapes, and both call sites only did truthy/falsy checks) but was worth correcting while touching yup types in this area. This closes out the type-reconciliation phase of the yup 1 / resolvers 5 / react-hook-form 7.80 bump — npm run tsc is now clean project-wide.
…w-flow missing awaits (WALLET-1335) rename-account.spec.ts: react-router 7 no longer unmounts the outgoing route synchronously with the incoming one (v6 did), so after navigate() there can be a brief window (confirmed via reproduction: under ~10ms) where the account-settings page's stale heading and the Home header banner both render the same account name. The unscoped getByText(name) assertion hit a Playwright strict-mode violation deterministically. Scope both assertions to the header banner, which is what the test actually intends to verify post-close. review-flow.spec.ts: 8 popupExpect(...).toBeVisible() calls across all three tests were missing await (pre-existing since #1151, unrelated to this bump), so the test function could return before the assertion resolved. Add the missing awaits.
…ux-9-reselect-5-redux-saga-1-5
…x-saga-1-5' into WALLET-1335-dep-8-router-forms-react-router-7-yup-1-resolvers-5-react-hook-form-7-80
…dep-8-router-forms-react-router-7-yup-1-resolvers-5-react-hook-form-7-80 # Conflicts: # package-lock.json # package.json
…dep-9-i-18-n-formatting-stack
…xt 17, @formatjs/intl 4) (WALLET-1334) Bumps i18next-http-backend 3.0.5 -> 4.0.0 (finishes the DEP-2 security fix, drops the bundled cross-fetch since MV3 service workers have native fetch), i18next 23 -> 26, react-i18next 14 -> 17, i18next-browser-languagedetector 7 -> 8, @formatjs/intl 2 -> 4 (pure ESM), and the i18next-conv dev tool 15 -> 17. i18next-parser stays on hold as an independent extractor. Extends jest.config.js transformIgnorePatterns to transpile @formatjs/intl v4's ESM dependency tree, including the unscoped intl-messageformat transitive that isn't covered by the @formatjs scope alone. Converts the two locale dev scripts to ESM to fix a latent ERR_REQUIRE_ESM from i18next-conv already being ESM-only. No changes needed in src/libs/i18n/i18n.ts or language-detector.ts — the runtime config and custom detector shape are forward-compatible.
Comp0te
approved these changes
Jul 2, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
i18next-http-backend3.0.5 → 4.0.0, finishing the security fix left open from DEP-2 (drops the bundledcross-fetch; MV3 service workers use nativefetch)i18next23 → 26,react-i18next14 → 17,i18next-browser-languagedetector7 → 8,@formatjs/intl2 → 4 (pure ESM), and the dev-onlyi18next-conv15 → 17i18next-parserstays on hold as an independent extractorjest.config.jstransformIgnorePatternsto transpile@formatjs/intlv4's ESM dependency tree, including the unscopedintl-messageformattransitivescripts/locale_extract_pot.js/locale_update_translations.jsto.mjs(ESM), fixing a latentERR_REQUIRE_ESMfromi18next-convalready being ESM-onlysrc/libs/i18n/i18n.tsorlanguage-detector.ts— runtime config and custom detector shape are forward-compatible with all 4 runtime bumpsJira: https://make-software.atlassian.net/browse/WALLET-1334
Test plan
npm run tsc— 0 errors across the 186-fileuseTranslation/Transsurfacenpm test— 12/12 suites, 32/32 testsnpm run lint/npm run format:check— clean (pre-existingimport/no-cyclewarnings only)npm run build:chrome/build:firefox— all 10 locale files copied, nocross-fetchbundled (native fetch confirmed)npm run build:safari— webpack bundling succeeds; fails at the pre-existing Xcode/pbxproj step (unrelated, tracked separately)npm run locale:extract_pot/locale:update_translationson Node 22 — run withoutERR_REQUIRE_ESMafter the ESM conversionnpm audit—i18next-http-backend/cross-fetchCVE no longer presentnpm run ci-check— green