diff --git a/.agents/AGENTS.md b/.agents/AGENTS.md index 1dcbe3ed5e7..4a26c3eb45f 100644 --- a/.agents/AGENTS.md +++ b/.agents/AGENTS.md @@ -14,7 +14,7 @@ See `./rules/design-system.md` for Chakra UI v3 design system configuration and ## Code Style & Quality -See `./rules/code-quality.md` for code style, lint commands plus conventions linters don't catch. +See `./rules/code-quality.md` for code style, lint commands plus conventions linters don't catch, and the complexity/CRAP scores. ## TypeScript Conventions @@ -27,7 +27,7 @@ See `./rules/env-vars.md` for where environment variables live, how they're deli ## Testing - Vitest unit tests (`*.spec.ts` / `*.spec.tsx`): See `./rules/tests-unit.md` for purpose, setup, utilities, and conventions. -- Playwright component visual tests (`*.pw.tsx`): See `./rules/tests-visual.md` for purpose, setup, fixtures, and conventions. +- Playwright component visual tests (`*.pw.tsx`): See `./rules/tests-visual.md` for their purpose, when to use them, setup, fixtures, and conventions. ## Running locally @@ -51,30 +51,34 @@ Some directories have a `CONTEXT.md` documenting non-obvious patterns specific t - `deploy/scripts/` — how the frontend container is built and starts up (Dockerfile stages, entrypoint). - `deploy/tools/envs-validator/` — startup validation of `NEXT_PUBLIC_*` envs against yup schemas. - `src/api/` — how a request URL is assembled (resource registry, runtime config, `/node-api/config`) and where resource response types come from. +- `src/config/` — the app-config convention (default-only exports, one import surface, envs read only in config modules), its ESLint enforcement and exemptions. - `src/features/connect-wallet/` — why the wallet stack is loaded lazily (off the critical path), how account state reaches boot-time consumers before a provider exists, and the connector-mode differences. - `src/server/primedRequests/` — the early-fetch primer: why it exists, the CSP-driven determinism constraint, its correctness guarantee, and the drift-test contract. +- `src/shared/pagination/` — the paginated-list components and utils. - `src/slices/` — slice ownership model (who owns an entity's rendering). - `src/sprite/` — SVG sprite build pipeline and which outputs are tracked vs. generated. - `src/toolkit/` — the `@blockscout/ui-toolkit` workspace package structure. +- `tools/code-complexity/` — the cognitive-complexity / CRAP code-quality gate: what a failure means and how to fix it. - `tools/dev-server/` — how the dev server and demo deploy get their env vars from a running instance config. +- `tools/doc-links/` — the doc-link checker. +- `tools/mutation-testing/` — the mutation-testing gate for unit-test assertion strength. +- `tools/playwright/` — the runner for Playwright component tests. - `tools/profiling/` — React render profiling: production profiling build and DevTools trace aggregation. +- `tools/react-doctor/` — the React Doctor PR gate: reading a failure, suppressing a finding, changing the rule allowlist. +- `tools/review-screen/` — the Jev review-screen pilot: what a suspect is, what a run must never do, where the sidecars go. -If you encounter a `CONTEXT.md` not listed here, read it too (and consider adding it to this list). +See `./rules/docs.md` before adding or editing any doc about the code — a `CONTEXT.md`, a file under +`docs/`, a module `README.md`. -## Architecture decision records +## Architecture decision records (ADRs) -Decisions with repo-wide consequences are recorded in `.agents/adr/`, named -`<0000>-.md`. Read the relevant one before changing what it decided — an ADR carries the -evidence and the trade-off, so it answers "why is it like this?" without a git archaeology session. +See `./rules/adr.md` when proposing or adding a new record. + +The repo-wide records index: - `0002-layer-shaped-ticket-leaves.md` — why a product task's tickets cut vertically while the leaves inside them run along layers. - `0003-turbopack-for-production-builds.md` — why production builds moved back to Turbopack. -Add a new record (next free number, and a line here) whenever a decision is expensive to rediscover: -it constrains future work, was reached by measurement or an investigation worth not repeating, or -looks wrong without its context. Supersede rather than rewrite — flip the old record's `Status` to -`superseded by ` and leave its reasoning intact. - ## Product task workflow Product tasks (GitHub issues) are worked through a spec-driven workflow — interview, spec, agent @@ -89,4 +93,5 @@ rules contract, the per-file symlink Cursor needs, and how references here are c ## Reaching people & channels on Slack To reach anyone or any channel on Slack, resolve the Slack IDs from `./TEAM.md`. Draft and get approval -before sending anything — unless the skill you are running names an explicit exception. +before sending anything — unless the skill you are running names an explicit exception. Before composing +any Slack message, read `./slack-message.md` (language, addressing, links, attribution). diff --git a/.agents/GLOSSARY.md b/.agents/GLOSSARY.md index f8bcfad9362..c4691624557 100644 --- a/.agents/GLOSSARY.md +++ b/.agents/GLOSSARY.md @@ -43,6 +43,7 @@ vars are documented in `docs/ENVS.md`. Architectural concepts like | **Hot Contracts** | feature | Ranked list of the most recently and frequently interacted-with smart contracts on the network. | | **Interchain Indexer** | service | Microservice that indexes cross-chain messages and token transfers across heterogeneous chains. General-purpose interop indexer, not ZetaChain-specific. Provides "Cross chain txs" feature. Distinct from **CCTX**. | | **Interop Messages** | entity | **Deprecated** Cross-rollup messages passed between OP Stack chains using the native interoperability protocol. Distinct from **Interchain Indexer** messages. | +| **Jev** | service | TypeSafe's typed-judgment model: answers a structured question (yes/no, a choice) with a probability, never a claim, so whatever consumes a score verifies it before acting on it. | | **Kettle** | entity | In the **SUAVE** architecture, a Kettle is a trusted execution environment (TEE) node that processes MEV bundles. SUAVE transactions are associated with a Kettle. | | **Marketplace** | feature | Curated directory of dApps and DeFi applications integrated with Blockscout. | | **MetaSuites** | service | Third-party browser extension that enhances the Blockscout UI with additional data and links. | diff --git a/.agents/README.md b/.agents/README.md index 45bf5e38ee2..8cfa9280714 100644 --- a/.agents/README.md +++ b/.agents/README.md @@ -8,7 +8,7 @@ here; each tool reads it through its own directory via symlinks. | `AGENTS.md` | Always-loaded project context | `.claude/CLAUDE.md` → here; Cursor reads `AGENTS.md` natively | | `rules/*.md` | Coding rules, scoped to file patterns | `.claude/rules/` and `.cursor/rules/` — see below | | `skills/*/SKILL.md` | Workflows loaded on invocation | `.claude/skills/` → here | -| `delegation.md`, `GLOSSARY.md`, `TEAM.md`, `slack-thread.md` | Read on demand, by pointer from `AGENTS.md` or a skill | — | +| `delegation.md`, `GLOSSARY.md`, `TEAM.md`, `slack-thread.md`, `slack-message.md` | Read on demand, by pointer from `AGENTS.md` or a skill | — | | `tasks/` | Product-task specs — see `tasks/README.md` | — | Cross-references between all of these are machine-checked: `pnpm lint:doc-links`. diff --git a/.agents/TEAM.md b/.agents/TEAM.md index 7dd933ca0df..f75ff44b850 100644 --- a/.agents/TEAM.md +++ b/.agents/TEAM.md @@ -13,15 +13,7 @@ Slack IDs are stored so addressing is deterministic (no runtime name lookup). Th identifiers, **not credentials** — knowing one grants no access. To find your own member ID in Slack: your profile → **⋯ More** → **Copy member ID**. Do not add emails to this file. -## How to address - -- **Person** — the *Slack member ID* (`U…`) from the People table. Mention as `<@U…>`; to DM, pass the ID as - the channel to the send tool. -- **Team / group** — the *Slack group ID* (`S…`) from the Groups table. Mention as ``. -- **Channel** — the *Channel ID* (`C…`). Pass it as the channel target when sending; refer to it in prose by - its `#name`. -- **Permalink** — `https://blockscout.slack.com/archives//p` (the message timestamp - with the dot removed). +How to turn an ID into a mention, a DM target, or a permalink: `./slack-message.md` → Addressing. ## Product managers @@ -54,6 +46,7 @@ Own: core API endpoints and response models, field propagation across services, | --- | --- | --- | --- | | Victor | @vbaranov | U8L403FEG | ✓ | | Nikita P. | @nikitosing | U0218K3MTC5 | | +| Alexey | @Qwerty5Uiop | U03EE7T5TJS | | ### Groups diff --git a/.agents/rules/adr.md b/.agents/rules/adr.md new file mode 100644 index 00000000000..1a09dd40c60 --- /dev/null +++ b/.agents/rules/adr.md @@ -0,0 +1,56 @@ +--- +description: ADR rules +paths: + - "**/adr/*.md" +globs: "**/adr/*.md" +alwaysApply: false +--- +# Architecture decision records + +An ADR answers "why is it like this?" without a git archaeology session. It carries the evidence and the +trade-off, so read the relevant one before changing what it decided. + +## Template + +```md +# {Short title of the decision} + +| | | +| --- | --- | +| Status | Accepted \/ Superseded by | + +## Decision + +{1-3 sentences: what's the context, what did we decide, and why.} +``` + +That's it. An ADR can be a single paragraph. The value is in recording *that* a decision was made and *why*, not in filling out sections. + +## Where a record goes + +| Scope | Folder | Listed in | +| --- | --- | --- | +| Affects the project as a whole | `.agents/adr/` | a line in `.agents/AGENTS.md` | +| Relevant only to one area or module | `/adr/` | that module's `CONTEXT.md` | + +Numbering is sequential and **scoped to its folder**; the filename is `<0000>-.md`. + +## When to offer one + +All three must be true: + +1. Hard to reverse: the cost of changing your mind later is meaningful. +2. Surprising without context: a future reader will look at the code and wonder "why on earth did + they do it this way?" +3. The result of a real trade-off: there were genuine alternatives and you picked one for specific + reasons. + +If a decision is easy to reverse, skip it: you'll just reverse it. If it's not surprising, nobody will +wonder why. If there was no real alternative, there's nothing to record beyond "we did the obvious thing." + +Offer the ADR; do not write one unasked. + +## Superseding + +Supersede rather than rewrite. Change the old record's `Status` to `superseded by ` and leave its +reasoning intact. diff --git a/.agents/rules/architecture.md b/.agents/rules/architecture.md index afea74593e6..c73a8f5e4b0 100644 --- a/.agents/rules/architecture.md +++ b/.agents/rules/architecture.md @@ -23,7 +23,7 @@ Blockscout frontend — a blockchain explorer UI. Distributed as a Docker image; | Schema validation | Valibot | | Unit tests | Vitest | | Visual tests | Playwright | -| Package manager | pnpm (Node >=22.14.0) | +| Package manager | pnpm (Node >=24.21.0) | ## Directory layout diff --git a/.agents/rules/code-quality.md b/.agents/rules/code-quality.md index bceedd6368d..540b6616568 100644 --- a/.agents/rules/code-quality.md +++ b/.agents/rules/code-quality.md @@ -7,15 +7,41 @@ alwaysApply: false --- # Code Quality -We use ESLint, cSpell and Typescript to maintain code quality and consistency across the project. -In order to check code quality run the following commands: +We use ESLint, cSpell, TypeScript to maintain code quality. + ```bash pnpm lint:eslint:fix pnpm lint:tsc pnpm lint:cspell ``` -Moreover, please find below the general sense rules that linters do not cover. +Pull requests also pass a React Doctor gate for React bugs ESLint misses, on changed code only: +`pnpm lint:react-doctor --scope changed --base origin/main`. Reading a failure and suppressing a +finding: `tools/react-doctor/CONTEXT.md`. + +The rest of this file covers code complexity scores and conventions that the linters do not. + +## Complexity and CRAP + +Two independent per-function gates: + +- **COG** (cognitive complexity) — readability. A `BROKE` here means decompose the function (flatten nesting, extract, early-return). +- **CRAP** — under-tested *behavior* (handlers, hooks, utils — not JSX render bodies). A `BROKE` here means add a Vitest spec; simplifying the function does not lower CRAP. + +How a score is counted, which functions are gated, and how to read a failure: `tools/code-complexity/CONTEXT.md`. + +How to run: + +```bash +# The file you are on — every function in it +pnpm test:code-complexity path/to/file.ts + +# This branch vs origin/main, including uncommitted edits +pnpm test:code-complexity --changed + +# For the full command usage +pnpm test:code-complexity --help +``` ## Code Style and Structure @@ -43,10 +69,10 @@ This applies to hooks, components, functions, and variables alike.. ### Magic numbers -Extract magic numbers into named `UPPER_SNAKE_CASE` constants, placed above the component or function that uses them. This makes intent clear and avoids silent duplication. +Name a literal only when a reader would still ask why this value. The name is `UPPER_SNAKE_CASE`, sits above the component or function, and carries a domain rule the surrounding identifiers do not: a limit, threshold, interval, or similar choice. ```ts -// BAD +// BAD: 4 is a product limit with no name const visibleItems = items.slice(0, 4); // GOOD @@ -54,7 +80,25 @@ const MAX_VISIBLE_ITEMS = 4; const visibleItems = items.slice(0, MAX_VISIBLE_ITEMS); ``` -The same applies to magic strings used as discriminators, keys, or thresholds. In tests, unexplained magic values should also be extracted into named constants so their meaning is clear. +Leave the literal when it is the meaning. A constant that restates the value, a nearby identifier, or a universal convention is noise. `1` is the first page, `0` is the first index, a URL is a URL. + +```ts +// BAD: the name restates href and page +const URL = 'https://example.com'; +const link = ; + +const FIRST_PAGE = 1; +if (page !== FIRST_PAGE) return null; + +// GOOD +const link = ; + +if (page !== 1) return null; +``` + +Apply the same test to strings used as discriminators, keys, or thresholds, and to values in tests. CSS-style props (margin, padding, font weight, color) are already named by the prop. + +If the same unexplained value must stay in sync at more than one call site, one constant is the share point. #### Shared unit constants @@ -134,31 +178,25 @@ Before writing custom utility logic (clamping, deep cloning, grouping, etc.), ch ### Comments -Comment the *why*, not the *what*. The code already shows what it does; a good comment explains what the -code can't — the rationale, a non-obvious constraint, a gotcha, a link to the issue or spec that motivated -it. +**Do not write comments. Never. Period.** +Prefer a name or a structure change that makes the comment unnecessary. + +A comment in the diff is allowed only when it explains *why* the obvious code is wrong — a quirk, workaround, or invariant git cannot show — optionally with a link to the issue. Required mechanical comments (`eslint-disable`, unavoidable double-cast, raw color) still explain *why*, in their own sections. -Never write *diff-narration* comments — what the code replaces, what it used to do, what behavior it -preserves. That belongs in the commit message and the PR, not in the source, where it goes stale the moment -the next change lands. +Never write *diff-narration* (what this change replaces). That belongs in the commit and the PR. Test: if the comment would confuse someone who never saw the previous file, delete it. + +Do not add JSDoc that restates the name or types. Do not rewrite comments you did not have to touch. Delete commented-out code; git keeps it. TODOs only with an issue or a concrete follow-up. ```ts -// BAD — narrates the change and restates the obvious -// Replaces the old useEffect approach; now we memoize the filtered list +// BAD — narrates the change and restates the next line +// Replaces the old useEffect; memoize the filtered list const active = useMemo(() => items.filter(isActive), [ items ]); -// GOOD — explains the non-obvious why -// Memoized because bails out of re-rendering on referential equality of `items`. -const active = useMemo(() => items.filter(isActive), [ items ]); +// GOOD — why the obvious approach fails +// Firefox: drag outside the window swallows mouse events until re-entry; see onMouseLeave(). +const onMouseMove = useCallback(() => {}, [ ]); ``` -Quick test: if a comment would confuse a reader who never saw the previous version of the file, it is -diff-narration — delete it. If it still helps that reader understand a decision, keep it. - -### Commented-out code - -Remove commented-out code blocks. The git history preserves anything that might be needed later. TODOs are acceptable only when paired with a clear follow-up plan or issue reference. - ### Links When links to **application pages** are constructed, verify that `nextjs-routes` or `src/shared/router/routes` utilities are used instead of string concatenation or template literals. The full list of application routes is available in `src/shared/router/nextjs-routes.d.ts`. diff --git a/.agents/rules/docs.md b/.agents/rules/docs.md new file mode 100644 index 00000000000..5d25f223d48 --- /dev/null +++ b/.agents/rules/docs.md @@ -0,0 +1,76 @@ +--- +description: Rules for CONTEXT.md and other documentation about the code +paths: + - "**/CONTEXT.md" + - "**/docs/**/*.md" +globs: "**/CONTEXT.md,**/docs/**/*.md" +alwaysApply: false +--- +# Documenting the code + +The code is the only source of truth for what it does. A doc that restates it is a second source that +nobody executes, so it drifts within weeks and then the reader has to guess which one is lying. Write +only what the code cannot say. + +Applies to every doc about the code: a `CONTEXT.md`, a file under `docs/`, a module `README.md`. + +## What earns a line + +- **Navigation.** Which directory or file to open for what, so the reader skips a repo-wide search. + Earned only by an area large or nested enough that the split of responsibility is unclear on a + first read. Two or three files need no map. +- **Recurring questions.** How to read the tool's output, which command answers a given question, where a + failure is explained. Prefer a table of question to location over prose. +- **Local vocabulary.** Terms that mean something only in this area. Project-wide terms go in + `.agents/GLOSSARY.md` (`update-glossary` skill). +- **Constraints.** What an editor here must keep true, and what breaks if they don't. +- **Gotchas**, under the test below. +- **Pointers to the why.** Decisions with real alternatives belong in an ADR (`./adr.md`), local or + repo-wide. Link to it; do not summarise it. + +## What the code and the environment already say + +Leave these out. Every one of them has a source that cannot go stale: + +- Control flow, signatures, prop lists, types, algorithms. The reader opens the file. +- Anything one lookup gives: `--help` output, `package.json` scripts, the directory listing, env vars + (`docs/ENVS.md`). +- What the change replaced. The commit and the PR carry that. +- Anything another doc states. Link instead, so the fact has one home. + +## The gotcha test + +"Not obvious" is subjective, so bind it. All three: + +1. A competent reader of this code would get it wrong. +2. Someone was actually caught: you, CI, a reviewer, or production. +3. Stating it takes a sentence or two, and does not require describing how the code works. + +Write the surprise and its consequence, not the mechanism. + +```md +BAD — describes the code; the file says this, and says it accurately +`generate.ts` runs vitest with a config that writes coverage to a temp dir, then `read.ts` +parses `coverage-final.json` and maps ranges onto functions. + +GOOD — the surprise and what it costs +A `behavior` function with no co-located spec reads 0% coverage. Simplifying it lowers COG, not CRAP. +``` + +If it fails the test, it belongs in the code as a name, a structure change, or (rarely) a comment. +See `./code-quality.md` for when a comment is allowed. + +## Keep it maintainable + +- Point at directories and files, not at symbols or line numbers. +- Prefer facts that survive a refactor: ownership, constraints, intent. +- Changing code a doc describes: fix or delete the stale lines in the same change. +- Shorter is more likely to stay true. Cut a line rather than qualify it. + +## Adding a new doc + +A directory earns a `CONTEXT.md` when getting oriented in it takes real reading and the layout does not +speak for itself. Offer it; do not write one unasked. + +A new `CONTEXT.md` needs a line in the per-directory list in `.agents/AGENTS.md`. Cross-references are +checked by `pnpm lint:doc-links`. diff --git a/.agents/rules/env-vars.md b/.agents/rules/env-vars.md index 92bf3b11345..2bf06611700 100644 --- a/.agents/rules/env-vars.md +++ b/.agents/rules/env-vars.md @@ -27,6 +27,7 @@ For the full pipeline (registry generation, container startup order, validation, - **Read env values via `getEnvValue('NEXT_PUBLIC_...')`** from `src/config/utils/envs.ts` — `process.env` is empty in the browser, and the helper handles SSR/client transparently. (Server-only code may use `process.env` directly.) - **Co-locate sub-configs with the code that owns them.** The aggregator at `src/config/index.ts` (sections: `app`, `apis`, `chain`, `shell`, `slices`, `features`, `services`, `metadata`, `misc`) re-exports them; add new values in the owning sub-config (e.g. `src/features/marketplace/config.ts`, `src/slices/tx/config.ts`), not in a central file. +- **Only a `config.ts` reads envs, and only `src/config` exposes the result.** The convention, its ESLint enforcement and its exemptions: `src/config/CONTEXT.md`. ## Value types diff --git a/.agents/rules/tests-unit.md b/.agents/rules/tests-unit.md index 4c5cec72f83..39ba1ce8010 100644 --- a/.agents/rules/tests-unit.md +++ b/.agents/rules/tests-unit.md @@ -108,17 +108,16 @@ Don't mock: ## File naming and location -Test files must be named `*.spec.ts` or `*.spec.tsx` and placed alongside the code they test. Run all tests: +Test files must be named `*.spec.ts` or `*.spec.tsx` and placed alongside the code they test. ```bash -pnpm test:vitest +pnpm test:vitest # entire suite +pnpm test:vitest path/to/file.spec.ts # one file +pnpm test:vitest --changed # tests related to uncommitted work (staged and unstaged) +pnpm test:vitest --changed origin/main # tests related to this branch vs origin/main ``` -Run a single file: - -```bash -pnpm test:vitest path/to/file.spec.ts -``` +`--changed` uses git, then the Vitest module graph, so editing a source file still runs the spec that imports it. Untracked files are invisible until `git add`. If nothing matches, the command fails unless you add `--passWithNoTests` (CI does this on PRs). Fetch `origin/main` first if that ref is missing locally. ## Setup diff --git a/.agents/rules/tests-visual.md b/.agents/rules/tests-visual.md index c95ce1df63c..815e29e5e69 100644 --- a/.agents/rules/tests-visual.md +++ b/.agents/rules/tests-visual.md @@ -1,5 +1,5 @@ --- -description: Playwright component visual tests — purpose, setup, fixtures, and conventions +description: Playwright component visual tests — when to extend, pack, or skip a suite; setup, fixtures, and conventions paths: - "**/*.pw.tsx" globs: "**/*.pw.tsx" @@ -9,19 +9,35 @@ alwaysApply: false ## Purpose -Playwright tests verify the visual presentation and interactive behavior of UI components across multiple viewports and color modes. Use them when a change affects what the user sees — layout, styling, conditional UI states. +Playwright tests catch layout and style regressions a Vitest assertion cannot. Each screenshot is slow in CI. A new `*.pw.tsx` is the last option, not a default deliverable of every UI change. + +Decide in this order: + +1. **Vitest first.** Branching, copy, mapping, and state machines go in `*.spec.ts(x)` (`tests-unit.md`). A screenshot whose job is to prove a string or a condition is the wrong tool. +2. **Extend.** If a `*.pw.tsx` already sits next to the component or page you changed, add the look there. A child, a row, or a field that already appears in a parent screenshot does not need its own file or test case. +3. **Pack.** One screenshot shows every look that can appear together. + - A list of token types is one shot with every type in the list, not one shot per type. + - A details page is one shot with every optional field visible, env-gated ones included and turned on, not one shot per flag. + A second case is justified only when packing would hide the thing you are checking: mutually exclusive layouts, an overlay that covers the page, a hover that replaces the idle look. Where possible, add a second screenshot to the same case instead of creating an entirely new case. +4. **Create only for owned layout.** Open a new `*.pw.tsx` only when this component owns layout or styling that no existing screenshot covers. Toolkit, entity, and tag components used in many places are the usual case, because one file covers every consumer. A page or wrapper that only assembles those does not get a file. Restyling a shared component would force every composing page's baselines to update anyway. + +Skip loading, empty, and error chrome unless they have distinct custom layout. Env-flag permutations belong in the packed full look, not as extra cases. + +A case that cannot name the layout or style it uniquely protects does not ship. ## File naming and location -Test files must be named `*.pw.tsx` and placed alongside the component they test. Run all tests: +Test files must be named `*.pw.tsx` and placed alongside the component they test. One entrypoint runs them: ```bash -pnpm test:pw # locally (fast, no Docker) -pnpm test:pw:docker # in Docker (required for screenshot updates) -pnpm test:pw:docker path/to/Component.pw.ts --update-snapshots # Run single file in Docker and update all corresponding screenshots +pnpm test:pw # whole suite, locally +pnpm test:pw --changed # only tests affected by the diff vs origin/main +pnpm test:pw --docker path/to/Component.pw.tsx --update-snapshots # one file in Docker, regenerating its screenshots ``` -**Never commit screenshots generated by `pnpm test:pw` (local).** Screenshots used in CI must be generated via `pnpm test:pw:docker`, otherwise tests will fail in CI due to rendering differences. +Flags, the Docker setup, and where the reports land are in `tools/playwright/CONTEXT.md`. + +**Never commit screenshots generated by a plain local `pnpm test:pw` run.** Screenshots used in CI must be generated via `pnpm test:pw --docker`, otherwise tests will fail in CI due to rendering differences. ## Test projects @@ -41,6 +57,8 @@ test('mobile only -@default +@mobile', async ({ render }) => { ... }); test('dark mode variant +@dark-mode', async ({ render }) => { ... }); ``` +Put `+@mobile` or `+@dark-mode` only on cases whose layout or color actually changes in that project. One packed shot tagged for those projects is enough; sibling cases that only reveal a field stay on `default`. + ## Imports **Never import directly from `@playwright/experimental-ct-react`.** Use the project's extended test: @@ -174,7 +192,7 @@ page.getByTestId('tx-hash') ## Environment and date -`playwright/lib.tsx` runs before every component mount and: +`playwright/lib.ts` runs before every component mount and: - Mocks `next/router.useRouter()` with a configurable router object. - Fixes the current date to `2022-11-11T12:00:00Z` via MockDate — all `new Date()` / `Date.now()` calls return this value. @@ -187,5 +205,4 @@ Extract any non-obvious test values (IDs, amounts, hashes) into named constants ## Conventions - Do not use the `testFn: TestFixture<...>` pattern. It makes refactoring harder and can bypass Playwright lint rules. Use it only when genuinely sharing fixture logic across multiple suites. -- Keep each test focused on one visual state or interaction. - Screenshots are the contract — keep them up to date and review diffs carefully in PRs. diff --git a/.agents/rules/typescript.md b/.agents/rules/typescript.md index 1d65d1c42a6..b735f70498e 100644 --- a/.agents/rules/typescript.md +++ b/.agents/rules/typescript.md @@ -204,26 +204,9 @@ interface C extends A, B { ``` -## JS-doc comments - -Use JSDoc comments to annotate functions and types. - -Be concise in JSDoc comments, and only provide JSDoc comments if the function's behavior is not self-evident. - -Use the JSDoc inline `@link` tag to link to other functions and types within the same file. - -```ts -/** - * Subtracts two numbers - */ -const subtract = (a: number, b: number) => a - b; - -/** - * Does the opposite to {@link subtract} - */ -const add = (a: number, b: number) => a + b; -``` +## JSDoc +JSDoc is a comment. Same rule as [Comments](./code-quality.md#comments): omit it unless it explains *why*. Never restate the name or types. If a JSDoc block already earned its place, `@link` is fine for same-file references. ## Naming conventions diff --git a/.agents/skills/add-api-resource/SKILL.md b/.agents/skills/add-api-resource/SKILL.md index 5373e1ebc64..adc7fca276d 100644 --- a/.agents/skills/add-api-resource/SKILL.md +++ b/.agents/skills/add-api-resource/SKILL.md @@ -14,7 +14,7 @@ This skill declares new resource(s) in an **existing** service. (registration + dispatch-type branches) and a `src/api/config.ts` block; not covered here. **Out of scope — consuming the resource.** Wiring it into UI (`useApiQuery` / -`useQueryWithPages`, stubs) is the caller's job; this skill only makes the resource exist and +`useApiPaginatedQuery`, stubs) is the caller's job; this skill only makes the resource exist and be correctly typed. **Background reading:** *Where a resource's response types come from* in diff --git a/.agents/skills/add-env-var/SKILL.md b/.agents/skills/add-env-var/SKILL.md index c59f0f419c7..8d507a7693b 100644 --- a/.agents/skills/add-env-var/SKILL.md +++ b/.agents/skills/add-env-var/SKILL.md @@ -32,7 +32,9 @@ Add a row in the section that mirrors where the variable's config lives: App configuration / APIs configuration / App shell / Slices / Features / External services / Misc. The doc sections mirror `src/config/index.ts`. -Fill in: name, type, description, required/optional, default, example. +Fill in the name, type, description, required/optional status, default, and example. +Description: one sentence maximum; avoid validation logic and implementation details; +be extremely concise. Set the **Version** column to `upcoming`. The release process replaces this with the actual version number when the change ships. @@ -167,4 +169,3 @@ the feature/option being enabled. Append the variable name to the `ASSETS_ENVS` array in `deploy/scripts/download_assets.sh`. The container entrypoint downloads the asset into the image at startup so the browser serves it same-origin. - diff --git a/.agents/skills/add-new-page/SKILL.md b/.agents/skills/add-new-page/SKILL.md index 1a299ca0083..1a7be9bcdb5 100644 --- a/.agents/skills/add-new-page/SKILL.md +++ b/.agents/skills/add-new-page/SKILL.md @@ -43,13 +43,13 @@ substitute the placeholders and delete any optional blocks you don't need: Template sets: - `templates/general/{Page,GeneralInfo}.tsx.tmpl` (`Page` = non-tabbed general page; `GeneralInfo` = a general-content body, used as a tab panel) - `templates/detail/{Page,Details}.tsx.tmpl` -- `templates/index/{Page,Content,Table,TableItem,List,ListItem}.tsx.tmpl` +- `templates/index/{Page,Content,Table,TableItem}.tsx.tmpl` - `templates/tabs/PageWithTabs.tsx.tmpl` (tabbed page shell — `PageTitle` + `RoutedTabs`) - `templates/route.tsx.tmpl` (shared route wrapper; has a commented dynamic-route variant) A **page shell** is one of: a non-tabbed `Page.tsx.tmpl` (embeds a single content body) or the tabbed `PageWithTabs.tsx.tmpl` (renders `RoutedTabs`). A **content body** is `Details` (detail), `Content` + -`Table`/`List`/items (index), or `GeneralInfo` (general). Tabbed pages combine one shell with one body per tab. +`Table`/`TableItem` (index), or `GeneralInfo` (general). Tabbed pages combine one shell with one body per tab. **Naming the destination files.** Drop the `.tmpl` extension and name each component file after the component it exports — i.e. the `__PageName__`-prefixed name used in the template's `const` / `import` @@ -65,8 +65,6 @@ route wrapper is named after the route, not the component. | `index/Content.tsx.tmpl` | `__PageName__Content.tsx` → `ValidatorsContent.tsx` | | `index/Table.tsx.tmpl` | `__PageName__Table.tsx` → `ValidatorsTable.tsx` | | `index/TableItem.tsx.tmpl` | `__PageName__TableItem.tsx` → `ValidatorsTableItem.tsx` | -| `index/List.tsx.tmpl` | `__PageName__List.tsx` → `ValidatorsList.tsx` | -| `index/ListItem.tsx.tmpl` | `__PageName__ListItem.tsx` → `ValidatorsListItem.tsx` | | `general/GeneralInfo.tsx.tmpl` | `__PageName__GeneralInfo.tsx` → `ValidatorsGeneralInfo.tsx` | | `tabs/PageWithTabs.tsx.tmpl` | `__PageName__.tsx` → `Validators.tsx` | | `route.tsx.tmpl` | `src/pages/__route__.tsx` → `src/pages/validators.tsx` | @@ -76,8 +74,7 @@ components live in their own kebab-case sub-folder** under it (mirrors `src/slices/tx/pages/details/{info,logs,state,…}`). Name a tab's components after the tab so same-type tabs don't collide: replace the `__PageName__` base with `__PageName__` (PascalCase tab name), and rename the body's sub-components/imports to match. E.g. an `index`-type tab "Active" on page `Validators` → folder -`active/` holding `ValidatorsActive` (from `Content`), `ValidatorsActiveTable`, `ValidatorsActiveTableItem`, -`ValidatorsActiveList`, `ValidatorsActiveListItem`. A `detail`-type tab "Details" → folder `info/` holding +`active/` holding `ValidatorsActive` (from `Content`), `ValidatorsActiveTable`, `ValidatorsActiveTableItem`. A `detail`-type tab "Details" → folder `info/` holding `ValidatorsDetails` (from `Details`). A `general`-type tab "Stats" → folder `stats/` holding `ValidatorsStats` (from `GeneralInfo`). @@ -90,7 +87,7 @@ Propose each of the following and **wait for the user's explicit approval**. Ask obvious from the conversation or the codebase. 1. **Layout** — does the page have **tabs**? Decide this first; ask if it isn't obvious from the conversation. - - **No tabs** → pick the single content type: `index` (list + table views), `detail` (label/value grid), + - **No tabs** → pick the single content type: `index` (table view), `detail` (label/value grid), or `general` (title + content). - **Tabs** → list the tabs **in order**; for each, give a **name** (the tab title) and a **content type** (`details` / `index` / `general info`). That's enough at this stage. **A `details` tab, if present, must @@ -141,10 +138,11 @@ The **content bodies** are the same regardless of tabs — reference these exemp `src/features/gas-tracker/pages/index/GasTracker.tsx`. - **detail** (`Details.tsx`) — a `DetailedInfo.Container` grid of `ItemLabel`/`ItemValue` rows wrapped in `Skeleton`. Exemplars `src/slices/block/pages/details/BlockDetails.tsx`; grid `src/shared/detailed-info/DetailedInfo.tsx`. -- **index** (`Content.tsx` + `Table`/`TableItem` + `List`/`ListItem`) — `Content.tsx` is `DataList` + dual - view (`` mobile `List`, `` desktop `Table`); `Table`/`List` render - one row per item via dedicated `TableItem`/`ListItem` components (mirroring `BlocksTableItem`/`BlocksListItem`). - Exemplars `src/slices/block/pages/index/{BlocksContent,BlocksTable,BlocksTableItem,BlocksList,BlocksListItem}.tsx`; +- **index** (`Content.tsx` + `Table`/`TableItem`) — `Content.tsx` is `DataList` wrapping the `Table` in + `TableContainerScrollable`, one view for every viewport (mobile scrolls it horizontally); the `Table` root's + `minWidth` is set from its columns' content so they don't compress. `Table` renders one row per item via a + dedicated `TableItem` component (mirroring `BlocksTableItem`). + Exemplars `src/slices/block/pages/index/{BlocksContent,BlocksTable,BlocksTableItem}.tsx`; `src/shared/lists/DataList.tsx`. Replace the `DataList` placeholders with the page's entity (don't leave the generic "items"): set `emptyText` to a plural no-data sentence (`"There are no blocks."`) and `emptyStateProps={{ term: '' }}` to @@ -158,7 +156,7 @@ Use the single page shell that embeds its one content body directly: - **general** → `general/Page.tsx.tmpl` (content inline; no separate body needed). - **detail** → `detail/Page.tsx.tmpl` (reads the route param, renders `PageTitle` + `Details`) + `detail/Details.tsx.tmpl`. -- **index** → `index/Page.tsx.tmpl` (renders `PageTitle` + `Content`) + `index/{Content,Table,TableItem,List,ListItem}.tsx.tmpl`. +- **index** → `index/Page.tsx.tmpl` (renders `PageTitle` + `Content`) + `index/{Content,Table,TableItem}.tsx.tmpl`. ### B. With tabs @@ -204,7 +202,7 @@ Run **`pnpm run routes:generate`** (runs `nextjs-routes`) to regenerate `src/sha ## Step 5 — Navigation -In `src/shell/navigation/useNavItems.tsx` add a `NavItem` to the group agreed in Step 0: +In `src/shell/navigation/useNavItems.ts` add a `NavItem` to the group agreed in Step 0: ```ts const __featureName__: NavItem | null = config.features.__featureName__.isEnabled ? { text: '__title__', @@ -236,7 +234,7 @@ title/description (`default` only, no `enhanced`): ## Step 7 — Page-type analytics -In `src/services/mixpanel/get-page-type.ts` add an entry to `PAGE_TYPE_DICT` (a +In `src/shared/analytics/get-page-type.ts` add an entry to `PAGE_TYPE_DICT` (a `Record`). **Suggest** a human-readable name; let the user adjust: ```ts '__pathname__': '__title__', diff --git a/.agents/skills/add-new-page/templates/index/Content.tsx.tmpl b/.agents/skills/add-new-page/templates/index/Content.tsx.tmpl index 5b0eb1f7f70..48a524b9fe8 100644 --- a/.agents/skills/add-new-page/templates/index/Content.tsx.tmpl +++ b/.agents/skills/add-new-page/templates/index/Content.tsx.tmpl @@ -1,18 +1,18 @@ // SPDX-License-Identifier: LicenseRef-Blockscout -import { Box } from '@chakra-ui/react'; import React from 'react'; -import __PageName__List from 'src/__area__/__featureName__/pages/index/__PageName__List'; import __PageName__Table from 'src/__area__/__featureName__/pages/index/__PageName__Table'; import DataList from 'src/shared/lists/DataList'; +import { TableContainerScrollable } from 'src/toolkit/chakra/table'; + const __PageName__Content = () => { - // TODO (api-data): wire up data (useQueryWithPages) — see wiring.md in the add-new-page skill. + // TODO (api-data): wire up data (useApiPaginatedQuery) — see wiring.md in the add-new-page skill. // Pagination, filtering, sorting and the action bar are intentionally omitted from // this scaffold. See src/slices/block/pages/index/BlocksContent.tsx for the full pattern. - // Three placeholder items so the table/list render visible example rows — replace with fetched data. + // Three placeholder items so the table renders visible example rows — replace with fetched data. const items: Array = [ {}, {}, {} ]; const isError = false; const isLoading = false; @@ -26,12 +26,9 @@ const __PageName__Content = () => { emptyText="There are no items." emptyStateProps={{ term: 'item' }} > - - <__PageName__List items={ items } isLoading={ isLoading }/> - - + <__PageName__Table items={ items } isLoading={ isLoading }/> - + ); }; diff --git a/.agents/skills/add-new-page/templates/index/List.tsx.tmpl b/.agents/skills/add-new-page/templates/index/List.tsx.tmpl deleted file mode 100644 index 40129fba34b..00000000000 --- a/.agents/skills/add-new-page/templates/index/List.tsx.tmpl +++ /dev/null @@ -1,24 +0,0 @@ -// SPDX-License-Identifier: LicenseRef-Blockscout - -import { Box } from '@chakra-ui/react'; -import React from 'react'; - -import __PageName__ListItem from 'src/__area__/__featureName__/pages/index/__PageName__ListItem'; - -interface Props { - // TODO (api-data): replace `unknown` with the real item type - items: Array; - isLoading?: boolean; -} - -const __PageName__List = ({ items, isLoading }: Props) => { - return ( - - { items.map((item, index) => ( - <__PageName__ListItem key={ index } item={ item } isLoading={ isLoading }/> - )) } - - ); -}; - -export default __PageName__List; diff --git a/.agents/skills/add-new-page/templates/index/ListItem.tsx.tmpl b/.agents/skills/add-new-page/templates/index/ListItem.tsx.tmpl deleted file mode 100644 index fc8b25b8183..00000000000 --- a/.agents/skills/add-new-page/templates/index/ListItem.tsx.tmpl +++ /dev/null @@ -1,37 +0,0 @@ -// SPDX-License-Identifier: LicenseRef-Blockscout - -import React from 'react'; - -import ListItemMobileGrid from 'src/shared/lists/ListItemMobileGrid'; - -import { Skeleton } from 'src/toolkit/chakra/skeleton'; - -interface Props { - // TODO (api-data): replace `unknown` with the real item type - item: unknown; - isLoading?: boolean; -} - -const __PageName__ListItem = ({ item, isLoading }: Props) => { - return ( - - { /* TODO (api-data): one Label/Value pair per field; render values from `item` */ } - Label 1 - - Value 1 - - - Label 2 - - Value 2 - - - Label 3 - - Value 3 - - - ); -}; - -export default __PageName__ListItem; diff --git a/.agents/skills/add-new-page/templates/index/Table.tsx.tmpl b/.agents/skills/add-new-page/templates/index/Table.tsx.tmpl index 8b342f65c80..c2d76c8ccfe 100644 --- a/.agents/skills/add-new-page/templates/index/Table.tsx.tmpl +++ b/.agents/skills/add-new-page/templates/index/Table.tsx.tmpl @@ -14,7 +14,8 @@ interface Props { const __PageName__Table = ({ items, isLoading }: Props) => { return ( - + // TODO (design): set `minWidth` from the columns' content so they don't compress when the table scrolls on mobile. + { /* TODO (api-data): define columns */ } diff --git a/.agents/skills/add-new-page/templates/tabs/PageWithTabs.tsx.tmpl b/.agents/skills/add-new-page/templates/tabs/PageWithTabs.tsx.tmpl index c7d79234555..b8de0f32025 100644 --- a/.agents/skills/add-new-page/templates/tabs/PageWithTabs.tsx.tmpl +++ b/.agents/skills/add-new-page/templates/tabs/PageWithTabs.tsx.tmpl @@ -18,7 +18,7 @@ const __PageName__ = () => { // const router = useRouter(); // const __entityParam__ = getQueryParamString(router.query.__entityParam__); - // TODO (api-data): wire up data (useApiQuery / useQueryWithPages) — see wiring.md in the add-new-page skill. + // TODO (api-data): wire up data (useApiQuery / useApiPaginatedQuery) — see wiring.md in the add-new-page skill. const tabs: Array = [ { id: 'tab1', title: 'Tab 1', component: <__PageName__Tab1/> }, diff --git a/.agents/skills/add-new-page/wiring.md b/.agents/skills/add-new-page/wiring.md index 7995fcae600..d7d0299e21b 100644 --- a/.agents/skills/add-new-page/wiring.md +++ b/.agents/skills/add-new-page/wiring.md @@ -6,13 +6,13 @@ was scaffolded earlier (its `TODO (api-data):` markers are still in place); on d run the *Data* item of the Step 0 interview first. **The worklist is the tag.** Grep the page's directories for `TODO (api-data):` — every hit -(page shell, `Content`, `Table`/`TableItem`, `List`/`ListItem`, `Details`, `GeneralInfo`) is +(page shell, `Content`, `Table`/`TableItem`, `Details`, `GeneralInfo`) is a spot this phase must resolve (the done-condition is checked in Step W5). `TODO (design):` markers are the next stage's worklist — leave them in place, and add new ones for any representation decision you defer (Step W4). **In scope:** resource declaration (via the `add-api-resource` skill), minimal loading stubs, -`useApiQuery` / `useQueryWithPages` wiring, pagination controls, plain rendering of every +`useApiQuery` / `useApiPaginatedQuery` wiring, pagination controls, plain rendering of every payload field. **Out of scope:** filters, sorting, search, socket live-updates, mocks, tests, and design polish — unless the task explicitly names them. @@ -45,10 +45,10 @@ Mocks and tests are **not** produced here. Replace the scaffold's placeholder `items` / `data` / `isLoading` / `isError` consts, and replace the `unknown` item/data types with the payload (item) type **everywhere it appears** — -the page shell *and* the `Table`/`TableItem`/`List`/`ListItem` (or `Details`) components each +the page shell *and* the `Table`/`TableItem` (or `Details`) components each carry their own `unknown` marker. -- **index, paginated resource** — in the page shell, `useQueryWithPages({ resourceName, +- **index, paginated resource** — in the page shell, `useApiPaginatedQuery({ resourceName, options: { placeholderData: generateListStub<'service:name'>(ITEM_STUB, 50, { next_page_params: { … } }) } })`; take the `next_page_params` keys from the sample body. Thread the query down to the content body; pass `isPlaceholderData` as `isLoading` and @@ -69,8 +69,7 @@ carry their own `unknown` marker. Inventory the payload fields from the type + sample body and render **all of them** in the scaffolded body — `DetailedInfo` rows in `Details` for detail pages; for index pages the -columns in `Table`, the cells in `TableItem`, **and** the label/value pairs in `ListItem` -(desktop and mobile views must show the same fields); free layout in `GeneralInfo` for +columns in `Table` and the cells in `TableItem`; free layout in `GeneralInfo` for general: - Use the obvious shared component **only when the field is unambiguous** — an address diff --git a/.agents/skills/finalize-task/SKILL.md b/.agents/skills/finalize-task/SKILL.md index 95df70c4877..f9d52e2c63a 100644 --- a/.agents/skills/finalize-task/SKILL.md +++ b/.agents/skills/finalize-task/SKILL.md @@ -19,9 +19,10 @@ ticket is unfinished — there is nothing to land yet. Then delete the disposable files, keeping only `spec.md` in the task folder: -- `tickets/` — the whole directory. +- `tickets/` — the whole directory, review files included. - `progress.md`. - `questions.md`. +- `review.md` / `review-.md`, if a task-level review ran in `md` mode. Commit the deletions on the feature branch as the task's final commit — a plain descriptive subject, the repo's `Co-Authored-By` trailer, no `#issue` reference. Pruning **before** the PR is finalized is what lets diff --git a/.agents/skills/grill-the-task/SKILL.md b/.agents/skills/grill-the-task/SKILL.md index 3883daac7e8..949e9632d9b 100644 --- a/.agents/skills/grill-the-task/SKILL.md +++ b/.agents/skills/grill-the-task/SKILL.md @@ -95,9 +95,9 @@ Route every question the session couldn't answer to the person who owns it. - When posting to a channel, **always mention the addressee** — `<@member ID>` from `.agents/TEAM.md` (people missing from the roster: resolve by name via `slack_search_users` and suggest adding them). 3. Draft one message per owner: brief task context (issue link), the questions, and why they block progress. - Write all Slack messages in **Russian** — the team's internal language (the spec itself stays in English). 4. **Show every draft (with its destination) to the user and wait for explicit approval** — never send - unreviewed outreach. + unreviewed *new* outreach. (In-thread follow-ups on a thread already approved here are exempt — see + "Watch for replies" below.) 5. Send (`slack_send_message`), then keep each thread's permalink for the question's `questions.md` entry. If the Slack MCP tools are unavailable, record the questions with owners anyway and tell the user to route @@ -106,6 +106,39 @@ them manually. Outreach is complete when every question has a recorded permalink — or an explicit note that the developer routes it manually. +### Watch for replies and drive each question to settled + +Once the questions are sent, don't hand the reply round-trip back to the developer to poll. Launch the +**`slack-watch`** skill with one `CHANNEL:THREAD_TS` argument per `pending` question thread (the channel +id and parent `ts` from each `slack_send_message` you just sent). That skill owns how to start, stop, and +relaunch on Claude Code vs Cursor — follow it. Adding or resolving a question later means stopping the +watcher and relaunching with the new thread list; questions move at human pace, so the restart is free. +If the tokens aren't set up, `slack-watch` guides the developer through it; until then, the developer +polls manually as before. + +On each `NEW …` notification, read the reply with `slack_read_thread` on that `thread_ts`, then for the +question that thread belongs to: + +- **Assess** the reply against the question's `Resolved when:` criterion — session-held here, since this + runs before `to-spec`; it only becomes a `questions.md` line once `to-spec` writes one (lifecycle step 2). +- **Insufficient** → send a clarifying **follow-up to the colleague directly, in the same thread, without + developer approval** — this is continuing a conversation the developer already opened. Cap: **3** + consecutive follow-ups per question, then escalate. Opening a *new* thread or contacting a *new* person + stays approval-gated (Step 3 rules unchanged). +- **Sufficient** → **propose in the session chat**: the answer arrived, the proposed resolution, and the + edits it implies. **Edit no file.** Apply only after the developer accepts in-session. Where the answer + lands depends on how far the task has come: once `to-spec` has run, fold it into `questions.md` (the + fold-in is `.agents/tasks/README.md` step 4) — so `implement-ticket`'s question gate never releases work + a human hasn't read; before that (still grilling, or a small task that never gets a spec), hold it in + session to inform the forthcoming spec or to unblock the in-session implementation. +- **Escalate** (one line: which task/question and why it's stuck) when the answer needs *frontend* + knowledge only the developer has, the 3-follow-up cap is hit, or the colleague goes silent. + Claude Code: `PushNotification` with that line. Cursor: the same line in this session chat — Cursor has + no OS nudge. + +Every message posted to a colleague carries a disclosure — they must be able to tell they're in a +technical back-and-forth with an agent — `.agents/slack-message.md` → Attribution. + ## Step 4 — Size and hand off Decide, by a rough sizing judgment, whether the task needs a spec — one session of work or not. No formal diff --git a/.agents/skills/prepare-release/SKILL.md b/.agents/skills/prepare-release/SKILL.md index 94f168814fc..cf9476e31cc 100644 --- a/.agents/skills/prepare-release/SKILL.md +++ b/.agents/skills/prepare-release/SKILL.md @@ -142,5 +142,5 @@ The pushed `-alpha` tag triggers `pre-release.yml`, which builds and publishes a Once the pre-release workflow has finished **successfully**, ask the DevOps team to roll up the new pre-release on staging. -- Build the message from `./slack-message-template.md` (in this skill directory) — it holds the format, the breaking-ENV-change rule, the target channel, and the QA cc. Fill it with the `-alpha` tag, the breaking-ENV list derived per that rule (or `None.`), and the pre-release URL from step 6. The breaking-ENV input comes from the "Changes in ENV variables" section / `release-prs-data.json`. +- Build the message from `./slack-message-template.md` (in this skill directory) — it holds the format, the breaking-ENV-change rule, the target channel, and the QA cc. Fill it with the `-alpha` tag, the breaking-ENV list derived per that rule, and the pre-release URL from step 6. The breaking-ENV input comes from the "Changes in ENV variables" section / `release-prs-data.json`. - **Draft first:** show the user the fully rendered message and **wait for their approval** before sending it to the channel. diff --git a/.agents/skills/prepare-release/slack-message-template.md b/.agents/skills/prepare-release/slack-message-template.md index 69ad65bb9c3..2b76ec0b5a3 100644 --- a/.agents/skills/prepare-release/slack-message-template.md +++ b/.agents/skills/prepare-release/slack-message-template.md @@ -17,26 +17,26 @@ channel and group ID, so a moved channel or renamed group is fixed in one place. | Placeholder | Meaning | | ------------------------ | ----------------------------------------------------------------------- | | `` | The pre-release tag, e.g. `v1.3.0-alpha`. | -| `` | Bulleted list of breaking ENV changes, or the single line `None.` | +| `` | Bulleted list of breaking ENV changes, or the single line `Отсутствуют.` | | `` | Link to the published GitHub pre-release. | | `` | QA team group mention token `>`, built from `.agents/TEAM.md`. | A change is **breaking** if a deployment must change its config to keep working: a **removed** variable, a **renamed** variable, or a change to a **required**/default value or **allowed value set**. New optional variables are *not* breaking. When in doubt, list it -and mark it `(potentially breaking)` — better to over-report than to miss one. +and mark it `(возможно, ломающее)` — better to over-report than to miss one. ## Template ``` -📦 Frontend pre-release *``* is ready for staging. +📦 Пре-релиз фронтенда *``* готов для стейджинга. -Could you please roll up this pre-release tag on the staging instances? +Раскатите, пожалуйста, этот тег на стейджинг-инстансы. -*Breaking ENV changes:* +*Ломающие изменения ENV:* -Release notes: +Содержание релиза: cc ``` @@ -44,16 +44,16 @@ cc ### Example — with breaking changes ``` -📦 Frontend pre-release *`v1.3.0-alpha`* is ready for staging. +📦 Пре-релиз фронтенда *`v1.3.0-alpha`* готов для стейджинга. -Could you please roll up this pre-release tag on the staging instances? +Раскатите, пожалуйста, этот тег на стейджинг-инстансы. -*Breaking ENV changes:* -• Removed `NEXT_PUBLIC_FOO` — the X feature now reads from `NEXT_PUBLIC_BAR` instead. -• Renamed `NEXT_PUBLIC_OLD` → `NEXT_PUBLIC_NEW`. -• `NEXT_PUBLIC_AD_BANNER_PROVIDER`: removed the `hype` option (potentially breaking). +*Ломающие изменения ENV:* +• Удалена `NEXT_PUBLIC_FOO` — фича X теперь читает `NEXT_PUBLIC_BAR`. +• Переименована `NEXT_PUBLIC_OLD` → `NEXT_PUBLIC_NEW`. +• `NEXT_PUBLIC_AD_BANNER_PROVIDER`: удалено значение `hype` (возможно, ломающее). -Release notes: https://github.com/blockscout/frontend/releases/tag/v1.3.0-alpha +Содержание релиза: https://github.com/blockscout/frontend/releases/tag/v1.3.0-alpha cc ``` @@ -61,13 +61,13 @@ cc ### Example — no breaking changes ``` -📦 Frontend pre-release *`v1.3.0-alpha`* is ready for staging. +📦 Пре-релиз фронтенда *`v1.3.0-alpha`* готов для стейджинга. -Could you please roll up this pre-release tag on the staging instances? +Раскатите, пожалуйста, этот тег на стейджинг-инстансы. -*Breaking ENV changes:* None. +*Ломающие изменения ENV:* Отсутствуют. -Release notes: https://github.com/blockscout/frontend/releases/tag/v1.3.0-alpha +Содержание релиза: https://github.com/blockscout/frontend/releases/tag/v1.3.0-alpha cc ``` diff --git a/.agents/skills/resolve-config-request/SKILL.md b/.agents/skills/resolve-config-request/SKILL.md index 857e42012ed..2ddd1cc0f95 100644 --- a/.agents/skills/resolve-config-request/SKILL.md +++ b/.agents/skills/resolve-config-request/SKILL.md @@ -73,7 +73,7 @@ Compose it per **The DevOps message** below. Show it to the user and wait. ### 6. Send, then hand over -Post the approved message to `blockscout-devops-requests` — resolve the channel ID from `.agents/TEAM.md`. Then reply in the original thread — same language as the thread — with a link to that message, saying the request has been handed over and is waiting on DevOps. +Post the approved message to `blockscout-devops-requests` — resolve the channel ID from `.agents/TEAM.md`. Then reply in the original thread with a link to that message, saying the request has been handed over and is waiting on DevOps. **Done when:** both posts exist and the original-thread reply carries the DevOps link. @@ -130,7 +130,7 @@ After merge to `main`, confirm each raw URL returns 200. If the instance already ## The DevOps message -Russian, informal. Target in the requester's own words — not registry aliases. Sets in one fenced `KEY=value` block; keys to drop as names only. Mention the requester with `<@U…>` from the thread author's `user_id` (`.agents/TEAM.md` → How to address). +Informal. Target in the requester's own words — not registry aliases. Sets in one fenced `KEY=value` block; keys to drop as names only. Mention the requester with `<@U…>` from the thread author's `user_id` (`.agents/slack-message.md` → Addressing). ```` Привет! 🐈 diff --git a/.agents/skills/resolve-config-request/SKIN-REQUESTS.md b/.agents/skills/resolve-config-request/SKIN-REQUESTS.md index 5f45f1c26f2..890bb6558c7 100644 --- a/.agents/skills/resolve-config-request/SKIN-REQUESTS.md +++ b/.agents/skills/resolve-config-request/SKIN-REQUESTS.md @@ -45,7 +45,7 @@ While the configs PR is open, asset URLs are the **PR branch's** raw GitHub URLs ## Demo mechanics -`.env.extra` is committed and is the **only** channel that carries env overrides into a review deploy. An instance not in the registry: add it to `tools/dev-server/registry.json`, run `pnpm presets:sync` (writes `.github/workflows/deploy-review.yml` and `.vscode/tasks.json`), commit all four **on the demo branch**. They die with that branch. Procedure: `tools/dev-server/CONTEXT.md`. +The **repo-root** `.env.extra` is committed and is the **only** channel that carries env overrides into a review deploy. An instance not in the registry: add it to `tools/dev-server/registry.json`, run `pnpm presets:sync` (writes `.github/workflows/deploy-review.yml` and `.vscode/tasks.json`), commit all four **on the demo branch**. They die with that branch. Procedure: `tools/dev-server/CONTEXT.md`. Hostname: `review-.k8s-dev.blockscout.com`. Follow the `deploy-demo` skill. @@ -55,18 +55,29 @@ Verify the theme by **computed values** — read the resolved CSS custom propert **Phase 1 runs to the demo link without stopping.** No gate — designer's or user's — on the branch, the commits, the configs PR, the demo deploy, or the demo-link post. The PR is open but unmerged and the demo dies with its branch, so nothing here can touch a production instance; the parent's commit confirmation does not apply. The demo *is* the review surface, and a confirmation asked before it exists is asked of someone who cannot yet see what they are confirming. -The requester (designer) is the gatekeeper for phase 2; the user relays that approval. There is no automated watch on the thread — the user monitors and continues this session. +The requester (designer) is the gatekeeper for phase 2. Do not hand the session back to the user to monitor: **subscribe to the designer's replies on the source thread with the `slack-watch` skill** as the last action of phase 1, then stop. 1. Produce the assets and JSON configs from the thread and Figma. 2. Open a PR on `frontend-configs` (target `main` branch). No confirmation, per above; `create-pr` exceptions: parent **Fetched configs**. 3. Point the demo at the PR-branch raw URLs (see **Demo mechanics**). Deploy it. -4. Verify on the live demo, post the demo link to the requester, and **stop**. Post it without asking — the named exception to `AGENTS.md`'s approve-before-sending rule. This is the run's only stop; what it waits for is the designer's reply. +4. Verify on the live demo, then post the demo link to the requester. Post it without asking — the named exception to `AGENTS.md`'s approve-before-sending rule. +5. Subscribe to the designer's replies on the source thread with `slack-watch`, then **stop**. This is the run's only stop; the watch is what resumes it — see **On the designer's reply**. -**Done when:** the configs PR is open, the demo is live, the requester has the link, and this run has stopped. +**Done when:** the configs PR is open, the demo is live, the requester has the link, the `slack-watch` subscription is running, and this run has stopped. + +## On the designer's reply + +When `slack-watch` wakes the session, read the new reply and act on it directly: + +- **Approval** (the designer signs off) → go to **Phase 2**. +- **Change request** → stay in phase 1: apply the asked changes using the same technique as for the initial request; redeploy the demo and re-post the refreshed demo link; leave the watch running. +- **A question, or anything turning on intent you cannot pin** → stop and ask the user (the *Sure* principle: intent questions go to the user, not the designer). + +Loop on replies until approval. The watch is torn down in phase 2. ## Phase 2 — ship -Starts only when the user relays the designer's approval. +Starts when the designer approves. 1. Merge the configs PR. Swap every branch URL to `main` and confirm each returns 200 — that is when instances can fetch them. 2. For inlined vars (hero banner, colour theme), regenerate the strings from the merged files (the converter above). URL vars (homepage highlights, asset URLs): the env value is the `main` raw URL. Navigation promo: the inlined string produced in phase 1. @@ -75,6 +86,7 @@ Starts only when the user relays the designer's approval. - demo destroyed: `gh workflow run cleanup.yml --ref `; hostname returns 404 - temporary frontend branch deleted, local and remote - `git status` clean + - the `slack-watch` thread subscription stopped The review-image cleanup is broken ([frontend#3638](https://github.com/blockscout/frontend/issues/3638)); hostname 404 is the bar, not image deletion. diff --git a/.agents/skills/resolve-review/SKILL.md b/.agents/skills/resolve-review/SKILL.md index 6a4576fb572..10491e2ef1e 100644 --- a/.agents/skills/resolve-review/SKILL.md +++ b/.agents/skills/resolve-review/SKILL.md @@ -1,37 +1,67 @@ --- name: resolve-review description: >- - Close out review findings on a PR — adjudicate each one, fix what deserves fixing, then reply and resolve - the threads. + Close out review findings — adjudicate each one, fix what deserves fixing, then reply on the PR threads or + in the task's review file. +argument-hint: pr|md [] [--scope branch|uncommitted] [--ticket ] disable-model-invocation: true --- # Resolve review -Work through a PR's review findings and close them out. The hard part is not fixing — it is deciding *which* -findings deserve a fix. So the centre of this skill is **adjudication**: every finding gets a **verdict**, -reached skeptically, by checking the claim against the real code and the project's intent rather than by -trusting how confidently it was worded. +Work through a change's review findings and close them out. The hard part is not fixing — it is deciding +*which* findings deserve a fix. So the centre of this skill is **adjudication**: every finding gets a +**verdict**, reached skeptically, by checking the claim against the real code and the project's intent +rather than by trusting how confidently it was worded. -In the product-task workflow this runs at **land**, against the whole-task PR — it adjudicates the inline -comments `review-changes` posted, alongside any human or bot comments on the same PR. +In the product-task workflow this runs at **land**, against the whole-task PR, and mid-ticket against the +review file `review-changes` wrote for uncommitted work. + +## Inputs + +Mirrors `review-changes`: the same output, first and required, decides where the findings are read from and +replied to. + +| Input | Values | Default | Meaning | +| --- | --- | --- | --- | +| `output` *(positional)* | `pr` · `md` | **required** | where the findings live | +| `target` *(positional)* | PR url, comment url, or review-file path | the current branch's PR / its review file | scopes the run | +| `--scope` | `branch` · `uncommitted` | `branch` | which review file: the task's, or the in-flight ticket's. `md` only | +| `--ticket ` | ticket number | inferred | `--scope uncommitted` only | + +``` +/resolve-review pr every open finding on the current branch's PR +/resolve-review pr that single comment, scoping the whole run to it +/resolve-review md the task-level review file +/resolve-review md --scope uncommitted the in-flight ticket's review file +/resolve-review md that review file — one reviewer's, where several ran +``` + +Contradictory inputs stop the run: `--scope` or `--ticket` with `pr`, or `--ticket` with `--scope branch`. + +`--scope` and `--ticket` are `review-changes`' own, and the file resolves off them exactly as it does there +([`../review-changes/output-md.md`](../review-changes/output-md.md)) — `branch` is the task-level +`review.md`, `uncommitted` the ticket's. An explicit `target` path settles the file outright and makes both +redundant. Several review files side by side means several reviewers ran: resolve them in one pass, and +where two raise the same defect, fix once and reply on both. ## Verdicts, by source -Which verdicts are even available depends on who raised the finding, so establish the source first. +Which verdicts are even available depends on who raised the finding, so establish the source first. In `md` +mode every finding came from `review-changes`, so the question does not arise — `fix` or `reject`. | Source | How you know it | Verdicts | | --- | --- | --- | | This workflow's review | a PR comment ending in a `— Reviewed by …` footer | `fix` · `reject` | | A bot | `user.type == "Bot"` | `fix` · `reject` | -| A human | neither of the above | `fix` · `answered` | +| A human | anything else | `fix` · `answered` | -Test in that order. The footer comes first because an agent posts through a human's account — `user.login` -is the repo owner's in every case, so nothing but the footer distinguishes this workflow's own review. -Bots are then caught by GitHub's own `user.type`, **not** by a list of logins: this repo alone sees -`Copilot` (no `[bot]` suffix, capitalised), `coderabbitai[bot]`, `cursor[bot]` (Cursor Bugbot, which -`.cursor/BUGBOT.md` aims at our smell baseline) and `github-advanced-security[bot]`, and a name list gets -three of those four wrong — silently promoting them to human, whose comments may never be rejected. +Test the rows in that order. The footer is what separates this workflow's own review from a colleague's — +why, in [`../review-changes/gh-commands.md`](../review-changes/gh-commands.md). Bots are then caught by +GitHub's own `user.type`, **not** by a list of logins: this repo still runs CodeQL and Copilot, so +`github-advanced-security[bot]` and `Copilot` (no `[bot]` suffix, capitalised) both reach a PR, and a name +list goes stale the moment the tooling around the repo changes. Miss either test and an agent's or a bot's +finding is silently promoted to human, whose comments may never be rejected. - **fix** — the concern is real *and* the fix belongs in this change. - **reject** — invalid premise, contradicts design intent, already addressed, or out of scope. Closes with @@ -48,27 +78,35 @@ Two further rules on verdicts: `needs-human`. - **Nits are `deferred`**, not fixed. The developer may promote one at Gate 1. -## Invocation - -- `/resolve-review` — the usual case: resolve the open findings on the current branch's PR. -- `/resolve-review ` — that PR, or that single comment, scoping the whole run to it. - ## 1. Scope -Establish what you are resolving. Derive `owner/repo` and the PR number, and confirm `gh auth status` -succeeds (commands: [`../review-changes/gh-commands.md`](../review-changes/gh-commands.md)). +Establish what you are resolving: +- in `pr` mode derive `owner/repo` and the PR number, and confirm `gh auth +status` succeeds (commands: [`../review-changes/gh-commands.md`](../review-changes/gh-commands.md)) +- in `md` mode resolve the review file and read it; a path that does not exist stops the run rather than +becoming a fresh review. -**Done when**: you know the unit of work and whether the scope is every open finding or one specific comment. +**Done when**: you know the unit of work and whether the scope is every open finding or one specific +finding. ## 2. Gather -Collect every **actionable** finding — inline review comments, PR-level reviews, and issue comments -([`../review-changes/gh-commands.md`](../review-changes/gh-commands.md)). Keep only unresolved, actionable -threads. Drop already-resolved threads, your own prior replies, and bot status noise (CodeRabbit "review -skipped", Copilot's PR overview). +In `pr` mode, collect every **actionable** finding — inline review comments, PR-level reviews, and issue +comments ([`../review-changes/gh-commands.md`](../review-changes/gh-commands.md)). Keep only unresolved, +actionable threads. Drop already-resolved threads and your own prior replies. Tag each with its **source** +per the table above. + +A footer-bearing issue comment titled `### 📎 Findings without a diff anchor` holds several findings at +once, one per `** F · **` title, the tag prefix present exactly when that +reviewer ran under `--as`. Split them and adjudicate each on its own. They have no resolved flag, so read +one as open unless a later footer-bearing comment already rules on that id. -Tag each with its **source** per the table above, then open the code it points at — `path` + `line`, or the -`diff_hunk` — so the next step judges against reality rather than against the comment text. +In `md` mode, collect every finding whose `**Status:**` is `open` or `disputed`. The file's reply +blockquotes carry the exchange history — read them, so a finding you already rejected once is not rejected +again on the same grounds. + +Either way, open the code each finding points at — `path` + `line`, or the `diff_hunk` — so the next step +judges against reality rather than against the comment text. **Done when**: every actionable finding is listed with its source, its location, and the current code it refers to. Exhaustive, not a sample. @@ -80,8 +118,8 @@ The heart of the skill. Reason hard here; do not rush toward the gate. - **Investigate before judging.** Verify the claim against the actual code. Check whether it still applies — it may be stale or already fixed. Weigh it against the spec and the conventions in `.agents/rules/`. - **Decompose multi-point findings.** One comment can be part-`fix`, part-`reject`. Adjudicate each point. -- **Give bots no deference.** A plausible-sounding suggestion is not automatically correct; a bot can - contradict the author's intent or argue from the wrong docs. +- **Give the reviewer no deference.** A plausible-sounding finding is not automatically correct; a review + agent or a bot can contradict the author's intent or argue from the wrong docs. - **When a verdict turns on design intent you cannot settle from the code and the spec, mark it `needs-human`.** Do not guess. @@ -92,9 +130,9 @@ text for a `reject` or an `answered`. ## 4. Gate 1 — confirm -**A hard stop.** Present a table — finding (`file:line` + short quote), source, verdict, reasoning, proposed -action — and list the `needs-human` items as questions. Then **stop and wait**. Edit no code until the -developer confirms; they may re-categorise anything or answer the open questions. This is the cheapest +**A hard stop.** Present a table — finding (id, `file:line` + short quote), source, verdict, reasoning, +proposed action — and list the `needs-human` items as questions. Then **stop and wait**. Edit no code until +the developer confirms; they may re-categorise anything or answer the open questions. This is the cheapest steering point in the whole process, which is why it comes before any edit. **Done when**: the developer has confirmed. @@ -110,26 +148,35 @@ findings untouched. ## 6. Gate 2 — review the diff **A hard stop.** Show `git diff` plus a per-finding summary of what changed, and wait for approval before -anything is pushed or replied to. The developer commits and pushes the fixes — the reviewer's next -arbitration round reads them from the PR. +anything is pushed or replied to. In `pr` mode the developer commits and pushes the fixes — the reviewer's +next follow-up round reads them from the PR. In `md` mode the fixes stay uncommitted, which is what the +reviewer's follow-up round reads. ## 7. Close out -Reply to every thread; **who resolves depends on the source.** +Reply to every finding; **who closes it depends on the source.** - `fix` → what changed, plus the commit sha once it exists. - `reject` → the explanation. - `answered` → the reasoning, the alternatives, why this path won. +- `deferred` → that it is a nit left as it stands, so the reviewer's follow-up round can rule `deferred` + rather than wait on a fix that is not coming. + +**This workflow's own findings — reply, never close.** The reviewer raised them and owns their close: it +verifies the fix (or agrees the reject) and closes them in its next follow-up round, which is what lets it +confirm the work landed and post the final all-clear. Closing here would end the loop before the reviewer +ever checked it. + +In `pr` mode that means replying on the thread and leaving it unresolved. **Bot findings** — resolve on +`fix` or `reject`; a bot has no arbitration round, so your verdict is the last word on its thread. +**Human findings** — resolve on `fix`, and leave `answered` open for the human. Leave every `needs-human` +thread open. -**This workflow's own findings — reply, never resolve.** The reviewer raised them and owns their close: it -verifies the fix (or agrees the reject) and resolves in its next arbitration round, which is what lets it -confirm the work landed and post the final all-clear. Resolving here would close the loop before the -reviewer ever checked it. +Non-anchorable findings have no thread. Reply to all of them in **one** new issue comment, each line naming +its id, so the reviewer's follow-up round can match the ids it raised. -**Bot findings** — resolve on `fix` or `reject`; a bot has no arbitration round, so its verdict stands on -posting. **Human findings** — resolve on `fix`, and leave `answered` open for the human. Leave every -`needs-human` thread open — those must stay visible. +In `md` mode, append a reply line under the finding — `> **resolve-review, round :** fix — ` — and leave its `**Status:**` alone. -**Done when**: every adjudicated finding has been replied to and (where settled) resolved on the PR. Then -report: counts per verdict, every `reject`/`answered` with its one-line reason, and anything left for a -human. +**Done when**: every adjudicated finding has been replied to and (where settled) resolved. Then report: +counts per verdict, every `reject`/`answered` with its one-line reason, and anything left for a human. diff --git a/.agents/skills/review-changes/SKILL.md b/.agents/skills/review-changes/SKILL.md index a332c39ee0a..1ce384d5d75 100644 --- a/.agents/skills/review-changes/SKILL.md +++ b/.agents/skills/review-changes/SKILL.md @@ -1,8 +1,9 @@ --- name: review-changes description: >- - Review a change on three axes — spec compliance, repo standards, correctness — each in a fresh subagent - context, then post the findings as inline PR comments (or report them in chat when there is no PR). + Review a change axis by axis — spec compliance, repo standards, correctness, and a Jev screen — then + publish the findings as inline PR comments or into a review file in the task folder. +argument-hint: pr|md [--round first|follow-up] [--scope branch|uncommitted] [--as ] [--ticket ] disable-model-invocation: true --- @@ -11,61 +12,83 @@ disable-model-invocation: true Review a change the way a lead reviewer would: the **axes** that apply, in parallel, each in its own fresh subagent context, then one normalized report. -You produce **findings** and nothing else. Fixing them is `resolve-review`'s job, so this skill never edits -a file — which is also why it runs `lint:eslint` and never `lint:eslint:fix`. +You produce **findings** and nothing else. Fixing them is `resolve-review`'s job. -## Scope and mode +## Inputs -Mode follows from where the code is. +Nothing here is inferred. The output is the one positional and the one required input; a run without it +stops rather than guessing. -| Working tree | Mode | Base | -| --- | --- | --- | -| Clean, PR open, `HEAD` = the PR's head sha | inline PR comments | merge-base with the PR's base branch | -| Clean, PR open, `HEAD` ≠ the PR's head sha | **stop — the branch must be synced first** | — | -| Clean, no PR | chat only, no file | merge-base with `main` | +| Input | Values | Default | Meaning | +| --- | --- | --- | --- | +| `output` *(positional)* | `pr` · `md` | **required** | where the findings are published | +| `--round` | `first` · `follow-up` | `first` | `follow-up` is arbitration (step 3), never a fresh pass | +| `--scope` | `branch` · `uncommitted` | `branch` | what to diff. `md` only | +| `--as ` | short slug (`cursor`, `codex`) | none | namespaces the findings, for concurrent reviewers | +| `--ticket ` | ticket number | inferred | `--scope uncommitted` only | -**An open PR plus an out-of-sync branch stops the run.** Say which way it diverged and what to run — `git -push` when `HEAD` is ahead, `git pull` when behind — then stop rather than review. Otherwise lines that were -never pushed are absent from the PR diff, every anchor fails, and the all-or-nothing POST discards the whole -review. +``` +/review-changes pr round 1 on the current branch's PR +/review-changes pr --round follow-up arbitrate the rulings on that PR +/review-changes md --scope uncommitted the ticket in flight, before it is committed +/review-changes pr --as cursor a second reviewer on a PR another agent already reviewed +``` + +Contradictory inputs stop the run: `--scope` or `--ticket` with `pr`, or `--ticket` with `--scope branch`. -The skill takes no invocation arguments — every case above is inferred. +A `--scope uncommitted` run also stops when you cannot determine **whose** working-tree changes these are — +the resolved ticket, task-level work spanning several tickets, or fixes from an earlier round. Ask the +operator which, and review only after they answer. -**Round detection.** Prior review comments on the PR make this run **arbitration** (step 3) rather than a -fresh review — keyed on the presence of prior comments, not on whether findings are still open, so a second -round never silently becomes another three-axis pass. +### `--as` and concurrent reviewers + +Two agents may review the same change simultaneously; comparing their findings is the point. The tag keeps +them separate by prefixing every finding ID (`cursor-F1`) and, in `md` mode, the file name. Without a tag, +IDs are bare (`F1`), with no collision because only one reviewer is present. A `follow-up` round applies +only to findings carrying **its own** tag, where an untagged round owns the untagged findings — the rest +belong to another reviewer's round. Do not derive the tag: guessing it from the running model makes it +change when the model does, causing round 2 to disown round 1's findings. + +**Guard.** In a `first` round, a review of this change already sitting under someone else's tag means +another agent is working. With no `--as` of your own, stop and ask for one rather than overwriting it — +with a tag, you are the second reviewer and carry on. ## 1. Pin the ground -Resolve the mode with exactly these three probes, then **fail fast before spawning anything**: a wrong mode, -a base that does not resolve (`git rev-parse`), or an empty diff stops the run here, not inside three -subagents. +Resolve the base and the preconditions, and **fail fast before spawning anything**. A breached +precondition, a base that does not resolve (`git rev-parse`), or an empty diff stops the run here, not +inside the subagents. + +| output · `--scope` | Base | Precondition | Repo checks | +| --- | --- | --- | --- | +| `pr` | merge-base with the PR's `baseRefName` | clean tree, and `HEAD` == the PR's `headRefOid` | none — the Checks workflow owns them | +| `md` · `branch` | merge-base with `main` | clean tree | run them | +| `md` · `uncommitted` | `HEAD`, against the working tree | a non-empty working diff | run them | ```bash -git status --porcelain # any output at all → dirty, not ready to review +git status --porcelain # output = dirty gh pr list --head "$(git branch --show-current)" --state open \ - --json number,headRefOid,baseRefName -git rev-parse HEAD # compare against headRefOid + --json number,headRefOid,baseRefName # pr mode; see gh-commands.md +git rev-parse HEAD ``` -**The `gh` exit code answers "could I ask?"; its output answers "is there one?"** Exit ≠ 0 is a tooling -failure that **aborts the run**; exit 0 with `[]` is genuinely no PR. Use `gh pr list --json` and never `gh pr -view`, which exits 1 *both* when no PR exists and when it cannot reach GitHub — so nothing downstream can tell -a missing PR from a sandbox with no network. Never wrap either command in `||`. +**A `md` `follow-up` round has no clean-tree precondition.** `resolve-review` leaves its `md`-mode fixes +uncommitted on purpose ([`../resolve-review/SKILL.md`](../resolve-review/SKILL.md) step 6), so the round +whose whole job is verifying those fixes expects a dirty tree — under either scope. -`--state open` matters: a merged or closed PR must not select PR mode. `headRefOid` and `baseRefName` come -back from that same call, so the base is read rather than guessed, and a plain string comparison against -`HEAD` decides PR mode — unequal in *either* direction means out of sync. +**An open PR plus an out-of-sync branch stops the run.** Say which way it diverged and what to run — `git +push` when `HEAD` is ahead, `git pull` when behind. Otherwise lines that were never pushed are absent from +the PR diff, every anchor fails, and the all-or-nothing POST discards the whole review. Collect, in the review's own context: - `git diff --stat ` and `git diff --name-only `, plus untracked files - (`git ls-files --others --exclude-standard`) — a new file is the most review-worthy thing in a change - and `git diff` alone misses it. -- The task's `spec.md` (if this is a task PR) and `.agents/delegation.md`. + (`git ls-files --others --exclude-standard`) — a new file is the most review-worthy thing in a change and + `git diff` alone misses it. Under `--scope uncommitted` the new files are usually most of the change. +- The task's `spec.md` (if this is a task PR) and [`../../delegation.md`](../../delegation.md). -**In chat mode**, run the repo's checks yourself — do not take "checks pass" on trust, because verifying that -claim is most of what a review is worth: +Where the table says to run the checks, run them yourself rather than taking "checks pass" on trust — +verifying that claim is most of what a review is worth: ```bash pnpm lint:eslint @@ -74,87 +97,44 @@ pnpm lint:cspell pnpm test:vitest --changed ``` -`vitest --changed` selects only the test files the change affects, and exits 0 when it affects none. - -**In PR mode, checks are not your business at all** — neither run them nor read them. The Checks workflow -reports to the PR where the developer already sees it, and it may still be running alongside this review. +**Done when**: the base, the touched-file list, and (where run) the check results are in hand, and the diff +is known non-empty. -**Done when**: mode, base, the touched-file list, and (in chat mode) the check results are all in hand, and -the diff is known non-empty. +## 2. Round `first` — spawn the axes -## 2. Round 1 — spawn the axes - -Send **one** message with the `general-purpose` subagents the change actually has axes for. Each gets: the -base ref, the touched-file list plus untracked files, the check output as established fact, and the paths it -must read. +Send **one** message with the `general-purpose` subagents the change actually has axes for — the `jev` +axis among them. Each gets: the scope, the base ref, the touched-file list plus untracked files, the spec +path when one resolved and the ticket when the run has one, the check output as established fact, and +[`axes.md`](axes.md) — its own brief, the finding shape and the per-finding cap all live there. **The spec axis is gated on a spec existing.** Plenty of changes have none — work done outside the task workflow, and any task finished inside its own grilling session (see "Not every task needs a spec" in [`../../tasks/README.md`](../../tasks/README.md)). Confirm `spec.md` is there before dispatching. With no -spec, run **two** axes and say so in the report — the spec axis with nothing to read invents a standard to -judge against, which is worse than the gap it papers over. Standards and correctness carry the review on -their own. - -Every axis returns findings in this shape, and nothing else — no preamble, no summary: - -``` -severity: blocker | major | nit -needs-human: yes | no -location: : -claim: -fix: -``` +spec, run **two** blind axes plus `jev` and say so in the report. -Each report is capped at **400 words**, which forces ranking instead of dumping. +**Done when**: every dispatched axis has returned, or one has failed and you have noted which. A `jev` +axis reporting `skipped`, `failed` or no suspects has returned: the review proceeds on the blind axes and +the terminal close carries its reason. -**Spec axis brief.** Read the spec (path given) and the diff. Its **Functional Requirements** are the -contract: take each one in turn and report whether the diff actually satisfies it, quoting the requirement -behind each finding. Then report what the requirements don't cover: behaviour in the diff the spec never -asked for, and requirements that look implemented but are implemented wrongly. Anything the spec's **Out of -scope** section names is not a finding. +## 3. Round `follow-up` — arbitration -**Standards axis brief.** Read `.agents/rules/*.md` matching the touched file types, every `CONTEXT.md` for -directories the diff touches, `.agents/delegation.md`, and **one** smell baseline, picked by what the diff -touches: - -| The diff touches | Baseline | -| --- | --- | -| code | [`smells.md`](smells.md) | -| the instruction surface — `.agents/**`, `AGENTS.md`, any `CONTEXT.md`, `.cursor/**`, `.github/*instructions*` | [`prose-smells.md`](prose-smells.md) | -| both | both, each applied only to the files it governs | - -Task specs under `.agents/tasks/**` are not the instruction surface — the Spec axis reads those as the -source of truth rather than reviewing them as instructions. - -Report: places the diff breaks a documented rule — **cite the rule file and the rule** — and smells from the -baseline, each named and quoted. A documented rule can be a hard breach; a smell is always a judgement call. -Skip anything the checks in step 1 already cover. - -**Correctness axis brief.** Read the touched files **in full**, plus the files the change depends on — a -hunk-only read produces exactly the shallow findings that make developers stop trusting review. Report: -logic errors; mishandled loading / empty / error / pagination paths; places where the types claim something -the runtime does not; and tests that assert the framework or the mock rather than real behaviour (per the -"What to test (and what not)" section of `.agents/rules/tests-unit.md`). - -**Done when**: every dispatched axis has returned, or one has failed and you have noted which. - -## 3. Round 2+ — arbitration - -An arbitration round is **not** a fresh three-axis review. Re-reviewing everything keeps surfacing unrelated -findings, so the round never converges. +Re-reviewing everything keeps surfacing unrelated findings, so the round never converges. This round rules +on what is already open and nothing else. Spawn **one** fresh `general-purpose` agent — fresh, so it rules on the evidence rather than defending a -claim it made itself. Give it the prior review comments (which carry the full exchange history) and the -current diff. It does exactly three things: +claim it made itself. Give it your own tag's open findings with their full reply history, and the current +code. It does exactly three things: 1. Verify each claimed fix actually addresses its finding, rather than cosmetically silencing it. -2. Rule on each `reject`: **agree** — the finding closes as `rejected-accepted` — or **disagree**, with a - counter-argument. +2. Rule on each `reject`: **agree** — the finding closes, in the words its output file's ruling table + gives — or **disagree**, with a counter-argument. 3. Flag regressions introduced **by the fixes only**. New findings elsewhere are out of scope for this round. -The reviewer posts these rulings and resolves the settled threads itself (step 5). +Under `--scope uncommitted` the fixes are not separable from the original work in the diff, so verify each +one against the code as it now stands rather than looking for what changed since round 1. -**Done when**: every open finding is either closed or carries a ruling. +**Done when**: every open finding of yours is either closed or carries a ruling — and a deferred nit's +ruling is that it stays open. ## 4. Normalize @@ -164,53 +144,54 @@ to `blocker` because that axis is all it can see. - **Severity.** `blocker` — a requirement missing or wrong, a correctness bug, or a rule breach with a real consequence. `major` — real cost, not shipping-critical. `nit` — taste. Set `needs-human` on any finding that turns on design intent the code cannot settle; it is orthogonal to severity and it is the loop's - escape hatch. + escape hatch. Only `pr` mode records it — `md` mode is read by agents alone, so the escape hatch there is + `resolve-review`'s Gate 1, which puts the question to the developer in the terminal. - **One defect, one label.** The same defect seen through two axes is one finding: pick the sharper label - and drop the other. Never report it twice. + and drop the other. Never report it twice. Keep a `sources` list per surviving finding so the merge keeps a record of who saw it; a `jev` finding's label is the + grid its brief named, `standards` or `spec`. +- **Another reviewer's finding stays theirs.** A defect already raised under a different tag gets a + concurrence on their thread, not a second id under yours. - **Anchor check.** For every surviving finding, open its `file:line`. Confirm the quoted code is actually - there, the claim still holds, and the line is in the PR diff. Drop what fails. Hallucinated line numbers - and stale claims are the two things that end a reviewer's credibility. + there and the claim still holds. Drop what fails. Hallucinated line numbers and stale claims are the two + things that end a reviewer's credibility. -**Done when**: every finding has a normalized severity, a verified anchor, and exactly one axis label. +**Done when**: every finding has a normalized severity, a verified anchor, exactly one axis label, and its +`sources`. -## 5. Report +## 5. Publish -Zero findings still produces a report with `Outcome: clear` and zeroed counts — a missing report is -indistinguishable from a review that never ran. +Both outputs carry the same findings, worded the same way, with the same ids and severities. They differ +only in where a finding lives and how its thread is closed. -**PR mode.** The PR is the record. `event` is always `COMMENT`: never `REQUEST_CHANGES`, which blocks the -author's own PR, and never `APPROVE`, which claims accountability an agent does not have. Name the reviewer -from the running model — any provider can run this skill — in a footer `— Reviewed by `, falling back to `— Reviewed by agent`. Commands and the all-or-nothing 422 hazard: -[`gh-commands.md`](gh-commands.md). +| Output | Mechanics | +| --- | --- | +| `pr` | [`output-pr.md`](output-pr.md) | +| `md` | [`output-md.md`](output-md.md) | -*A fresh review (round 1)* posts one batched review event — never N separate comments. Each inline comment -opens with its finding id and severity — `**F1 · blocker** — ` — then the suggested fix and the -footer. The review body is the header table, listing each finding by id, plus a `## Not anchorable` section -for findings with no diff line to sit on: approach-level questions, or lines outside the diff. With zero -findings the body is `Review clear` and the zeroed counts. The id is the handle a human greps for and the -coder cites back, so it is stable: an arbitration round recovers the highest `F` from the existing -comments and numbers any regression from there. +Zero findings still publishes, with the outcome `cleared` and zeroed counts — a missing report is +indistinguishable from a review that never ran. -*An arbitration round (round 2+)* acts on each ruling on its own thread, because the reviewer owns the -threads it raised and is the one that closes them: +**The outcome is defined here; both output files render this definition rather than restating it.** It is +`blocked` while any `blocker` or `major` is open, and `cleared` once only nits remain. -| Ruling | Reply | Thread | -| --- | --- | --- | -| fix verified | `F — verified` | resolve | -| reject agreed | `F — accepted, ` | resolve | -| reject disputed | the counter-argument | leave open | -| regression from a fix | a new inline comment, next free id | leave open | +**Then record the origins**, whenever the `jev` axis returned a sidecar path. Spawn one fresh +`general-purpose` subagent with the `## Jev axis` brief in [`axes.md`](axes.md), the sidecar path, the +final table in the shape its Origins section gives — `| id | axis | location | sources |`, where a +requirement finding with no line writes its `FR` as the location — and the `jev` axis's drop table +verbatim. It returns the tool's JSON: an origin per finding, a fate per suspect. -When no `blocker` or `major` thread is left open — only `deferred` nits remain — post a final review whose -body is `Review clear` and the counts. That terminal comment is the PR's `Outcome: clear`. +Then close **in the terminal** with where the review was published — the review's `html_url` from the POST +response, or the review file's path — counts per severity, counts per axis (an axis that came back empty is +worth a second look), one `jev` line, and the outcome. The `jev` line: -**Chat mode.** Same content, no file, no PR — report the findings in the conversation. +``` +jev: suspects · confirmed · dropped · origins jev / axis / both +``` -Close by reporting counts per severity, counts per axis (an axis that came back empty is worth a second -look), and the `Outcome`. +Suspects, confirmed and dropped come from the axis's return, the origin split from the `--origins` JSON. +When the screen was `skipped` or `failed`, or sent no suspects, the line carries the reason instead. -**Done when**: the review is posted (or reported in chat) and the counts have been reported. +**Done when**: the review is published, the origins recorded, and the counts reported. ## Out of bounds @@ -219,10 +200,12 @@ reader to skim past real problems. - **Any visual or styling judgement.** Presentation belongs to the `[human]` style leaf. - **Anything the spec's Out of scope section names.** -- **Missing Playwright screenshot baselines** — human-generated, per `.agents/delegation.md`. +- **Missing Playwright screenshot baselines** — human-generated, per [`../../delegation.md`](../../delegation.md). - **Style preferences with no basis** in `.agents/rules/`, a `CONTEXT.md`, or the surrounding code. No citable rule or precedent, no finding. -- **"Add a comment explaining what this does"** — `code-quality.md` forbids *what* comments. +- **Any "add a comment here" suggestion.** `code-quality.md` bans comments outright; its one exception — a + *why* comment on code whose obvious reading is wrong — is the author's call, never a reviewer's request. + A comment the diff *adds* that is not such a why comment is the opposite: a citable rule breach. - **A primitive or shortcut with a why-comment on it.** The comment is an override signal: read it and back off rather than arguing with it. - **A `TODO (design):` marker that has been consumed.** An *unconsumed* marker, though, is a finding here: diff --git a/.agents/skills/review-changes/axes.md b/.agents/skills/review-changes/axes.md new file mode 100644 index 00000000000..bf4031f4104 --- /dev/null +++ b/.agents/skills/review-changes/axes.md @@ -0,0 +1,126 @@ +# The review axes + +One brief per axis, dispatched by [`SKILL.md`](SKILL.md) step 2. You are one axis: read **your** section +and ignore the others. The orchestrator has already given you the scope, the base ref, the touched files +(untracked included), and the check results — those are established fact, so do not re-run them. + +## What every axis returns + +Findings in this shape, and nothing else. No preamble, no summary. + +``` +severity: blocker | major | nit +needs-human: yes | no +location: : +claim: +fix: +``` + +Report every in-bounds finding you have, ranked most severe first; the count is uncapped. Each finding is +capped at **120 words** of prose — quoted code does not count. Severity here is your own read; the +orchestrator recalibrates across axes. + +## Spec axis + +Read the spec (path given) and the diff. Its **Functional Requirements** are the contract: take each one in +turn and report whether the diff actually satisfies it, quoting the requirement behind each finding. Then +report what the requirements don't cover: behaviour in the diff the spec never asked for, and requirements +that look implemented but are implemented wrongly. Anything the spec's **Out of scope** section names is +not a finding. + +## Standards axis + +Read `.agents/rules/*.md` matching the touched file types, every `CONTEXT.md` for directories the diff +touches, [`../../delegation.md`](../../delegation.md), and **one** smell baseline, picked by what the diff +touches: + +| The diff touches | Baseline | +| --- | --- | +| code | [`smells.md`](smells.md) | +| the instruction surface — `.agents/**`, `AGENTS.md`, any `CONTEXT.md`, `.cursor/**`, `.github/*instructions*` | [`prose-smells.md`](prose-smells.md) | +| both | both, each applied only to the files it governs | + +Task specs under `.agents/tasks/**` are not the instruction surface — the Spec axis reads those as the +source of truth rather than reviewing them as instructions. + +Report: places the diff breaks a documented rule — **cite the rule file and the rule** — and smells from the +baseline, each named and quoted. A documented rule can be a hard breach; a smell is always a judgement call. +Skip anything the checks already cover. + +## Correctness axis + +Read the touched files **in full**, plus the files the change depends on — a hunk-only read produces exactly +the shallow findings that make developers stop trusting review. Report: +- logic errors; +- mishandled loading / empty / error / pagination paths; +- places where the types claim something the runtime does not; +- tests that assert the framework or the mock rather than real behaviour (per the "What a good test is" +section of `.agents/rules/tests-unit.md`). + +## Jev axis + +A screen, then verdicts. `pnpm review:screen` scores every rubric rule against every touched file with +Jev, a typed-judgment model, and prints the cells over threshold as **suspects** — a probability each, +never a claim. It has read no rule; you decide. Given a findings table instead of a change, you are the +second dispatch: skip to **Origins**. + +Run it on the ground the orchestrator pinned, never re-derived: + +```bash +pnpm review:screen --scope --base [--spec ] [--ticket ] +``` + +`pr` output is `--scope branch`. `--spec` goes exactly when a spec resolved, `--ticket` when the run has +one. The JSON on stdout carries `status` (`ok` · `skipped` · `failed`) with a `reason`, the `sidecar` path, +and two suspect lists: `standards` (a rubric rule, a file, a line, a score) and `spec` (an `FR`, its +best-scoring file, no line, a score). A `skipped` or `failed` status, or no suspect in either list, ends +the axis: return the status line and the sidecar path and stop. + +**Suspects are the whole territory.** A defect you notice beside one belongs to the other axes. For each +suspect: open the location; read the rule the rubric cites (`cites` on its entry in +`tools/review-screen/rubric.ts`) or the requirement in the spec; decide. A breach or an unmet requirement +becomes a finding in the shape above plus one line, `axis: standards | spec`, naming the grid; a spec +finding with no line writes `location: FR`. Anything else is one row of the drop table, in the shape +`--origins` reads back unchanged: + +``` +| suspect | fate | reason | +| --- | --- | --- | +| : | dropped | | +| FR | dropped | | +``` + +The reasons that recur: the flagged line is unchanged context; the rule's `not_for` covers it; the +requirement lives outside the diff (docs, a later ticket, code the change removes); the Out of bounds list +names it. + +Return, in this order: `status: — ` and `sidecar: `, the findings, the drop table. +**Every suspect appears exactly once**, as a finding or a drop — the later `--origins` step matches them +mechanically and records an unreported one as dropped for no reason. The per-finding cap applies. + +### Origins + +After publishing, a fresh dispatch gets the sidecar path, the final findings table and the drop table. +Write both, as given, to one file beside the sidecar — the sidecar's name with `.findings.md` in place of +`.json`, so it lands in the same git-ignored folder — and run: + +```bash +pnpm review:screen --origins --findings +``` + +The tables: + +``` +| id | axis | location | sources | +| suspect | fate | reason | +``` + +`location` is `:`, `FR` for a requirement finding with no line, or `—`; `sources` lists the +axes that raised the finding; `suspect` is ` :` or `FR` and `fate` is `dropped`. +Return the tool's JSON verbatim — it matches by window and requirement id with no code in front of it, and +that is the point: a judgement added here would be one made blind. + +## Out of bounds + +The **Out of bounds** list in [`SKILL.md`](SKILL.md) binds every axis. Read it before you report: a finding +it names is dropped whichever axis raised it. diff --git a/.agents/skills/review-changes/gh-commands.md b/.agents/skills/review-changes/gh-commands.md index 21c72821076..315aca7bf81 100644 --- a/.agents/skills/review-changes/gh-commands.md +++ b/.agents/skills/review-changes/gh-commands.md @@ -1,9 +1,9 @@ # gh / GraphQL command reference -The whole PR surface both review skills need: `review-changes` posts findings and, in arbitration rounds, -replies and resolves the threads it raised; `resolve-review` gathers, replies, and resolves bot and human -threads. Substitute `{owner}`, `{repo}`, `{N}` (PR number), `{commentId}`. Derive `{owner}/{repo}` once -and reuse. +The whole PR surface both review skills need in `pr` mode: `review-changes` posts findings and, in +follow-up rounds, replies and resolves the threads it raised; `resolve-review` gathers, replies, and +resolves threads. Substitute `{owner}`, `{repo}`, `{N}` (PR number), `{commentId}`. Derive +`{owner}/{repo}` once and reuse. Confirm `gh auth status` succeeds before anything else — follow the `check-github-cli` skill if it does not. Never authenticate on the developer's behalf. @@ -14,11 +14,12 @@ not. Never authenticate on the developer's behalf. # owner / repo for the current checkout gh repo view --json nameWithOwner,owner,name -# PR for the current branch — its absence is what selects chat mode. Use `gh pr list`, never `gh pr view`, -# which exits 1 both when no PR exists and when it can't reach GitHub: exit ≠ 0 is a tooling failure that -# aborts, exit 0 with `[]` is genuinely no PR. +# PR for the current branch. Use `gh pr list`, never `gh pr view`, which exits 1 both when no PR exists and +# when it can't reach GitHub: exit ≠ 0 is a tooling failure that aborts the run, exit 0 with `[]` is +# genuinely no PR — and a `pr`-mode run with no open PR stops there. `--state open` matters: a merged or +# closed PR is not something to review onto. Never wrap the call in `||`. gh pr list --head "$(git branch --show-current)" --state open \ - --json number,title,url,headRefName,baseRefName,state,isDraft + --json number,title,url,headRefName,headRefOid,baseRefName,state,isDraft # head sha, needed as commit_id when posting a review git rev-parse HEAD @@ -26,7 +27,8 @@ git rev-parse HEAD ## Posting a review (review-changes) -One batched review event per round. Build the payload as a file, then: +One batched review event per round, whose body is the summary; the finding format, the summary shape and +the footer are [`output-pr.md`](output-pr.md). Build the payload as a file, then: ```bash gh api -X POST repos/{owner}/{repo}/pulls/{N}/reviews --input review.json @@ -36,20 +38,28 @@ gh api -X POST repos/{owner}/{repo}/pulls/{N}/reviews --input review.json { "commit_id": "", "event": "COMMENT", - "body": "
", + "body": "", "comments": [ { "path": "src/slices/token/pages/Holders.tsx", "line": 41, "side": "RIGHT", - "body": "**F1 · blocker** — \n\n\n\n— Reviewed by " + "body": "**🛑 F1 · blocker**\n\n\n\n\n\n— Reviewed by " } ] } ``` -`event` is always `COMMENT`. For a multi-line anchor add `start_line` (and `start_side`) alongside -`line`. +For a multi-line anchor add `start_line` (and `start_side`) alongside `line`. + +Findings with no anchor, and a `follow-up` round's rulings on them, are issue comments — posted after the +review event, one comment per round: + +```bash +gh api -X POST repos/{owner}/{repo}/issues/{N}/comments -F body=@not-anchorable.md +``` + +`-F body=@` reads the body from a file; `-f body="…"` mangles a multi-line string. ### Validate every anchor first — the POST is all-or-nothing @@ -64,9 +74,9 @@ gh api repos/{owner}/{repo}/pulls/{N}/files --paginate \ For each file, walk its `patch`: every `@@ -a,b +c,d @@` header starts a hunk whose RIGHT-side line numbers run from `c`; added (`+`) and context (` `) lines each advance that counter and are anchorable, removed (`-`) lines do not advance it and are not. A finding whose line is outside that set moves into the -review body under `## Not anchorable`. +non-anchorable issue comment. -If a POST still 422s, retry once with the offending comments demoted into the body. Never drop them. +If a POST still 422s, retry once with the offending comments moved into that comment. Never drop them. ## Gather (resolve-review) @@ -77,7 +87,7 @@ gh api repos/{owner}/{repo}/pulls/{N}/comments --paginate # PR-level reviews (summary body + state per reviewer) gh api repos/{owner}/{repo}/pulls/{N}/reviews --paginate -# issue-level comments (the conversation tab, incl. most bot posts) +# issue-level comments (the conversation tab) gh api repos/{owner}/{repo}/issues/{N}/comments --paginate ``` @@ -89,16 +99,24 @@ Fields worth reading per inline comment: | `in_reply_to_id` | `null` = top-level; otherwise a reply within a thread | | `path`, `line` / `original_line` | where it sits — open this code | | `diff_hunk` | the snippet the reviewer saw | -| `user.login` | author — this is how you tell a human from a bot from this workflow's own review | +| `user.login` | author — who to address in a reply | | `body` | the comment text | | `html_url` | link back to the comment | **Telling the sources apart matters**, because they are adjudicated differently: a comment whose body ends in a `— Reviewed by …` footer is this workflow's own review, whichever provider produced it, and may be -rejected; a bot's gets no deference at all; a human's may never be rejected. Test the footer **first** — -`user.login` is the repo owner's account for every agent, so nothing else separates this workflow's review -from a human's — then `user.type == "Bot"` for the bots. Never match bot logins by name; see the source -table in `../resolve-review/SKILL.md` for why. +rejected — and where several agents reviewed the same PR, the tag in that footer says which one, matching +the prefix on its finding ids. + +The footer is the only thing that separates this workflow's own review from a human's: `user.login` is the +repo owner's account for every agent, so nothing else tells them apart. It does not settle bots, which +`user.type` catches — the full source split, and which verdicts each source allows, is the table in +[`../resolve-review/SKILL.md`](../resolve-review/SKILL.md). + +A footer-bearing **issue** comment titled `### 📎 Findings without a diff anchor` carries findings, not +conversation: one per `** F · **` title, the tag prefix present exactly when the +review ran under `--as`. It has no thread, so a reply is a new issue comment naming those ids, and there is +nothing to resolve. ### Parse a comment / PR link @@ -145,7 +163,5 @@ Notes: - Reply *before* resolving — a resolved thread still accepts replies, but replying first keeps the explanation visible. - Skip threads already `isResolved`. -- Leave `disputed`, `needs-human` and `answered` threads **unresolved** — they are exactly the ones that - must stay visible. -- Bot status posts (CodeRabbit "review skipped", Copilot's PR overview) arrive as issue comments, have no - thread to resolve, and are not actionable. +- Leave `disputed`, `deferred`, `needs-human` and `answered` threads **unresolved** — they are exactly the + ones that must stay visible. diff --git a/.agents/skills/review-changes/output-md.md b/.agents/skills/review-changes/output-md.md new file mode 100644 index 00000000000..1b7a042ff6d --- /dev/null +++ b/.agents/skills/review-changes/output-md.md @@ -0,0 +1,82 @@ +# Publishing to a review file + +`md` mode. One Markdown file holds the whole exchange — findings, replies, rulings — in place of the PR's +threads. Only agents read it (`resolve-review` replies in the same file), so a finding carries no +`— Reviewed by` footer: there is no human to tell an agent's comment from a colleague's. + +## Where the file goes + +The task folder comes off the branch name, `issue-` → `.agents/tasks/-/`. If no task +folder resolves, stop and ask — this mode reviews tasks and tickets, and inventing a path elsewhere buries +the review where nothing will look for it. + +| `--scope` | The file | +| --- | --- | +| `branch` | `/review.md` | +| `uncommitted` | `/tickets/-/review.md` | + +`` comes from `--ticket`, or from the first unchecked box in `progress.md` — uncommitted work is by +definition the ticket in flight, since `implement-ticket` checks the box only once the commit exists. An +absent or ambiguous `progress.md` stops the run. + +Under `--as ` the file is `review-.md` beside it, so concurrent reviewers never write the same +file. Before writing a `first` round, glob `review*.md` in the resolved folder and apply +[`SKILL.md`](SKILL.md)'s guard to what comes back — a sibling under a tag that is not yours means another +agent is already reviewing this change. + +Both are working files: `finalize-task` prunes them with the rest of the task's scaffolding, and only +`spec.md` survives the land. + +## The file + +A `## Rounds` log, then one `###` section per finding — the thread. Replies append inside the section as +blockquote lines; the `**Status:**` line is the only record of what is still open, so it is edited in place +rather than restated in the index. + +```md +# Review — tickets/03-holders-table · uncommitted + +## Rounds + +- **1** — 2026-09-07, . blocker 1 · major 2 · nit 1. Outcome: blocked. + +## Findings + +| id | severity | axis | location | +| --- | --- | --- | --- | +| F1 | blocker | correctness | `src/slices/token/pages/Holders.tsx:41` | + +### F1 · blocker · correctness · `src/slices/token/pages/Holders.tsx:41` + +**Claim.** + +**Fix.** + +**Status:** open + +> **resolve-review, round 1:** fix — +> **review, round 2:** verified +``` + +A finding with no line to sit on keeps its section and writes `location: —`. The PR's split into a separate +comment has nothing to do here, and neither do its severity emoji or its `needs-human` flag: only agents +read this file, so that flag would have no reader — in `md` mode the escape hatch is `resolve-review`'s +Gate 1, which puts the question to the developer in the terminal. + +Zero findings still writes the file: the `## Rounds` line reads `Outcome: cleared` and `## Findings` is +empty. + +## A `follow-up` round + +Append a line to `## Rounds`, then work each of your open findings: + +| Ruling | Reply line | Status | +| --- | --- | --- | +| fix verified | `verified` | `resolved — verified` | +| reject agreed | `accepted, ` | `resolved — rejected` | +| reject disputed | the counter-argument | `disputed` | +| deferred nit | `deferred` | `deferred` | +| regression from a fix | a new `###` section, next free id | `open` | + +The round's `## Rounds` line carries the outcome [`SKILL.md`](SKILL.md) defines — `Outcome: blocked` or +`Outcome: cleared`. diff --git a/.agents/skills/review-changes/output-pr.md b/.agents/skills/review-changes/output-pr.md new file mode 100644 index 00000000000..5301a7b1b3d --- /dev/null +++ b/.agents/skills/review-changes/output-pr.md @@ -0,0 +1,154 @@ +# Publishing to a PR + +`pr` mode. The PR is the record: findings are inline review comments, and a thread stays open until this +reviewer closes it. Commands and the all-or-nothing 422 hazard: [`gh-commands.md`](gh-commands.md). + +`event` is always `COMMENT`: never `REQUEST_CHANGES`, which blocks the author's own PR, and never `APPROVE`, +which claims accountability an agent does not have. + +## The severity emoji + +One set, used on every finding title and on the summary's status line. Nowhere else — a reader who sees +`🛑` skips to it, and that only works while it is rare. + +| Severity | Emoji | +| --- | --- | +| `blocker` | 🛑 | +| `major` | ⚠️ | +| `nit` | 💬 | +| `needs-human` (orthogonal, appended) | 🙋 | + +## A finding's comment + +The title line carries the id and the severity and nothing else; the claim starts below it. One blank line +between every block. + +```md +**🛑 F1 · blocker** + + + + + +— Reviewed by +``` + +`needs-human` extends the title: `**🛑 F1 · blocker · 🙋 needs human**`. + +Title with bold, never a heading. + +The severity **word** stays next to the emoji. `resolve-review` reads that line to classify a finding, and +nobody greps for a glyph. + +## Ids and the footer + +The footer names the reviewer from the running model, falling back to `— Reviewed by agent`. Any provider +can run this skill, and that footer is the only thing telling `resolve-review` an agent raised this finding +rather than a colleague — why, in [`gh-commands.md`](gh-commands.md). + +Under `--as ` the id carries the tag (`cursor-F1`) and the footer names it too — `— Reviewed by cursor +()`. + +The id is the handle a human greps for and the coder cites back, so it is stable: a `follow-up` round +recovers the highest `F` **of its own tag** from the existing comments and numbers any regression from +there. + +## Before posting a `first` round + +Read the existing review threads ([`gh-commands.md`](gh-commands.md)) and sort them by footer: + +- **A footer-bearing thread whose tag is not yours, and you were given no `--as`** — another agent is + already reviewing this PR. Stop and ask for a tag. +- **A thread under another tag that raises a defect you also found** — reply `+1 — concurs` on + it and drop your own copy. Agreement between two reviewers is worth recording; a duplicate id is not. +- **Your own earlier threads** — you are running `first` over a change you already reviewed. Say so and + confirm with the operator before posting a second set. + +## A `first` round + +One batched review event, never N separate comments. Its body is the summary and nothing else; the findings +are the inline comments. Then, if any finding has no line to sit on, one issue comment for those. + +Post the review event **first**, the non-anchorable comment after it. A 422 discards the review event +whole, so in that order the retry can fold anything that failed to anchor into a comment you have not sent +yet. + +## Findings with no diff anchor + +Approach-level questions, and lines outside the diff. They do not go in the summary — mixed in there they +are read as commentary on the summary, and the point of the split is that a reader can find them. + +One issue comment holds all of them, titled the same way as an inline finding, with a single footer at the +end: + +```md +### 📎 Findings without a diff anchor + +**⚠️ F3 · major** + + + + + +--- + +**💬 F7 · nit** + + + + + +— Reviewed by +``` + +An issue comment is not a review thread: it cannot be resolved, and a reply to it is another issue comment. +So these findings carry no per-finding open state, and their state lives in the summary's counters instead. +They count as open until a `follow-up` round rules on them. + +## The summary + +Same shape every round, first and follow-up alike, so two rounds can be read against each other. + +```md +🛑 **Blocked** — 1 blocker, 1 major open + +blocker 1 · major 1 · nit 3 · not anchorable 1 + +Axes: spec · standards · correctness + +— Reviewed by +``` + +- **Status.** `🛑 Blocked` and `✅ Cleared` render the two outcomes [`SKILL.md`](SKILL.md) defines, zero + findings included. Append `· 🙋 needs human` when any `needs-human` finding is open whatever its + severity. +- **The counters are open counts, never raised counts.** On a `first` round the two are the same. Keeping + the meaning fixed is what makes the rounds comparable. +- **`Axes:`** is the coverage record, and it names a skipped axis rather than dropping it. + +No table of findings. A reader on the PR already has them inline, and a second copy in the summary is one +more thing to keep in sync with the threads. + +## A `follow-up` round + +Act on each ruling on its own thread, because the reviewer owns the threads it raised and is the one that +closes them. Threads under another tag are not yours to touch. + +| Ruling | Reply | Thread | +| --- | --- | --- | +| fix verified | `F — verified` | resolve | +| reject agreed | `F — accepted, ` | resolve | +| reject disputed | the counter-argument | leave open | +| deferred nit | `F — deferred` | leave open | +| regression from a fix | a new inline comment, next free id | leave open | + +Non-anchorable findings have no thread, so one issue comment carries their rulings — the reply column +above, one line each, for those ids only, closing with the same `— Reviewed by` footer: that footer is how +`resolve-review` tells a ruling from its own reply comment on the same ids. Write the id as +`F`, the tag prefix present exactly when the finding was raised under one, so the line names the +id `resolve-review` is matching against — bare `F` matches nothing, and with two reviewers on the PR it +would be ambiguous even if it did. Leave the original comment as posted. An id with no ruling stays in the +open counters. + +Then post the summary, in the shape above. Nothing marks a round as terminal beyond its status reading +`✅ Cleared`, which is this PR's `cleared` outcome. diff --git a/.agents/skills/slack-watch/SKILL.md b/.agents/skills/slack-watch/SKILL.md new file mode 100644 index 00000000000..f8f0bb3f490 --- /dev/null +++ b/.agents/skills/slack-watch/SKILL.md @@ -0,0 +1,102 @@ +--- +name: slack-watch +description: >- + Watch Slack threads (DMs and channels) for new replies over a Socket Mode + WebSocket, printing one line per genuinely-new reply. Use to be notified + inside a live local Claude Code or Cursor session (not a cloud agent). +--- + + + +# Slack watch + +A push subscription to replies in specific Slack threads — **DM threads and channel threads both**. The script holds one Socket Mode WebSocket, acks every event, and prints one line per new reply — nothing else. It knows nothing about tasks or questions; it filters a thread list you give it and reports. The caller (a session, a skill) decides what a reply means and reads it via the Slack MCP (`slack_read_thread`). + +macOS. Three tokens live in the Keychain; the script reads them and never prints them. It does **not** print message content or event payloads — the underlying `message.im` feed is a DM firehose. DMs are watched with the developer's **user** token; channels are watched with the **bot** token, which only sees channels the Honk bot was invited into (so no firehose there). A single Socket Mode connection carries both the bot and user events. + +## 1. Ready + +From **this skill's directory** (the folder that contains this `SKILL.md`): + +1. `scripts/slack-watch` is executable. +2. Keychain has all three services, metadata only (no `-w`): + - `security find-generic-password -s slack-orchestrator-app-token` + - `security find-generic-password -s slack-orchestrator-user-token` + - `security find-generic-password -s slack-orchestrator-bot-token` +3. Node 22+ is on `PATH` (`node --version`) — the script uses the built-in `WebSocket` and `fetch`, no npm deps. + +If 2 fails: give the user **Setup** below and stop. Do not store the tokens yourself. + +**Local session only** — the Keychain and the socket both live on this Mac. If you are a cloud agent, stop and tell the developer to run this in local Claude Code or Cursor. + +**Done when:** every check passes, or the run has stopped for setup (or cloud). + +## 2. Run + +Each watched thread is one `CHANNEL:THREAD_TS` argv item — the channel id (a DM is `D…`, a channel `C…`, a private group `G…`) and the parent `ts`, both from the `slack_send_message` / permalink you already hold. The script routes each by its id prefix; channel threads only report if the Honk bot is a member of that channel. + +Launch so each stdout line wakes this session. Use the recipe whose tools you have. + +### Claude Code + +`Monitor` with `persistent: true`. The sandbox cannot read the Keychain or reach `slack.com` — allow both on that Monitor. + +``` +Monitor( + command: "scripts/slack-watch C0123ABCD:1700000000.000200 D0456WXYZ:1700000100.000700", + description: "Slack reply watcher", + persistent: true, +) +``` + +Give the absolute path to `scripts/slack-watch` if Monitor's working directory is not this skill's directory. + +### Cursor (local IDE) + +Background `Shell` (`block_until_ms: 0`) with `notify_on_output` and `required_permissions: ["all"]` (Keychain + `slack.com`). Cursor floors notification debounce at 5s — two `NEW` lines in one burst may arrive as one wake; on wake, read the terminal output and handle every `NEW` line, not only the last. + +``` +Shell( + command: "/scripts/slack-watch C0123ABCD:1700000000.000200 D0456WXYZ:1700000100.000700", + description: "Slack reply watcher", + block_until_ms: 0, + required_permissions: ["all"], + notify_on_output: { pattern: "^NEW |^RECONNECTED", reason: "Slack reply" }, +) +``` + +Use the absolute path. Smoke-check the terminal output file once so a failed start is not silent. + +### Output lines + +- `NEW channel= thread_ts= ts=` — a new reply landed (or was missed during downtime and reconciled on reconnect). Read it with `slack_read_thread` on that `thread_ts`. Our own messages — the developer's **and** the Honk bot's, including agent follow-ups posted as either — and the thread's pre-existing history are **not** reported. +- `RECONNECTED` — the socket re-opened after a drop; a catch-up for every watched thread has just run, so any `NEW` lines around it are the replies missed while it was down. + +Diagnostics (auth or reconnect failures) go to stderr — Claude's Monitor output file, or Cursor's terminal file; Read it if the watcher seems quiet. + +### Changing the watched set + +All state lives in the process (thread list, per-thread last-seen `ts`, `event_id` dedupe). There is no state file. To add or remove a thread, **stop and relaunch** with the new argument list. + +- Claude Code: `TaskStop` on that Monitor, then launch again. +- Cursor: kill the PID in that shell's header, `AwaitShell` so the completion ping is consumed, then launch again. + +Re-spawning re-baselines to "from now" — earlier replies are already in the session transcript, so nothing is double-reported. + +**Done when:** the watcher is running and reporting, or the run has stopped for setup. + +## Setup + +One private Slack app in the Blockscout workspace (**api.slack.com/apps → your app**), carrying three tokens. Enable the bot user **Honk** 🪿 (`honk`) under **App Home** first — it is both the channel-reading identity and the disclosure identity. + +- **App-level token** (`xapp-`), scope `connections:write` — created under **Socket Mode → toggle on → generate token**. Opens the WebSocket. +- **User token** (`xoxp-`), **User Token Scopes** `im:history` + `chat:write`. Under **Event Subscriptions → on behalf of users**, subscribe **`message.im` only** — not `message.channels` / `message.groups` (that is a firehose on a user token). Reads DM replies and posts in the developer's own voice. +- **Bot token** (`xoxb-`), **Bot Token Scopes** `channels:history` (add `groups:history` for private channels) + `chat:write`. Under **Event Subscriptions → bot events**, subscribe **`message.channels`** (and `message.groups` for private channels). Bot events fire only for channels the bot is a member of, so **invite Honk to each question channel** (`/invite @Honk`) — that is what scopes the channel feed instead of firehosing. + +Store all three in the Keychain yourself (the script never writes them): + +```bash +security add-generic-password -U -s slack-orchestrator-app-token -a "$USER" -w # prompts for xapp-… +security add-generic-password -U -s slack-orchestrator-user-token -a "$USER" -w # prompts for xoxp-… +security add-generic-password -U -s slack-orchestrator-bot-token -a "$USER" -w # prompts for xoxb-… +``` diff --git a/.agents/skills/slack-watch/scripts/slack-watch b/.agents/skills/slack-watch/scripts/slack-watch new file mode 100755 index 00000000000..50a57b7e1c0 --- /dev/null +++ b/.agents/skills/slack-watch/scripts/slack-watch @@ -0,0 +1,226 @@ +#!/usr/bin/env node +// Slack Socket Mode reply watcher. Holds the WebSocket, acks every event, and +// prints one line per genuinely-new reply in a watched thread. It does no +// reasoning: the live session reads the thread itself (via the Slack MCP) when +// a NEW line arrives. It never prints message content and never logs event +// payloads — the user-token message.im feed is a DM firehose and must not leak. +// +// A single Socket Mode connection carries the app's bot events and its user +// events together, so DM and channel threads are watched over one socket. The +// two identities split by conversation type: DMs are the developer's own (user +// token), channels belong to the Honk bot invited into them (bot token). +// +// Three tokens, all from the macOS Keychain, never in argv, a dotfile, or output: +// app-level (xapp-) opens the Socket Mode socket — slack-orchestrator-app-token +// user (xoxp-) reads DM replies, is the DM identity — slack-orchestrator-user-token +// bot (xoxb-) reads channel replies, is the bot ident — slack-orchestrator-bot-token +// +// Node 22+ only: relies on the built-in global WebSocket and fetch — no npm deps. +// cspell:ignore acks xapp xoxp xoxb +'use strict'; + +const { execFileSync } = require('node:child_process'); + +const APP_TOKEN_SERVICE = 'slack-orchestrator-app-token'; +const USER_TOKEN_SERVICE = 'slack-orchestrator-user-token'; +const BOT_TOKEN_SERVICE = 'slack-orchestrator-bot-token'; +const FATAL_AUTH_ERRORS = new Set(['invalid_auth', 'not_authed', 'account_inactive', 'token_revoked']); + +function die(msg) { + process.stderr.write(`slack-watch: ${msg}\n`); + process.exit(1); +} + +function warn(msg) { + // stderr only — Monitor routes it to the output file, not to notifications. + process.stderr.write(`slack-watch: ${msg}\n`); +} + +function emit(line) { + process.stdout.write(line + '\n'); +} + +function keychain(service) { + try { + return execFileSync('security', ['find-generic-password', '-s', service, '-w'], { + encoding: 'utf8', + }).trim(); + } catch { + die(`no token in Keychain under service '${service}' — see the slack-watch skill (Setup)`); + } +} + +const num = (ts) => parseFloat(ts); + +// A message counts as a real reply unless its subtype marks it as noise +// (edits, joins/leaves, bot_message, channel chrome). Two subtypes are genuine +// human replies and must be kept: `thread_broadcast` (a reply the author also +// sent to the channel) and `file_share` (a reply that carries attachments — +// e.g. screenshots). Plain replies have no subtype at all. +const KEPT_SUBTYPES = new Set([ 'thread_broadcast', 'file_share' ]); +const isReply = (m) => !m.subtype || KEPT_SUBTYPES.has(m.subtype); + +// argv: one "CHANNEL:THREAD_TS" per watched thread. lastTs is the per-thread +// watermark; it starts at thread_ts and only ever advances. +const watched = new Map(); +for (const arg of process.argv.slice(2)) { + const i = arg.indexOf(':'); + if (i < 0) die(`bad thread arg '${arg}', expected CHANNEL:THREAD_TS`); + const channel = arg.slice(0, i); + const thread_ts = arg.slice(i + 1); + watched.set(`${channel}:${thread_ts}`, { channel, thread_ts, lastTs: thread_ts }); +} +if (watched.size === 0) die('no threads to watch — pass CHANNEL:THREAD_TS args'); + +const appToken = keychain(APP_TOKEN_SERVICE); +const userToken = keychain(USER_TOKEN_SERVICE); +const botToken = keychain(BOT_TOKEN_SERVICE); + +const seenEventIds = new Set(); +const ownIds = new Set(); // our own user ids (developer + Honk bot) — never a reply to notify on. +let ws = null; +let established = false; // flips on the first `hello`; later `hello`s mean a reconnect. +let reconnectTimer = null; + +// DM channels (D…) are read with the user token; channels (C…) and private +// groups (G…) are read with the bot token, which has history only where the bot +// was invited. +const tokenFor = (channel) => (channel[0] === 'D' ? userToken : botToken); + +async function call(method, token, params = {}, httpMethod = 'GET') { + const qs = new URLSearchParams(params).toString(); + const url = `https://slack.com/api/${method}` + (httpMethod === 'GET' && qs ? `?${qs}` : ''); + const res = await fetch(url, { + method: httpMethod, + headers: { + Authorization: `Bearer ${token}`, + ...(httpMethod === 'POST' ? { 'Content-Type': 'application/x-www-form-urlencoded' } : {}), + }, + body: httpMethod === 'POST' ? qs : undefined, + }); + return res.json(); +} + +// Advance each thread's watermark to Slack's current state. emitMissed=false is +// the first-connect baseline (adopt "from now" silently — earlier replies are +// already in the session transcript); emitMissed=true is the post-downtime +// catch-up that prints what was missed while the socket was down. +async function reconcile(emitMissed) { + for (const w of watched.values()) { + let res; + try { + res = await call('conversations.replies', tokenFor(w.channel), { + channel: w.channel, + ts: w.thread_ts, + oldest: w.lastTs, + inclusive: 'false', + limit: '200', + }); + } catch (e) { + warn(`replies ${w.channel}: ${e.message}`); + continue; + } + if (!res.ok) { + warn(`replies ${w.channel}: ${res.error}`); + continue; + } + for (const m of res.messages || []) { + // Drop edits/joins/bot_message and our own posts; keep genuine replies + // (plain, thread_broadcast, file_share — see isReply). + if (!isReply(m) || ownIds.has(m.user)) continue; + if (num(m.ts) <= num(w.lastTs)) continue; + if (emitMissed) emit(`NEW channel=${w.channel} thread_ts=${w.thread_ts} ts=${m.ts}`); + w.lastTs = m.ts; + } + } +} + +function handleEvent(payload) { + const eventId = payload.event_id; + if (eventId) { + if (seenEventIds.has(eventId)) return; + seenEventIds.add(eventId); + } + const e = payload.event; + // genuine replies only (plain, thread_broadcast, file_share) — drop edits/joins/bot_message. + if (!e || e.type !== 'message' || !isReply(e)) return; + if (ownIds.has(e.user)) return; // our own follow-ups (developer or bot) arrive here too — ignore them + const thread_ts = e.thread_ts; + if (!thread_ts) return; // a top-level message, not a threaded reply + const w = watched.get(`${e.channel}:${thread_ts}`); + if (!w || num(e.ts) <= num(w.lastTs)) return; + emit(`NEW channel=${e.channel} thread_ts=${thread_ts} ts=${e.ts}`); + w.lastTs = e.ts; +} + +function handleFrame(data) { + let msg; + try { + msg = JSON.parse(data); + } catch { + return; + } + if (msg.type === 'hello') { + if (!established) { + established = true; + reconcile(false); + } else { + emit('RECONNECTED'); + reconcile(true); + } + return; + } + if (msg.type === 'disconnect') { + // Slack asks us to reconnect (refresh/too-many-connections). Close and the + // close handler reopens on a fresh URL. + if (ws) ws.close(); + return; + } + if (msg.envelope_id) { + // Ack within Slack's 3s window, before doing anything else with the event. + ws.send(JSON.stringify({ envelope_id: msg.envelope_id })); + } + if (msg.type === 'events_api') handleEvent(msg.payload); +} + +function scheduleReconnect() { + if (reconnectTimer) return; + reconnectTimer = setTimeout(() => { + reconnectTimer = null; + // Retry the whole init, not just connect(): if a startup auth.test threw + // (network/JSON), ownIds is still empty, and opening the socket then would + // report our own posts as NEW. init() re-runs auth.test until ownIds is + // populated; on a plain socket drop it skips straight to connect(). + init().catch(scheduleReconnect); + }, 1000); +} + +async function connect() { + const open = await call('apps.connections.open', appToken, {}, 'POST'); + if (!open.ok) { + if (FATAL_AUTH_ERRORS.has(open.error)) die(`apps.connections.open: ${open.error} (check the app-level token)`); + throw new Error(`apps.connections.open: ${open.error}`); + } + ws = new WebSocket(open.url); + ws.addEventListener('message', (ev) => handleFrame(typeof ev.data === 'string' ? ev.data : '')); + ws.addEventListener('close', scheduleReconnect); + ws.addEventListener('error', () => {}); // a close event always follows; reconnect there +} + +// Establish our own identities, then open the socket. ownIds is populated +// atomically — both auth.test calls succeed before either id is added — so a +// thrown call leaves it empty and a retry re-runs both. Once it is filled (the +// common case on a socket-drop reconnect) init() skips auth.test and reconnects. +async function init() { + if (ownIds.size === 0) { + const user = await call('auth.test', userToken); + if (!user.ok) die(`auth.test: ${user.error} (check the user token)`); + const bot = await call('auth.test', botToken); + if (!bot.ok) die(`auth.test: ${bot.error} (check the bot token)`); + ownIds.add(user.user_id); + ownIds.add(bot.user_id); + } + await connect(); +} + +init().catch(scheduleReconnect); diff --git a/.agents/skills/to-spec/questions-template.md b/.agents/skills/to-spec/questions-template.md index d8c4b557152..7406a21c82a 100644 --- a/.agents/skills/to-spec/questions-template.md +++ b/.agents/skills/to-spec/questions-template.md @@ -3,11 +3,16 @@ +edit — the decision as a phrase plus its date, not the deliberation. + +`Resolved when:` names the discrete facts a sufficient reply must contain — write it when authoring the +question. It gives "is this answer enough?" something concrete to check against and gives a follow-up a +target. Phrase it as a checklist of facts." --> ### Q01 — - Owner: () - Status: `pending` \| `resolved` \| `waived` +- Resolved when: - Slack: - Answer: diff --git a/.agents/skills/to-tickets/SKILL.md b/.agents/skills/to-tickets/SKILL.md index ce2e91dd651..6dae2c2af63 100644 --- a/.agents/skills/to-tickets/SKILL.md +++ b/.agents/skills/to-tickets/SKILL.md @@ -31,7 +31,7 @@ Look for opportunities to prefactor the code to make the implementation easier. ## Step 3 — Draft slices Break the work into tracer bullet tickets. -- Each slice cuts a narrow but COMPLETE path through every layer (schema, API, UI, tests) — a vertical slice. +- Each slice cuts a narrow but COMPLETE path through every layer (schema, API, UI, tests) — a vertical slice. Visual tests (`*.pw.tsx`) are not a required layer of that path; read `.agents/rules/tests-visual.md` Purpose before adding a suite. - A completed slice is demoable or verifiable on its own - Each slice is sized to fit in a single fresh context window - Any prefactoring should be done first diff --git a/.agents/skills/to-tickets/ticket-template.md b/.agents/skills/to-tickets/ticket-template.md index 7fec9875b30..29cb37f58d3 100644 --- a/.agents/skills/to-tickets/ticket-template.md +++ b/.agents/skills/to-tickets/ticket-template.md @@ -55,7 +55,8 @@ the developer and records the answer here. --> then styling) while the ticket cuts across them. Tag every leaf `[agent]` or `[human]` — explicitly, never implied; `implement-ticket` reads the tags as its -state machine. A UI component is two linked leaves (scaffold → style). A leaf names the skill it runs; its +state machine. A UI component is two linked leaves (scaffold → style). Visual tests are not a third leaf by +default; extend or pack per `.agents/rules/tests-visual.md`. A leaf names the skill it runs; its answers live in Skill inputs above, not inline here. A leaf's checkbox is PROGRESS STATE — how far the ticket has got, since it has no commit yet. It is never diff --git a/.agents/slack-message.md b/.agents/slack-message.md new file mode 100644 index 00000000000..02faa0d23de --- /dev/null +++ b/.agents/slack-message.md @@ -0,0 +1,29 @@ +# Compose a Slack message + +How to write a message the Slack connector (`slack_send_message`) posts. Who to send it to is up to the skill that sends it. Slack IDs come from `./TEAM.md`. + +## Language + +Russian — the team's internal language. A reply in an existing thread matches the thread's language. + +## Addressing + +- **Person** — the *Slack member ID* (`U…`). Mention as `<@U…>`; to DM, pass the ID as the channel to the send tool. +- **Team / group** — the *Slack group ID* (`S…`). Mention as ``. +- **Channel** — the *Channel ID* (`C…`). Pass it as the channel target when sending; refer to it in prose by its `#name`. +- **Permalink** — `https://blockscout.slack.com/archives//p` (the message timestamp with the dot removed). + +## Links + +End a bare URL with a space, an empty line, or the end of the message. Slack's link parser runs through a single line break: with `https://example.com` followed by a newline and `Next line`, the next line's first word becomes part of the link, and Cyrillic gets punycoded into the hostname (`…xn--com-…`). + +``` +Демо: +https://review-foo.k8s-dev.blockscout.com + +Посмотри, пожалуйста. +``` + +## Attribution + +The connector appends a *Sent using* footer naming the agent to every message, so a message sent through it already discloses that an agent wrote it. diff --git a/.agents/tasks/3650-shared-multi-bridge-indexer/spec.md b/.agents/tasks/3650-shared-multi-bridge-indexer/spec.md new file mode 100644 index 00000000000..21698c13289 --- /dev/null +++ b/.agents/tasks/3650-shared-multi-bridge-indexer/spec.md @@ -0,0 +1,202 @@ +# Scope every interchain request to the deployment's slice of a shared indexer + +| | | +| --- | --- | +| Issue | https://github.com/blockscout/frontend/issues/3650 | +| Feature branch | `issue-3650` | +| PM | Ulyana | +| Designer | Tatyana | +| Backend | Evgenii | +| Minimum API version | `interchain-indexer` **v1.8.1** | +| Slack channel | #dev-interchain | + +## Context & goal + +Until now each frontend got its interchain scope from *where it pointed*: one `interchain-indexer` +instance per consumer, indexing exactly that consumer's bridge. Numine pointed at an Avalanche-only +instance, Gnosis at an Omnibridge-only one. The per-frontend instances are being consolidated into one +shared instance per environment (mainnets and testnets), indexing every bridge and every chain, and the +same instance will back Multichain Explorer. + +Repointing a frontend at the shared instance **without** frontend changes makes it show every +cross-chain message the instance knows about — Gnosis↔Ethereum Omnibridge traffic on Numine, Avalanche +subnet traffic on Gnosis. Because unknown query params are silently ignored by the service, a dropped or +misspelled filter is not an error but a cross-frontend data leak that returns `200`. + +Goal: every interchain request declares the deployment's slice, and adding a bridge or a chain on the +backend needs at most an env change — never a frontend release. + +## Functional requirements + +1. Every request to an `interchainIndexer:*` resource carries the deployment's filter profile, including + page 2+, refetches and requests issued from a chain-scoped multichain view. +2. The profile resolves per consumer, with no per-call-site opt-in: + - single-chain explorer, and a **per-chain** multichain view → `home_chain_id` (the focal chain); + - **aggregated** multichain views → `bridge_ids` from env; + - `/stats/chains` and `/interchain/chains` on **every** consumer → `bridge_ids` from env, because + neither endpoint accepts a chain predicate. +3. `include_unindexed_chains` is sent from its own env, defaulting to `false`, on **every** + `interchainIndexer:*` resource except `interchainIndexer:message` — that is, on all list, stats and + directory resources, but never on message details, which deliberately does not accept the param so + that a link to a details page always resolves even for a row the lists hide. No code path hardcodes + the value. +4. A test fails when an `interchainIndexer:*` resource is added without declaring its scope, or declares + a filter name nothing resolves. Asserting that the request returned `200` is explicitly not sufficient. +5. Pointed at the shared instance, the Numine frontend shows only Numine-related traffic and the Gnosis + frontend only Gnosis↔Ethereum traffic. +6. Adding a bridge on the backend surfaces on a deployment after an env change alone; adding a chain to + an existing bridge surfaces with no frontend or env change at all. +7. The message-details request carries `bridge_id`, including after a page reload and from a deep link — + the bridge travels in the URL, not in component state. +8. A malformed message identifier (`400`, `code: 3`) is surfaced as invalid input, not as a service + error. A wrong `bridge_id` (`404`, `code: 5`) surfaces as not-found. An ambiguous unqualified lookup + (`400`, `code: 9`) is not recovered from by retrying — the qualified route makes it unreachable. +9. Rendering never treats `has_unindexed_chain: false` as "all fields are populated", and never caches + the flag as an immutable property of a row: it is derived from live bridge config and flips without a + migration. +10. No regression in cross-chain search (`q`), sorting, pagination or the Protocol column on any + affected surface. + +## Data & API + +**Service** — `interchainIndexer`, host from `NEXT_PUBLIC_INTERCHAIN_INDEXER_API_HOST`. Shared instances +are already deployed: mainnets `interchain-indexer.k8s-prod-4.blockscout.com`, testnets +`interchain-indexer-testnet.k8s-dev.blockscout.com`. Repointing the instances is DevOps' step at release +time (out of scope here, flagged in the PR description for the release notes). + +**Filter vocabulary** — all params optional, `snake_case`, `AND`-combined, `*_ids` comma-separated. +Blank/comma-only normalizes to "absent"; malformed returns `400 InvalidArgument` naming the param. + +| Param | Applies to | Used by this task | +| --- | --- | --- | +| `home_chain_id` | messages, transfers, their `:byTx` / `:byAddress` variants, `stats/common`, `stats/daily` | yes — focal-chain consumers | +| `bridge_ids` | the above, plus `stats/chains`, `messages-paths/*`, `bridged-tokens`; `/interchain/chains` after Q03 | yes — aggregated multichain, and the two chain-predicate-less resources everywhere | +| `include_unindexed_chains` | the above | yes — from env | +| `counterparty_chain_ids` | as above | already sent by the chain-stats sankey chart; unchanged | +| `src_chain_ids` / `dst_chain_ids` | lists and stats | no — for future "outgoing only" / "incoming only" sub-views | + +**Resources** — all needed resources are already registered under `interchainIndexer:*`. No new resource +is added: the bridge scope comes from env, so `GET /interchain/bridges` is deliberately **not** +registered (see *Out of scope*). + +**Message details** — `GET /interchain/messages/{message_id}` takes an optional `bridge_id` query param. +Verified against the live instance: a correct value resolves; omitting it resolves while no collision +exists; a **wrong** value returns `404 code: 5` (not the `400 code: 9` the issue documents — that is the +cross-bridge-collision case only, which the current bridge set makes unreachable). + +**Pagination** — `page_token` and the raw cursor fields are valid only for the exact filter combination +that produced them. The profile is constant per deployment (and per selected chain inside multichain), so +this holds as long as the profile is applied uniformly; a chain change inside multichain already resets +pagination through the existing chain-select flow. + +**Env vars** — two new, both documented in `docs/ENVS.md` and validated by the envs-validator: + +| Env | Type | Requirement | +| --- | --- | --- | +| `NEXT_PUBLIC_CROSS_CHAIN_TXS_BRIDGE_IDS` | array of bridge ids | Required when `NEXT_PUBLIC_CROSS_CHAIN_TXS_ENABLED=true`. Omitting it would leave `/stats/chains` and `/interchain/chains` unscoped — a silent leak, not an error — so the validator must fail at startup. | +| `NEXT_PUBLIC_CROSS_CHAIN_TXS_INCLUDE_UNINDEXED_CHAINS` | boolean | Optional, default `false`. | + +The neutral (non-multichain) name is deliberate: a single-chain deployment spanning two bridges is +expected, and this way it needs no rename or deprecation cycle. + +**Bridge ids are deployment data, not constants.** Today: mainnet AMB/Omnibridge `1`, mainnet Avalanche +ICTT/ICM `2`, testnet Sepolia↔Chiado AMB `1001`. They must never be hardcoded in `src/`. + +**Types** — bump `@blockscout/interchain-indexer-types` to `1.8.1` (note: `1.7.0` was never +published). Stubs and mocks gain `bridge.id` and `has_unindexed_chain`. Two known type facts, both being +fixed upstream per Q03: `Bridge.indexed_chain_ids` is `number[]` while `Chain.id` is `string`, and +`has_unindexed_chain` is optional despite always being sent. + +## UI inventory + +No visual changes and no mockups — the bridge is already rendered. `Protocol` is an existing column in +both cross-chain tables and both mobile list items, backed by `CrossChainBridgeLink`, so bridge identity +is visible wherever rows from more than one bridge can appear. + +**Route change** — the message-details route becomes `/bridge/[bridgeId]/cross-chain-tx/[id]`. +The old `/cross-chain-tx/[id]` is **deleted with no redirect**. Every link to the page is built from a +row or a message object that already carries `bridge.id`, so no call site needs new data. +Touch points: the page under `src/pages/`, the metadata template registry, the mixpanel page-type map, +`CrossChainMessageEntity`'s default href, and the two `tab: 'transfers'` links. + +**Surfaces the profile must reach** — cross-chain transactions list and its stats block, token-transfers +list, bridged tokens, ICTT users, the message details page and its transfers tab, the latest-cross-chain +block on the home page, the cross-chain messages/transfers blocks on tx details, the cross-chain tab on +address pages, the chain-stats cross-chain sankey chart and its counterparty selector, and the multichain +equivalents of the home, address and chain-stats surfaces. + +## Implementation decisions + +- **The profile is injected centrally, not at call sites.** The API layer applies it to every + `interchainIndexer:*` request. Fifteen call sites spread the same three params by hand would leak the + moment one is missed, and a missed one returns `200`. +- **Which params a resource accepts is declared on the resource entry**, in a *generic* field usable by + any service that needs static filters — not an interchain-specific map. Uniform injection is rejected: + it would send `home_chain_id` to endpoints that silently ignore it, which is the same trap from the + other side. Adding a resource then forces its author to answer the question. +- **Profile derivation** is a single decision point: + - multichain disabled → `home_chain_id = config.chain.id`; + - multichain enabled with a chain in `MultichainContext` → `home_chain_id = `; + - multichain enabled, no chain in context → `bridge_ids` from env. + A per-chain multichain view therefore sends `home_chain_id` **only**, byte-identical to a single-chain + explorer — the standing multichain rule that per-chain views mirror a single-chain explorer. The + consequence is accepted: were a cluster chain ever served by a bridge outside the env list, a per-chain + view would show it while the aggregated view hid it. Unreachable today, and reaching such a view would + require a link nobody can construct. +- **`home_chain_id` alone is the correct scope for the list endpoints**, per the backend owner: sending + it means accepting three defaults — unindexed chains excluded, events from **all** bridges covering the + focal chain included, and stats spanning all chains of those bridges. Adding `bridge_ids` there would + only narrow it, so it is not sent. +- **`/stats/chains` and `/interchain/chains` cannot take `home_chain_id`.** "Which chains are reachable + from the focal chain" would mean scanning the full messages/transfers table per request, since a + message can arrive from any chain, and the messages table is already past a million rows. `bridge_ids` + is the agreed scope for these two, confirmed at the 2026-09-02 sync. +- **No client-side intersection of `indexed_chain_ids`.** The env supplies bridge ids directly and the + service does the chain filtering, so `/interchain/bridges` is never fetched and no request is gated on + a directory lookup. +- **The bridge travels in the path, not a query param, and there is no retry on ambiguity.** `bridge_id` + is effectively required in the new paradigm, so the route reflects that; a wrong value 404s, which + makes guessing worse than not guessing. +- **The param-drop test enumerates the resource registry** rather than listing resources by hand, so a + newly added interchain resource is covered on the day it is added. It asserts two invariants — every + resource declares a scope (bar `message`), and every declared name has a resolver — not the specific + filters each resource takes. Pinning those would restate the registry: the expectation would have to be + edited in lockstep with every legitimate change, so it would only ever catch a deliberate edit, which + is code review's job. The leak risk FR4 guards is a *new* resource whose author never answered the + question, and a name that silently resolves to nothing; both are caught without duplication. + +## Out of scope + +- **Indexing-progress UI** from `GET /status/indexing` (the issue's optional item). Live data currently + reads 718k failed blocks on chain `100` and 19% catch-up on `43114`; shipping it would advertise a + backend state without a product decision. Separate ticket if product wants it. +- **A user-facing bridge filter control.** No deployment today sees more than one bridge (Numine → `2`, + Gnosis → `1`, multichain → `1`), and there are no mockups. Revisit when a deployment actually spans two. +- **Deriving bridge or chain selectors from `/interchain/bridges`** — replaced by the env, and rejected + on its own merits at the 2026-09-02 sync: a bridge's `indexed_chain_ids` is not the set of chains the + focal chain has actually talked to, so with ~100 Avalanche chains configured the selector would list + all of them while Numine exchanges messages only with C-Chain. +- **Marking `has_unindexed_chain` rows in the UI** and explaining their empty destination side. Product + decided (2026-09-02) not to show such messages at all — the env exists only as an escape hatch and no + deployment sets it to `true`, so there is nothing to mark. Requirement 9 covers defensive rendering + only, since the flag can still arrive on a row. +- **Repointing the instances** and retiring the old ones — DevOps, at release time. The Numine explorer + is explicitly not being retired: it is the reference instance for the Autoscout rollout. +- **`src_chain_ids` / `dst_chain_ids`** — no "outgoing only" / "incoming only" sub-view exists. +- **Moving the cross-chain summary tiles to the stats service.** Raised at the 2026-09-02 sync: the + `stats/common` endpoints were built for debugging small per-frontend indexers and do direct DB counts, + so they don't hold at shared scale; the same counters exist in the stats service, at ~2 hours' + staleness (accepted). Needs the counter names from the backend owner and its own issue — this task + keeps reading `stats/common`, merely scoped. +- **Rendering a native asset for the new token `type` field** (`native` vs erc20), which the + xDAI/Omnibridge onboarding introduces — a native coin must render without a token link. Same sync, own + issue. +- **Closing the selector/statistics divergence.** Once `bridge_ids` reaches `/interchain/chains` (Q03), + the counterparty selector lists the chains **configured** on the deployment's bridges; the statistics + below it show the chains the deployment has **actually exchanged messages with**. A configured chain + with no traffic therefore appears in the selector and is absent from the chart. Bridge scoping cannot + narrow this further — such a chain is on our own bridge — and the set of "chains we have really talked + to" is not something the service can answer today. Invisible with the current config (all four chains + of the two bridges have traffic), visible as soon as a bridge gains an idle chain. Knowingly accepted + at the 2026-09-02 sync, where the chart itself was taken as the answer to "which chains actually + participate" — not a defect to file. diff --git a/.agents/tasks/3663-code-complexity-ci-gate/spec.md b/.agents/tasks/3663-code-complexity-ci-gate/spec.md new file mode 100644 index 00000000000..521cf83d96c --- /dev/null +++ b/.agents/tasks/3663-code-complexity-ci-gate/spec.md @@ -0,0 +1,162 @@ +# Add a cognitive-complexity and CRAP-score CI gate + +| | | +| --- | --- | +| Issue | https://github.com/blockscout/frontend/issues/3663 | +| Feature branch | `issue-3663` | +| PM | — (frontend tooling task) | +| Designer | — | +| Backend | — | +| Minimum API version | — | +| Slack channel | — | + +## Context & goal + +Complex, under-tested code lands unnoticed today: nothing measures per-function +complexity, and the coverage tooling reports only statement/branch/function/line +coverage, never complexity or CRAP. The goal is a CI gate that flags code which is either hard to +read *or* both complex and under-tested — CRAP = `c²·(1 − cov)³ + c` — before it merges, scoped to +what a PR actually changes so it never blocks on pre-existing debt. + +## Functional requirements + +1. A `tools/code-complexity/` CLI computes per-function scores by walking the TS/TSX syntax tree via + the `typescript` compiler API (`ts.createSourceFile`), with no type-checker. +2. Each function gets two scores, both counted on that one walk: **cognitive complexity**, following + the SonarSource model (flat control flow is cheap, nesting is penalised progressively, boolean + runs collapse), and **cyclomatic complexity**, following ESLint's `complexity` rule. + Nested/arrow/method functions count as their own units under both. The exact increment rules and + the deliberate divergences from those two references are documented in the tool. +3. The tool computes CRAP `c²·(1 − cov)³ + c` per function by joining **cyclomatic** complexity with + per-function line coverage read from a v8/istanbul `coverage-final.json`. Cognitive complexity + never feeds CRAP. +4. Every function is classified from the AST — not its file extension — by whether JSX appears + directly in its own body (`JsxElement` / `JsxSelfClosingElement` / `JsxFragment`), outside any + nested function: **`jsx`** for a render body, **`behavior`** for everything else (handlers, + `useCallback`/`useMemo` bodies, hooks, utils) wherever it lives, including nested inside a + component. Only `behavior` functions are CRAP-scored. A `jsx` function is covered, if at all, by + Playwright visual tests that emit no vitest coverage, so scoring it would flood the report with + 0%s; its cognitive cap is the backstop instead. +5. A *file* needs coverage generated for it when it is JSX-less or has a co-located vitest spec + (`X.spec.tsx` beside `X.tsx`) — this decides only whether the tool's vitest run pulls the file + in, not which functions are scored. `*.primed.spec.tsx` drift tests never count as that spec and + are excluded from the coverage runs the tool triggers (they mount pages without exercising + behavior). +6. The gate scopes to **functions touched by the diff** against a base ref (default + `origin/main`) — a function is in scope when a changed line falls within its body. No + baseline artifact is kept. +7. Three independent thresholds gate, all configurable: a **cognitive-complexity cap per function + class** — a high backstop for `jsx`, a tighter one for `behavior` — and a **CRAP threshold** on + `behavior` functions. The cognitive caps are enforced even when no coverage data is present. +8. File scope is an allowlist of two roots — the app (`src/`) and the repo's own tooling (`tools/`, + so the gate also scores its own implementation) — with the extension set `.ts` `.tsx` `.mjs` `.js` + `.cjs`. Excluded within them: specs (`*.spec.*`, `*.pw.tsx`, `*.pwstory.tsx`), generated files + (`*.d.ts`, `envs.js`), configuration (`*.config.*`), and build output + (`src/toolkit/package/**`, `tools/**/dist/`). `deploy/**` stays out — it is outside ESLint and the + root TypeScript project, so this gate should not be its first automated check (issue #3675) — and + so do `playwright/**`, `vitest/**` and the repo-root runtime files. +9. On any violation the CLI exits non-zero. It always prints a table of **all** checked functions, + sorted by CRAP descending, offenders flagged with which threshold they broke. Under + `$GITHUB_ACTIONS` it additionally emits `::error file=,line=::` annotations for offenders and + writes the table to `$GITHUB_STEP_SUMMARY`. +10. The gate runs inside the existing `vitest_tests` CI job: coverage is enabled on the + affected-tests run, and the tool runs as a post-step selecting diff scope with `--changed` and + consuming that `coverage-final.json` via `--coverage-file` (so CI never regenerates coverage). +11. The interface has two independent axes, mirroring vitest. **Selection** — which functions to + score — is *full repo* by default (every in-scope function, FR8), *focused* when explicit + file paths are given, or *diff-scoped* under `--changed[=]` / `--base ` (functions a + changed line falls within, vs the base ref, default `origin/main`). **Coverage source** — + generated by running vitest scoped to the selection (default, no pre-step), consumed from a + prebuilt report via `--coverage-file ` (skips vitest — the CI path), or skipped via + `--no-coverage` (cognitive gate only). A `pnpm test:code-complexity` script exposes the tool. +12. **Full-repo mode** (the default selection) scores every in-scope function and is what the + threshold calibration uses to produce the repo-wide distributions. **Focused mode** + (explicit paths) scores every function in those files and generates their coverage via `vitest + related` (the specs transitively importing them, minus the excluded `*.primed.spec.tsx`), so a + single command verifies a newly written or edited function sits within threshold before pushing. + Because all three selections collect coverage through the module graph, they agree on a given + file's number. +13. Thresholds live in the tool as configurable defaults (constants/config under + `tools/code-complexity/`), overridable by CLI flags; CI carries no threshold numbers, so a + local run and a CI run gate identically. + +## Data & API + +None. No endpoints, resources, env vars, or feature flags. + +## UI inventory + +None. This is CI/tooling only; no routes, pages, or components. + +## Implementation decisions + +- **Bespoke tool, no external service.** Built on the `typescript` compiler API, already a + dependency (the `@typescript/typescript6` alias exposes `ts.createSourceFile` and + `ts.SyntaxKind`), keeping the pipeline self-contained with no external service or CI + secrets. This rationale stays in the spec only — the shipped tool names no third-party + quality-gate service, and cites SonarSource and ESLint solely as the published counting models it + is measured against. +- **Coverage rides the per-PR affected-tests run.** CI's `vitest_tests` job already runs + `vitest run --changed=origin/main`; add `--coverage` (`@vitest/coverage-v8`, already + resolvable in the pnpm store). Because `vitest --changed` runs every spec that + *transitively imports* a changed module, the coverage collected for a changed file equals + its whole-suite coverage — so affected-scoped coverage is correct, not an undercount. This + corrects the issue's "free rider on the coverage run CI already produces" premise: CI + produced **no** coverage before, and on PRs runs only changed specs. +- **The tool generates coverage itself for local runs; CI feeds it the artifact.** Coverage + has three sources behind one interface: generated by default (focused: `vitest related + `; diff: `vitest run --changed `, both into a throwaway temp dir), + consumed from a prebuilt report via `--coverage-file`, or skipped via `--no-coverage`. This + keeps the local UX a single command (no "generate coverage first, then pass it" pre-step) + while CI stays efficient by passing `--coverage-file` pointed at the coverage the + `vitest_tests` step already produced — the gate regenerates nothing in CI. The generation + scope reuses the same transitive-importer logic as the CI run, so a local number matches + CI's for the same files. +- **Missing coverage = 0%.** A changed in-scope file absent from `coverage-final.json` is + treated as 0% coverage (derived from the git-changed file list), rather than enabling a + global `coverage.all`. For a JSX-less logic file this correctly flags untested code. The one + exception is a hand-passed `--coverage-file` in focused mode, where absence may simply mean the + report predates the file, so it reports "no data" instead of 0%. +- **Granularity: touched functions only.** Map git diff hunks to function line ranges; gate + only functions the diff added or modified. No committed baseline, no keying scheme to + maintain. +- **JSX detection is AST-based, not extension-based.** ~78 non-test `.tsx` files currently + contain no JSX (mostly `useX.tsx` hooks), so the extension is an unreliable + component-vs-logic signal. Classification is free — the tool already parses every file. + (Aligning extensions with content is tracked separately in issue #3668, out of scope here.) +- **Cognitive complexity is the readability gate; cyclomatic only feeds CRAP.** Cyclomatic counts + independent paths ≈ tests needed, which makes it the right CRAP input and a poor decomposition + signal — it scores a flat `switch` the same as an equivalent nested `if` chain and is blind to + nesting. So the gate that says "a human should split this up" is cognitive complexity, and it + charges nesting **quadratically** (`1 + nesting²`) rather than SonarSource's linear `1 + nesting`: + the two agree at depths 0 and 1 and diverge from the third level in, which is what lets a single + cap forgive wide-but-shallow logic while still catching the nested tail. Recorded as an ADR in the + tool, alongside the exclusions from the count. +- **CRAP applies per function class, not per file.** Every `behavior` function is CRAP-scored and no + `jsx` function ever is, decided from the AST per function rather than per file — so a component's + render body loses its noise 0% while the handlers inside it gain a real score. The co-located + `X.spec.tsx` rule survives only as *generation scope*: it decides whether a file causes a vitest + run at all, not which of its functions are scored. `*.primed.spec.tsx` drift tests are excluded + from those runs entirely — they mount whole page trees (importing much of the app) without + exercising behavior, so `vitest related` would otherwise pull them in for almost any file, adding + tens of seconds and no meaningful coverage. +- **Thresholds calibrated from real data as the final step.** The tool was run repo-wide against + whole-suite coverage to produce the CRAP and cognitive-complexity distributions; each cap was set + from the far tail so the gate flags genuine offenders rather than ordinary code. The three caps are + coupled — to each other and to the counting model — so none can be re-tuned in isolation. The live + values and their rationale live in the tool's `config.ts`; the calibration figures are in the + landing PR. +- **CI-only gate, no husky hook** — coverage-dependent and too slow for a commit hook. +- **Residual risk:** a changed file exercised only through a dynamic import or other + non-static path the module graph misses would be undercounted and could fail falsely; rare + in this codebase, and the changed-scope + touched-function limits contain the blast radius. + +## Out of scope + +- Adopting SonarCloud / any external quality-gate service. +- A baseline of pre-existing violations (superseded by the touched-functions scope). +- Remapping Playwright component-test (`*.pw.tsx`) coverage into the gate. (`jsx` render bodies are + never CRAP-scored; their cognitive cap is the backstop instead.) +- Enforcing the `.tsx`→`.ts` rename and the `react/jsx-filename-extension` rule (issue #3668). +- Gating the whole repo or historical code in **CI**; the CI gate is strictly diff-scoped. (A + full-repo mode exists for local use and calibration; CI never invokes it.) diff --git a/.agents/tasks/3665-mutation-testing/progress.md b/.agents/tasks/3665-mutation-testing/progress.md new file mode 100644 index 00000000000..f8f68bdf925 --- /dev/null +++ b/.agents/tasks/3665-mutation-testing/progress.md @@ -0,0 +1,9 @@ +# Progress — Add diff-scoped mutation testing + +- [x] 01 → `tickets/01-stryker-boots/` +- [x] 02 → `tickets/02-cli-selection-modes/` +- [x] 03 → `tickets/03-mutation-ranges/` +- [x] 04 → `tickets/04-bounded-run/` +- [x] 05 → `tickets/05-findings-report/` +- [x] 06 → `tickets/06-ci-gate/` (its `(human)` criterion deferred to task land — see the ticket's `notes.md`) +- [x] 07 → `tickets/07-docs/` diff --git a/.agents/tasks/3665-mutation-testing/spec.md b/.agents/tasks/3665-mutation-testing/spec.md new file mode 100644 index 00000000000..f7dea978315 --- /dev/null +++ b/.agents/tasks/3665-mutation-testing/spec.md @@ -0,0 +1,123 @@ +# Add diff-scoped mutation testing + +| | | +| --- | --- | +| Issue | https://github.com/blockscout/frontend/issues/3665 | +| Feature branch | `issue-3665` | +| PM | — (frontend tooling task) | +| Designer | — | +| Backend | — | +| Minimum API version | — | +| Slack channel | — | + +## Context & goal + +Coverage answers "was this line executed?". It cannot answer "would a bug here be caught?" — a test can +execute a branch and assert nothing about it. Mutation testing answers the second question directly: change +the code, and see whether any test notices. + +The goal is to measure **assertion strength of the vitest specs the repo already has**, and to stop new, +weakly-asserted logic from landing. + +## Functional requirements + +1. A CLI runs StrykerJS with the vitest test runner over a selected set of source files and reports which + mutants survived — that is, which code changes no test detected. +2. **Only logic mutators are generated**: conditional expressions, equality operators, logical operators, + arithmetic operators, and boolean literals. Every other mutator class is excluded at generation time, not + filtered afterwards. +3. A file is **eligible** only when a co-located vitest spec exists beside it (`X.spec.ts` / `X.spec.tsx` + next to `X.ts` / `X.tsx`). Ineligible files are + never mutated, so a file with no unit test produces no findings rather than a run of unkillable mutants. +4. Mutants located inside a **`jsx` function body** are excluded, using the same AST function + classification the complexity gate applies. `behavior` functions are mutated wherever they live. +5. The CLI has three **selection** modes, mirroring the complexity gate's flags: + the **full eligible set** when invoked bare, **focused** on whole files when given explicit paths, + and **diff-scoped** under `--changed[=]` (default `origin/main`, resolved through the merge-base so + it captures the branch's own commits plus uncommitted edits, and never base-branch churn). +6. In diff-scoped mode, only the **changed lines** are mutated, expressed as Stryker mutation ranges. This + restricts mutant *creation*, not merely reporting . Focused mode mutates whole files. +7. When the selection resolves to nothing, the CLI exits 0 **without invoking Stryker at all**, printing one line + explaining why it found nothing to do. +8. Output is produced by consuming Stryker's JSON report rather than its clear-text reporter, which is + unusable at this scale. It comprises a per-file table (score, mutants, killed, survived, no-coverage) and + a listing of findings **grouped by source line**. Survivors and no-coverage mutants are separated, keyed off the + `status` field Stryker records per mutant; no-coverage is summarised compactly, being a coverage finding + the CRAP gate already owns. +9. An HTML report is written to a gitignored path and its location printed. +10. Results are **deterministic**: repeated runs over an unchanged tree produce identical counts. This + requires static mutants to be ignored by default, a raised mutation timeout, and the exclusion in FR11. +11. The `*.primed.spec.tsx` drift tests are excluded from the vitest runs this tool triggers. They mount + whole pages without exercising behaviour, so their kills are incidental to the question being asked, and + they dominated runtime. This matches the complexity gate, which already excludes them from its coverage runs. +12. A **wall-clock budget** bounds any single run. Results are written to disk as each mutant is tested, so + when the budget expires the run is stopped and everything completed so far is reported. The output always + states that the run was truncated and how much of the selection it covered; truncation is never silent. +13. Stryker's own configuration lives in a **committed config file**, so every setting is reviewable in a + diff and schema-validated in an editor. The CLI contributes only what genuinely varies per run. +14. A **CI job** runs the tool diff-scoped on pull requests. It is its own job — it cannot reuse the unit-test + job's artifacts, because Stryker must drive vitest itself — and it chains on the unit-test job, since a + red suite makes Stryker abort during its dry run with an error that reads like a tooling failure. +15. **The CI job fails when any mutant on a changed line survives.** Findings are additionally emitted as + inline annotations on the PR diff, naming the surviving mutator and the line, so they are visible during + review rather than buried in a job log. +16. Suppressing a mutant requires Stryker's disable comment carrying **both the mutator name and a reason**; + blanket file-level disables are not permitted. This makes each suppression a reviewable claim rather + than a mute button. + +## Data & API + +None. This task adds no runtime code, no API resource, and no environment variable. + +## UI inventory + +None. This task ships no user-facing surface. + +## Implementation decisions + +- **Two pnpm integration blockers must be handled; both were reproduced and solved during grilling.** + - Stryker fails to boot because the repo pins `brace-expansion` to a 1.x security override globally, while + the `minimatch` version Stryker depends on needs a 5.x ESM build. The fix is a **scoped** override for + that dependency path only, following the precedent already in the workspace file for `vite-plugin-dts`. + The global security pin is not widened. + - Stryker discovers plugins by globbing its own scope in `node_modules`, which pnpm's isolated layout + defeats: the run fails with "no TestRunner plugins were loaded". The vitest runner must be named + explicitly in the config's plugin list. +- Stryker copies the project into a sandbox per run, so its ignore patterns need tuning; left untuned it + also fails outright on symlinks under agent worktree directories. +- **Diff resolution and file-scope logic are reused from the complexity gate**, not reimplemented — the + merge-base resolution, the `--unified=0` hunk-range parsing, the scope allowlist, and the AST function + classifier FR4 needs. Direct import across tool directories is the right coupling for now; extracting a + shared location is warranted only if a third consumer appears. +- **StrykerJS has no `--since` flag.** The issue proposed one, but that option belongs to Stryker.NET; the + JS CLI has no equivalent. Diff-scoping is therefore something this tool computes and expresses through + mutation ranges, which is why FR5 and FR6 are stated the way they are. +- **No incremental mode and no committed report artifact.** The issue proposed committing + `reports/stryker-incremental.json`; it is rejected. +- **Runtime is governed by import-graph centrality, not mutant count.** Five leaf utilities with 140 mutants + ran in 10 seconds; one 28-mutant file imported across the app took 70 seconds, because Stryker's dry run + executes everything related to it. This is why FR12 bounds by wall-clock time rather than mutant count: + diff size does not predict cost, and a mutant-count bound would have to track Stryker's own generator to + stay honest. +- The gating mutator set in FR2 was chosen from evidence, not taste. Sampling 22 survivors across all + mutators found roughly a third unkillable by any vitest test — React dependency arrays, theme-variable + objects, colour-token string literals, all of them visual or idiomatic rather than behavioural. Sampling + 18 survivors restricted to the logic mutators found **zero** such cases; all 18 were real gaps. +- Promoting or demoting a mutator class is a one-line config change, which is the intended ratchet if the + excluded classes later prove to hide a real bug. + +## Out of scope + +- **The `review-changes` skill's "mutation-thinking" prompt.** The issue proposed adding an LLM reasoning + step to that skill's correctness axis; it is dropped from this task entirely. +- **Playwright tests.** Stryker's vitest runner cannot execute them, and browser mode is explicitly + unsupported. Nothing in the 228-file screenshot suite is measured. +- **Non-logic mutators** — string literals, array and object declarations, block statements, optional + chaining, method and call expressions, arrow functions, regexes. Not generated, not reported. +- **A scheduled or whole-repo CI run.** The full eligible set took 17 min 29 s locally before FR2's mutator + restriction; it is reproducible on demand via the CLI's bare invocation, but no workflow runs it. +- **A mutation-score percentage threshold.** The gate is "no survivor on a changed line" (FR15). A + percentage is unstable at the small mutant counts a diff produces, where one survivor can swing the number + by double digits. +- **Files without a co-located vitest spec**, including newly added untested files. Catching those is the + CRAP gate's role. diff --git a/.agents/tasks/3665-mutation-testing/tickets/01-stryker-boots/spec.md b/.agents/tasks/3665-mutation-testing/tickets/01-stryker-boots/spec.md new file mode 100644 index 00000000000..e90cb3fb109 --- /dev/null +++ b/.agents/tasks/3665-mutation-testing/tickets/01-stryker-boots/spec.md @@ -0,0 +1,65 @@ +# 01 — Stryker boots on this repo + +| | | +| --- | --- | +| Parent spec | [`../../spec.md`](../../spec.md) → ticket 01 of #3665 | +| Blocked by | none | + +## What to build + +StrykerJS installed and configured so that a bare `stryker run` over one hand-named source file completes +and reports killed/survived counts. Two pnpm integration blockers stand in the way and are both solved by +configuration, not by code (see the parent spec's Implementation decisions). Everything the run needs that +does not vary per invocation lives in a committed `stryker.config.json`: the mutator restriction, the +determinism settings, the reporters and their output paths. + +No CLI yet — this ticket's deliverable is that Stryker itself works here, which is the thing every later +ticket assumes. + +## Acceptance criteria + +How to verify: `./node_modules/.bin/stryker run --mutate ` + +- [ ] The run completes: no "no TestRunner plugins were loaded", no `minimatch`/`brace-expansion` boot error, + no failure on symlinks under `.claude/worktrees/`. +- [ ] Only conditional-expression, equality-operator, logical-operator, arithmetic-operator and + boolean-literal mutants appear in the report. No string-literal, array/object-declaration, + block-statement, optional-chaining, method/call-expression, arrow-function or regex mutants. +- [ ] Two consecutive runs over an unchanged tree report identical killed / survived / no-coverage counts. +- [ ] The vitest runs Stryker drives do not execute `*.primed.spec.tsx`. +- [ ] `stryker.config.json` carries a `$schema` pointer and every non-varying setting; nothing needed for a + plain run is passed on the command line. +- [ ] The JSON and HTML reports land under `tools/mutation-testing/reports/`, the sandbox under + `tools/mutation-testing/.stryker-tmp`, and both are gitignored. +- [ ] The global `brace-expansion` security override in `pnpm-workspace.yaml` is unchanged. +- [ ] `(human)` The lockfile delta is reviewed: Stryker's dependency tree brings nothing unexpected, and the + scoped override does not widen the global 1.x pin. + +## Details + +**The two blockers.** The scoped `brace-expansion` override follows the `vite-plugin-dts>brace-expansion` +precedent already in `pnpm-workspace.yaml`'s `overrides` block — add a sibling entry for the dependency path +that reaches Stryker's `minimatch`, and leave the global `brace-expansion: "1.1.12"` pin alone. The vitest +test runner must be named explicitly in the config's `plugins` array, because Stryker discovers plugins by +globbing `node_modules/@stryker-mutator/*` and pnpm's isolated layout defeats that. + +**Determinism (FR10).** Static mutants ignored by default, and a raised mutation timeout — a mutant that +times out under load would otherwise flip between `timeout` and `survived` between runs. + +**Sandbox.** Stryker copies the project per run, so its ignore patterns need tuning; left untuned it fails +outright on the symlinks under agent worktree directories. + +**Reporters.** `json` and `html` both on; the clear-text reporter is unusable at this scale (FR8) and later +tickets read the JSON. `tempDirName` points the sandbox inside the tool folder rather than the repo root. + +**Gitignore.** The existing `/tools/code-complexity/dist/` line is the placement precedent. + +## Leaf worklist + +- [x] 1 `[agent]` Add the Stryker dependencies and the scoped `brace-expansion` override; verify a bare run + boots +- [x] 2 `[agent]` Write `tools/mutation-testing/stryker.config.json` — explicit plugin list, logic-only + mutators, `ignoreStatic`, raised timeout, primed-spec exclusion, sandbox ignores, json + html reporters + into `tools/mutation-testing/reports/` +- [x] 3 `[agent]` Gitignore the reports directory and the sandbox +- [x] 4 `[agent]` Verify determinism: two runs, identical counts diff --git a/.agents/tasks/3665-mutation-testing/tickets/02-cli-selection-modes/spec.md b/.agents/tasks/3665-mutation-testing/tickets/02-cli-selection-modes/spec.md new file mode 100644 index 00000000000..a90882ab703 --- /dev/null +++ b/.agents/tasks/3665-mutation-testing/tickets/02-cli-selection-modes/spec.md @@ -0,0 +1,63 @@ +# 02 — The CLI and its three selection modes + +| | | +| --- | --- | +| Parent spec | [`../../spec.md`](../../spec.md) → ticket 02 of #3665 | +| Blocked by | T01 | + +## What to build + +`pnpm test:mutation-testing`, a CLI that decides which files to mutate, invokes Stryker over them, and +prints a per-file score table read from Stryker's JSON report. + +Three selection modes mirror the complexity gate's flags: bare invocation takes the full eligible set, +explicit paths focus on those files, and `--changed[=]` scopes to the diff against a base ref +(default `origin/main`, through the merge-base). A file is eligible only when a vitest spec sits beside it; +a file with no unit test produces no findings rather than a run of unkillable mutants. When the selection +resolves to nothing, the CLI says why in one line and exits 0 without invoking Stryker at all. + +Whole files are mutated in this ticket. Narrowing to changed lines and excluding `jsx` bodies is T03. + +## Acceptance criteria + +How to verify: `pnpm test:mutation-testing tools/code-complexity/measure/crap.ts` + +- [x] Bare invocation selects every eligible file under the scope allowlist; explicit paths select exactly + those files; `--changed` selects the files the diff touches, and `--changed=` honours the ref. +- [x] A file with no co-located `*.spec.ts` / `*.spec.tsx` is never mutated in any mode, including when + named explicitly. +- [x] `--changed` on a branch with no in-scope changes prints one explanatory line, exits 0, and never + spawns Stryker (verifiable by the absence of a fresh sandbox and the run's wall time). +- [x] `--changed` resolves through the merge-base: commits landing on `origin/main` after the fork point + never appear in the selection. +- [x] Output is a per-file table with score, mutants, killed, survived and no-coverage columns, built from + the JSON report — Stryker's clear-text reporter is not used. +- [x] `--help` documents the three modes. +- [x] The tool's own files pass the complexity gate (`pnpm test:code-complexity --changed`). +- [x] Unit specs cover eligibility, mode resolution and the empty-selection exit. + +## Details + +**Reuse, do not reimplement (parent spec).** `tools/code-complexity/select/diff.ts` supplies +`resolveBaseCommit`, `getChangedFiles`, `getAllSourceFiles` and `getChangedLineRanges`; +`tools/code-complexity/select/scope.ts` supplies `isInScope`. Import them directly across tool directories — +extracting a shared location is warranted only if a third consumer appears. + +**Eligibility (FR3)** is the same naming-convention check `tools/code-complexity/index.ts` already makes in +`hasCoLocatedSpec`; Playwright `*.pw.tsx` files do not count. + +**Shape.** Follow the code-complexity layout: a `run.sh` that compiles through a local `tsconfig.json` into a +gitignored `dist/` and then runs `dist/index.js`, an `index.ts` that parses flags and orchestrates, specs +beside what they test. Its flag-parsing table (`FLAGS`, `applyArg`) is the pattern to follow for +`--changed[=]`, which must not swallow the following token. + +**Package script.** `"test:mutation-testing": "./tools/mutation-testing/run.sh"`, beside +`test:code-complexity`. + +## Leaf worklist + +- [x] 1 `[agent]` Scaffold `tools/mutation-testing/` — `run.sh`, `tsconfig.json`, gitignored `dist/`, package + script +- [x] 2 `[agent]` Selection: eligibility check, the three modes, the empty-selection early exit; specs +- [x] 3 `[agent]` Invoke Stryker over the selection and parse its JSON report +- [x] 4 `[agent]` Render the per-file table; specs diff --git a/.agents/tasks/3665-mutation-testing/tickets/03-mutation-ranges/spec.md b/.agents/tasks/3665-mutation-testing/tickets/03-mutation-ranges/spec.md new file mode 100644 index 00000000000..c3bfb9154a2 --- /dev/null +++ b/.agents/tasks/3665-mutation-testing/tickets/03-mutation-ranges/spec.md @@ -0,0 +1,53 @@ +# 03 — Mutate only behavior code, only on changed lines + +| | | +| --- | --- | +| Parent spec | [`../../spec.md`](../../spec.md) → ticket 03 of #3665 | +| Blocked by | T02 | + +## What to build + +Narrow what Stryker mutates from whole files down to the lines worth mutating, expressed as Stryker mutation +ranges so this restricts mutant *creation* rather than filtering findings afterwards. + +Two independent restrictions compose. Everywhere, mutants inside a `jsx` function body are excluded — sampling +during grilling found those survivors to be visual or idiomatic rather than behavioural, so they are noise by +construction. Additionally, under `--changed` only the lines the diff touched are mutated, so a one-line fix +in a large file does not pay for the whole file. Focused and bare modes still mutate whole files, minus the +`jsx` bodies. + +## Acceptance criteria + +- [x] A `.tsx` file whose only logic lives in a render body yields zero mutants; a `behavior` function in the + same file is still mutated. +- [x] A `behavior` function declared inside a component (an `onClick` handler, a `useCallback` body) is + mutated — the exclusion keys off the function's own classification, not its enclosing file. +- [x] Under `--changed`, mutants appear only on lines the diff added or modified. An untouched conditional + elsewhere in a changed file produces no mutant. +- [x] Focused mode (`pnpm test:mutation-testing `) mutates the whole file, minus `jsx` bodies. +- [x] When the ranges for a file come out empty, that file is dropped from the selection rather than passed + to Stryker with an empty range. +- [x] Range computation is unit-tested, including a `jsx` body overlapping a changed hunk. + +## Details + +**Function classification (FR4)** reuses `computeFunctionComplexities` from +`tools/code-complexity/measure/complexity.ts`, whose `FunctionComplexity` carries `startLine`, `endLine` and +`containsJsx` — the same AST classification the complexity gate applies. `containsJsx` means JSX appears +directly in that function's own body, outside any nested function, which is exactly the `jsx` / `behavior` +split FR4 names. + +**Ranges are an include-list.** Stryker's `mutate` accepts `file.ts:startLine[:col]-endLine[:col]`; there is +no exclude form. So a `jsx` body is excluded by emitting the complement — the spans of the file *not* covered +by any `jsx` function — and under `--changed` by intersecting that complement with the changed line ranges +from `getChangedLineRanges`. + +**Changed lines** come from the complexity gate's `--unified=0` hunk parsing (`parseHunkNewRanges`), already +resolved against the merge-base in T02. + +## Leaf worklist + +- [x] 1 `[agent]` Compute per-file `jsx`-body spans from the complexity gate's classifier and derive their + complement; specs +- [x] 2 `[agent]` Intersect with changed line ranges under `--changed`; specs +- [x] 3 `[agent]` Emit the ranges as Stryker `mutate` entries and drop files whose ranges are empty diff --git a/.agents/tasks/3665-mutation-testing/tickets/04-bounded-run/spec.md b/.agents/tasks/3665-mutation-testing/tickets/04-bounded-run/spec.md new file mode 100644 index 00000000000..682a54b03ed --- /dev/null +++ b/.agents/tasks/3665-mutation-testing/tickets/04-bounded-run/spec.md @@ -0,0 +1,52 @@ +# 04 — Bound a run by wall-clock time, report what completed + +| | | +| --- | --- | +| Parent spec | [`../../spec.md`](../../spec.md) → ticket 04 of #3665 | +| Blocked by | T02 | + +## What to build + +A wall-clock budget on any single run, so a selection that reaches deep into the import graph cannot grind +for hours. When the budget expires the Stryker process is stopped and the results collected up to that point +are reported, with the output stating plainly that the run was truncated and how much of the selection it +covered. + +Stryker writes its JSON report only when the whole run finishes, so a killed process would otherwise leave +nothing behind. A small custom reporter plugin closes that gap: it appends each mutant's result as it is +tested, and the CLI falls back to that stream when the run did not finish. The normal, complete path still +reads Stryker's own JSON report untouched. + +## Acceptance criteria + +- [x] A run that exceeds the budget is stopped and still prints a report covering the mutants tested before + the cutoff. +- [x] The output states the run was truncated and how much of the selection was covered. A truncated run is + never presented as a complete one. +- [x] A run that finishes inside the budget is byte-identical to what T02 produced — the streaming path + changes nothing on the normal path. +- [x] The reporter plugin is named explicitly in `stryker.config.json`'s plugin list, alongside the vitest + runner (pnpm's layout defeats Stryker's plugin globbing). +- [x] Stopping the run leaves no orphaned vitest or Stryker child processes and no stale sandbox directory. +- [x] The budget is a named constant in the tool's config module, overridable per invocation. +- [x] Unit specs cover parsing the streamed results and the truncated-vs-complete decision. + +## Details + +**Why time and not mutant count (FR12).** Runtime is governed by import-graph centrality, not mutant count — +five leaf utilities with 140 mutants ran in 10 seconds while one 28-mutant file imported across the app took +70, because Stryker's dry run executes everything related to it. A mutant-count bound would additionally have +to track Stryker's own generator to stay honest, and any drift would make the bound lie. + +**The plugin** hooks Stryker's per-mutant reporter event and appends one record per line to a file under +`tools/mutation-testing/reports/`, so a partial file is always parseable. It is declared through Stryker's +plugin declaration API and loaded by explicit path. + +**Config precedent** for the budget constant: `tools/code-complexity/config.ts` keeps its caps in one module +so a local run and a CI run gate identically. + +## Leaf worklist + +- [x] 1 `[agent]` Write the streaming reporter plugin and register it in `stryker.config.json` +- [x] 2 `[agent]` Enforce the budget: stop the run at expiry, clean up children and the sandbox +- [x] 3 `[agent]` Read the streamed results on a truncated run and mark the report truncated; specs diff --git a/.agents/tasks/3665-mutation-testing/tickets/05-findings-report/spec.md b/.agents/tasks/3665-mutation-testing/tickets/05-findings-report/spec.md new file mode 100644 index 00000000000..1581ec667f6 --- /dev/null +++ b/.agents/tasks/3665-mutation-testing/tickets/05-findings-report/spec.md @@ -0,0 +1,47 @@ +# 05 — Findings grouped by source line + +| | | +| --- | --- | +| Parent spec | [`../../spec.md`](../../spec.md) → ticket 05 of #3665 | +| Blocked by | T04 | + +## What to build + +The listing half of the output: after the per-file table, the mutants that nothing killed, named by mutator +and grouped by the source line they sit on. Grouping by line is what makes the report actionable — several +mutators surviving on one line is one weak assertion, not several findings. + +No-coverage mutants are a different problem from survivors: they mean no test executed the line at all, +which the CRAP gate already owns. They are separated off the `status` field Stryker records per mutant and +summarised compactly rather than listed one by one. The HTML report's location is printed at the end for +anyone who wants to browse the whole thing. + +## Acceptance criteria + +How to verify: `pnpm test:mutation-testing ` + +- [x] Survivors are listed grouped by source line, each naming its mutator, under the file they belong to. +- [x] No-coverage mutants never appear in the survivor listing; they are summarised compactly and separately. +- [x] The split reads Stryker's per-mutant `status` field rather than inferring it. +- [x] A run with no survivors and no no-coverage mutants says so in one line instead of printing empty + sections. +- [x] The HTML report path is printed, and the path resolves to a file that opens. +- [x] A truncated run (T04) renders its partial findings the same way, still marked truncated. +- [x] Formatting is unit-tested, including the multiple-mutators-on-one-line grouping. +- [x] `(human)` The report is legible on a real diff: the table, the grouped findings and the no-coverage + summary read as one thing a reviewer can act on, not three dumps. + +## Details + +**Precedent** for the rendering split is `tools/code-complexity/render/` — pure formatters, with the side +effects kept in `index.ts`. Follow it: a module that turns parsed report rows into text, tested directly. + +**Why not Stryker's own reporter (FR8).** The clear-text reporter prints every mutant with its full diff and +is unusable at this scale; consuming the JSON is what makes the compact form possible. + +## Leaf worklist + +- [x] 1 `[agent]` Split survivors from no-coverage mutants off the `status` field; specs +- [x] 2 `[agent]` Render the grouped-by-line survivor listing and the compact no-coverage summary; specs +- [x] 3 `[agent]` Print the HTML report location +- [x] 4 `[human]` Read the output on a real diff and confirm it is actionable diff --git a/.agents/tasks/3665-mutation-testing/tickets/06-ci-gate/notes.md b/.agents/tasks/3665-mutation-testing/tickets/06-ci-gate/notes.md new file mode 100644 index 00000000000..19691095e4d --- /dev/null +++ b/.agents/tasks/3665-mutation-testing/tickets/06-ci-gate/notes.md @@ -0,0 +1,58 @@ +# Notes — 06 + +## `process.chdir()` in a spec aborts the whole run (found while verifying T03) + +Stryker's vitest runner passes `pool: 'threads'` to `createVitest` as a command-line override +(`vitest-test-runner.js`, `#getVitestPoolConfig`), so a config-file `pool` setting cannot win. In a worker +thread `process.chdir()` throws, and any spec calling it fails the **dry run** — which aborts the entire run +with `ConfigError: There were failed tests in the initial test run.`, exit code 1, and no report. + +`tools/mutation-testing/select/files.spec.ts` switches into a throwaway git repository that way for its +git-backed modes, so a diff-scoped run that selects `select/files.ts` aborts: + +``` +ERROR DryRunExecutor One or more tests failed in the initial test run: + git-backed modes selects only the eligible files the branch itself touched + process.chdir() is not supported in workers +``` + +This matters for the gate: a PR touching that file (this branch's own PR included) fails the job for a +tooling reason, not a finding. The fix is to make the selection's git calls take a working directory rather +than move the process into one — which means threading a `cwd` through the complexity gate's +`select/diff.ts` helpers, wider than T03 was scoped for. Decide in this ticket whether the gate works around +it (skipping those specs under `STRYKER_MUTATOR_WORKER`, which downgrades their mutants to no-coverage) or +the specs get fixed properly. + +**Decided: the specs were fixed properly, inside T03.** `selectFiles` takes the repository to resolve +against as an argument, and `select/files.spec.ts` addresses its throwaway repo by path +(`execFileSync(..., { cwd: repo })`) instead of moving the process into it. The gate needs no +workaround, and a PR touching `select/files.ts` — this branch's own included — runs clean. + +## What stood in for the deferred PR run + +The `(human)` criterion needs a non-draft PR, which is after T07. Verified locally instead, with +`GITHUB_ACTIONS=1` set so the annotation path ran: + +- **Fails on a survivor.** Deleting one assertion from `render/truncation.spec.ts` produced + `::error file=tools/mutation-testing/render/truncation.ts,line=21::…` and exit 1. Restoring it: + exit 0, no annotations. +- **A well-formed disable suppresses.** `// Stryker disable next-line ConditionalExpression: ` + above that line dropped the file from 12 scored mutants to 10 and the survivor with it. Swapping it + for `// Stryker disable all` made the ESLint rule error on the real file. +- **Wall time.** A diff-scoped run over this branch's whole diff — 12 files, 761 generated / 288 scored + mutants — took **34 s**. The job's prelude (checkout at `fetch-depth: 0`, pnpm, node, frozen install) + measured **86 s** on run `33516039521`'s identical `vitest_tests` steps, which is what `timeout-minutes: + 20` is sized against: 15-min budget + prelude + teardown ≈ 17 min worst case. + +What could not be checked without GitHub rendering it: that the annotations land on the right diff +lines. The file/line pairs in the directives are correct and repo-relative. + +## Outstanding: the gate fails this branch + +That same rehearsal reported **18 survivors, score 77%** — in the tool's own code, not tooling noise: +`index.ts` L120/L128, `render/table.ts` L9/L13, `select/files.ts` L65/L68/L85, `select/ranges.ts` +L31/L32, `stryker/report.ts` L111/L112/L130, `stryker/stream.ts` L44. Plus 49 no-coverage mutants +(31 of them in `stryker/invoke.ts`), which do not gate. + +So the task's own PR is red until those assertions are strengthened. Not T06's scope — T06's job was to +make the gate fail correctly, which it does — but it has to be dealt with before this lands. diff --git a/.agents/tasks/3665-mutation-testing/tickets/06-ci-gate/spec.md b/.agents/tasks/3665-mutation-testing/tickets/06-ci-gate/spec.md new file mode 100644 index 00000000000..a0b411b6130 --- /dev/null +++ b/.agents/tasks/3665-mutation-testing/tickets/06-ci-gate/spec.md @@ -0,0 +1,59 @@ +# 06 — The PR gate + +| | | +| --- | --- | +| Parent spec | [`../../spec.md`](../../spec.md) → ticket 06 of #3665 | +| Blocked by | T03, T05 | + +## What to build + +A CI job that runs the tool diff-scoped on every pull request and fails when any mutant on a changed line +survives. Findings are additionally emitted as inline annotations on the PR diff, naming the surviving +mutator and the line, so a weak assertion is visible during review rather than buried in a job log. + +Suppressing a mutant stays possible but must be a reviewable claim: Stryker's disable comment carrying both +the mutator name and a reason. Blanket file-level disables are not permitted, and an ESLint rule enforces +that rather than leaving it to review discipline. + +## Acceptance criteria + +- [x] The job runs only on pull requests, diff-scoped against `origin/main`, and carries an explicit timeout. +- [x] It is its own job, chained on the unit-test job. It does not attempt to reuse that job's coverage + artifacts. +- [x] A surviving mutant on a changed line fails the job; no survivors passes it. +- [x] Each survivor produces a `::error file=…,line=…::` annotation naming the mutator, and the annotation + appears inline on the diff. +- [x] A no-coverage mutant alone does not fail the job — that is the CRAP gate's finding, not this one. +- [x] `// Stryker disable all` and any file-scoped disable are an ESLint error. +- [x] A disable comment missing either the mutator name or the `: reason` is an ESLint error; one carrying + both passes and does suppress the mutant. +- [x] The ESLint rule has unit coverage, or is verified against fixture files in both directions. +- [ ] `(human)` The first real PR run behaves: the job is ordered correctly, its wall time is acceptable, and + the annotations land on the right lines. **Deferred to task land** — the job cannot run until the PR + leaves draft, which is after T07. See `notes.md` for what was verified locally in its place. + +## Details + +**Job placement.** `.github/workflows/checks.yml`; `vitest_tests` is the job to chain on and the shape to +copy (checkout with `fetch-depth: 0` so the merge-base resolves, pnpm + node setup, frozen-lockfile install). + +**Why its own job (FR14).** Stryker must drive vitest itself, so the unit-test job's coverage artifacts are +useless here. It chains on that job because a red suite makes Stryker abort during its dry run with an error +that reads like a tooling failure. + +**Annotations** follow `tools/code-complexity/render/github.ts` — pure formatters returning the directive +strings, with the `process.env.GITHUB_ACTIONS` guard and the stdout writes in `index.ts`. Annotation messages +are single-line; newlines truncate the directive. + +**The ESLint rule** reads comments via `context.sourceCode.getAllComments()`. It was built inline in +`eslint.config.mjs` on the `spdxLicenseRule` precedent, then extracted to +`tools/mutation-testing/eslint/well-formed-disable.mjs` and imported from the config — it belongs to the +tool it enforces, and out there a co-located spec both tests it and makes it mutation-eligible itself. + +## Leaf worklist + +- [x] 1 `[agent]` Emit `::error` annotations for survivors and set a failing exit code; specs +- [x] 2 `[agent]` Add the CI job to `checks.yml`, chained on `vitest_tests`, with a timeout +- [x] 3 `[agent]` Add the inline ESLint rule for well-formed Stryker disable comments; specs +- [ ] 4 `[human]` Confirm the first real PR run — ordering, wall time, annotation placement + (deferred to task land, see the acceptance criteria) diff --git a/.agents/tasks/3665-mutation-testing/tickets/07-docs/spec.md b/.agents/tasks/3665-mutation-testing/tickets/07-docs/spec.md new file mode 100644 index 00000000000..b5670b5b01a --- /dev/null +++ b/.agents/tasks/3665-mutation-testing/tickets/07-docs/spec.md @@ -0,0 +1,46 @@ +# 07 — Document the tool + +| | | +| --- | --- | +| Parent spec | [`../../spec.md`](../../spec.md) → ticket 07 of #3665 | +| Blocked by | T06 | + +## What to build + +The documentation a developer needs when this gate fails on their PR and they have never seen it before: +what a surviving mutant means, why it is not the same complaint as a coverage miss, what to do about it, and +when suppressing one is legitimate. + +The complexity gate's docs are the model to follow — a `CONTEXT.md` that opens with "the gate failed, what +now" and a file map, plus a `docs/` for the detail. It is also the place to state what this tool deliberately +does not measure, so nobody rediscovers those boundaries by trying. + +## Acceptance criteria + +- [ ] `tools/mutation-testing/CONTEXT.md` exists, opens with how to reproduce a CI failure locally, explains + what a surviving mutant means as distinct from a coverage miss, and carries a file map. +- [ ] The exclusions are stated with their reasons: Playwright tests, non-logic mutators, files without a + co-located spec, `jsx` function bodies, and the absence of a whole-repo CI run. +- [ ] The suppression policy is documented — the disable-comment form that passes the ESLint rule, and what + makes a suppression a legitimate claim. +- [ ] `.claude/CLAUDE.md`'s per-directory context list has an entry for `tools/mutation-testing/`. +- [ ] `.agents/rules/code-quality.md` mentions the gate beside the complexity and CRAP sections, with the + commands to run it. +- [ ] Cross-references to `tools/code-complexity/` are accurate: the reuse of its selection and classification + code is described where it matters, without restating what those files already document. +- [ ] `pnpm lint:cspell` passes on the new files. + +## Details + +Follow the model of `tools/code-complexity/CONTEXT.md`: a "where to look" table, the failure-triage section +first, the file map last, with per-file detail left in each file's own header comment rather than duplicated. + +Reasoning that belongs in the docs rather than the spec: why the gating mutator set is the five logic classes +(sampling found roughly a third of all-mutator survivors unkillable by any vitest test, versus zero of the +logic-only ones), and that promoting or demoting a mutator class is a one-line config change. + +## Leaf worklist + +- [x] 1 `[agent]` Write `tools/mutation-testing/CONTEXT.md` +- [x] 2 `[agent]` Write the `docs/` detail pages +- [x] 3 `[agent]` Add the entries to `.claude/CLAUDE.md` and `.agents/rules/code-quality.md` diff --git a/.agents/tasks/3671-token-multiplier/spec.md b/.agents/tasks/3671-token-multiplier/spec.md new file mode 100644 index 00000000000..3616a1fb52e --- /dev/null +++ b/.agents/tasks/3671-token-multiplier/spec.md @@ -0,0 +1,135 @@ +# ERC-8056 token multiplier support + +| | | +| --- | --- | +| Issue | https://github.com/blockscout/frontend/issues/3671 | +| Feature branch | `issue-3671` | +| PM | Nikita S. | +| Designer | Tatyana | +| Backend | Alexey | +| Minimum API version | Core API v12.0.0+ | +| Slack channel | — | + +## Context & goal + +ERC-8056 is a token standard whose contract exposes a *UI multiplier*: the amount a holder should see is the +raw ERC-20 amount scaled by that factor (`uiAmount = rawAmount × uiMultiplier`). The multiplier changes over +time, each change announced by a `UIMultiplierUpdated` event carrying an activation timestamp, so a change +can be scheduled ahead of when it takes effect. + +The explorer currently renders these tokens as plain ERC-20s, showing raw amounts. A holder comparing the +explorer against their wallet sees different numbers for the same balance, with nothing explaining the +discrepancy. + +This task makes the explorer speak the token's own units: every displayed amount of an ERC-8056 token is +scaled, the raw value stays reachable in a tooltip, and a tag marks that scaling is in effect at all. + +## Functional requirements + +1. `ERC-8056` is a selectable token type wherever token types are filtered, and appears as the type tag on + token entities. +2. Every amount of an ERC-8056 token rendered anywhere in the app is the scaled amount, never the raw one — + the single exception is the amount tooltip, which discloses the raw value. +3. An amount of an ERC-8056 token carries a multiplier tag showing the factor in effect, shown even when the + factor is exactly 1. +4. The amount tooltip states the scaled value, the raw value, and the multiplier applied. +5. The token details page shows a Multiplier row with the currently effective factor and an explanatory + tooltip. +6. The token details page shows the most recent multiplier changes inline, capped at five, linking to the + full history when more exist. +7. A Multiplier history tab lists every recorded multiplier change with its transaction, block, old and new + factor, activation date, and whether it is the active one; the tab carries a count. +8. A token whose multiplier data is absent, or one on an instance where the type is not enabled, renders + exactly as an ERC-20 does today — no scaling, no tag, no tooltip, no Multiplier row. +9. Fiat values of ERC-8056 amounts are computed from the scaled amount. + +## Data & API + +Shipped in **Core API v12.0.0** (testing builds sit on top of 11.3.0). All fields are nullable; the +multiplier is a fixed-point integer with **18 decimals of precision**, independent of the token's own +`decimals`. + +**Token model** (`token`, wherever it is embedded): + +- `ui_multiplier` — the factor in effect at request time. The API already resolves a scheduled change, so + this is the effective value, not the raw stored one. +- `new_ui_multiplier` — the factor scheduled to replace it. +- `ui_multiplier_effective_at` — when that replacement takes effect. A change is *pending* exactly when this + is in the future. + +**Token transfers** — `total.ui_multiplier` carries the factor in force when the transfer happened. + +**State changes** — `ui_multiplier` sits alongside `balance_before` / `balance_after` / `change`. + +**Type filtering** — `?type=ERC-20` no longer returns ERC-8056 tokens; `?type=ERC-8056` selects them. + +**Supplies** — `total_supply` is raw and must be scaled by `token.ui_multiplier`. `circulating_supply` is +already in UI units and must not be scaled. `circulating_market_cap` is fiat, not a token quantity, and must +not be scaled either. + +**Exchange rate** — quoted per *UI* token, so fiat is `uiAmount × rate`. The existing USD path is correct +once it receives the scaled amount. + +**New resource** — the multiplier history endpoint `/tokens/{hash}/ui-multiplier-changes` needs a +`service:name` resource; its response shape is Q02. Its count arrives as `ui_multiplier_changes_count` in +`/tokens/{hash}/counters`. + +**Readiness** — the token fields, transfer `total.ui_multiplier`, state-change `ui_multiplier` and type +filtering are deployed and observable on a live instance. The history endpoint and the counter are not. + +**Live example** — https://eth-sepolia.blockscout.com/address/0x6D50E6CBca0e390BbCF82bEA80B31F4c2694395e + +**Env** — instances opt in through the existing `NEXT_PUBLIC_NETWORK_ADDITIONAL_TOKEN_TYPES` by listing +`ERC-8056`. No new env var. + +## UI inventory + +Parent frame: +[Token multiplier support #3671](https://www.figma.com/design/CEgxqWOzVulwfTUHhs0gUC/?node-id=6004-41185). + +| Surface | Change | Node | +| --- | --- | --- | +| Token transfers table | type filter option, multiplier tag, amount tooltip | [5995:28720](https://www.figma.com/design/CEgxqWOzVulwfTUHhs0gUC/?node-id=5995-28720) | +| Address → token transfers tab | same treatment | [5995:28716](https://www.figma.com/design/CEgxqWOzVulwfTUHhs0gUC/?node-id=5995-28716) | +| Token details → Details tab | Multiplier row, info tooltip, inline history (max 5) with link to the tab, scaled supply | [5995:20900](https://www.figma.com/design/CEgxqWOzVulwfTUHhs0gUC/?node-id=5995-20900) | +| Token details → Multiplier history tab | new tab, table + mobile list, tab counter | [5995:28735](https://www.figma.com/design/CEgxqWOzVulwfTUHhs0gUC/?node-id=5995-28735) | +| Advanced filter | Multiplier column, amount tooltip | [6004:40199](https://www.figma.com/design/CEgxqWOzVulwfTUHhs0gUC/?node-id=6004-40199) | + +Route for the new tab: `/token/{hash}?tab=multiplier_history`. + +Surfaces absent from the mockups but in scope, because they render an amount of the token and would +otherwise disagree with the ones that are: token holders (Quantity column), address token balances and net +worth, the address token-select dropdown, and transaction state changes. They reuse the amount treatment +above — no new elements. + +Search results need a fix of a different kind: they branch on `ERC-20` to choose between showing a price and +showing an NFT-style item count, so an ERC-8056 token currently renders as if it were an NFT collection. +Search carries no multiplier fields and displays no amount, so it needs the branch corrected, not scaling. + +## Implementation decisions + +- **The frontend scales; the API never does.** API v2 hands over raw values plus the multiplier. (Backend + scaling does exist, but only on the old-UI and notification-email paths.) +- **One predicate owns the decision to scale.** A single helper resolves a token to its multiplier or to + nothing, and every amount site multiplies by what it returns. It yields nothing unless the instance + enables `ERC-8056` *and* the response carries multiplier data. When it yields nothing the whole treatment + is suppressed together — scaling, tag, tooltip and Multiplier row — so a half-configured instance degrades + to today's ERC-20 rendering rather than to a mix of scaled and raw figures. +- **Multiplier formatting is one shared rule**, reused everywhere the factor appears: six decimals, no + trailing zeros, and the app's existing "below the smallest representable value" treatment underneath that. + This departs from the mockups, which render `1.00x`; the PM confirmed the trailing zeros carry no meaning. +- **Amount precision is unchanged.** Scaled amounts use the app's existing accuracy in the cell and full + precision in the tooltip; computation stays full-precision throughout, rounding only at render. +- **A transfer shows the multiplier that applied to it**, from `total.ui_multiplier`, not the token's current + factor — pending Q01 for what to do when that value is absent. +- **The Details Multiplier row and the history block are separable.** The row comes from the token model and + works with no history at all; the inline block and the tab come from the history endpoint. When history is + unavailable or empty, the row renders alone. + +## Out of scope + +- **B-20.** It carries a comparable multiplier interface but brings a much larger surface of its own; + supporting it is a separate task. +- **CSV exports.** Generated backend-side; scaling them is a backend change. +- **Reconstructing historical fiat.** Fiat follows from the scaled amount and the current rate. +- **Analytics events.** No custom Mixpanel events; tab views are covered by existing page-view tracking. diff --git a/.agents/tasks/3674-tools-scripts-rewrite/spec.md b/.agents/tasks/3674-tools-scripts-rewrite/spec.md new file mode 100644 index 00000000000..8e4f5101420 --- /dev/null +++ b/.agents/tasks/3674-tools-scripts-rewrite/spec.md @@ -0,0 +1,115 @@ +# Rewrite the .mjs scripts under tools/ in TypeScript + +| | | +| --- | --- | +| Issue | https://github.com/blockscout/frontend/issues/3674 | +| Feature branch | `issue-3674` | +| PM | — | +| Designer | — | +| Backend | — | +| Minimum API version | — | +| Slack channel | — | + +## Context & goal + +Three `.mjs` scripts under `tools/` are the only executable logic there that no gate can see. The +`.mjs` extension keeps them out of the root `tsconfig.json` `include`, so `pnpm lint:tsc` never +type-checks them; a `*.spec.mjs` would not be discovered by `vitest.config.ts` without widening +suite-wide test discovery, so they have no tests; and with no tests they report 0% coverage, which +makes the CRAP half of the complexity gate uninformative for exactly the highest-complexity tooling +code in the repo. + +Rewriting them in TypeScript brings all three under the type-check and the unit suite, and lets the +complexity gate report something meaningful. While `check-doc-links` is open it also takes a scope +extension: its check surface currently reaches only files named exactly `CONTEXT.md`, so a directory +documenting itself across several markdown files gets one of them checked and the relative +references *inside* the others can break silently. + +Blocker issue #3663 (the complexity gate, its caps, and its coverage of `tools/**`) is closed; the +gate is live and already measures `.mjs`. + +## Functional requirements + +1. `pnpm lint:doc-links`, `pnpm profile:analyze`, `pnpm presets:lint` and `pnpm presets:sync` behave + identically from a caller's point of view — same arguments, same output, same exit codes. +2. All three tools' sources are TypeScript and are type-checked by `pnpm lint:tsc`. +3. `pnpm test:code-complexity` passes on all three tools: every function is within the cognitive cap + for its class and within the CRAP cap. +4. Co-located `*.spec.ts` files exercise all three tools, so their reported coverage is real rather + than 0%. `vitest.config.ts` `include` is unchanged. +5. `pnpm lint:doc-links` checks every markdown file in and below any directory that contains a + `CONTEXT.md`, nested subdirectories of records included, and resolves their relative references. + `src/toolkit/package/` is exempt: it is the published npm package wrapper and its readme addresses + package consumers rather than agents. +6. Every reference in the extended surface resolves — including the two that do not today, in + `tools/code-complexity/docs/RUNNING.md` and `tools/mutation-testing/docs/RUNNING.md`. +7. Compiled JavaScript is git-ignored; no build artifact is committed. +8. `pnpm dev:preset`, `pnpm dev:local`, `pnpm prod:preset`, the Docker image build and the container + entrypoint are unaffected. + +## Data & API + +None. No endpoint, no `service:name` resource, no env var, no feature flag, no analytics event. + +## UI inventory + +None. No user-visible surface; developer tooling only. + +## Implementation decisions + +### Layout + +- **`tools/doc-links/`** — a new folder replacing `tools/scripts/check-doc-links.mjs`, flat files + split by concern: surface collection, line stripping, reference resolution, per-line checking, + reporting, entry. That checker's per-file check function is the task's one real decomposition + target, and the scope extension lands in its collection step. +- **`tools/dev-server/sync-presets/`** — a subfolder for the preset-list sync tool alone, named for + the job: `presets/` would read as though the registry lived in it. `registry.json` and + `envs-rules.json` stay at the `tools/dev-server/` root — the registry's other reader, the env + fetcher, is not moving, and its path is a contract in the Dockerfile, the container entrypoint, + several skills and the contributing guide. +- **`tools/profiling/`** keeps its shape; the aggregator stays one module. It breaks no cognitive + cap, so its lever is coverage, not decomposition. + +### Constraints + +Not free choices — the rest of how these tools are shaped internally is the implementing agent's +call, judged against the functional requirements and whatever the gate reports at the time: + +- **The preset sync tool needs its own `tsconfig.json`**, not the existing `tools/dev-server/` one: + giving that config an `outDir` relocates the env fetcher's emitted entry and breaks the + Dockerfile's `COPY` of it. +- **Each tool's CLI invocation lives in its own entry file**, not behind an entry-point guard inside + the module. +- **No committed real-world DevTools profile export.** Hand-built minimal fixtures instead: an + export pins the fixture to one DevTools version, and the wire format is what's under test. + +### Doc-link scope extension + +- Two references in the newly-covered surface don't resolve, both writing `./run.sh` from a `docs/` + subdirectory where the file sits one level up. Fix them as docs — not by resolving relative paths + against the `CONTEXT.md` directory, since `./` means "beside this file" everywhere else in the + instruction surface and special-casing it would weaken the check the extension exists to add. +- The `src/toolkit/package/` exemption (FR5) goes in the script's existing exclusion list as a path, + not as a `README.md` filename rule: `.agents/rules/docs.md` governs a module `README.md` as a doc + kind, so a filename rule would pre-emptively unprotect the files that rule expects to be checked. + +### Docs + +`tools/doc-links/` gets a `CONTEXT.md` — a file map, registered in the per-directory list in +`.agents/AGENTS.md`. Convention rationale stays in per-file header comments rather than moving into +it. Note the loop this closes: the tool then checks its own docs. + +`tools/code-complexity/select/scope.spec.ts` asserts scope membership using two of the `.mjs` paths +this task deletes. `.mjs` stays a legitimate in-scope extension so the assertions keep passing, but +swap the strings for names that exist. + +## Out of scope + +- **Moving the env fetcher** out of `tools/dev-server/` — tempting while restructuring its + neighbours, but its path is a contract in the Docker build and `COPY`, the container entrypoint, + four `deploy/tools/*` dev scripts, three `tools/scripts/*.sh` wrappers and a documented skill + command line. A separate issue if wanted at all. +- **Bringing `deploy/` under the complexity gate** — tracked as issue #3675. +- **Moving the caps** in the complexity gate's config. It is the cheap way to satisfy FR3 and it is + not available. diff --git a/.agents/tasks/3697-paginated-query-hook-refactor/spec.md b/.agents/tasks/3697-paginated-query-hook-refactor/spec.md new file mode 100644 index 00000000000..9b91bc28b4f --- /dev/null +++ b/.agents/tasks/3697-paginated-query-hook-refactor/spec.md @@ -0,0 +1,140 @@ +# Stop redundant requests and skeleton cycles in paginated list navigation + +| | | +| --- | --- | +| Issue | https://github.com/blockscout/frontend/issues/3697 | +| Feature branch | `issue-3697` | +| PM | Ulyana | +| Designer | Tatyana | +| Backend | — | +| Minimum API version | — | +| Slack channel | — | + +## Context & goal + +Every paginated list in the app (address transactions, token transfers, logs, blocks, and ~100 more +call sites) is driven by one shared hook, `useQueryWithPages`. An audit of that hook on the address +transactions tab found that it is not a render-cost hotspot itself, but its design produces +user-visible waste on every navigation: extra API requests, requests for page/filter combinations +the user never asked for, and skeleton passes over data that is already cached. + +The root cause is that page, cursor, filter and sort state live in three places at once — the URL, +the hook's own React state, and the calling component's state — and the hook writes the URL first +and then re-syncs its state from the URL through effects. Measured on the address transactions tab: + +| Interaction | API requests today | Should be | +| --- | --- | --- | +| "First" from page 3 | 3 (page 3 refetched, page 1 twice; page-3 skeleton flashes first) | 1 | +| Filter change while on page 3 | 2 (first one = old cursor + new filter) | 1 | +| "Prev" from page 2 to page 1 | 1, with skeleton, although page 1 is cached | 0 blocking (background refresh at most) | +| Unrelated `router.query` change | 0, but a full re-render of the list subtree | no re-render | + +The goal is to make the URL the single source of truth for list state, so that one user action is +exactly one URL push, one query key, one request and one render; to stop discarding cached pages; +to make the hook's return value stable so the memoization already present in list components and +tab containers actually holds. Replacing the skeleton pass on page changes with keeping the current +rows visible (dimmed) is deferred to #3704 pending design; this task ships its plumbing inert. + +## Functional requirements + +1. Each pagination action — next page, previous page, first page, filter change, sorting change — + issues exactly one API request, for the query the user asked for, and never a request for a + transient combination (for example the previous page's cursor combined with a new filter). +2. Navigating back to a page whose data is cached shows that data immediately, with no skeleton. + Page 1 may refresh in the background while its cached rows are displayed; it is never dropped + from the cache before being displayed. +3. Cache operations performed by the hook affect only the queries of the list it drives (that + resource with those path params), never other addresses, chains, or lists sharing the resource + name. +4. Page, cursor, filters, sorting and (on multichain instances) selected chain are read from the + URL on every render. The hook keeps no React state that mirrors a URL value, and no effect + re-syncs state from the URL. Reloading, sharing or navigating back to a URL restores the same + list state as today (page number, `next_page_params`, filter and sort params keep their current + names and encoding). +5. Callers no longer keep their own copy of filter or sort state; they read typed values from the + hook and call its change handlers. Tab switches, which strip list params from the URL, therefore + reset filters and sorting without caller-side bookkeeping. +6. The hook's return value, its `pagination` object and every callback it exposes are referentially + stable between renders when their inputs have not changed, so a memoized consumer does not + re-render on an unrelated route change or on a socket-driven cache update. +7. When the user moves to another page of the same list (same filters and sorting, different + cursor), skeleton rows are shown until the new page's data arrives, as today. The hook and the + shared list wrapper carry an `isTransitioning` flag / prop wired to every migrated list but never + set; the dimmed, non-interactive page-transition state is deferred to #3704 pending design. +8. Skeleton rows are still shown on first load of a list and after a filter or sorting change. The + lazy-render window of long lists resets on every dataset change as it does today. +9. All existing pagination behaviour that is not named above is preserved: the "next page" button + hides when the API returns no cursor, the "first" and "previous" buttons stay disabled on page 1, + scroll-to-top (or to the configured element) happens on every navigation, the socket-driven + "new items" notices keep prepending into page 1, and CSV export and count/label props keep + reading the same values. +10. The composed hook is renamed to `useApiPaginatedQuery` to sit next to `useApiQuery` and + `useApiInfiniteQuery`; the rename is applied to every call site. +11. The multichain-only capabilities (selected chain value and its change handler) are provided by + a separate opt-in hook; single-chain callers neither receive nor pay for them. +12. The existing unit test coverage for the hook is kept or extended, with a realistic router + stand-in that re-renders on push so that the number of requests and renders per action is + asserted, not only the final state. + +## Data & API + +None. No endpoints, resources, env vars or feature flags change. Pagination params (`page`, +`next_page_params`), filter and sorting query parameter names and encodings stay exactly as they are +so that existing links keep working. + +## UI inventory + +- Every paginated list page and tab in the app (the ~106 call sites of the current hook), with the + address details page transactions tab (single chain) as the reference integration: `RoutedTabs` + → tab content → sorting wrapper → list content → table/list rows → `Pagination`. +- No new screens and no new visual state. The **page-transition** state for the list body (current + rows dimmed and non-interactive) is deferred to #3704 (see `questions.md`, Q01); `DataList` + carries the `isTransitioning` prop for it, never set. +- Skeleton state stays as is for first load, page changes and filter/sort changes. + +## Implementation decisions + +- **URL is the single source of truth.** All list state is derived from `router.query` on each + render; the previous-page cursors needed for "previous" are kept in a ref keyed by page number, + populated on "next", and are the only in-memory list state. Handlers perform one shallow + `router.push` each and nothing else; there are no `.then` callbacks, no `isMounted` timers, no + router-sync effects. +- **Split into single-responsibility hooks, composed by a thin public hook.** `usePaginationParams` + reads page, cursor, filters and sorting from the URL (pure derivation, no state, no effects). + `usePaginationActions` returns stable next/prev/first/filter/sorting callbacks that read the + latest router through a ref. The composed `useApiPaginatedQuery` builds the query key, calls + `useApiQuery`, and returns a memoized object; it is what the vast majority of callers use with + the same call shape as today. `useChainValue` is the opt-in multichain piece. A caller that + needs only part (resource chosen from the filter, chain selection) composes the pieces itself. +- **No prefix-scoped cache removal.** The `removeQueries([resourceName])` calls go away. Page 1 keeps + `staleTime: 0` so it refreshes in the background on return; deeper pages keep `staleTime: + Infinity`. If a forced refresh is ever needed it targets the exact query key. +- **Placeholder strategy decided inside the hook.** `placeholderData` stays the caller's stub for + every query-key change (first load, page change, filter/sort change). The hook exposes two flags: + `isInitialLoading` (placeholder is the stub → rows render skeletons) and `isTransitioning`, always + `false` until #3704 flips the placeholder to the previous data on page changes. + `pagination.isLoading` stays true whenever a placeholder is shown, for button disabling. +- **Dimming is one global wrapper, not per-row.** The shared list wrapper (`DataList`) gains an + `isTransitioning` prop and applies opacity, `pointer-events: none` and `aria-busy` to the body + it already wraps, leaving the action bar outside. Row and cell components keep their `isLoading` + prop with its current meaning (skeleton). List content components wire two props: + `isLoading={ isInitialLoading }` to rows and `isTransitioning` to the wrapper. The prop is never + true in this task; #3704 styles and activates it. +- **Row keys no longer depend on loading state** for real data: the index suffix on keys applies + only while rendering the stub, so during a page transition the previous rows keep their keys and + the new rows replace them in a single commit. +- **Rename last, mechanically.** `useQueryWithPages` → `useApiPaginatedQuery` is a separate + codemod commit after the behavioural change, so the behavioural diff stays reviewable. +- **Migration is per call-site family.** The reference integration (address transactions tab) is + migrated first and reviewed; the remaining call sites follow in groups by shared list component + (transactions lists, token transfers, logs, blocks, and the long tail), each group removing its + duplicated filter/sort state. + +## Out of scope + +- Row-level render cost (cell components, `TxsTableItem` and siblings) and the lazy-render window + mechanics; those were addressed in the previous list-view optimization and are unchanged here. +- Changing pagination URL parameter names or encoding. +- The infinite-scroll hook (`useLazyLoadedList`) and its callers. +- Backend or API changes. +- Any redesign of the pagination controls themselves. diff --git a/.agents/tasks/3706-playwright-affected-tests-ci/spec.md b/.agents/tasks/3706-playwright-affected-tests-ci/spec.md new file mode 100644 index 00000000000..13c9bbf6111 --- /dev/null +++ b/.agents/tasks/3706-playwright-affected-tests-ci/spec.md @@ -0,0 +1,123 @@ +# Rework how Playwright component tests are selected and run in CI + +| | | +| --- | --- | +| Issue | https://github.com/blockscout/frontend/issues/3706 | +| Feature branch | `issue-3706` | +| PM | — (frontend tooling task) | +| Designer | — | +| Backend | — | +| Minimum API version | — | +| Slack channel | — | + +## Context & goal + +Playwright component tests (`*.pw.tsx`) run on every PR, scoped by a custom affected-tests tool that walks +the import graph with `dependency-tree`. The walker has known gaps (type-only imports count as dependencies, +the diff is taken against the base-branch tip rather than the merge-base, no forced full run when the test +harness or the lockfile changes), and the CI wiring wastes runner time: the three matrix jobs boot even when +the resolve job found nothing to run, the `default` project alone takes ~16 min while the other two take +4–5 min, and every matrix job spends ~3 min in redundant post-job cache saves plus a git-lfs install nothing +uses. The tooling is also split between `deploy/tools/affected-tests` (a path CI's `paths-ignore` excludes, +so changes to the tool never trigger checks) and `tools/scripts/pw*.sh`. + +The goal is one `pnpm test:pw` entrypoint shaped like the sibling gates (`test:code-complexity`, +`test:mutation-testing`), affected-test selection delegated to Playwright's own `--only-changed`, and a CI +layout that skips empty runs and balances the buckets — so an average feature-branch run finishes in a +fraction of today's wall time and a human or agent has one command to reach for. + +## Functional requirements + +1. A `tools/playwright/` tool, exposed as `pnpm test:pw`, is the single entrypoint for running Playwright + component tests locally, in Docker, and in CI. It follows the sibling tools' conventions: TypeScript + compiled on run via a `run.sh` wrapper, a `--help` usage text, co-located specs, and a `CONTEXT.md`. +2. Its own flags are `--changed[=]`, `--base `, `--docker`, `--docker-deps`, and `--help`. Every + other argument (`--project`, `--shard`, `-g`, `--update-snapshots`, `--clear-cache`, file paths, …) is + passed through to `playwright test` untouched. +3. `--changed` selects affected tests against the **merge-base** of the base ref (default `origin/main`) + and `HEAD`, including uncommitted working-tree edits — the same semantics as the sibling gates' + `--changed`. Playwright's bare "uncommitted vs HEAD" mode is not exposed. +4. Under `--changed`, if any changed file falls under a **force-full list** — `src/sprite/icons/**`, + `playwright/**`, `playwright-ct.config.ts`, `pnpm-lock.yaml` — the tool runs the whole suite. Otherwise + it runs Playwright with `--only-changed=`, so the bundler's real module graph decides + which test files are affected; type-only imports never count because the bundler erases them. +5. Before every run the tool regenerates `playwright/envs.js` from `playwright/.env.pw` and builds the SVG + sprite for the `pw` app env, as the current script does. The unconditional deletion of Playwright's build + cache stays in the tool but commented out, with the reason recorded next to it, so it can be flipped back + on if a stale build is ever observed. +6. `--docker` runs the same command inside the pinned Playwright image with the Linux `node_modules` + mounted; `--docker-deps` installs those Linux dependencies. The existing `test:pw:docker` and + `test:pw:docker:deps` package scripts remain as aliases of the flags. +7. Every run writes a JSON report alongside the existing reporter output (`blob` in CI, `html` locally) so + an agent can read outcomes and screenshot-diff paths without parsing the console reporter. +8. `deploy/tools/affected-tests` and `tools/scripts/pw*.sh` are removed; `package.json` scripts point at + the new tool. +9. In `checks.yml`, the affected-tests resolve job becomes a **no-install gate**: it computes the + merge-base and lists changed files under the watched paths (`src/`, `playwright/`, + `playwright-ct.config.ts`, `pnpm-lock.yaml`); the matrix jobs are skipped when the list is empty. The + gate's path list and the tool's force-full list are documented as a pair that must stay in sync. +10. The matrix is five jobs via `include`: `default` split into three shards, `mobile`, and + `dark-color-mode`. Each runs `pnpm test:pw --changed --project= [--shard=/3] + --pass-with-no-tests`, checks out with full history (the merge-base needs it), and uploads its blob + report as today; the report-merge job is unchanged. +11. The matrix jobs no longer install git-lfs or check out with LFS, and the duplicate dependency cache + (`cache: true` on the pnpm setup action, alongside `setup-node`'s pnpm cache) is dropped. +12. `.agents/rules/tests-visual.md` and the tool's `CONTEXT.md` document the new commands; a + module-scoped ADR under `tools/playwright/adr/` records why selection is delegated to Playwright rather + than a custom dependency walk. +13. ~~An upstream Playwright issue is filed for `--only-changed --list` reporting zero tests in + component-testing mode (list mode never sets up the CT plugin, so no component dependencies are + populated), and linked from the tool's `CONTEXT.md`.~~ **Dropped at ticket 05**: Playwright 1.63 + froze the experimental CT packages and the maintainers rule out source-to-test mapping in the + replacement stories model (microsoft/playwright#40691). Reproduction and draft kept in + `tickets/05-upstream-playwright-issue/notes.md`; the migration question is deferred, deliberately. + +## Data & API + +None. No endpoints, resources, env vars, or feature flags. + +## UI inventory + +None. This is CI/tooling only. + +## Implementation decisions + +- **Playwright's `--only-changed` replaces the custom walker.** Verified in this repo: with + `@playwright/experimental-ct-react` the CT plugin builds the Vite bundle, records each registered + component's module graph, and maps changed files to test files through it. Touching a type-only file + selected 0 tests; touching a leaf component selected only its 31 dependents; touching a theme recipe, a + toolkit component, or `TestApp` selected the full project, because `TestApp` is registered as a + component through the render fixture. The custom walker with `dependency-tree` could be patched + (`detective: { ts, tsx: { skipTypeImports: true } }` cuts ~20% of edges) but stays a parallel model of + what the bundler already knows. +- **Icons are the one blind spot, hence the force-full list.** SVG icons reach the browser as a sprite + built at run time, not through the module graph. `playwright/**` covers template files (`index.ts`, + `index.html`, `.env.pw`, fonts) that are not component dependencies; the config and the lockfile cover + build-shape and dependency changes. `package.json` needs no special handling: under `--frozen-lockfile` + any dependency change also changes `pnpm-lock.yaml`, while script/metadata edits do not. +- **Merge-base, not base-tip.** A two-dot diff against `origin/main` reports files that only changed on + main since the branch forked, over-selecting on stale branches. The tool resolves `git merge-base + HEAD` and passes that sha to Playwright, matching `tools/code-complexity`'s diff scoping. +- **The empty-run gate is plain shell in the workflow, not the tool.** The gate is one `git diff + --name-only -- `; running the TypeScript tool there would cost a dependency install + (~1.5 min serial) on every PR. The four paths are duplicated between the workflow step and the tool's + config and documented as such; if drift ever bites, the gate moves into the tool. +- **Sharding, not workers.** `default` runs every test not tagged `-@default` (~720 tests) versus ~150–180 + in the other two projects. Three shards bring it to the others' size deterministically; raising + `workers` above 1 is left for a later, measured change because the flake rate is already non-trivial. +- **Agent surface is the JSON report, not a dry-run.** Playwright's `--list` is the natural "which tests + would run" answer but is broken with `--only-changed` in CT mode. A bespoke dry-run would have to + drive Playwright internals and would break on upgrade; the JSON report is the chosen shape for now + (the upstream bug report was dropped, see requirement 13). +- **Full runs stay on pre-release and manual dispatch.** Two years of this cadence have surfaced few broken + tests on `main`; adding a push-to-main full run is deferred. + +## Out of scope + +- The vitest gate's `--changed=origin/main` selection: vitest already diffs `...HEAD` (merge-base + semantics), so it has no equivalent bug and needs no change. +- Raising Playwright `workers` in CI or tuning `retries`. +- A scheduled or push-to-main full Playwright run. +- Sharding `mobile` or `dark-color-mode`. +- A dry-run / "list affected tests" command. +- Any change to the tests themselves, screenshots, or the `playwright/` harness (fixtures, mocks, `TestApp`). diff --git a/.agents/tasks/3720-jev-review-screen/spec.md b/.agents/tasks/3720-jev-review-screen/spec.md new file mode 100644 index 00000000000..b54c6011d34 --- /dev/null +++ b/.agents/tasks/3720-jev-review-screen/spec.md @@ -0,0 +1,127 @@ +# Pilot Jev typed judgments as a fourth subagent in the agent code-review workflow + +| | | +| --- | --- | +| Issue | https://github.com/blockscout/frontend/issues/3720 | +| Feature branch | `issue-3720` | +| PM | — (frontend tooling task; owner: tom) | +| Designer | — | +| Backend | — | +| Minimum API version | — | +| Slack channel | — | + +## Context & goal + +The agent code review (`review-changes`) runs three LLM axes — spec, standards, correctness — each reading +the whole change blind. A fast typed-judgment model (TypeSafe's Jev) can score every rubric rule against +every touched file in well under a second per call, but it returns probabilities, not claims. The pilot +measures whether such a screen, verified by a subagent, finds things the axes miss — precision, unique +contribution, recall gaps, added latency — over about ten reviews, so the developer can decide keep or kill +from evidence rather than a demo. + +## Functional requirements + +1. A CLI tool under `tools/` screens a change and emits suspects as JSON. It resolves the base ref, the + touched files (untracked included) and the task spec from `--scope branch|uncommitted`, or takes them + explicitly via `--base ` and `--spec `; explicit inputs are never re-derived. It runs standalone + (`pnpm review:screen`) for rubric calibration, and the pilot dataset can be replayed from it. +2. **Standards grid.** For each rubric rule and each touched file matching the rule's glob, the tool scores + the probability of a breach with a yes/no (`noul`) judgment. The scored state is the file's changed hunks + plus surrounding context, each line prefixed with a line id. A cell over its threshold gets a second, + `choice` call over the changed line ids to locate the offending line. Hunks exceeding the model's state + limit are split into windows and the file takes the maximum score. +3. **Spec grid.** When a task spec exists, each Functional Requirement is scored per touched file for the + probability that the diff addresses it; the requirement's score is its maximum across files. A + requirement whose score falls below the spec threshold is a suspect. With no spec the grid is skipped and + the output says so. +4. **The rubric** is a hand-written file of 8–10 conventions that linters cannot catch. Each rule has an id, + the rule file it cites, a file glob, a question, and counterexamples expressed as the model's structured + criteria (`not_for` / `examples`). The rules are drafted from `.agents/rules/*.md`, `smells.md` and the + `CONTEXT.md` files and approved by the developer before implementation. +5. **Thresholds** live in the tool's config: one default for the standards grid with optional per-rule + overrides, a separate threshold for the spec grid, and a cap on the number of suspects passed on. Values + are calibrated during implementation against a few past diffs with known findings. +6. Each suspect carries a rule or requirement id, the file, the line (or `—` for a spec suspect) and the + score. The tool sets no severity and writes no claim or fix text. +7. **The key is the tool's concern only.** It reads `TYPESAFE_API_KEY` from the shell environment. With no + key, or on any API failure, it exits 0 with `status: skipped|failed` and a reason; no agent instruction + names the variable. +8. **Sidecar.** Every run of the screen writes one JSON file to `.ai/jev/` in the repository's **main + checkout** (resolved through the git common dir, so worktrees share one dataset), named by date, branch, + scope and ticket. It records every cell score (not only over-threshold ones), the suspects, the model + version, per-call timings and token usage. A later `--origins` invocation, given the review's final + findings table, records each suspect's fate (confirmed / dropped with reason / merged with an axis finding) + and each finding's origin (`axis`, `jev`, `both`) into the same file. +9. **Workflow integration.** In a `first` round, `review-changes` dispatches a fourth `general-purpose` + subagent — the `jev` axis — alongside the three blind axes, with the same inputs they get. Its brief in + `axes.md` is the only place the tool is mentioned. It runs the screen, then for each suspect opens the + location, reads the cited rule or requirement, and either writes a finding in the standard shape or drops + the suspect with a one-line reason. It returns every suspect exactly once — confirmed or dropped — plus the + sidecar path. It never looks beyond its suspects. +10. A confirmed Jev finding is labelled by the grid that produced it — `standards` or `spec`. Findings carry + no provenance marker; `resolve-review` and the `follow-up` round are unchanged. +11. Normalization (step 4) de-duplicates across all four sources as today; the orchestrator additionally + notes which sources raised each surviving finding. After publishing, it spawns a fresh subagent with the + `jev` brief, the sidecar path and the final table (id, axis, location, sources) to run `--origins`. +12. When the `jev` axis returns `skipped` or `failed`, or with zero suspects, the review proceeds on three + axes and the terminal report states the reason. A tool failure never fails a review. +13. The terminal report gains one line for the `jev` axis: suspects sent, confirmed, dropped, and the + origin split of the published findings. +14. `pnpm review:screen --report` reads every sidecar in `.ai/jev/` and prints one table: confirmed vs + dropped per rule, the `jev`-only / `axis`-only / `both` counts, and added seconds per review. Nothing is + computed during a review. +15. The integration works for `pr` and `md` outputs and for both `branch` and `uncommitted` scopes. +16. **Keep-or-kill** is the developer's judgment, informed by the `--report` table and their own reading of + the `jev`-only findings at Gate 1 / on the PR. No numeric gate is fixed. + +## Data & API + +- TypeSafe Jev: `POST https://api.typesafe.ai/v1/systemone`, bearer auth with `TYPESAFE_API_KEY`, via the + typed `@typesafe-ai/sdk` npm package (dev dependency, Node 20+, MIT). Primitives used: `noul` (breach + probability), `choice` (line location). Model pinned to `jev-1.13.0`; the response's `model` is recorded in + the sidecar. Limits that shape FR2: 64k tokens per request, 32k for state plus the longest question. +- No `NEXT_PUBLIC_*` env var, no runtime code, no API resource. + +## UI inventory + +None. This task ships no user-facing surface. + +## Implementation decisions + +- **Tool name `review-screen`**, script `pnpm review:screen`. The name describes the job, not the vendor; + `jev` remains the provenance value, the axis name and the sidecar folder. +- Follows the `tools//` layout of the complexity and mutation gates (`index.ts`, `config.ts`, `run.sh`, + `CONTEXT.md`), with flag parsing from `tools/cli`. +- **The SDK over raw `fetch`**: it ships its own typings and retries 429/529 with backoff. +- **Hunks, not whole files** (FR2): a review covers the diff, and whole-file scoring would flag breaches that + predate the change. +- **Spec grid is per file, ranked by max**, never one call over the whole diff — the state limit. +- **The tool lives inside the fourth subagent**, not the orchestrator: the orchestrator learns only "spawn + a `jev` axis" and "hand the final table to a `jev` subagent". Removing the pilot is deleting the tool, the + `jev` brief, and those two lines. +- **A fresh subagent for `--origins`** rather than resuming the finished `jev` one. It needs only the + sidecar path and the table, so nothing is lost, and it behaves identically on every agent host (Cursor's + parent-initiated resume is unverified; Claude Code's would need a `SendMessage` grant on `code-reviewer`). +- **Sidecar in the main checkout's gitignored `.ai/`** (already ignored) because task folders are pruned at + land, non-task branch reviews have no task folder, and a worktree's own files vanish with the worktree. +- **All cell scores are logged**, not just suspects, so thresholds can be re-tuned offline and recall gaps + measured without re-running. +- **`--origins` in the tool, not in the agent**: the tool matches finding locations to its confirmed suspects + mechanically; the subagent only forwards the table. +- **Cost is not a decision input**: at $0.042 per million input tokens a 10-rule × 20-file grid costs cents. +- **Prerequisite**: #3724 (per-finding word cap replacing the per-axis 400-word cap) lands before the first + pilot review, since it changes the axis baseline the pilot compares against. The `jev` axis has no total + word cap either — a suspect silently cut for length would skew the counts. + +## Out of scope + +- **Test-gap and general correctness screening.** The complexity and mutation gates cover test gaps; the + model gives no explanation to act on for correctness. +- **Running the screen on `follow-up` rounds** (regressions from fixes). Revisit after the pilot. +- **Accepted-on-resolve tracking** — writing findings' final verdicts back to the sidecar. The developer + judges `jev`-only findings directly. +- **Numeric keep-or-kill thresholds.** Ten reviews are too few for them to mean much. +- **A CI integration** posting comments on PRs. Deferred until precision is known: fork PRs have no + repository secrets, and unverified output on public PRs is noise. +- **Any change to `resolve-review`**, or to the existing axes' briefs beyond #3724. +- **Any provenance marker on findings** — in the PR, in `review.md`, or in their ids. diff --git a/.agents/tasks/3722-mobile-lists-to-tables/spec.md b/.agents/tasks/3722-mobile-lists-to-tables/spec.md new file mode 100644 index 00000000000..b3f10e21df8 --- /dev/null +++ b/.agents/tasks/3722-mobile-lists-to-tables/spec.md @@ -0,0 +1,125 @@ +# Replace the remaining mobile list views with table views and remove the list components + +| | | +| --- | --- | +| Issue | https://github.com/blockscout/frontend/issues/3722 | +| Feature branch | `issue-3722` | +| PM | Ulyana | +| Designer | Tatyana | +| Backend | — | +| Minimum API version | — | +| Slack channel | — | + +## Context & goal + +Almost every paginated view in the app ships two layouts: a table for `lg` and up, and a +purpose-built list of stacked label/value rows below it. The address page moved to table-on-mobile in +#3502 behind an A/B experiment (`txns_view_exp`, with a user-facing view toggle and a preference +cookie), and the list implementation was deliberately left in place as a rollback path. The +experiment has since gone stale — the toggle was never wired to a call site, `showTableView` is +hardcoded on the migrated views, and no user complaints followed. The decision to keep table view on +mobile is final. + +That leaves the codebase carrying two renderings of the same data on ~68 surfaces. Both branches are +mounted in the DOM on every page (the split is CSS `hideFrom` / `hideBelow`, not a conditional +render), so each one costs render work and markup on both viewports, and every new column has to be +added twice — which is how defects like #3702 (a tooltip present in the table view and missing from +the list view) appear. + +The goal is one rendering per view: mobile shows the same table as desktop, scrolled horizontally, +and every list container, list item and mobile-list primitive is deleted. + +## Functional requirements + +1. On mobile viewports, every migrated view renders the same table as desktop — same columns, same + order, no column dropped or hidden — inside a horizontally scrollable container. +2. Sorting on mobile is performed through the action-bar sort dropdown, not the table header. The + dropdown is present on every sortable view, including the transaction views where the experiment + suppressed it and the sortable views that never had one (bridged tokens, name domain history, + transaction internals). +3. Action bars, pagination, filters, CSV export links and socket "new items" notices keep their + current mobile behaviour and placement; only the rows below them change. +4. No list layout remains: no `*List` container, no `*ListItem` component, and neither of the shared + `ListItemMobile` / `ListItemMobileGrid` primitives, nor the mobile variant of the socket + new-items notice. +5. No experiment code remains: the view-toggle hook and button, the `showTableView` prop threading, + the `table_view_on_mobile` cookie and its `NAMES` entry, the `txns_view_exp` GrowthBook flag + declaration and its test mock, the `Txn view switch` Mixpanel event, and the `list_view` sprite + icon. +6. Existing Playwright coverage keeps its meaning: tests and cases that exercised a list-only + component are removed, and so is the mobile coverage of every migrated view — a `+@mobile` tag or + a mobile-only case whose job was the list layout is dropped together with its screenshots. A table + on a mobile viewport gets no screenshot of its own; the desktop one already covers it. + +## Data & API + +None. No endpoint, `service:name` resource, response type, env var or feature flag is added or +changed. The `txns_view_exp` GrowthBook flag declaration is removed from the client; PMs confirmed +the experiment and its analytics event are stale and safe to drop, so no data needs preserving and +no dashboard coordination is required. + +## UI inventory + +Affected surfaces, all of which already have a desktop table counterpart — the migration is the same +transform on each, no new table is designed: + +- **Core slices** — transactions index and its tabs, blocks index, tokens index, token transfers + (index, token page, transaction page), internal transactions (index, transaction and block tabs), + verified contracts, addresses index, search results, token holders, transaction state. +- **Rollup features** — Optimism (batches, deposits, withdrawals, output roots, dispute games), + Arbitrum (batches, messages, transaction withdrawals), Scroll (batches, deposits, withdrawals), + ZkSync batches, Shibarium deposits and withdrawals. +- **Chain variants** — beacon chain deposits and withdrawals (index and block tabs), Celo epochs, + TAC operations, ZetaChain CCTXs, the Zilliqa / Stability / Blackfort validator pages. +- **Multichain and cross-chain** — multichain accounts, contracts, internal transactions, token + transfers, user ops, ecosystems, address portfolio; cross-chain bridged tokens, ICTT users, and the + transaction-level cross-chain transfer tabs. +- **Remaining features** — name services (domains index, domain history, clusters leaderboard and + directory), hot contracts, DEX pools, flashblocks, user ops, blobs, FHE operations, transaction + authorizations, asset flows, tag search, interop messages, dapp approvals. +- **Account pages** — watchlist, API keys, private address and transaction tags, custom ABI, + verified addresses. +- **Home page** — the latest cross-chain transactions widget, the one surface whose desktop side is a + narrow headerless table inside a card. Converted in its own commit and reviewed by the designer on + a preview deployment; see `questions.md` Q01. + +No new screens, no new states, no mockups: the treatment is the one #3502 established. + +## Implementation decisions + +- **The per-view transform is fixed**: delete the mobile list branch, unwrap the desktop-only + wrapper box, wrap the table in `TableContainerScrollable`, and give the table root a `minW` chosen + from its content so the columns do not compress. Deviating from it needs a reason recorded on the + ticket. +- **Deletion happens with the migration, not after it.** Each ticket deletes the list containers and + list items it orphans, so no ticket leaves dead code behind for a later sweep. +- **The shared primitives die last.** `ListItemMobile`, `ListItemMobileGrid` and the socket notice's + mobile variant are removed in a final teardown ticket, once no consumer remains. +- **The experiment teardown goes first.** It is pure deletion, it blocks nothing, and it restores the + mobile sort dropdown that the experiment suppressed — so the sorting requirement is satisfied from + the first ticket rather than at the end. +- **Sorting stays a dropdown on mobile.** A sortable table header sits off-screen behind horizontal + scroll, so the header is not a usable mobile affordance. The shared `Sort` control is already + viewport-adaptive (icon button on mobile, labelled select on desktop) and is reused as is. +- **The home widget is isolated and reversible.** It is one commit of its own so that a "no" from the + designer is a single revert. Whichever way that decision goes, the list item it uses has its + container inlined so that the shared primitives are deleted regardless. +- **Verification is per ticket.** Each ticket carries a human acceptance criterion: open its routes at + 375px against the matching dev-server preset (`eth` for the core slices, `optimism` / `arbitrum` / + `scroll_sepolia` for rollups, `celo` / `zetachain` / `tac` for chain variants, `multichain` for the + multichain pages) and confirm the table scrolls, the action bar and pagination survive, and nothing + clips. +- **Screenshots travel with their code.** Each ticket deletes the mobile screenshots it orphans and + runs its remaining Playwright files through the Docker runner so they match CI; a desktop baseline + that does change is regenerated there and committed alongside the change. The branch is never + intentionally red between commits. +- **One PR, one commit per ticket**, so any single area can be reverted on its own. + +## Out of scope + +- Designing new tables or changing any existing table's columns, order or content. +- Card-based surfaces that have no table counterpart — the home page's latest blocks and + transactions widgets, and the marketplace dapp cards. +- Making table headers usable for sorting on mobile (sticky or pinned columns). +- Any change to desktop rendering. +- Backend or API changes. diff --git a/.agents/tasks/3754-posthog-analytics/spec.md b/.agents/tasks/3754-posthog-analytics/spec.md new file mode 100644 index 00000000000..65916f076f4 --- /dev/null +++ b/.agents/tasks/3754-posthog-analytics/spec.md @@ -0,0 +1,105 @@ +# Integrate PostHog for product analytics + +| | | +| --- | --- | +| Issue | https://github.com/blockscout/frontend/issues/3754 | +| Feature branch | `issue-3754` | +| PM | Ulyana | +| Designer | — | +| Backend | — | +| Minimum API version | — | +| Slack channel | — | + +## Context & goal + +Product analytics today goes to Mixpanel only, and the Mixpanel module *is* the app's analytics API: +every call site imports it directly, and page views, identity, person profiles, and logout reset are all +Mixpanel-specific. An instance operator who wants PostHog instead of (or alongside) Mixpanel has no +option. The goal is PostHog as a second, optional, env-configured provider that receives exactly the +same data Mixpanel receives — page views, custom events, button clicks, identity and person +properties — so dashboards in either tool answer the same questions. + +## Functional requirements + +1. PostHog is enabled by a `NEXT_PUBLIC_POSTHOG_API_KEY` env var; a companion + `NEXT_PUBLIC_POSTHOG_CONFIG_OVERRIDES` (JSON subset of the SDK config) can only be set when the key + is. Both are documented, validated, mocked and listed in `.env.example` like their Mixpanel + counterparts. +2. PostHog is subject to the same gating as Mixpanel: off in private mode, and off when Usercentrics + is configured and the user has not consented to the "PostHog" service. +3. Every event the app sends to Mixpanel — same event name, same property keys and values — is also + sent to PostHog when it is enabled, including the GrowthBook "Experiment started" event. Enabling + either provider alone, both, or neither works. +4. Page views reach PostHog as the SDK's native `$pageview` event, carrying the same properties the + Mixpanel "Page view" event carries (page type, tab, page, source, color mode, color theme), fired on + every route change by the app, not by the SDK's own pageview autocapture. +5. Identity and profile parity: the same uuid cookie is the distinct id; the same super-properties are + registered on every event; the same person properties are set / set-once; logout resets the + PostHog identity as it resets Mixpanel's. +6. SDK defaults when enabled: EU ingestion host, autocapture off, SDK pageview and pageleave capture + off, session recording off, feature flags disabled, `localStorage` persistence. All overridable via + the config-overrides env. +7. Events fired before the SDK chunk has loaded are buffered and replayed to every enabled provider + with their original timestamp; a provider whose SDK fails to load is silently disabled for the page + load without affecting the other. +8. The Content Security Policy allows PostHog hosts (script, connect, img) only when PostHog is + enabled and not in private mode. +9. The analytics debug switch is `_analytics_debug` (query param and cookie) and turns on SDK debug + logging for every enabled provider. The former `_mixpanel_debug` is removed, not aliased. +10. No analytics call site imports a vendor module; all go through a provider-neutral analytics + facade. Event names, payload types and the page-type dictionary keep their current shape. + +## Data & API + +No backend API involved. Environment variables (next release, `v2.12.0+`): + +- `NEXT_PUBLIC_POSTHOG_API_KEY` — PostHog project API key; presence enables the provider. +- `NEXT_PUBLIC_POSTHOG_CONFIG_OVERRIDES` — JSON subset of the `posthog-js` config merged over the + app defaults (FR6); requires the key. + +Third-party references: `posthog-js` config and API docs +(https://posthog.com/docs/libraries/js/config, https://posthog.com/docs/libraries/js/features), CSP +guidance (https://posthog.com/docs/advanced/content-security-policy). + +## UI inventory + +No user-facing UI. Affected surfaces are invisible: the app-level analytics init in the page wrapper, +CSP headers, and the Usercentrics consent service list. + +## Implementation decisions + +- **Provider-neutral facade under `src/shared/analytics/`** owns the public API every call site uses: + `logEvent`, `EventTypes` + payload types, `userProfile` (`set`, `setOnce`), `reset`, `useInit`, + `useLogPageView`, `getPageType`. It is cross-cutting with no vendor owner, hence `shared`. + `userProfile.increment` is dropped: no caller uses it and PostHog has no equivalent. +- **Providers are thin vendor wrappers under `src/services/`** — the existing Mixpanel module reduced + to one, a new PostHog module as the other — each implementing one common provider interface: + `init(options)` plus synchronous `track`, `identify`/`register`, `peopleSet`, `peopleSetOnce`, + `reset`. Each has its own `config.ts` (consent + private-mode gated, exposed via + `config.services.`), CSP policy, and Usercentrics service entry. +- **One buffer queue in the facade**, moved out of the Mixpanel module: one idle-deferred init loads + every enabled provider's SDK chunk, runs its setup (super-props, identify, profile writes), then + replays the buffer into each. Replay backdates events: Mixpanel via the `time` property, PostHog via + the capture `timestamp` option. A provider whose init throws is disabled individually (FR7). +- **Page views** — the facade's `useLogPageView` emits one logical event; the PostHog provider maps the + "Page view" name to `$pageview` and passes the properties through unchanged. All other events keep + their name verbatim. +- **Call-site migration** is mechanical: every `import * as mixpanel from 'src/services/mixpanel'` + becomes the facade import, usages renamed accordingly; done with `ast-grep` in one pass. Tests + mocking the Mixpanel module move to mocking the facade or the provider as appropriate. +- **Debug flag** — the Mixpanel-specific query param and cookie are renamed to `_analytics_debug` in + place (dev-only switch, no backward compatibility). +- **GrowthBook** keeps calling the facade, so experiment events fan out to both providers for free. +- **CSP** — a `posthog` policy adding `*.posthog.com` to `script-src`, `connect-src` and `img-src` + (PostHog's own guidance: wildcard, since lazy-loaded bundles come from a separate assets subdomain). +- **Consent** — new `posthog` entry named "PostHog" in the Usercentrics services list; the CMP-side + ruleset must list the same service name (Q01). + +## Out of scope + +- PostHog session recording, surveys, feature flags, toolbar, or using PostHog as a GrowthBook + replacement — all explicitly disabled by default. +- Removing or deprecating Mixpanel. +- A single "analytics provider" switch env; providers are enabled independently by their own keys. +- Rolling the key out to deployed instances (operator / DevOps task after release). +- Google Analytics is untouched: it is a script tag with no event API and stays outside the facade. diff --git a/.agents/tasks/README.md b/.agents/tasks/README.md index 86b9e0cb629..c5d0229d4e9 100644 --- a/.agents/tasks/README.md +++ b/.agents/tasks/README.md @@ -34,8 +34,9 @@ Each step names the skill that runs it; the session model — what runs where, a answer changes the work, realise it through `to-tickets` — a new sibling ticket, or an edit to the affected ticket if it isn't implemented yet, retargeting `Blocked by` edges. 5. **Implement** — run `implement-ticket ` repeatedly, **one ticket per run**. -6. **Land** — `finalize-task` prunes `tickets/`, `progress.md`, and `questions.md` (only `spec.md` survives), - then hands off to `create-pr` to push, write the real description, and flip the draft to ready for review. +6. **Land** — `finalize-task` prunes `tickets/`, `progress.md`, `questions.md` and any `md`-mode review file + (only `spec.md` survives), then hands off to `create-pr` to push, write the real description, and flip + the draft to ready for review. ## Supporting files diff --git a/.agents/tasks/concepts.md b/.agents/tasks/concepts.md index 852777d4c69..d419df02411 100644 --- a/.agents/tasks/concepts.md +++ b/.agents/tasks/concepts.md @@ -117,11 +117,12 @@ as a whole. ### Disposability and pruning -At land, `finalize-task` prunes `tickets/`, `progress.md`, and `questions.md` — **only `spec.md` survives** -in the tree. The decomposition is preserved in git history (one commit per ticket), so nothing is lost: -precedent to browse is the accumulated specs; decomposition precedent is git history. Pruning runs -**before** the whole-task `review-changes` pass, which reads the spec and the diff via inline PR comments -and so needs no ticket files. +At land, `finalize-task` prunes `tickets/`, `progress.md`, `questions.md` and any review file — **only +`spec.md` survives** in the tree. The decomposition is preserved in git history (one commit per ticket), so +nothing is lost: precedent to browse is the accumulated specs; decomposition precedent is git history. +Pruning runs **before** the whole-task `review-changes` pass, which reads the spec and the diff and posts to +the PR, so it needs no ticket files. A mid-ticket review in `md` mode is disposable for the same reason: +its findings were resolved before the ticket was committed, and the diff is the record. ## What the spec holds, and what it doesn't diff --git a/.agents/tasks/structure.md b/.agents/tasks/structure.md index 70a2ef384cd..bd28b482699 100644 --- a/.agents/tasks/structure.md +++ b/.agents/tasks/structure.md @@ -13,12 +13,14 @@ One folder per specced task, under `.agents/tasks/`: spec.md task spec — the durable statement of intent progress.md one checkbox per ticket questions.md open questions, each with a stable id (Q01) + review.md optional — an md-mode review of the whole branch tickets/ NN-/ spec.md the ticket (once scoped) brief.md OR this, for a deferred ticket (no spec.md) research.md optional — research / prototype notes notes.md optional — implementation findings, PR evidence + review.md optional — an md-mode review of this ticket's uncommitted work ``` **Naming is mechanical.** The task folder is `-/` — the bare GitHub issue number, then a @@ -36,6 +38,7 @@ lets a skill infer the task from the branch with no arguments. | `tickets/NN-/spec.md` | mutable until fully implemented, then **frozen** | What to build, Acceptance criteria (with `(human)` tags), Skill inputs (grouped by skill), Leaf worklist. Header: `Blocked by` (`T` ticket + `Q` question blockers). | `to-tickets` creates it; `implement-ticket` checks its leaf boxes as it works them. | | `tickets/NN-/brief.md` | informal | Deferred-ticket marker. Goal, known context, the blocking unknowns and who owns each. | `to-tickets` when it can't scope the ticket; or dropped in by the developer. | | `tickets/NN-/{research,notes}.md` | optional | Research / prototype notes; implementation findings kept as PR evidence. | session / developer. | +| `review.md` (task or ticket level) | mutable | An `md`-mode review: one thread per finding — claim, fix, status, replies. `review-.md` where several reviewers ran at once. | `review-changes` in `md` mode writes it; `resolve-review` appends the replies. Pruned at land like the rest. | ## Status is derived, never stored diff --git a/.claude/agents/code-reviewer.md b/.claude/agents/code-reviewer.md index 8dc515be15e..b2403172b2b 100644 --- a/.claude/agents/code-reviewer.md +++ b/.claude/agents/code-reviewer.md @@ -1,6 +1,6 @@ --- name: code-reviewer -description: Reviews a change on three axes (spec, standards, correctness) by running the repo's review-changes skill, and returns the outcome. Invoked manually (e.g. to review a task at land, or ad hoc); not a general-purpose reviewer. +description: Reviews a change axis by axis by running the repo's review-changes skill, and returns the outcome. Invoked manually (e.g. to review a task at land, or ad hoc); not a general-purpose reviewer. model: inherit effort: high color: cyan @@ -10,11 +10,12 @@ tools: Read, Glob, Grep, Bash, Write, Edit, Agent Read `.agents/skills/review-changes/SKILL.md` and follow it. That file is the procedure; this definition only launches it. -You are the orchestrator described there: you spawn the axis agents, normalize what they return, and post -the findings as inline PR comments (or report them in chat when there is no PR). You never edit source code, -and you write no review record — the findings live on the PR. +You are the orchestrator described there: you spawn the axis agents, normalize what they return, and +publish the findings. Pass on the inputs you were dispatched with — the output (`pr` or `md`), and any +`--round`, `--scope`, `--as` or `--ticket`. A dispatch that names no output stops at the skill's input +check; ask for one rather than picking. You never edit source code. -Your final text is a **return value**, not a message to a person. Return exactly: the PR review URL (or, in -chat mode, that the findings were reported there), counts per severity, counts per axis, and the `Outcome`. -Whoever dispatched you gates on that `Outcome`: `clear` when no `blocker` or `major` finding is open, -otherwise the open counts. +Your final text is a **return value**, not a message to a person. Return exactly: where the review was +published (the PR review URL, or the review file's path), counts per severity, counts per axis, the `jev` +line, and the `Outcome`. Whoever dispatched you gates on that `Outcome`: `cleared` when no `blocker` or `major` finding +is open, otherwise the open counts. diff --git a/.claude/hooks/worktree-deps.sh b/.claude/hooks/worktree-deps.sh index cc9689b1b1d..b423fedced9 100755 --- a/.claude/hooks/worktree-deps.sh +++ b/.claude/hooks/worktree-deps.sh @@ -44,7 +44,7 @@ if [ "$mode" = "session-start" ]; then jq -n '{ hookSpecificOutput: { hookEventName: "SessionStart", - additionalContext: "This worktree has no node_modules. A PreToolUse hook runs `pnpm install --frozen-lockfile` automatically before the first Bash command that needs the local toolchain (pnpm/npx/eslint/tsc/vitest/playwright/next), so do not install by hand. Expect that one command to take an extra minute." + additionalContext: "This worktree has no node_modules. A PreToolUse hook runs `pnpm install --frozen-lockfile` automatically before the first Bash command that needs the local toolchain (pnpm/npx/eslint/tsc/vitest/playwright/next, or a `git commit`/`git push` that runs husky hooks), so do not install by hand. Expect that one command to take an extra minute." } }' exit 0 @@ -55,7 +55,10 @@ fi cmd="$(field '.tool_input.command // empty')" [ -n "$cmd" ] || exit 0 -needs='(^|[;&|(]|&&|\|\|)[[:space:]]*(pnpm|npx|next|eslint|tsc|vitest|playwright)[[:space:]]|node_modules/\.bin/' +# `git commit`/`git push` are here because the husky pre-commit and pre-push hooks shell +# into node_modules (lint-staged, eslint, tsc); without deps they fail and tempt a +# --no-verify bypass. `[^;&|]*` lets global flags like `git -C dir commit` still match. +needs='(^|[;&|(]|&&|\|\|)[[:space:]]*(pnpm|npx|next|eslint|tsc|vitest|playwright)[[:space:]]|node_modules/\.bin/|(^|[;&|(]|&&|\|\|)[[:space:]]*git[[:space:]][^;&|]*(commit|push)([[:space:]]|$)' # Dependency-management subcommands run fine without node_modules, and skipping them # keeps the hook from recursing into the install it is about to perform. selfmanaged='pnpm[[:space:]]+(install|i|add|remove|rm|up|update|dlx|store|why|licenses|approve-builds|rebuild)([[:space:]]|$)' diff --git a/.claude/settings.json b/.claude/settings.json index ce1c24730f0..0f501cc7ccb 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -6,6 +6,11 @@ "SessionStart": [ { "hooks": [ + { + "type": "command", + "command": "git -C \"$CLAUDE_PROJECT_DIR\" fetch --quiet origin 2>/dev/null || true", + "timeout": 30 + }, { "type": "command", "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/worktree-deps.sh session-start", diff --git a/.cursor/BUGBOT.md b/.cursor/BUGBOT.md deleted file mode 100644 index 2c684ec0fe8..00000000000 --- a/.cursor/BUGBOT.md +++ /dev/null @@ -1,39 +0,0 @@ -# Bugbot PR Review Rules - -In addition to the regular bug-finding process, check the following aspects in the code changes introduced by the Pull Request. - -## 1. Design System & Theming - -See `.agents/rules/design-system.md`. - -## 2. TypeScript - -See `.agents/rules/typescript.md`. - -## 3. Code Quality - -See `.agents/rules/code-quality.md`. - -## 4. Environment Variables - -See `.agents/rules/env-vars.md`. - -## 5. Testing - -- Unit tests: See `.agents/rules/tests-unit.md`. -- Visual component tests: See `.agents/rules/tests-visual.md`. - -## 6. Code smells - -See `.agents/skills/review-changes/smells.md` — thirteen smells, each a judgement call, each overridden by -anything the rules files above document. - -When the change touches the agent instruction surface instead of code — `.agents/`, `AGENTS.md`, any -`CONTEXT.md`, `.cursor/` — use `.agents/skills/review-changes/prose-smells.md` in its place. Those files are -code that runs on an agent, and none of the thirteen above apply to them. - -## 7. Not findings - -See the **Out of bounds** section of `.agents/skills/review-changes/SKILL.md`. Most importantly: styling and -visual judgements belong to a human, a `TODO (design):` marker is a scaffold working as designed, and a style -preference with no rule or precedent behind it is not a finding. diff --git a/.cursor/mcp.json b/.cursor/mcp.json index e5f807b0ce1..9667a1ac9f9 100644 --- a/.cursor/mcp.json +++ b/.cursor/mcp.json @@ -13,6 +13,15 @@ "-y", "next-devtools-mcp@latest" ] + }, + "playwright": { + "command": "npx", + "args": [ + "-y", + "@playwright/mcp@latest", + "--headless", + "--isolated" + ] } } -} \ No newline at end of file +} diff --git a/.cursor/rules/adr.mdc b/.cursor/rules/adr.mdc new file mode 120000 index 00000000000..2ea80472b00 --- /dev/null +++ b/.cursor/rules/adr.mdc @@ -0,0 +1 @@ +../../.agents/rules/adr.md \ No newline at end of file diff --git a/.cursor/rules/docs.mdc b/.cursor/rules/docs.mdc new file mode 120000 index 00000000000..dbddf32213f --- /dev/null +++ b/.cursor/rules/docs.mdc @@ -0,0 +1 @@ +../../.agents/rules/docs.md \ No newline at end of file diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index cc48cec34ce..3482f005eeb 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,6 +1,6 @@ { "name": "blockscout dev", - "image": "mcr.microsoft.com/devcontainers/typescript-node:20", + "image": "mcr.microsoft.com/devcontainers/typescript-node:24", "forwardPorts": [ 3000 ], "customizations": { "vscode": { diff --git a/.dockerignore b/.dockerignore index fd4ab7d0bc5..42549158fba 100644 --- a/.dockerignore +++ b/.dockerignore @@ -9,6 +9,6 @@ README.md .git *.tsbuildinfo .eslintcache -/test-results/ +/playwright-results/ /playwright-report/ /blob-report/ \ No newline at end of file diff --git a/.env.example b/.env.example index 427c9f69b48..de2797ca1f8 100644 --- a/.env.example +++ b/.env.example @@ -3,5 +3,6 @@ NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID=xxx NEXT_PUBLIC_RE_CAPTCHA_APP_SITE_KEY=xxx NEXT_PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID=UA-XXXXXX-X NEXT_PUBLIC_MIXPANEL_PROJECT_TOKEN=xxx +NEXT_PUBLIC_POSTHOG_API_KEY=xxx NEXT_PUBLIC_GROWTH_BOOK_CLIENT_KEY=xxx NEXT_PUBLIC_ACCOUNT_DYNAMIC_ENVIRONMENT_ID=xxx \ No newline at end of file diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 3f5c22acd12..978abfec600 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -13,7 +13,6 @@ on: - 'deploy/**' - 'docs/**' - 'public/**' - - 'tools/**' # concurrency: # group: ${{ github.workflow }}__${{ github.job }}__${{ github.ref }} @@ -42,13 +41,13 @@ jobs: - name: Install pnpm uses: pnpm/action-setup@v6 with: - version: 11.5.1 + version: 12.4.1 cache: true - name: Setup node uses: actions/setup-node@v7 with: - node-version: 22.14.0 + node-version: 24.21.0 cache: 'pnpm' - name: Install dependencies @@ -84,13 +83,13 @@ jobs: - name: Install pnpm uses: pnpm/action-setup@v6 with: - version: 11.5.1 + version: 12.4.1 cache: true - name: Setup node uses: actions/setup-node@v7 with: - node-version: 22.14.0 + node-version: 24.21.0 cache: 'pnpm' - name: Install project dependencies @@ -132,13 +131,13 @@ jobs: - name: Install pnpm uses: pnpm/action-setup@v6 with: - version: 11.5.1 + version: 12.4.1 cache: true - name: Setup node uses: actions/setup-node@v7 with: - node-version: 22.14.0 + node-version: 24.21.0 cache: 'pnpm' - name: Install dependencies @@ -160,65 +159,111 @@ jobs: - name: Install pnpm uses: pnpm/action-setup@v6 with: - version: 11.5.1 + version: 12.4.1 cache: true - name: Setup node uses: actions/setup-node@v7 with: - node-version: 22.14.0 + node-version: 24.21.0 cache: 'pnpm' - name: Install dependencies run: pnpm install --frozen-lockfile - name: Run Vitest - run: pnpm test:vitest ${{ github.event_name == 'pull_request' && '--changed=origin/main' || '' }} --passWithNoTests + run: pnpm test:vitest ${{ github.event_name == 'pull_request' && '--changed=origin/main --coverage --coverage.provider=v8 --coverage.reporter=json' || '' }} --passWithNoTests - pw_affected_tests: - name: Resolve affected Playwright tests + - name: Cognitive complexity & CRAP gate + if: ${{ github.event_name == 'pull_request' }} + run: pnpm test:code-complexity --changed=origin/main --coverage-file coverage/coverage-final.json + + - name: React Doctor gate + if: ${{ github.event_name == 'pull_request' }} + run: pnpm lint:react-doctor --scope changed --base origin/main --no-color + + mutation_tests: + name: Mutation testing gate + needs: [ vitest_tests ] runs-on: ubuntu-latest - needs: [ code_quality, envs_validation ] if: github.event_name == 'pull_request' + # Outside the run's own 15-min wall-clock budget (tools/mutation-testing/config.ts), leaving room + # for checkout and install so that expiring the budget still reaches the truncation report rather + # than being killed mid-write. A normal PR gets nowhere near either bound. + timeout-minutes: 20 steps: - name: Checkout repo uses: actions/checkout@v7 with: + # The diff is resolved through the merge-base with origin/main, which a shallow clone lacks. fetch-depth: 0 - name: Install pnpm uses: pnpm/action-setup@v6 with: - version: 11.5.1 + version: 12.4.1 cache: true - name: Setup node uses: actions/setup-node@v7 with: - node-version: 22.14.0 + node-version: 24.21.0 cache: 'pnpm' - name: Install dependencies run: pnpm install --frozen-lockfile - - name: Run script - run: pnpm test:pw:detect-affected + - name: Mutation testing gate + run: pnpm test:mutation-testing --changed=origin/main +<<<<<<< HEAD - name: Upload result file uses: actions/upload-artifact@v7 +======= + pw_changes: + name: Check for Playwright-relevant changes + runs-on: ubuntu-latest + needs: [ code_quality, envs_validation ] + if: github.event_name == 'pull_request' + outputs: + has_changes: ${{ steps.diff.outputs.has_changes }} + steps: + - name: Checkout repo + uses: actions/checkout@v4 +>>>>>>> v2.12.0 with: - name: playwright-affected-tests - path: ./playwright/affected-tests.txt - retention-days: 3 + # The diff is resolved through the merge-base with origin/main, which a shallow clone lacks. + fetch-depth: 0 + + - name: Diff watched paths against the merge-base + id: diff + # No dependency install here on purpose: the gate is one git command, so a PR that touches + # nothing Playwright cares about skips the whole matrix in seconds. + # The path list is paired with FORCE_FULL_PATHS in tools/playwright/config.ts (the tool's + # "run the whole suite" triggers); it is a superset of that list plus src/, where the tests + # and everything they import live. Keep the two in sync. + run: | + base=$(git merge-base origin/main HEAD) + changed=$(git diff --name-only "$base" -- src playwright playwright-ct.config.ts pnpm-lock.yaml) + if [ -n "$changed" ]; then + echo "Changed files under the watched paths:" + echo "$changed" + echo "has_changes=true" >> "$GITHUB_OUTPUT" + else + echo "No changes under the watched paths; the Playwright matrix will be skipped." + echo "has_changes=false" >> "$GITHUB_OUTPUT" + fi pw_tests: - name: 'Playwright tests / Project: ${{ matrix.project }}' - needs: [ code_quality, envs_validation, pw_affected_tests ] + name: 'Playwright tests / ${{ matrix.name }}' + needs: [ code_quality, envs_validation, pw_changes ] + # The gate only runs on pull requests; a skipped gate (workflow_call from pre-release, manual + # dispatch) means a full run. if: | always() && needs.code_quality.result == 'success' && needs.envs_validation.result == 'success' && - (needs.pw_affected_tests.result == 'success' || needs.pw_affected_tests.result == 'skipped') + (needs.pw_changes.result == 'skipped' || (needs.pw_changes.result == 'success' && needs.pw_changes.outputs.has_changes == 'true')) runs-on: ubuntu-latest container: image: mcr.microsoft.com/playwright:v1.57.0-noble @@ -226,32 +271,43 @@ jobs: strategy: fail-fast: false matrix: - project: [ default, mobile, dark-color-mode ] + include: + - name: default-1 + project: default + shard: 1/3 + - name: default-2 + project: default + shard: 2/3 + - name: default-3 + project: default + shard: 3/3 + - name: mobile + project: mobile + - name: dark-color-mode + project: dark-color-mode steps: - - name: Install git-lfs - run: apt-get update && apt-get install git-lfs - - name: Checkout repo uses: actions/checkout@v7 with: - lfs: 'true' + # --changed resolves the merge-base with origin/main, which a shallow clone lacks. + fetch-depth: 0 - name: Install pnpm uses: pnpm/action-setup@v6 with: - version: 11.5.1 - cache: true + version: 12.4.1 - name: Setup node uses: actions/setup-node@v7 with: - node-version: 22.14.0 + node-version: 24.21.0 cache: 'pnpm' - name: Install dependencies run: pnpm install --frozen-lockfile +<<<<<<< HEAD - name: Download affected tests list if: ${{ needs.pw_affected_tests.result == 'success' }} uses: actions/download-artifact@v8 @@ -259,18 +315,26 @@ jobs: with: name: playwright-affected-tests path: ./playwright +======= + - name: Trust the checkout inside the container + # actions/checkout registers the workspace as a safe.directory in the global git config of the + # HOME it ran under (/github/home); the run step below sets HOME=/root, where git finds no such + # entry and refuses the repo ("dubious ownership") when --changed resolves the merge-base. + run: git config --global --add safe.directory "$GITHUB_WORKSPACE" + env: + HOME: /root +>>>>>>> v2.12.0 - name: Run PlayWright - run: pnpm test:pw:ci --affected=${{ github.event_name == 'pull_request' }} --pass-with-no-tests + run: pnpm test:pw ${{ github.event_name == 'pull_request' && '--changed' || '' }} --project=${{ matrix.project }} ${{ matrix.shard && format('--shard={0}', matrix.shard) || '' }} --pass-with-no-tests env: HOME: /root - PW_PROJECT: ${{ matrix.project }} - name: Upload blob report to GitHub Actions Artifacts if: ${{ !cancelled() }} uses: actions/upload-artifact@v7 with: - name: blob-report-${{ matrix.project }} + name: blob-report-${{ matrix.name }} path: blob-report retention-days: 1 @@ -307,14 +371,14 @@ jobs: if: steps.check-reports.outputs.has_reports == 'true' uses: pnpm/action-setup@v6 with: - version: 11.5.1 + version: 12.4.1 cache: true - name: Setup node if: steps.check-reports.outputs.has_reports == 'true' uses: actions/setup-node@v7 with: - node-version: 22.14.0 + node-version: 24.21.0 cache: 'pnpm' - name: Install dependencies @@ -337,4 +401,13 @@ jobs: if: steps.check-reports.outputs.has_reports == 'true' run: | echo "### Playwright report" >> $GITHUB_STEP_SUMMARY +<<<<<<< HEAD echo "Download the **playwright-html-report** artifact below to view the report (open \`index.html\` in a browser)." >> $GITHUB_STEP_SUMMARY +======= + if [ -n "${{ env.PLAYWRIGHT_URL }}" ]; then + echo "[View report (S3)](${{ env.PLAYWRIGHT_URL }})" >> $GITHUB_STEP_SUMMARY + else + echo "Download the **playwright-html-report** artifact below to view the report (open \`index.html\` in a browser)." >> $GITHUB_STEP_SUMMARY + fi + echo "[Read about ducks](https://animalkingdom.org/animals/ducks/)" >> $GITHUB_STEP_SUMMARY +>>>>>>> v2.12.0 diff --git a/.github/workflows/copilot-setup-steps.yml b/.github/workflows/copilot-setup-steps.yml index c161233a8e0..6f7c8dc678f 100644 --- a/.github/workflows/copilot-setup-steps.yml +++ b/.github/workflows/copilot-setup-steps.yml @@ -23,12 +23,12 @@ jobs: - name: Install pnpm uses: pnpm/action-setup@v6 with: - version: 11.5.1 + version: 12.4.1 - name: Setup node uses: actions/setup-node@v7 with: - node-version: 22.14.0 + node-version: 24.21.0 cache: 'pnpm' - name: Install dependencies diff --git a/.github/workflows/toolkit-npm-publisher.yml b/.github/workflows/toolkit-npm-publisher.yml new file mode 100644 index 00000000000..0c6a56ef5e9 --- /dev/null +++ b/.github/workflows/toolkit-npm-publisher.yml @@ -0,0 +1,68 @@ +name: Publish toolkit package to NPM + +on: + workflow_dispatch: + inputs: + version: + description: Package version + type: string + required: true + workflow_call: + inputs: + version: + description: Package version + type: string + required: true + +jobs: + publish: + runs-on: ubuntu-latest + name: Publish package to NPM registry + permissions: + id-token: write + contents: read + + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Install pnpm + uses: pnpm/action-setup@v6 + with: + version: 12.4.1 + + # Also it will setup .npmrc file to publish to npm + - name: Setup node + uses: actions/setup-node@v4 + with: + node-version: '24.21.0' + registry-url: 'https://registry.npmjs.org' + cache: 'pnpm' + + # Ensure npm 12.4.1 is installed + - name: Update npm + run: npm install -g npm@12.4.1 + + - name: Update package version + run: | + cd ./src/toolkit/package + npm version ${{ inputs.version }} + + - name: Install dependencies and build the package + run: | + pnpm install --frozen-lockfile + pnpm --filter @blockscout/ui-toolkit build + + - name: Publish to NPM registry + run: | + cd ./src/toolkit/package + # Determine npm tag based on version + if [[ "${{ inputs.version }}" =~ ^v?[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + # Stable release (e.g., 1.7.2 or v1.7.2) + npm publish --access public --tag latest + else + # Pre-release (e.g., 1.7.2-alpha.1 or v1.7.2-alpha.1) + npm publish --access public --tag beta + fi + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.gitignore b/.gitignore index 21bf70ead54..427176b3a69 100644 --- a/.gitignore +++ b/.gitignore @@ -55,19 +55,45 @@ npm-debug.log* # compiled dev-server fetch script /tools/dev-server/fetch.js +# compiled preset-list sync tool +/tools/dev-server/sync-presets/dist/ + +# compiled code-complexity gate +/tools/code-complexity/dist/ + +# compiled doc-link checker +/tools/doc-links/dist/ + +# react-doctor standalone install (not part of the root workspace) +/tools/react-doctor/node_modules/ + +# compiled React profile aggregator +/tools/profiling/dist/ + +# compiled mutation-testing CLI, its reports and Stryker's per-run sandbox +/tools/mutation-testing/dist/ +/tools/mutation-testing/reports/ +/tools/mutation-testing/.stryker-tmp/ + +# compiled Playwright runner +/tools/playwright/dist/ + +# compiled review-screen CLI +/tools/review-screen/dist/ + # typescript *.tsbuildinfo .eslintcache **.decrypted~** -/test-results/ +/playwright-results/ /playwright-report/ /blob-report/ /playwright/.cache/ /playwright/.browser/ /playwright/envs.js -/playwright/affected-tests.txt +/.playwright-mcp/ **.dec** @@ -77,4 +103,8 @@ license.json release-prs-data.json .ai +<<<<<<< HEAD .codex/worktrees/ +======= +/ds-bundle +>>>>>>> v2.12.0 diff --git a/.license-reportrc b/.license-reportrc index e90e8c13951..9df43f3af92 100644 --- a/.license-reportrc +++ b/.license-reportrc @@ -1,6 +1,7 @@ { "exclude": [ - // It currently has two licenses (MIT and Apache-2.0), which are not correctly recognized by license-report-check. - "@helia/verified-fetch" + // These have two licenses (MIT and Apache-2.0), which are not correctly recognized by license-report-check. + "@helia/verified-fetch", + "posthog-js" ] } \ No newline at end of file diff --git a/.mcp.json b/.mcp.json new file mode 100644 index 00000000000..1bd22b5d166 --- /dev/null +++ b/.mcp.json @@ -0,0 +1,13 @@ +{ + "mcpServers": { + "playwright": { + "command": "npx", + "args": [ + "-y", + "@playwright/mcp@latest", + "--headless", + "--isolated" + ] + } + } +} diff --git a/.nvmrc b/.nvmrc index 200763baef7..0a6e235142e 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ ->=22.14.0 \ No newline at end of file +>=24.21.0 \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json index bdd9e81ba44..7105a4abb18 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -73,7 +73,7 @@ // PW TESTS { "type": "shell", - "command": "${input:pwDebugFlag} pnpm test:pw:local ${relativeFileDirname}/${fileBasename} ${input:pwArgs}", + "command": "${input:pwDebugFlag} pnpm test:pw ${relativeFileDirname}/${fileBasename} ${input:pwArgs}", "problemMatcher": [], "label": "pw: local", "detail": "run visual components tests for current file", @@ -94,7 +94,7 @@ }, { "type": "shell", - "command": "pnpm test:pw:docker ${relativeFileDirname}/${fileBasename} ${input:pwArgs}", + "command": "pnpm test:pw --docker ${relativeFileDirname}/${fileBasename} ${input:pwArgs}", "problemMatcher": [], "label": "pw: docker", "detail": "run visual components tests for current file", @@ -115,7 +115,7 @@ }, { "type": "shell", - "command": "pnpm test:pw:docker ${input:pwArgs}", + "command": "pnpm test:pw --docker ${input:pwArgs}", "problemMatcher": [], "label": "pw: docker all", "detail": "run visual components tests", @@ -136,10 +136,10 @@ }, { "type": "shell", - "command": "npx playwright show-report", + "command": "pnpm test:pw --changed ${input:pwArgs}", "problemMatcher": [], - "label": "pw: report", - "detail": "serve test report", + "label": "pw: changed", + "detail": "run visual components tests affected by the changed files in the current branch", "presentation": { "reveal": "always", "panel": "shared", @@ -149,7 +149,7 @@ }, "icon": { "color": "terminal.ansiBlue", - "id": "output" + "id": "beaker" }, "runOptions": { "instanceLimit": 1 @@ -157,10 +157,10 @@ }, { "type": "shell", - "command": "pnpm test:pw:detect-affected", + "command": "npx playwright show-report", "problemMatcher": [], - "label": "pw: detect affected", - "detail": "detect PW tests affected by changes in current branch", + "label": "pw: report", + "detail": "serve test report", "presentation": { "reveal": "always", "panel": "shared", @@ -170,11 +170,11 @@ }, "icon": { "color": "terminal.ansiBlue", - "id": "diff" + "id": "output" }, "runOptions": { "instanceLimit": 1 - }, + } }, // VITEST TESTS @@ -221,6 +221,50 @@ }, }, + // CODE COMPLEXITY + { + "type": "shell", + "command": "pnpm test:code-complexity ${relativeFileDirname}/${fileBasename}", + "problemMatcher": [], + "label": "code complexity: file", + "detail": "run code complexity for current file", + "presentation": { + "reveal": "always", + "panel": "shared", + "focus": true, + "close": false, + "revealProblems": "onProblem", + }, + "icon": { + "color": "terminal.ansiBlue", + "id": "file-binary" + }, + "runOptions": { + "instanceLimit": 1 + }, + }, + { + "type": "shell", + "command": "pnpm test:code-complexity --changed", + "problemMatcher": [], + "label": "code complexity: changed", + "detail": "run code complexity for changed files in the current branch", + "presentation": { + "reveal": "always", + "panel": "shared", + "focus": true, + "close": false, + "revealProblems": "onProblem", + }, + "icon": { + "color": "terminal.ansiBlue", + "id": "file-binary" + }, + "runOptions": { + "instanceLimit": 1 + }, + }, + { "type": "npm", "script": "build:docker", @@ -305,7 +349,6 @@ "options": [ "", "--update-snapshots", - "--update-snapshots --affected", "--ui", ], "default": "" diff --git a/.worktreeinclude b/.worktreeinclude new file mode 100644 index 00000000000..5ed92037411 --- /dev/null +++ b/.worktreeinclude @@ -0,0 +1,2 @@ +.zed/ +.env.local diff --git a/Dockerfile b/Dockerfile index 1bd5d0ee8c7..3c5a251153e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,14 +1,14 @@ # ***************************** # *** STAGE 0: Shared base **** # ***************************** -FROM node:22.14.0-alpine AS base +FROM node:24.21.0-alpine AS base # corepack prepare makes a single network request to the npm registry and has no # built-in retry. Transient registry failures (HTTP 429, timeout, DNS) are not # uncommon, so retry with a linear backoff. RUN set -eu; \ corepack enable; \ n=0; \ - until corepack prepare pnpm@11.5.1 --activate; do \ + until corepack prepare pnpm@12.4.1 --activate; do \ n=$((n + 1)); \ if [ "$n" -ge 5 ]; then echo "corepack prepare failed after $n attempts" >&2; exit 1; fi; \ echo "corepack prepare attempt $n failed, retrying in $((n * 10))s..." >&2; \ @@ -101,7 +101,7 @@ RUN pnpm exec tsc -p ./tools/dev-server/tsconfig.json # ******* STAGE 3: Run ******** # ***************************** # Production image, copy all the files and run next -FROM node:22.14.0-alpine AS runner +FROM node:24.21.0-alpine AS runner RUN apk add --no-cache --upgrade bash curl jq unzip ### APP diff --git a/cspell.jsonc b/cspell.jsonc index 9d6eaf1e4a5..381559de1f4 100644 --- a/cspell.jsonc +++ b/cspell.jsonc @@ -55,6 +55,7 @@ "Ahrefs", "airtable", "Alexa", + "Alexey", "alfajores", "andb", "anyblock", @@ -95,6 +96,7 @@ "chainid", "chainscout", "chakra", + "chdir", "clstr", "commitish", "Computor", @@ -166,6 +168,7 @@ "LCIA", "libc", "libp", + "lifi", "linkedinbot", "Liquality", "llms", @@ -193,6 +196,7 @@ "negb", "nextjs", "noopener", + "noul", "noreferrer", "noves", "numine", @@ -262,6 +266,7 @@ "smol", "sokol", "solidityscan", + "sonarjs", "Sourcify", "Succ", "superchain", @@ -284,6 +289,7 @@ "uidotdev", "Ulyana", "unfinalized", + "unindexed", "unminified", "UNKN", "unparse", diff --git a/deploy/tools/affected-tests/.gitignore b/deploy/tools/affected-tests/.gitignore deleted file mode 100644 index 30bc1627986..00000000000 --- a/deploy/tools/affected-tests/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/node_modules \ No newline at end of file diff --git a/deploy/tools/affected-tests/index.js b/deploy/tools/affected-tests/index.js deleted file mode 100644 index 6cb58f03090..00000000000 --- a/deploy/tools/affected-tests/index.js +++ /dev/null @@ -1,208 +0,0 @@ -/* eslint-disable no-console */ -const { execSync } = require('child_process'); -const dependencyTree = require('dependency-tree'); -const fs = require('fs'); -const path = require('path'); - -const ROOT_DIR = path.resolve(__dirname, '../../../'); - -const TARGET_FILE = path.resolve(ROOT_DIR, './playwright/affected-tests.txt'); - -const NON_EXISTENT_DEPS = []; - -const DIRECTORIES_WITH_TESTS = [ - path.resolve(ROOT_DIR, './src'), -]; -const VISITED = {}; - -function getAllPwFilesInDirectory(directory) { - const files = fs.readdirSync(directory, { recursive: true }); - return files - .filter((file) => file.endsWith('.pw.tsx')) - .map((file) => path.join(directory, file)); -} - -function getFileDeps(filename, changedNpmModules) { - return dependencyTree.toList({ - filename, - directory: ROOT_DIR, - filter: (path) => { - if (path.indexOf('node_modules') === -1) { - return true; - } - - if (changedNpmModules.some((module) => path.startsWith(module))) { - return true; - } - - return false; - }, - tsConfig: path.resolve(ROOT_DIR, './tsconfig.json'), - nonExistent: NON_EXISTENT_DEPS, - visited: VISITED, - }); -} - -async function getChangedFiles() { - const command = process.env.CI ? - `git diff --name-only origin/${ process.env.GITHUB_BASE_REF } ${ process.env.GITHUB_SHA } -- ${ ROOT_DIR }` : - `git diff --name-only main $(git branch --show-current) -- ${ ROOT_DIR }`; - - console.log('Executing command: ', command); - const files = execSync(command) - .toString() - .trim() - .split('\n') - .filter(Boolean); - - return files.map((file) => path.join(ROOT_DIR, file)); -} - -function checkChangesInChakraTheme(changedFiles) { - const themeDir = path.resolve(ROOT_DIR, './src/toolkit/theme'); - return changedFiles.some((file) => file.startsWith(themeDir)); -} - -function checkChangesInSvgSprite(changedFiles) { - const iconDir = path.resolve(ROOT_DIR, './src/icons'); - const areIconsChanged = changedFiles.some((file) => file.startsWith(iconDir)); - - if (!areIconsChanged) { - return false; - } - - const svgNamesFile = path.resolve(ROOT_DIR, './public/icons/name.d.ts'); - const areSvgNamesChanged = changedFiles.some((file) => file === svgNamesFile); - - if (!areSvgNamesChanged) { - // If only the icons have changed and not the names in the SVG file, we will need to run all tests. - // This is because we cannot correctly identify the test files that depend on these changes. - return true; - } - - // If the icon names have changed, then there should be changes in the components that use them. - // Otherwise, typescript would complain about that. - return false; -} - -function createTargetFile(content) { - fs.writeFileSync(TARGET_FILE, content); -} - -function getPackageJsonUpdatedProps(packageJsonFile) { - const command = process.env.CI ? - `git diff --unified=0 origin/${ process.env.GITHUB_BASE_REF } ${ process.env.GITHUB_SHA } -- ${ packageJsonFile }` : - `git diff --unified=0 main $(git branch --show-current) -- ${ packageJsonFile }`; - - console.log('Executing command: ', command); - const changedLines = execSync(command) - .toString() - .trim() - .split('\n') - .filter(Boolean) - .filter((line) => line.startsWith('+ ') || line.startsWith('- ')); - - const changedProps = [ ...new Set( - changedLines - .map((line) => line.replaceAll(' ', '').replaceAll('+', '').replaceAll('-', '')) - .map((line) => line.split(':')[0].replaceAll('"', '')), - ) ]; - - return changedProps; -} - -function getUpdatedNpmModules(changedFiles) { - const packageJsonFile = path.resolve(ROOT_DIR, './package.json'); - - if (!changedFiles.includes(packageJsonFile)) { - return []; - } - - try { - const packageJsonContent = JSON.parse(fs.readFileSync(packageJsonFile, 'utf-8')); - const usedNpmModules = [ - ...Object.keys(packageJsonContent.dependencies || {}), - ...Object.keys(packageJsonContent.devDependencies || {}), - ]; - const updatedProps = getPackageJsonUpdatedProps(packageJsonFile); - - return updatedProps.filter((prop) => usedNpmModules.includes(prop)); - } catch (error) {} -} - -async function run() { - // NOTES: - // - The absence of TARGET_FILE implies that all tests should be run. - // - The empty TARGET_FILE implies that no tests should be run. - - const start = Date.now(); - - fs.unlink(TARGET_FILE, () => {}); - - const changedFiles = await getChangedFiles(); - - if (!changedFiles.length) { - createTargetFile(''); - console.log('No changed files found. Exiting...'); - return; - } - - console.log('Changed files in the branch: ', changedFiles); - - if (checkChangesInChakraTheme(changedFiles)) { - console.log('Changes in Chakra theme detected. It is advisable to run all test suites. Exiting...'); - return; - } - - if (checkChangesInSvgSprite(changedFiles)) { - console.log('There are some changes in the SVG sprite that cannot be linked to a specific component. It is advisable to run all test suites. Exiting...'); - return; - } - - let changedNpmModules = getUpdatedNpmModules(changedFiles); - - if (!changedNpmModules) { - console.log('Some error occurred while detecting changed NPM modules. It is advisable to run all test suites. Exiting...'); - return; - } - - console.log('Changed NPM modules in the branch: ', changedNpmModules); - - changedNpmModules = [ - ...changedNpmModules, - ...changedNpmModules.map((module) => `@types/${ module }`), // there are some deps that are resolved to .d.ts files - ].map((module) => path.resolve(ROOT_DIR, `./node_modules/${ module }`)); - - const allTestFiles = DIRECTORIES_WITH_TESTS.reduce((acc, dir) => { - return acc.concat(getAllPwFilesInDirectory(dir)); - }, []); - - const isDepChanged = (dep) => changedFiles.includes(dep) || changedNpmModules.some((module) => dep.startsWith(module)); - - const testFilesToRun = allTestFiles - .map((file) => ({ file, deps: getFileDeps(file, changedNpmModules) })) - .filter(({ deps }) => deps.some(isDepChanged)); - const testFileNamesToRun = testFilesToRun.map(({ file }) => path.relative(ROOT_DIR, file)); - - if (!testFileNamesToRun.length) { - createTargetFile(''); - console.log('No tests to run. Exiting...'); - return; - } - - createTargetFile(testFileNamesToRun.join('\n')); - - const end = Date.now(); - - const testFilesToRunWithFilteredDeps = testFilesToRun.map(({ file, deps }) => ({ - file, - deps: deps.filter(isDepChanged), - })); - - console.log('Total time: ', ((end - start) / 1_000).toLocaleString()); - console.log('Total test to run: ', testFileNamesToRun.length); - console.log('Tests to run with changed deps: ', testFilesToRunWithFilteredDeps); - console.log('Non existent deps: ', NON_EXISTENT_DEPS); -} - -run(); diff --git a/deploy/tools/affected-tests/package.json b/deploy/tools/affected-tests/package.json deleted file mode 100644 index a34f145916c..00000000000 --- a/deploy/tools/affected-tests/package.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "name": "affected-tests", - "version": "1.0.0", - "main": "index.js", - "dependencies": { - "dependency-tree": "10.0.9" - } -} diff --git a/deploy/tools/envs-validator/schemas/features/crossChainTxs.ts b/deploy/tools/envs-validator/schemas/features/crossChainTxs.ts index ebb16e6de99..8b2eb891bce 100644 --- a/deploy/tools/envs-validator/schemas/features/crossChainTxs.ts +++ b/deploy/tools/envs-validator/schemas/features/crossChainTxs.ts @@ -1,10 +1,34 @@ import * as yup from 'yup'; +import { replaceQuotes } from 'src/config/utils/envs'; import { urlTest } from '../../utils'; export const crossChainTxsSchema = yup .object() .shape({ NEXT_PUBLIC_CROSS_CHAIN_TXS_ENABLED: yup.boolean(), + NEXT_PUBLIC_CROSS_CHAIN_TXS_BRIDGE_IDS: yup + .array() + .transform(replaceQuotes) + .json() + .of(yup.number().required()) + .when('NEXT_PUBLIC_CROSS_CHAIN_TXS_ENABLED', { + is: (value: boolean) => value, + then: (schema) => schema.required(), + otherwise: (schema) => schema.max( + -1, + 'NEXT_PUBLIC_CROSS_CHAIN_TXS_BRIDGE_IDS can only be used with NEXT_PUBLIC_CROSS_CHAIN_TXS_ENABLED', + ), + }), + NEXT_PUBLIC_CROSS_CHAIN_TXS_INCLUDE_UNINDEXED_CHAINS: yup + .boolean() + .when('NEXT_PUBLIC_CROSS_CHAIN_TXS_ENABLED', { + is: (value: boolean) => value, + otherwise: (schema) => schema.test( + 'not-exist', + 'NEXT_PUBLIC_CROSS_CHAIN_TXS_INCLUDE_UNINDEXED_CHAINS can only be used with NEXT_PUBLIC_CROSS_CHAIN_TXS_ENABLED', + value => value === undefined, + ), + }), NEXT_PUBLIC_INTERCHAIN_INDEXER_API_HOST: yup .string() .when('NEXT_PUBLIC_CROSS_CHAIN_TXS_ENABLED', { @@ -16,4 +40,4 @@ export const crossChainTxsSchema = yup value => value === undefined, ), }), - }); \ No newline at end of file + }); diff --git a/deploy/tools/envs-validator/schemas/features/marketplace.ts b/deploy/tools/envs-validator/schemas/features/marketplace.ts index 066d905d114..c33b4ce5de5 100644 --- a/deploy/tools/envs-validator/schemas/features/marketplace.ts +++ b/deploy/tools/envs-validator/schemas/features/marketplace.ts @@ -110,7 +110,6 @@ export const marketplaceSchema = yup const valueSchema = yup.object().transform(replaceQuotes).json().shape({ swap: yup.lazy(value => value ? yup.object().shape({ - url: yup.string().test(urlTest).required(), chains: chainsSchema, fee: yup.string().required(), integrator: yup.string().required(), diff --git a/deploy/tools/envs-validator/schemas/features/multichainButton.ts b/deploy/tools/envs-validator/schemas/features/multichainButton.ts index f00b884444f..5955d593e55 100644 --- a/deploy/tools/envs-validator/schemas/features/multichainButton.ts +++ b/deploy/tools/envs-validator/schemas/features/multichainButton.ts @@ -1,5 +1,5 @@ import { replaceQuotes } from 'src/config/utils/envs'; -import { MultichainProviderConfig } from 'src/features/multichain-button/types/client'; +import { MultichainProviderConfig, MultichainProviderView } from 'src/features/multichain-button/types/client'; import * as yup from 'yup'; const multichainProviderConfigSchema: yup.ObjectSchema = yup.object({ @@ -7,7 +7,7 @@ const multichainProviderConfigSchema: yup.ObjectSchema url_template: yup.string().required(), logo: yup.string().required(), dapp_id: yup.string(), - promo: yup.boolean(), + view: yup.string().oneOf([ 'full', 'icon' ]), }); export const multichainButtonSchema = yup diff --git a/deploy/tools/envs-validator/schemas/services.ts b/deploy/tools/envs-validator/schemas/services.ts index cb49c53a2ce..40dd15f06c9 100644 --- a/deploy/tools/envs-validator/schemas/services.ts +++ b/deploy/tools/envs-validator/schemas/services.ts @@ -26,4 +26,20 @@ export default yup.object({ value => value === undefined, ), }), + + // posthog + NEXT_PUBLIC_POSTHOG_API_KEY: yup.string(), + NEXT_PUBLIC_POSTHOG_CONFIG_OVERRIDES: yup + .object() + .transform(replaceQuotes) + .json() + .when('NEXT_PUBLIC_POSTHOG_API_KEY', { + is: (value: string) => Boolean(value), + then: (schema) => schema, + otherwise: (schema) => schema.test( + 'not-exist', + 'NEXT_PUBLIC_POSTHOG_CONFIG_OVERRIDES can only be used if NEXT_PUBLIC_POSTHOG_API_KEY is set to a non-empty string', + value => value === undefined, + ), + }), }); \ No newline at end of file diff --git a/deploy/tools/envs-validator/test/.env.base b/deploy/tools/envs-validator/test/.env.base index 5b5d782195d..6eb6b6c8cc3 100644 --- a/deploy/tools/envs-validator/test/.env.base +++ b/deploy/tools/envs-validator/test/.env.base @@ -5,6 +5,8 @@ NEXT_PUBLIC_WALLET_CONNECT_FEATURED_WALLET_IDS=['xxx'] NEXT_PUBLIC_RE_CAPTCHA_APP_SITE_KEY=xxx NEXT_PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID=UA-XXXXXX-X NEXT_PUBLIC_MIXPANEL_PROJECT_TOKEN=xxx +NEXT_PUBLIC_POSTHOG_API_KEY=xxx +NEXT_PUBLIC_POSTHOG_CONFIG_OVERRIDES='{"disable_session_recording": false}' NEXT_PUBLIC_USERCENTRICS_CONFIG='{"settingsId":"xxx","rulesetId":"xxx"}' NEXT_PUBLIC_USERCENTRICS_DRAFT=true NEXT_PUBLIC_MIXPANEL_CONFIG_OVERRIDES='{"record_sessions_percent": 0.5,"record_heatmap_data": true}' diff --git a/deploy/tools/envs-validator/test/.env.cross_chain_txs b/deploy/tools/envs-validator/test/.env.cross_chain_txs index 333ca6054a1..dd74352b64f 100644 --- a/deploy/tools/envs-validator/test/.env.cross_chain_txs +++ b/deploy/tools/envs-validator/test/.env.cross_chain_txs @@ -1,2 +1,4 @@ NEXT_PUBLIC_CROSS_CHAIN_TXS_ENABLED=true -NEXT_PUBLIC_INTERCHAIN_INDEXER_API_HOST=https://api.example.com \ No newline at end of file +NEXT_PUBLIC_CROSS_CHAIN_TXS_BRIDGE_IDS=[1,2] +NEXT_PUBLIC_CROSS_CHAIN_TXS_INCLUDE_UNINDEXED_CHAINS=true +NEXT_PUBLIC_INTERCHAIN_INDEXER_API_HOST=https://api.example.com diff --git a/deploy/tools/envs-validator/test/.env.marketplace b/deploy/tools/envs-validator/test/.env.marketplace index 280d887184d..eefabb2521e 100644 --- a/deploy/tools/envs-validator/test/.env.marketplace +++ b/deploy/tools/envs-validator/test/.env.marketplace @@ -7,6 +7,6 @@ NEXT_PUBLIC_ADMIN_SERVICE_API_HOST=https://example.com NEXT_PUBLIC_MARKETPLACE_FEATURED_APP=aave NEXT_PUBLIC_MARKETPLACE_BANNER_CONTENT_URL=https://gist.githubusercontent.com/maxaleks/36f779fd7d74877b57ec7a25a9a3a6c9/raw/746a8a59454c0537235ee44616c4690ce3bbf3c8/banner.html NEXT_PUBLIC_MARKETPLACE_BANNER_LINK_URL=https://www.basename.app -NEXT_PUBLIC_MARKETPLACE_ESSENTIAL_DAPPS_CONFIG={'swap': {'url': 'https://example.com', 'chains': ['1', '10', '100', '11155111'], 'fee': '0.004', 'integrator': 'blockscout'}, 'revoke': {'chains': ['1', '10', '100', '11155111']}, 'multisend': {'chains': ['1', '10', '100', '11155111'], 'posthogKey': '123', 'posthogHost': 'https://example.com'}} +NEXT_PUBLIC_MARKETPLACE_ESSENTIAL_DAPPS_CONFIG={'swap': {'chains': ['1', '10', '100', '11155111'], 'fee': '0.004', 'integrator': 'blockscout'}, 'revoke': {'chains': ['1', '10', '100', '11155111']}, 'multisend': {'chains': ['1', '10', '100', '11155111'], 'posthogKey': '123', 'posthogHost': 'https://example.com'}} NEXT_PUBLIC_MARKETPLACE_TITLES={'menu_item': 'Dapps', 'title': 'Dappscout'} NEXT_PUBLIC_MARKETPLACE_ESSENTIAL_DAPPS_AD_ENABLED=true diff --git a/deploy/tools/essential-dapps-chains-config-generator/package.json b/deploy/tools/essential-dapps-chains-config-generator/package.json index 373dba12f8a..e7f72c0253a 100644 --- a/deploy/tools/essential-dapps-chains-config-generator/package.json +++ b/deploy/tools/essential-dapps-chains-config-generator/package.json @@ -13,7 +13,7 @@ "viem": "2.38.6" }, "devDependencies": { - "@types/node": "22.12.0", + "@types/node": "24.13.4", "dotenv-cli": "10.0.0", "typescript": "7.0.2", "vite": "6.4.3" diff --git a/deploy/tools/feature-reporter/package.json b/deploy/tools/feature-reporter/package.json index edd88931656..f30ef028ed2 100644 --- a/deploy/tools/feature-reporter/package.json +++ b/deploy/tools/feature-reporter/package.json @@ -10,7 +10,7 @@ "dev": "./dev.sh" }, "devDependencies": { - "@types/node": "22.12.0", + "@types/node": "24.13.4", "dotenv-cli": "^7.4.4", "typescript": "7.0.2", "vite": "6.4.3" diff --git a/deploy/tools/llms-txt-generator/package.json b/deploy/tools/llms-txt-generator/package.json index a10364f51a7..f08c538aade 100644 --- a/deploy/tools/llms-txt-generator/package.json +++ b/deploy/tools/llms-txt-generator/package.json @@ -14,7 +14,7 @@ }, "devDependencies": { "@types/dedent": "0.7.2", - "@types/node": "22.12.0", + "@types/node": "24.13.4", "dotenv-cli": "10.0.0", "typescript": "7.0.2", "vite": "6.4.3" diff --git a/deploy/tools/multichain-config-generator/package.json b/deploy/tools/multichain-config-generator/package.json index 8957e0c2186..87be64d121b 100644 --- a/deploy/tools/multichain-config-generator/package.json +++ b/deploy/tools/multichain-config-generator/package.json @@ -11,6 +11,6 @@ "devDependencies": { "typescript": "7.0.2", "vite": "6.4.3", - "@types/node": "22.12.0" + "@types/node": "24.13.4" } } diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index fe7a838f9b3..bc9effeeb4a 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -26,7 +26,7 @@ Please note that we accept contributions for newly submitted issues or those lab cd ``` -3. Make sure you're running Node.js 20+ and NPM 10+; if not, upgrade it accordingly, for example using [nvm](https://github.com/nvm-sh/nvm). +3. Make sure you're running Node.js 24+ and NPM 10+; if not, upgrade it accordingly, for example using [nvm](https://github.com/nvm-sh/nvm). ```sh node -v npm -v @@ -142,7 +142,9 @@ We have 3 pre-configured projects. You can run your test with the desired projec - `mobile` - project for testing on mobile devices, uses Safari mobile browser; add tag `+@mobile` to run test with this project - `dark-color-mode` - project for testing app in the dark color mode, uses desktop Chrome desktop device with forced dark color mode; add tag `+@dark-mode` to run test with this project. -*Note* that, since we are developing not on the same operating system as our CI system, we have to use Docker to generate or update the screenshots. In order to do that use `pnpm test:pw:docker --update-snapshots` command. Please **do not commit** any screenshots generated via `pnpm test:pw:local` command, their associated tests will fail in the CI run. +Run the tests with `pnpm test:pw` (the whole suite) or `pnpm test:pw --changed` (only the tests affected by your branch's diff against `origin/main`, which is what CI runs on a pull request). + +*Note* that, since we are developing not on the same operating system as our CI system, we have to use Docker to generate or update the screenshots. In order to do that use `pnpm test:pw --docker --update-snapshots` command (after a one-off `pnpm test:pw --docker-deps`). Please **do not commit** any screenshots generated via a plain local `pnpm test:pw` run, their associated tests will fail in the CI run.   @@ -205,9 +207,9 @@ We have 3 pre-configured projects. You can run your test with the desired projec | `pnpm svg:build-sprite` | build SVG icons sprite | | **Testing** | | `pnpm test:vitest` | run all Vitest unit tests | -| `pnpm test:pw:local` | run Playwright component tests locally | -| `pnpm test:pw:docker` | run Playwright component tests in docker container | -| `pnpm test:pw:ci` | run Playwright component tests in CI | +| `pnpm test:pw` | run Playwright component tests locally; `--changed` runs only the tests affected by the branch's diff, `--help` lists the tool's flags | +| `pnpm test:pw:docker` | alias of `pnpm test:pw --docker`: run Playwright component tests in a docker container (required to generate screenshots) | +| `pnpm test:pw:docker:deps` | alias of `pnpm test:pw --docker-deps`: install the Linux dependencies the docker run mounts (once) |   diff --git a/docs/DEPRECATED_ENVS.md b/docs/DEPRECATED_ENVS.md index de687f2586b..303c6d9f725 100644 --- a/docs/DEPRECATED_ENVS.md +++ b/docs/DEPRECATED_ENVS.md @@ -34,3 +34,4 @@ | NEXT_PUBLIC_ROLLUP_L1_BASE_URL | `string` | Blockscout base URL for parent network. | Required | - | `'http://eth-goerli.blockscout.com'` | v1.24.0+ | v2.7.0+ | Replaced with NEXT_PUBLIC_ROLLUP_PARENT_CHAIN | | NEXT_PUBLIC_HAS_MUD_FRAMEWORK | `boolean` | Set to `true` for instances that use MUD framework (Optimistic stack only) | - | - | `true` | v1.33.0+ | v2.9.0+ | Feature is deprecated. | | NEXT_PUBLIC_ACCOUNT_API_KEYS_BUTTON | `boolean \| string` | Pass `true` or `false` to enable or disable the "Add API key" button, or provide a URL to convert it into a link. | - | `true` | `https://example.com` | v2.7.0+ | v2.10.0+ | Replaced with automatic behavior based on NEXT_PUBLIC_PRO_API_SUPPORTED | +| NEXT_PUBLIC_TOKEN_ACTION_BUTTON_CONFIG | `TokenActionButtonConfig` | Configuration of an action button on the ERC-20 token page, leading to an external site. The button is shown only when this variable is set. | - | - | `{'text':'Buy on Example Portal','url':'https://portal.example.com/swap?chainId=1&token={hash}','logo':['https://example.com/logo.svg'],'colors':{'_default':{'bg':['rgb(134, 86, 239)'],'text':['white']}}}` | v2.11.2+ | v2.12.0+ | Replaced with the app action button driven by the metadata service (`appActionButton*` fields of a protocol tag). | diff --git a/docs/ENVS.md b/docs/ENVS.md index 0b45338dee7..affa9c3a51a 100644 --- a/docs/ENVS.md +++ b/docs/ENVS.md @@ -102,6 +102,7 @@ All json-like values should be single-quoted. If it contains a hash (`#`) or a d - [Google ReCaptcha](#google-recaptcha) - [GrowthBook](#growthbook) - [Mixpanel](#mixpanel) + - [PostHog](#posthog) - [OpenTelemetry](#opentelemetry) - [Rollbar](#rollbar) - [Usercentrics CMP](#usercentrics-cmp) @@ -386,7 +387,11 @@ Meta tags, Open Graph, and SEO. | NEXT_PUBLIC_FEATURED_NETWORKS | `string` | URL of configuration file (`.json` format only) or file content string representation. It contains list of featured networks that will be shown in the network menu. See [below](#featured-network-configuration-properties) list of available properties for particular network | - | - | `https://example.com/featured_networks_config.json` \| `[{'title':'Astar(EVM)','url':'https://astar.blockscout.com/','group':'Mainnets','icon':'https://example.com/astar.svg'}]` | v1.0.x+ | | NEXT_PUBLIC_FEATURED_NETWORKS_ALL_LINK | `string` | Link to the all chains resource. Will be displayed at the bottom of featured networks list. | Works only if NEXT_PUBLIC_FEATURED_NETWORKS is set | - | `https://example.com` | v2.3.0+ | | NEXT_PUBLIC_FEATURED_NETWORKS_MODE | `tabs \| list` | Indicates how the networks are presented: in one list or in separate tabs. | Works only if NEXT_PUBLIC_FEATURED_NETWORKS is set | `list` | `tabs` | v2.5.0+ | +<<<<<<< HEAD | NEXT_PUBLIC_COLOR_THEMES | `Array<'light' \| 'dim' \| 'midnight' \| 'dark'>` | Color themes the app offers in the settings menu. | - | all themes | `['light', 'dark']` | upcoming | +======= +| NEXT_PUBLIC_COLOR_THEMES | `Array<'light' \| 'dim' \| 'midnight' \| 'dark'>` | Color themes the app offers in the settings menu. | - | all themes | `['light', 'dark']` | v2.11.2+ | +>>>>>>> v2.12.0 | NEXT_PUBLIC_COLOR_THEME_DEFAULT | `'light' \| 'dim' \| 'midnight' \| 'dark'` | Preferred color theme of the app. Must be one of the themes listed in NEXT_PUBLIC_COLOR_THEMES, if that variable is set. | - | none, unless the themes offered by NEXT_PUBLIC_COLOR_THEMES cannot cover both light and dark `prefers-color-scheme`, in which case the last (darkest) one of them | `midnight` | v1.30.0+ | | NEXT_PUBLIC_COLOR_THEME_OVERRIDES | `string` | Color overrides for the default theme; pass a JSON-like string that represents a subset of the `DEFAULT_THEME_COLORS` object (see `toolkit/theme/foundations/colors.ts`) to customize the app's main colors. See [here](https://www.figma.com/design/4In0X8UADoZaTfZ34HaZ3K/Blockscout-design-system?node-id=29124-23813&t=XOv4ahHUSsTDlNkN-4) the Figma worksheet with description of available color tokens. | - | - | `{'text':{'primary':{'_light':{'value':'rgba(16,17,18,0.80)'},'_dark':{'value':'rgba(222,217,217)'}}}}` | v2.3.0+ | @@ -890,6 +895,8 @@ This feature enables cross-chain transaction tracking and visualization, allowin | Variable | Type | Description | Compulsoriness | Default value | Example value | Version | | --- | --- | --- | --- | --- | --- | --- | | NEXT_PUBLIC_CROSS_CHAIN_TXS_ENABLED | `boolean` | The flag that enables the feature | Required | - | `true` | v2.7.0+ | +| NEXT_PUBLIC_CROSS_CHAIN_TXS_BRIDGE_IDS | `Array` | Ids of the interchain indexer bridges whose messages and transfers this instance is allowed to show. The indexer is shared between deployments and indexes every bridge it knows about, so the ids define this instance's slice of it. | Required | - | `'[1,2]'` | v2.12.0+ | +| NEXT_PUBLIC_CROSS_CHAIN_TXS_INCLUDE_UNINDEXED_CHAINS | `boolean` | Set to `true` to also show messages and transfers whose counterparty chain is not indexed by any of the configured bridges. Such rows have an incomplete counterparty side. | - | `false` | `true` | v2.12.0+ | **Dependencies** @@ -928,7 +935,11 @@ If the feature is enabled, a single button or a dropdown (if more than 1 item is | Variable | Type | Description | Compulsoriness | Default value | Example value | Version | | --- | --- | --- | --- | --- | --- | --- | | NEXT_PUBLIC_DEFI_DROPDOWN_ITEMS | `[{ text: string; icon?: string; dappId?: string, url?: string, isEssentialDapp?: boolean }]` | An array of dropdown items containing the button text, icon name and dappId in Dappscout or an external url | - | - | `[{'text':'Swap','icon':'swap','dappId':'swap','isEssentialDapp':true},{'text':'Payment link','icon':'payment_link','dappId':'peanut-protocol'}]` | v1.31.0+ | +<<<<<<< HEAD | NEXT_PUBLIC_DEFI_DROPDOWN_BUTTON_TEXT | `{ desktop: string; mobile?: string }` | Text on the button that opens the dropdown. `mobile` is the shorter form shown on narrow screens. Keep `desktop` within ~24 characters and `mobile` within ~10. | - | `{'desktop':'Blockscout DeFi','mobile':'DeFi'}` | `{'desktop':'MyChain DeFi','mobile':'DeFi'}` | upcoming | +======= +| NEXT_PUBLIC_DEFI_DROPDOWN_BUTTON_TEXT | `{ desktop: string; mobile?: string }` | Text on the button that opens the dropdown. `mobile` is the shorter form shown on narrow screens. Keep `desktop` within ~24 characters and `mobile` within ~10. | - | `{'desktop':'Blockscout DeFi','mobile':'DeFi'}` | `{'desktop':'MyChain DeFi','mobile':'DeFi'}` | v2.11.2+ | +>>>>>>> v2.12.0   @@ -1065,7 +1076,7 @@ Essential dapps are built-in dapps that are displayed on the Marketplace page in | Property | Type | Description | Compulsoriness | Example value | | --- | --- | --- | --- | --- | -| swap | `{ url: string, chains: Array, fee: string, integrator: string }` | Swap config | - | `{'url': 'https://example.com', 'chains': ['1'], 'fee': '0.004', 'integrator': 'blockscout'}` | +| swap | `{ chains: Array, fee: string, integrator: string }` | Swap config | - | `{'chains': ['1'], 'fee': '0.004', 'integrator': 'blockscout'}` | | revoke | `{ chains: Array }` | Revoke config | - | `{'chains': ['100']}` | | multisend | `{ chains: Array }` | Multisend config | - | `{'chains': ['1', '10']}` | @@ -1087,7 +1098,7 @@ If the feature is enabled, a Multichain balance button will be displayed on the | Variable | Type | Description | Compulsoriness | Default value | Example value | Version | | --- | --- | --- | --- | --- | --- | --- | -| NEXT_PUBLIC_MULTICHAIN_BALANCE_PROVIDER_CONFIG | `[{ name: string; url_template: string; dapp_id?: string; logo: string }]` | Multichain portfolio application config. See [below](#multichain-button-configuration-properties) | - | - | `[{ name: 'zerion', url_template: 'https://app.zerion.io/{address}/overview', logo: 'https://example.com/icon.svg'}]` | v1.31.0+ | +| NEXT_PUBLIC_MULTICHAIN_BALANCE_PROVIDER_CONFIG | `[{ name: string; url_template: string; dapp_id?: string; logo: string; view?: 'full' \| 'icon' }]` | Multichain portfolio application config. See [below](#multichain-button-configuration-properties) | - | - | `[{ name: 'zerion', url_template: 'https://app.zerion.io/{address}/overview', logo: 'https://example.com/icon.svg'}]` | v1.31.0+ | #### Multichain button configuration properties @@ -1097,7 +1108,7 @@ If the feature is enabled, a Multichain balance button will be displayed on the | url_template | `string` | Url template to the portfolio. Should be a template with `{address}` variable | Required | - | `https://app.zerion.io/{address}/overview` | | dapp_id | `string` | Set for open a Blockscout dapp page with the portfolio instead of opening external app page | - | - | `zerion` | | logo | `string` | Multichain portfolio application logo (.svg) url | - | - | `https://example.com/icon.svg` | -| promo | `boolean` | Make the provider stand out by placing their logo prominently at the first place in the section and in the page subheader. | - | - | `true` | +| view | `'full' \| 'icon'` | Button view: `full` shows the logo with the name, `icon` shows the logo only | - | `full` | `icon` |   ### Multichain explorer @@ -1322,6 +1333,15 @@ To obtain the variable values, please refer to the [reCAPTCHA documentation](htt   +### PostHog + +| Variable | Type | Description | Compulsoriness | Default value | Example value | Version | +| --- | --- | --- | --- | --- | --- | --- | +| NEXT_PUBLIC_POSTHOG_API_KEY | `string` | Project API key for [PostHog](https://posthog.com/) product analytics | Required | - | `` | upcoming | +| NEXT_PUBLIC_POSTHOG_CONFIG_OVERRIDES | `string` | JSON-like string with a subset of the [posthog-js config](https://posthog.com/docs/libraries/js/config) merged over the app defaults | - | - | `{"disable_session_recording": false}` | upcoming | + +  + ### OpenTelemetry OpenTelemetry SDK for the Node.js server-side process. Configure the OpenTelemetry Protocol Exporter using the generic environment variables described in the [OT docs](https://opentelemetry.io/docs/specs/otel/protocol/exporter/#configuration-options). Note that this Next.js feature is currently experimental. The Docker image must be built with `NEXT_OPEN_TELEMETRY_ENABLED=true` to enable it. diff --git a/doctor.config.json b/doctor.config.json new file mode 100644 index 00000000000..5c019acb3dc --- /dev/null +++ b/doctor.config.json @@ -0,0 +1,44 @@ +{ + "$schema": "https://react.doctor/schema/config.json", + "categories": { + "Security": "off", + "Bugs": "off", + "Performance": "off", + "Accessibility": "off", + "Maintainability": "off" + }, + "rules": { + "react-doctor/jsx-key": "error", + "react-doctor/no-impure-state-updater": "error", + "react-doctor/no-side-effect-in-state-updater-function": "error", + "react-doctor/no-direct-state-mutation": "error", + "react-doctor/no-mutating-array-method-on-prop-or-hook-result": "error", + "react-doctor/no-adjust-state-on-prop-change": "error", + "react-doctor/no-derived-state": "error", + "react-doctor/no-derived-state-effect": "error", + "react-doctor/no-initialize-state": "error", + "react-doctor/no-effect-chain": "error", + "react-doctor/no-set-state-after-await-in-effect": "error", + "react-doctor/no-create-object-url-without-revoke": "error", + "react-doctor/query-no-query-in-effect": "error" + }, + "ignore": { + "files": [ + "public/**", + "playwright/**", + "vitest/**", + "deploy/**", + "tools/**", + "**/*.pw.tsx", + "**/*.spec.ts", + "**/*.spec.tsx" + ] + }, + "projects": [ "." ], + "warnings": false, + "blocking": "error", + "adoptExistingLintConfig": false, + "noScore": true, + "share": false, + "supplyChain": { "enabled": false } +} diff --git a/eslint.config.mjs b/eslint.config.mjs index 576fc17cd24..413c1b668ab 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1,3 +1,4 @@ +import eslintReactPlugin from '@eslint-react/eslint-plugin'; import { includeIgnoreFile } from '@eslint/compat'; import jsPlugin from '@eslint/js'; import nextJsPlugin from '@next/eslint-plugin-next'; @@ -19,6 +20,8 @@ import { fileURLToPath } from 'node:url'; import tseslint from 'typescript-eslint'; +import { strykerDisableRule } from './tools/mutation-testing/eslint/well-formed-disable.mjs'; + const SPDX_HEADER = '// SPDX-License-Identifier: LicenseRef-Blockscout'; const spdxLicenseRule = { @@ -80,7 +83,41 @@ const RESTRICTED_MODULES = { }, ], patterns: [ - 'src/sprite/icons/*', + { group: [ 'src/sprite/icons/*' ] }, + ], +}; + +const ANALYTICS_VENDOR_MESSAGE = + 'Analytics vendors are reached only through the provider-neutral facade. Please import from "src/shared/analytics" instead.'; + +const ANALYTICS_VENDOR_ALLOWED_FILES = [ + 'src/shared/analytics/**', + 'src/services/mixpanel/**', + 'src/services/posthog/**', + 'src/config/**', + 'src/server/csp/**', + '**/*.spec.{ts,tsx}', +]; + +const RESTRICTED_MODULES_WITH_ANALYTICS_VENDORS = { + paths: [ + ...RESTRICTED_MODULES.paths, + { name: 'mixpanel-browser', message: ANALYTICS_VENDOR_MESSAGE }, + { name: 'posthog-js', message: ANALYTICS_VENDOR_MESSAGE }, + ], + patterns: [ + ...RESTRICTED_MODULES.patterns, + { + group: [ + 'mixpanel-browser/**', + 'posthog-js/**', + 'src/services/mixpanel', + 'src/services/mixpanel/**', + 'src/services/posthog', + 'src/services/posthog/**', + ], + message: ANALYTICS_VENDOR_MESSAGE, + }, ], }; @@ -95,11 +132,61 @@ const ARCH_BOUNDARY_ELEMENTS = [ { type: 'src-features', pattern: 'src/features/**', mode: 'full' }, ]; +/* + * App config convention (src/config/CONTEXT.md) — the modules the root aggregator assembles. + * `types/config.ts` and `mocks/config.ts` are companions, not config modules, and stay out. + */ +const APP_CONFIG_MODULE_GLOBS = [ + 'src/api/config.ts', + 'src/features/**/config.ts', + 'src/services/*/config.ts', + 'src/shell/*/config.ts', + 'src/slices/*/config.ts', +]; +const APP_CONFIG_COMPANION_GLOBS = [ '**/types/config.ts', '**/mocks/config.ts' ]; + +const APP_CONFIG_ELEMENTS = [ + { type: 'app-config-envs', pattern: 'src/config/utils/envs.ts', mode: 'full' }, + { type: 'app-config-root', pattern: 'src/config/**', mode: 'full' }, + { type: 'app-config-module', pattern: APP_CONFIG_MODULE_GLOBS.map((glob) => glob.replace('**/config.ts', '**/!(types|mocks)/config.ts')), mode: 'full' }, + { type: 'app-config-spec', pattern: 'src/**/config.spec.ts', mode: 'full' }, +]; + +const APP_CONFIG_IMPORT_MESSAGE = + 'Read app config through the root aggregator: `import config from \'src/config\'`. ' + + 'Only another config.ts, src/config/** or a config.spec.ts may import a config module directly.'; + +const APP_CONFIG_ENVS_MESSAGE = + 'Env values are read only in config modules. Move the getEnvValue / parseEnvJson / getExternalAssetFilePath call into the owning config.ts ' + + 'and read the structured result via src/config.'; + +const APP_CONFIG_NAMED_EXPORT_MESSAGE = + 'A config module exposes only its default export. Put the value inside the config object (widen the Feature payload) ' + + 'so it is unreachable while the feature is disabled; types and constants go to a sibling role file (types/config.ts, types.ts, consts.ts).'; + +// app config convention (src/config/CONTEXT.md): the raw runtime env map is read by getEnvValue only +const WINDOW_ENVS_RESTRICTION = { + object: 'window', + property: '__envs', + message: APP_CONFIG_ENVS_MESSAGE, +}; + +const RESTRICTED_SYNTAX = [ + { + selector: 'CallExpression[callee.property.name=\'localeCompare\']', + message: 'Use the shared collator from src/shared/texts/collator.ts (collator.compare) instead of String.prototype.localeCompare.', + }, + { + selector: 'NewExpression[callee.object.name=\'Intl\'][callee.property.name=\'Collator\']', + message: 'Use the shared collator from src/shared/texts/collator.ts instead of constructing Intl.Collator inline.', + }, +]; + /** @type {import('eslint').Linter.Config[]} */ export default tseslint.config( includeIgnoreFile(gitignorePath), - { files: [ '**/*.{js,mjs,cjs,ts,jsx,tsx}', '**/*.pw.tsx' ] }, + { files: [ '**/*.{js,mjs,cjs,ts,mts,jsx,tsx}', '**/*.pw.tsx' ] }, { ignores: [ 'deploy/tools/', @@ -108,6 +195,8 @@ export default tseslint.config( // agent worktrees are full checkouts of the repo; linting them doubles the work and can exhaust the heap '.claude/worktrees/', 'next.config.js', + 'tools/code-complexity/dist/', + 'tools/review-screen/dist/', ] }, { languageOptions: { globals: { ...globals.browser, ...globals.node } } }, @@ -115,7 +204,18 @@ export default tseslint.config( { settings: { react: { version: 'detect' }, - 'boundaries/elements': ARCH_BOUNDARY_ELEMENTS, + // first matching descriptor wins, so the file-level config elements go before the layer-level ones; + // the trailing catch-all makes every src file a known element — policies never run from unknown files + 'boundaries/elements': [ ...APP_CONFIG_ELEMENTS, ...ARCH_BOUNDARY_ELEMENTS, { type: 'src-other', pattern: 'src/**', mode: 'full' } ], + 'boundaries/dependency-nodes': [ 'import', 'dynamic-import', 'export' ], + // without a resolver every import target is "unknown" and no boundaries policy ever matches; + // `paths` makes the tsconfig-style root-relative specifiers (src/…, playwright/…) resolvable + 'import/resolver': { + node: { + extensions: [ '.ts', '.tsx', '.js', '.mjs' ], + paths: [ __dirname ], + }, + }, }, }, @@ -239,6 +339,7 @@ export default tseslint.config( }, } ], 'react/jsx-equals-spacing': [ 'error', 'never' ], + 'react/jsx-filename-extension': [ 'error', { allow: 'as-needed', extensions: [ '.tsx' ] } ], 'react/jsx-fragments': [ 'error', 'syntax' ], 'react/jsx-no-duplicate-props': 'error', 'react/jsx-no-target-blank': 'off', @@ -271,6 +372,17 @@ export default tseslint.config( }, }, + { + // type-aware, so TS files only + files: [ '**/*.{ts,tsx}' ], + plugins: { + '@eslint-react': eslintReactPlugin, + }, + rules: { + '@eslint-react/no-leaked-conditional-rendering': 'error', + }, + }, + { plugins: { '@next/next': nextJsPlugin, @@ -364,9 +476,46 @@ export default tseslint.config( 'default': 'allow', rules: [ { - from: 'src-api', - disallow: [ 'src-slices', 'src-features' ], - importKind: 'value', + // by path, not by element type: `src/api/config.ts` is an `app-config-module` element (a file + // is one element only), and the layer policy still applies to it + from: { path: 'src/api/**' }, + disallow: { to: { type: [ 'src-slices', 'src-features' ] }, dependency: { kind: 'value' } }, + }, + ], + } ], + }, + }, + + /* + * App config convention (src/config/CONTEXT.md): the root aggregator is the only import surface for + * config modules, and env values are read only inside them. Later policies override earlier ones. + */ + { + files: [ + 'src/**/*.{ts,tsx}', + ], + plugins: { + boundaries: boundariesPlugin, + }, + rules: { + 'boundaries/dependencies': [ 'error', { + 'default': 'allow', + rules: [ + { + disallow: { to: { type: 'app-config-module' }, dependency: { kind: 'value' } }, + message: APP_CONFIG_IMPORT_MESSAGE, + }, + { + from: { type: [ 'app-config-module', 'app-config-root', 'app-config-spec' ] }, + allow: { to: { type: 'app-config-module' } }, + }, + { + disallow: { to: { type: 'app-config-envs' } }, + message: APP_CONFIG_ENVS_MESSAGE, + }, + { + from: { type: [ 'app-config-module', 'app-config-root' ] }, + allow: { to: { type: 'app-config-envs' } }, }, ], } ], @@ -526,23 +675,17 @@ export default tseslint.config( 'prefer-const': 'error', // restricted imports, properties and syntax - 'no-restricted-syntax': [ 'error', + 'no-restricted-syntax': [ 'error', ...RESTRICTED_SYNTAX ], + 'no-restricted-imports': [ 'error', RESTRICTED_MODULES_WITH_ANALYTICS_VENDORS ], + 'no-restricted-properties': [ 2, { - selector: 'CallExpression[callee.property.name=\'localeCompare\']', - message: 'Use the shared collator from src/shared/texts/collator.ts (collator.compare) instead of String.prototype.localeCompare.', - }, - { - selector: 'NewExpression[callee.object.name=\'Intl\'][callee.property.name=\'Collator\']', - message: 'Use the shared collator from src/shared/texts/collator.ts instead of constructing Intl.Collator inline.', + object: 'process', + property: 'env', + // FIXME: restrict the rule only NEXT_PUBLIC variables + message: 'Please use src/config/index.ts to import any NEXT_PUBLIC environment variables. For other properties please disable this rule for a while.', }, + WINDOW_ENVS_RESTRICTION, ], - 'no-restricted-imports': [ 'error', RESTRICTED_MODULES ], - 'no-restricted-properties': [ 2, { - object: 'process', - property: 'env', - // FIXME: restrict the rule only NEXT_PUBLIC variables - message: 'Please use src/config/index.ts to import any NEXT_PUBLIC environment variables. For other properties please disable this rule for a while.', - } ], }, }, { @@ -558,8 +701,26 @@ export default tseslint.config( '*.config.js', ], rules: { - // for configs allow to consume env variables from process.env directly - 'no-restricted-properties': 'off', + // for configs allow to consume env variables from process.env directly; the raw browser env map stays off-limits + 'no-restricted-properties': [ 2, WINDOW_ENVS_RESTRICTION ], + }, + }, + { + // app config convention (src/config/CONTEXT.md): a config module has no export besides the default + files: APP_CONFIG_MODULE_GLOBS, + ignores: APP_CONFIG_COMPANION_GLOBS, + rules: { + 'no-restricted-syntax': [ 'error', + ...RESTRICTED_SYNTAX, + { selector: 'ExportNamedDeclaration', message: APP_CONFIG_NAMED_EXPORT_MESSAGE }, + { selector: 'ExportAllDeclaration', message: APP_CONFIG_NAMED_EXPORT_MESSAGE }, + ], + }, + }, + { + files: ANALYTICS_VENDOR_ALLOWED_FILES, + rules: { + 'no-restricted-imports': [ 'error', RESTRICTED_MODULES ], }, }, { @@ -596,4 +757,12 @@ export default tseslint.config( 'spdx-license/header': 'error', }, }, + + { + plugins: { stryker: { rules: { 'well-formed-disable': strykerDisableRule } } }, + files: [ '**/*.{ts,tsx,mjs,js,cjs}' ], + rules: { + 'stryker/well-formed-disable': 'error', + }, + }, ); diff --git a/next.config.js b/next.config.js index f9b4d3b6bba..ba2d82419a5 100644 --- a/next.config.js +++ b/next.config.js @@ -105,6 +105,8 @@ const moduleExports = { // workaround for passing outDir to nextjs-routes CLI, see ROUTES_OUT_DIR above. // Next.js warns about this unrecognized key on startup; the warning is harmless. outDir: ROUTES_OUT_DIR, + + agentRules: false }; module.exports = withBundleAnalyzer(withRoutes(moduleExports)); diff --git a/package.json b/package.json index 7bcaac4a2a2..a953765a96a 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "private": false, "homepage": "https://github.com/blockscout/frontend#readme", "engines": { - "node": ">=22.14.0", + "node": ">=24.21.0", "pnpm": ">=9.0.0" }, "license": "SEE LICENSE IN ./LICENSE", @@ -13,9 +13,9 @@ "dev:preset": "./tools/dev-server/dev.preset.sh", "dev:local": "./tools/dev-server/dev.local.sh", "prod:preset": "./tools/dev-server/prod.preset.sh", - "profile:analyze": "node ./tools/profiling/aggregate-react-profile.mjs", - "presets:sync": "node ./tools/dev-server/sync-preset-lists.mjs --write", - "presets:lint": "node ./tools/dev-server/sync-preset-lists.mjs", + "profile:analyze": "./tools/profiling/run.sh", + "presets:sync": "./tools/dev-server/sync-presets/run.sh --write", + "presets:lint": "./tools/dev-server/sync-presets/run.sh", "build": "next build", "build:next": "./deploy/scripts/download_assets.sh ./public/assets/configs && pnpm svg:build-sprite && ./deploy/scripts/make_envs_script.sh && next build", "build:docker": "./tools/scripts/build.docker.sh", @@ -30,20 +30,21 @@ "lint:eslint:fix": "eslint . --fix", "lint:tsc": "tsc -p ./tsconfig.json", "lint:cspell": "cspell . --no-must-find-files", - "lint:doc-links": "node ./tools/scripts/check-doc-links.mjs", + "lint:doc-links": "./tools/doc-links/run.sh", + "lint:react-doctor": "./tools/react-doctor/run.sh", "lint:license:check": "license-report > ./license.json && license-report-check --source=./license.json --output=table --forbidden=n/a", "lint:envs-validator:test": "cd ./deploy/tools/envs-validator && ./test.sh", "prepare": "husky install", "svg:format": "svgo -r --config svgo-format.config.js ./src/sprite/icons ./src/toolkit/chakra/assets", "svg:build-sprite": "./deploy/scripts/build_sprite.sh", - "test:pw": "./tools/scripts/pw.sh", - "test:pw:local": "export NODE_PATH=$(pwd)/node_modules && pnpm test:pw", - "test:pw:docker": "docker run --rm --ipc=host -v $(pwd):/work/ -v $(pwd)/node_modules_linux:/work/node_modules -w /work/ -it mcr.microsoft.com/playwright:v1.57.0-noble ./tools/scripts/pw.docker.sh", - "test:pw:docker:deps": "docker run --rm --ipc=host -v $(pwd):/work/ -v blockscout-pnpm-linux:/pnpm-store -w /work/ -it mcr.microsoft.com/playwright:v1.57.0-noble ./tools/scripts/pw.docker.deps.sh", - "test:pw:ci": "pnpm test:pw --project=$PW_PROJECT", - "test:pw:detect-affected": "node ./deploy/tools/affected-tests/index.js", + "test:pw": "./tools/playwright/run.sh", + "test:pw:docker": "pnpm test:pw --docker", + "test:pw:docker:deps": "pnpm test:pw --docker-deps", "test:vitest": "vitest run", "test:vitest:watch": "vitest", + "test:code-complexity": "./tools/code-complexity/run.sh", + "test:mutation-testing": "./tools/mutation-testing/run.sh", + "review:screen": "./tools/review-screen/run.sh", "favicon:generate:dev": "./tools/scripts/favicon-generator.dev.sh", "og-image:generate:dev": "./tools/scripts/og-image-generator.dev.sh", "sitemap:generate:dev": "./tools/scripts/sitemap-generator.dev.sh", @@ -53,13 +54,17 @@ }, "dependencies": { "@blockscout/admin-rs-types": "1.5.1", +<<<<<<< HEAD "@blockscout/api-types": "0.0.1-beta.d261ddc", +======= + "@blockscout/api-types": "0.0.1-beta.40c5c457b4", +>>>>>>> v2.12.0 "@blockscout/bens-types": "1.7.1", "@blockscout/contracts-info-types": "1.5.3", - "@blockscout/interchain-indexer-types": "1.6.0", - "@blockscout/multichain-aggregator-types": "2.1.6", + "@blockscout/interchain-indexer-types": "1.11.0", + "@blockscout/multichain-aggregator-types": "2.1.8", "@blockscout/points-types": "1.4.1", - "@blockscout/stats-types": "2.11.1", + "@blockscout/stats-types": "2.19.1", "@blockscout/tac-operation-lifecycle-types": "0.0.1-beta.71a05d5", "@blockscout/visualizer-types": "0.2.0", "@blockscout/zetachain-cctx-types": "1.0.0-rc.10", @@ -73,6 +78,7 @@ "@growthbook/growthbook-react": "0.21.0", "@helia/verified-fetch": "2.6.12", "@internationalized/date": "3.12.2", + "@lifi/widget-light": "4.3.2", "@metamask/post-message-stream": "^7.0.0", "@metamask/providers": "^10.2.1", "@monaco-editor/react": "4.7.0", @@ -129,7 +135,7 @@ "magic-bytes.js": "1.8.0", "mixpanel-browser": "2.67.0", "monaco-editor": "0.52.2", - "next": "16.3.1", + "next": "16.3.5", "next-themes": "0.4.4", "nextjs-routes": "2.2.5", "node-fetch": "^3.3.2", @@ -138,6 +144,7 @@ "phoenix": "^1.8.7", "pino-http": "^8.6.1", "pino-pretty": "^9.4.1", + "posthog-js": "1.434.15", "prom-client": "15.1.1", "qrcode": "^1.5.4", "react": "19.1.4", @@ -160,11 +167,15 @@ }, "devDependencies": { "@chakra-ui/cli": "3.36.1", + "@eslint-react/eslint-plugin": "5.19.0", "@eslint/compat": "1.2.2", "@eslint/js": "9.14.0", "@next/eslint-plugin-next": "16.2.6", "@playwright/experimental-ct-react": "1.57.0", "@playwright/test": "1.57.0", + "@stryker-mutator/api": "10.0.0", + "@stryker-mutator/core": "10.0.0", + "@stryker-mutator/vitest-runner": "10.0.0", "@stylistic/eslint-plugin": "5.2.3", "@svgr/webpack": "^6.5.1", "@tanstack/eslint-plugin-query": "5.100.5", @@ -177,7 +188,7 @@ "@types/dom-to-image": "^2.6.7", "@types/js-cookie": "^3.0.6", "@types/mixpanel-browser": "^2.66.0", - "@types/node": "20.16.7", + "@types/node": "24.13.4", "@types/phoenix": "^1.6.7", "@types/qrcode": "^1.5.6", "@types/react": "19.1.7", @@ -185,13 +196,16 @@ "@types/react-google-recaptcha": "^2.1.9", "@types/swagger-ui-react": "5.18.0", "@types/ws": "^8.18.1", + "@typesafe-ai/sdk": "0.6.0", "@typescript-eslint/eslint-plugin": "^5.62.0", "@typescript/native": "npm:typescript@7.0.2", "@vitejs/plugin-react": "^4.3.4", + "@vitest/coverage-v8": "5.0.0", "css-loader": "^6.11.0", "dotenv-cli": "10.0.0", "eslint": "9.39.2", "eslint-config-next": "16.2.6", + "eslint-import-resolver-node": "0.3.10", "eslint-plugin-boundaries": "6.0.2", "eslint-plugin-consistent-default-export-name": "^0.0.15", "eslint-plugin-import": "2.31.0", @@ -216,14 +230,15 @@ "ts-node": "^10.9.2", "typescript": "npm:@typescript/typescript6@6.0.2", "typescript-eslint": "8.59.2", + "vite": "6.4.3", "vite-plugin-svgr": "5.2.0", "vite-tsconfig-paths": "5.1.4", - "vitest": "4.1.7", + "vitest": "5.0.0", "vitest-fetch-mock": "0.4.5", "ws": "8.21.0" }, "lint-staged": { "*.{js,jsx,ts,tsx}": "eslint --cache --fix" }, - "packageManager": "pnpm@11.5.1" + "packageManager": "pnpm@12.4.1" } diff --git a/patches/@lifi__widget-light@4.3.2.patch b/patches/@lifi__widget-light@4.3.2.patch new file mode 100644 index 00000000000..ee25f694532 --- /dev/null +++ b/patches/@lifi__widget-light@4.3.2.patch @@ -0,0 +1,187 @@ +diff --git a/dist/esm/handlers/ethereum/useEthereumIframeHandler.js b/dist/esm/handlers/ethereum/useEthereumIframeHandler.js +index 4759c2e65418e8739dc020bf3ada1901b5c16b63..63e98fb081e053faf9573d7bcf144ac60a238f45 100644 +--- a/dist/esm/handlers/ethereum/useEthereumIframeHandler.js ++++ b/dist/esm/handlers/ethereum/useEthereumIframeHandler.js +@@ -1,7 +1,19 @@ + import { handleRpcRequest } from "./rpcHandler.js"; + import { useCallback, useEffect, useMemo, useRef } from "react"; +-import { useConnection, usePublicClient, useSwitchChain, useWalletClient } from "wagmi"; ++import { useAccount, useConfig, useSwitchChain } from "wagmi"; ++import { getAccount, getPublicClient, getWalletClient } from "wagmi/actions"; + //#region src/handlers/ethereum/useEthereumIframeHandler.ts ++const WALLET_METHODS = new Set([ ++ "eth_sendTransaction", ++ "personal_sign", ++ "eth_sign", ++ "eth_signTypedData_v4", ++ "wallet_addEthereumChain", ++ "wallet_sendCalls", ++ "wallet_getCallsStatus", ++ "wallet_showCallsStatus", ++ "wallet_getCapabilities" ++]); + /** + * Host-side hook that creates an `IframeEcosystemHandler` for EVM. + * +@@ -9,25 +21,20 @@ import { useConnection, usePublicClient, useSwitchChain, useWalletClient } from + * forwards EIP-1193 RPC requests from the guest iframe. + */ + function useEthereumIframeHandler() { +- const { address, chainId, connector } = useConnection(); +- const { data: walletClient } = useWalletClient(); +- const publicClient = usePublicClient(); +- const { mutateAsync: switchChainAsync } = useSwitchChain(); ++ const { address, chainId, connector } = useAccount(); ++ const config = useConfig(); ++ const { switchChainAsync } = useSwitchChain(); + const connectorName = connector?.name; + const connectorIcon = connector?.icon; + const stateRef = useRef({ + address, + chainId, +- walletClient, +- publicClient, + connectorName, + connectorIcon + }); + stateRef.current = { + address, + chainId, +- walletClient, +- publicClient, + connectorName, + connectorIcon + }; +@@ -66,7 +73,11 @@ function useEthereumIframeHandler() { + }; + }, []); + const handleRequest = useCallback(async (_id, method, params) => { +- const { address, chainId, walletClient, publicClient } = stateRef.current; ++ const { address, chainId, connector } = getAccount(config); ++ const walletClient = address && WALLET_METHODS.has(method) ++ ? await getWalletClient(config, { account: address, chainId, connector }) ++ : undefined; ++ const publicClient = getPublicClient(config, { chainId }); + return handleRpcRequest(method, params, { + address, + chainId, +@@ -76,7 +87,7 @@ function useEthereumIframeHandler() { + await switchChainAsync({ chainId: targetChainId }); + } + }); +- }, [switchChainAsync]); ++ }, [config, switchChainAsync]); + const subscribe = useCallback((emit) => { + emitRef.current = emit; + return () => { +diff --git a/dist/esm/host/useWidgetLightHost.js b/dist/esm/host/useWidgetLightHost.js +index 93e96184f3a00879d496554d34bdcfaedf94c95e..74bd0f0f7aeadcd007a8140f0debe6274d6d1e75 100644 +--- a/dist/esm/host/useWidgetLightHost.js ++++ b/dist/esm/host/useWidgetLightHost.js +@@ -65,7 +65,7 @@ function useWidgetLightHost({ config, handlers = EMPTY_HANDLERS, iframeOrigin, a + return; + } + if (msg.type === "RESIZE" && autoResize) { +- iframeRef.current.style.height = `${msg.height}px`; ++ iframeRef.current.style.height = `calc(${msg.height}px + var(--lifi-iframe-height-offset, 0px))`; + return; + } + if (msg.type === "WIDGET_EVENT") { +diff --git a/src/handlers/ethereum/useEthereumIframeHandler.ts b/src/handlers/ethereum/useEthereumIframeHandler.ts +index 30f119089950c81dd66b6937f710e6657a0a0164..10a97557ad0c825f4efdf8d77cbb0d2af4399748 100644 +--- a/src/handlers/ethereum/useEthereumIframeHandler.ts ++++ b/src/handlers/ethereum/useEthereumIframeHandler.ts +@@ -1,13 +1,21 @@ + import { useCallback, useEffect, useMemo, useRef } from 'react' +-import { +- useConnection, +- usePublicClient, +- useSwitchChain, +- useWalletClient, +-} from 'wagmi' ++import { useAccount, useConfig, useSwitchChain } from 'wagmi' ++import { getAccount, getPublicClient, getWalletClient } from 'wagmi/actions' + import type { IframeEcosystemHandler } from '../../shared/protocol.js' + import { handleRpcRequest } from './rpcHandler.js' + ++const WALLET_METHODS = new Set([ ++ 'eth_sendTransaction', ++ 'personal_sign', ++ 'eth_sign', ++ 'eth_signTypedData_v4', ++ 'wallet_addEthereumChain', ++ 'wallet_sendCalls', ++ 'wallet_getCallsStatus', ++ 'wallet_showCallsStatus', ++ 'wallet_getCapabilities', ++]) ++ + /** + * Host-side hook that creates an `IframeEcosystemHandler` for EVM. + * +@@ -15,10 +23,9 @@ import { handleRpcRequest } from './rpcHandler.js' + * forwards EIP-1193 RPC requests from the guest iframe. + */ + export function useEthereumIframeHandler(): IframeEcosystemHandler { +- const { address, chainId, connector } = useConnection() +- const { data: walletClient } = useWalletClient() +- const publicClient = usePublicClient() +- const { mutateAsync: switchChainAsync } = useSwitchChain() ++ const { address, chainId, connector } = useAccount() ++ const config = useConfig() ++ const { switchChainAsync } = useSwitchChain() + + const connectorName = connector?.name + const connectorIcon = connector?.icon +@@ -26,16 +33,12 @@ export function useEthereumIframeHandler(): IframeEcosystemHandler { + const stateRef = useRef({ + address, + chainId, +- walletClient, +- publicClient, + connectorName, + connectorIcon, + }) + stateRef.current = { + address, + chainId, +- walletClient, +- publicClient, + connectorName, + connectorIcon, + } +@@ -76,7 +79,11 @@ export function useEthereumIframeHandler(): IframeEcosystemHandler { + + const handleRequest = useCallback( + async (_id: string, method: string, params?: unknown) => { +- const { address, chainId, walletClient, publicClient } = stateRef.current ++ const { address, chainId, connector } = getAccount(config) ++ const walletClient = address && WALLET_METHODS.has(method) ++ ? await getWalletClient(config, { account: address, chainId, connector }) ++ : undefined ++ const publicClient = getPublicClient(config, { chainId }) + return handleRpcRequest(method, params as unknown[] | undefined, { + address, + chainId, +@@ -87,7 +94,7 @@ export function useEthereumIframeHandler(): IframeEcosystemHandler { + }, + }) + }, +- [switchChainAsync] ++ [config, switchChainAsync] + ) + + const subscribe = useCallback( +diff --git a/src/host/useWidgetLightHost.ts b/src/host/useWidgetLightHost.ts +index 96dd214321f91aa0d1198c2b04d54e96e70415eb..8984d5db7c82ee47c502c4a4da488221da6393b7 100644 +--- a/src/host/useWidgetLightHost.ts ++++ b/src/host/useWidgetLightHost.ts +@@ -140,7 +140,7 @@ export function useWidgetLightHost({ + + // ── RESIZE ───────────────────────────────────────────────────────────── + if (msg.type === 'RESIZE' && autoResize) { +- iframeRef.current.style.height = `${msg.height}px` ++ iframeRef.current.style.height = `calc(${msg.height}px + var(--lifi-iframe-height-offset, 0px))` + return + } + diff --git a/patches/@stryker-mutator__vitest-runner@10.0.0.patch b/patches/@stryker-mutator__vitest-runner@10.0.0.patch new file mode 100644 index 00000000000..9877e79d47b --- /dev/null +++ b/patches/@stryker-mutator__vitest-runner@10.0.0.patch @@ -0,0 +1,13 @@ +diff --git a/dist/src/vitest-test-runner.js b/dist/src/vitest-test-runner.js +index 84f1ca026503b8e92b7adc952ec6c6a401943656..f9090b6f5e97bdeabd79de69f17d06b562eb2b7d 100644 +--- a/dist/src/vitest-test-runner.js ++++ b/dist/src/vitest-test-runner.js +@@ -144,7 +144,7 @@ export class VitestTestRunner { + if (testIds.length > 0) { + const parsedTests = testIds.map(fromTestId); + const regexTestNameFilter = parsedTests +- .map(({ test: name }) => escapeRegExp(name)) ++ .map(({ test: name }) => escapeRegExp(name).replace(/ /g, '(?: > | )')) + .join('|'); + const regex = new RegExp(regexTestNameFilter); + testFilesToRun = parsedTests.map(({ file }) => file); diff --git a/playwright-ct.config.ts b/playwright-ct.config.ts index 378eb852d09..324d9a11baf 100644 --- a/playwright-ct.config.ts +++ b/playwright-ct.config.ts @@ -7,6 +7,8 @@ import tsconfigPaths from 'vite-tsconfig-paths'; import appConfig from 'src/config'; +const isInsideWorktree = __dirname.includes('/.claude/worktrees/'); + /** * See https://playwright.dev/docs/test-configuration. */ @@ -15,8 +17,7 @@ const config: PlaywrightTestConfig = defineConfig({ testMatch: /.*\.pw\.tsx/, - // agent worktrees are full checkouts of the repo; their tests would run a second time - testIgnore: '.claude/worktrees/**', + testIgnore: isInsideWorktree ? undefined : '.claude/worktrees/**', snapshotPathTemplate: '{testDir}/{testFileDir}/__screenshots__/{testFileName}_{projectName}_{arg}{ext}', @@ -37,8 +38,15 @@ const config: PlaywrightTestConfig = defineConfig({ // so we opt out of parallel tests in any environment workers: 1, + outputDir: 'playwright-results', + /* Reporter to use. See https://playwright.dev/docs/test-reporters */ - reporter: process.env.CI ? 'blob' : 'html', + // The json report sits under outputDir rather than next to the html one: the html reporter wipes + // playwright-report/ when the run ends, whereas outputDir is cleared at run start. + reporter: [ + process.env.CI ? [ 'blob' ] : [ 'html' ], + [ 'json', { outputFile: 'playwright-results/report.json' } ], + ], expect: { toHaveScreenshot: { @@ -121,6 +129,8 @@ const config: PlaywrightTestConfig = defineConfig({ // Mock for reCaptcha hook { find: 'src/services/re-captcha/useReCaptcha', replacement: './playwright/mocks/client/services/re-captcha/useReCaptcha.js' }, + // Mock for reCaptcha widget, so Google's script never loads and never fires the init error + { find: /^react-google-recaptcha$/, replacement: './playwright/mocks/modules/react-google-recaptcha.js' }, // The createWeb3Modal() function from web3modal/wagmi/react somehow pollutes the global styles which causes the tests to fail // We don't call this function in TestApp and since we use useWeb3Modal() and useWeb3ModalState() hooks in the code, we have to mock the module @@ -147,8 +157,9 @@ const config: PlaywrightTestConfig = defineConfig({ }, // configured projects - // these projects are also used for sharding tests in CI - // when adding or deleting a project, make sure to update github workflow accordingly + // these projects are also used for sharding tests in CI: the `pw_tests` matrix in + // .github/workflows/checks.yml runs one job per project, with `default` split into three shards + // when adding or deleting a project, or changing the shard count, make sure to update github workflow accordingly projects: [ { name: 'default', diff --git a/playwright/.env.pw b/playwright/.env.pw index b3c2d1cf83a..62f231ee9ae 100644 --- a/playwright/.env.pw +++ b/playwright/.env.pw @@ -55,6 +55,7 @@ NEXT_PUBLIC_ZETACHAIN_SERVICE_API_HOST=http://localhost:3111 NEXT_PUBLIC_MULTICHAIN_AGGREGATOR_API_HOST=http://localhost:3012 NEXT_PUBLIC_MULTICHAIN_STATS_API_HOST=http://localhost:3013 NEXT_PUBLIC_INTERCHAIN_INDEXER_API_HOST=http://localhost:3014 +NEXT_PUBLIC_CROSS_CHAIN_TXS_BRIDGE_IDS=[1,2] NEXT_PUBLIC_MULTICHAIN_CLUSTER=test NEXT_PUBLIC_RE_CAPTCHA_APP_SITE_KEY=xxx NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID=xxx diff --git a/playwright/TestApp.tsx b/playwright/TestApp.tsx index cb5e0c53d54..2008dadd528 100644 --- a/playwright/TestApp.tsx +++ b/playwright/TestApp.tsx @@ -1,3 +1,4 @@ +import { Box } from '@chakra-ui/react'; import { GrowthBookProvider } from '@growthbook/growthbook-react'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import React from 'react'; @@ -93,7 +94,14 @@ const TestApp = ({ children, withSocket, appContext, marketplaceContext = defaul - { children } + { /* + The mount locator resolves to the only child of #root, or to #root when there are several. + Without the wrapper, a component whose first render is null leaves the color mode script + as the only child, and the screenshot waits for that hidden script to become visible. + */ } + + { children } + diff --git a/playwright/lib.tsx b/playwright/lib.ts similarity index 100% rename from playwright/lib.tsx rename to playwright/lib.ts diff --git a/playwright/mocks/modules/react-google-recaptcha.js b/playwright/mocks/modules/react-google-recaptcha.js new file mode 100644 index 00000000000..951a2bc04ff --- /dev/null +++ b/playwright/mocks/modules/react-google-recaptcha.js @@ -0,0 +1,19 @@ +// Mock for react-google-recaptcha to keep Playwright component tests off the network +// When Google's script slips past the global request interceptor, it renders with the fake site key and fires onErrored, +// so the reCAPTCHA initialization warning shows up in screenshots at random + +import React from 'react'; + +const ReCAPTCHA = React.forwardRef((props, ref) => { + React.useImperativeHandle(ref, () => ({ + execute: () => {}, + executeAsync: () => Promise.resolve('recaptcha_token'), + reset: () => {}, + getValue: () => null, + getWidgetId: () => null, + }), []); + + return null; +}); + +export default ReCAPTCHA; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 22fa57f877d..1610e7da600 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,3 +1,161 @@ +--- +lockfileVersion: '9.0' + +importers: + + .: + configDependencies: {} + packageManagerDependencies: + pnpm: + specifier: 12.4.1 + version: 12.4.1 + +packages: + + '@pnpm/exe.android-arm64@12.4.1': + resolution: {integrity: sha512-/HwsqXMSmlOfgtV9+O0ratzjV6Vd/8n1hh4rGHpCGmDURvt52MwZxdbhyxP4K03ZfvgSDvotFPr8O+RzE5Eu8A==} + cpu: [arm64] + os: [android] + + '@pnpm/exe.android-x64@12.4.1': + resolution: {integrity: sha512-+l74Qb4c2YjOzNKHXJLg+1wr8xHM1ckkUhnU5KRUK9TJqiiczt6yeTZqqzHIlJ8i6pAoj5V0OJevDRwnQKLgrQ==} + cpu: [x64] + os: [android] + + '@pnpm/exe.darwin-arm64@12.4.1': + resolution: {integrity: sha512-6rkZkT3iGfaxknUdGHraqSWFvTa6N0ajAHluv9Ax0GRWs0sIcGNiFhDopv6xSZCsJZmG483aNS/b6UEDy3blfw==} + cpu: [arm64] + os: [darwin] + + '@pnpm/exe.darwin-x64@12.4.1': + resolution: {integrity: sha512-Vb1CHlR88HghC1qUxjxjs82zQSXnXacPD+btG2CmG8Q/hBU7Q0/b2YWJKSQqZXicunV5khFtfTlAwJddV9RkYA==} + cpu: [x64] + os: [darwin] + + '@pnpm/exe.freebsd-x64@12.4.1': + resolution: {integrity: sha512-iT3iHz3Nl0Sxxj7UPOtZ/aQ81AFsQhjoeWMAlPkSRO04gsgDGAXv3UYxOFaesMWsfNxaGn0A+CITbuCHFF66FA==} + cpu: [x64] + os: [freebsd] + + '@pnpm/exe.linux-arm64-musl@12.4.1': + resolution: {integrity: sha512-aBooZfNXM5f+OGUgCAMFWpE/kAhsWfvmqIyMtHy6zl3aNxyInWrcY/Saln/UElzo7lZWMC0Yktroxd/2J26lNQ==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@pnpm/exe.linux-arm64@12.4.1': + resolution: {integrity: sha512-TlOdacTTP09BgcMvwWBFRsu8VAjfwqwnslBj+XSq1JFM3ck4f3k+1O/747EuctxZxs3/o785b6Q3s7Pd92Ptsg==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@pnpm/exe.linux-ppc64@12.4.1': + resolution: {integrity: sha512-r/ab/MlIBo75oizUP5ITiziCCrnXz4SJwfErLQ+603AshB+Yq7xTMCoMtzTSCAMu6aaymIKkAFtZvd2J75Wq0w==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@pnpm/exe.linux-riscv64@12.4.1': + resolution: {integrity: sha512-C/D1QWdKMiB8+wv/spl1rGITFUuqC+aI/fb6h8NB5sqxsOaGXeYc/g891oCuzggHn6SODk9p+I09hI76x/cMNw==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@pnpm/exe.linux-s390x@12.4.1': + resolution: {integrity: sha512-nxz5zD4yXt94uzbStDk0QTPKW+aE92hH1b5tFXK9ctB1HE9Xcq1vjTiA2LsZMFC6p4gdu5OwQeFqYNAVGa6QlA==} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@pnpm/exe.linux-x64-musl@12.4.1': + resolution: {integrity: sha512-5AwgFdGhVUg2kIweYGfxzSLEHiIG77PQhZAkXC3TwofQHsu1Wr+TrV5/rNX2PopFnHRzuE581zoB8F6Wle32yg==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@pnpm/exe.linux-x64@12.4.1': + resolution: {integrity: sha512-FJOZuuuQMhp0oLzBtcKkLXknBI92hfkmSnlKc47vfin4HrmfID5khY2lGekL9tCzk1cpR+HShEw/meFl+nHtzQ==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@pnpm/exe.win32-arm64@12.4.1': + resolution: {integrity: sha512-OO7eKBL9S+xk5hRy+JUUZSNJknGuGe3GEUffsrlC2LbqKF02g+VX1anGIzSbJDvkkdnpJhSlxF5AUz2JzJu2Jw==} + cpu: [arm64] + os: [win32] + + '@pnpm/exe.win32-x64@12.4.1': + resolution: {integrity: sha512-x7gJHZgHo6hp354xCYA2NvoFzYJkHwovt2kVsUBK5EmXULLcP51JGMq09CX+5FRFJUZFKoXjLu3mZWmfP7o6PQ==} + cpu: [x64] + os: [win32] + + pnpm@12.4.1: + resolution: {integrity: sha512-LoHjmdc/6DkNqyXgaqeIq3pZCCSNL1o3D4K0gRR6ano2e/gEj5pv22Rg8hpm8FQt7bi5TKLIcjWWdBkgsWVtTA==} + engines: {node: '>=18.*'} + hasBin: true + +snapshots: + + '@pnpm/exe.android-arm64@12.4.1': + optional: true + + '@pnpm/exe.android-x64@12.4.1': + optional: true + + '@pnpm/exe.darwin-arm64@12.4.1': + optional: true + + '@pnpm/exe.darwin-x64@12.4.1': + optional: true + + '@pnpm/exe.freebsd-x64@12.4.1': + optional: true + + '@pnpm/exe.linux-arm64-musl@12.4.1': + optional: true + + '@pnpm/exe.linux-arm64@12.4.1': + optional: true + + '@pnpm/exe.linux-ppc64@12.4.1': + optional: true + + '@pnpm/exe.linux-riscv64@12.4.1': + optional: true + + '@pnpm/exe.linux-s390x@12.4.1': + optional: true + + '@pnpm/exe.linux-x64-musl@12.4.1': + optional: true + + '@pnpm/exe.linux-x64@12.4.1': + optional: true + + '@pnpm/exe.win32-arm64@12.4.1': + optional: true + + '@pnpm/exe.win32-x64@12.4.1': + optional: true + + pnpm@12.4.1: + optionalDependencies: + '@pnpm/exe.android-arm64': 12.4.1 + '@pnpm/exe.android-x64': 12.4.1 + '@pnpm/exe.darwin-arm64': 12.4.1 + '@pnpm/exe.darwin-x64': 12.4.1 + '@pnpm/exe.freebsd-x64': 12.4.1 + '@pnpm/exe.linux-arm64': 12.4.1 + '@pnpm/exe.linux-arm64-musl': 12.4.1 + '@pnpm/exe.linux-ppc64': 12.4.1 + '@pnpm/exe.linux-riscv64': 12.4.1 + '@pnpm/exe.linux-s390x': 12.4.1 + '@pnpm/exe.linux-x64': 12.4.1 + '@pnpm/exe.linux-x64-musl': 12.4.1 + '@pnpm/exe.win32-arm64': 12.4.1 + '@pnpm/exe.win32-x64': 12.4.1 + +--- lockfileVersion: '9.0' settings: @@ -15,8 +173,13 @@ overrides: js-yaml: 4.1.1 '@noble/hashes': 1.8.0 vite-plugin-dts>brace-expansion: 2.0.2 + minimatch@10>brace-expansion: 5.0.9 protobufjs: 7.5.5 +patchedDependencies: + '@lifi/widget-light@4.3.2': 8ee9f6665560f5ce16422afed5a38522d274ad98ca518fe83619f682b53ed378 + '@stryker-mutator/vitest-runner@10.0.0': 6ec965dcc2376b60fb1c0062fd95899901410eefe7508e286c1b1b0ff7c01159 + importers: .: @@ -25,8 +188,13 @@ importers: specifier: 1.5.1 version: 1.5.1 '@blockscout/api-types': +<<<<<<< HEAD specifier: 0.0.1-beta.d261ddc version: 0.0.1-beta.d261ddc +======= + specifier: 0.0.1-beta.40c5c457b4 + version: 0.0.1-beta.40c5c457b4 +>>>>>>> v2.12.0 '@blockscout/bens-types': specifier: 1.7.1 version: 1.7.1 @@ -34,17 +202,17 @@ importers: specifier: 1.5.3 version: 1.5.3 '@blockscout/interchain-indexer-types': - specifier: 1.6.0 - version: 1.6.0 + specifier: 1.11.0 + version: 1.11.0 '@blockscout/multichain-aggregator-types': - specifier: 2.1.6 - version: 2.1.6 + specifier: 2.1.8 + version: 2.1.8 '@blockscout/points-types': specifier: 1.4.1 version: 1.4.1 '@blockscout/stats-types': - specifier: 2.11.1 - version: 2.11.1 + specifier: 2.19.1 + version: 2.19.1 '@blockscout/tac-operation-lifecycle-types': specifier: 0.0.1-beta.71a05d5 version: 0.0.1-beta.71a05d5 @@ -56,37 +224,40 @@ importers: version: 1.0.0-rc.10 '@chakra-ui/react': specifier: 3.36.1 - version: 3.36.1(@emotion/react@11.14.0(@types/react@19.1.7)(react@19.1.4))(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + version: 3.36.1(@emotion/react@11.14.0(@types/react@19.1.7)(react@19.1.4)(supports-color@10.2.2))(react-dom@19.1.4(react@19.1.4))(react@19.1.4) '@dynamic-labs/ethereum': specifier: 4.91.4 - version: 4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4)(use-sync-external-store@1.4.0(react@19.1.4))(utf-8-validate@6.0.6)(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76))(zod@3.25.76) + version: 4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(debug@4.4.3(supports-color@10.2.2))(fastestsmallesttextencoderdecoder@1.0.22)(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6))(react@19.1.4)(supports-color@10.2.2)(use-sync-external-store@1.4.0(react@19.1.4))(utf-8-validate@6.0.6)(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3))(zod@4.4.3) '@dynamic-labs/ethereum-aa': specifier: 4.91.4 - version: 4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(@zerodev/webauthn-key@5.5.0(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76)))(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6)(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76)) + version: 4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(@zerodev/webauthn-key@5.5.0(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3)))(bufferutil@4.1.0)(debug@4.4.3(supports-color@10.2.2))(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6)(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3)) '@dynamic-labs/sdk-react-core': specifier: 4.91.4 - version: 4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(@types/react@19.1.7)(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6) + version: 4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(@types/react@19.1.7)(bufferutil@4.1.0)(debug@4.4.3(supports-color@10.2.2))(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6) '@dynamic-labs/wagmi-connector': specifier: 4.91.4 - version: 4.91.4(cd9ebb1a64f614b48e624e4e0701fe2e) + version: 4.91.4(86bf20e8ca9767cde8328256d5f07007) '@emotion/react': specifier: 11.14.0 - version: 11.14.0(@types/react@19.1.7)(react@19.1.4) + version: 11.14.0(@types/react@19.1.7)(react@19.1.4)(supports-color@10.2.2) '@graphiql/toolkit': specifier: 0.11.3 - version: 0.11.3(@types/node@20.16.7)(graphql-ws@5.16.2(graphql@16.14.0))(graphql@16.14.0) + version: 0.11.3(@types/node@24.13.4)(graphql-ws@5.16.2(graphql@16.14.0))(graphql@16.14.0) '@growthbook/growthbook-react': specifier: 0.21.0 version: 0.21.0(react@19.1.4) '@helia/verified-fetch': specifier: 2.6.12 - version: 2.6.12(bufferutil@4.1.0)(react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(utf-8-validate@6.0.6) + version: 2.6.12(bufferutil@4.1.0)(react-native@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6))(supports-color@10.2.2)(utf-8-validate@6.0.6) '@internationalized/date': specifier: 3.12.2 version: 3.12.2 + '@lifi/widget-light': + specifier: 4.3.2 + version: 4.3.2(patch_hash=8ee9f6665560f5ce16422afed5a38522d274ad98ca518fe83619f682b53ed378)(@wagmi/core@2.22.1(@tanstack/query-core@5.55.4)(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(react@19.1.4)(use-sync-external-store@1.4.0(react@19.1.4))(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3)))(@wallet-standard/base@1.1.0)(react@19.1.4)(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3))(wagmi@2.19.5(@tanstack/query-core@5.55.4)(@tanstack/react-query@5.55.4(react@19.1.4))(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(debug@4.4.3(supports-color@10.2.2))(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6))(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6)(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3))(zod@4.4.3)) '@metamask/post-message-stream': specifier: ^7.0.0 - version: 7.0.0 + version: 7.0.0(supports-color@10.2.2) '@metamask/providers': specifier: ^10.2.1 version: 10.2.1 @@ -95,7 +266,7 @@ importers: version: 4.7.0(monaco-editor@0.52.2)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) '@multisender.app/multisender-react-widget': specifier: 0.2.3 - version: 0.2.3(b9d9ab1f9c537c8f45225bdc5044ccc9) + version: 0.2.3(4c18776c2376832d04d8e6a63f20812b) '@next/bundle-analyzer': specifier: 16.2.4 version: 16.2.4(bufferutil@4.1.0)(utf-8-validate@6.0.6) @@ -104,13 +275,13 @@ importers: version: 0.10.0 '@nouns/sdk': specifier: ^1.2.0 - version: 1.2.0(@tanstack/react-query@5.55.4(react@19.1.4))(@wagmi/core@2.22.1(@tanstack/query-core@5.55.4)(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(react@19.1.4)(use-sync-external-store@1.4.0(react@19.1.4))(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76)))(wagmi@2.19.5(@tanstack/query-core@5.55.4)(@tanstack/react-query@5.55.4(react@19.1.4))(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6)(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76))(zod@3.25.76)) + version: 1.2.0(@tanstack/react-query@5.55.4(react@19.1.4))(@wagmi/core@2.22.1(@tanstack/query-core@5.55.4)(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(react@19.1.4)(use-sync-external-store@1.4.0(react@19.1.4))(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3)))(wagmi@2.19.5(@tanstack/query-core@5.55.4)(@tanstack/react-query@5.55.4(react@19.1.4))(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(debug@4.4.3(supports-color@10.2.2))(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6))(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6)(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3))(zod@4.4.3)) '@opentelemetry/api': specifier: ^1.9.1 version: 1.9.1 '@opentelemetry/auto-instrumentations-node': specifier: 0.75.0 - version: 0.75.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.8.0(@opentelemetry/api@1.9.1)) + version: 0.75.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.8.0(@opentelemetry/api@1.9.1))(supports-color@10.2.2) '@opentelemetry/exporter-jaeger': specifier: 2.8.0 version: 2.8.0(@opentelemetry/api@1.9.1) @@ -128,7 +299,7 @@ importers: version: 2.8.0(@opentelemetry/api@1.9.1) '@opentelemetry/sdk-node': specifier: 0.218.0 - version: 0.218.0(@opentelemetry/api@1.9.1) + version: 0.218.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) '@opentelemetry/sdk-trace-node': specifier: 2.8.0 version: 2.8.0(@opentelemetry/api@1.9.1) @@ -140,10 +311,10 @@ importers: version: 5.4.0 '@reown/appkit': specifier: 1.7.20 - version: 1.7.20(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@3.25.76) + version: 1.7.20(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@4.4.3) '@reown/appkit-adapter-wagmi': specifier: 1.7.20 - version: 1.7.20(09e58c13ebbf9932f0a56e13a5b340ff) + version: 1.7.20(df3f0d3afef4414b7f4264d6db7d6a30) '@scure/base': specifier: 1.1.9 version: 1.1.9 @@ -176,7 +347,7 @@ importers: version: 2.4.1(react-dom@19.1.4(react@19.1.4))(react@19.1.4) abitype: specifier: 1.2.3 - version: 1.2.3(@typescript/typescript6@6.0.2)(zod@3.25.76) + version: 1.2.3(@typescript/typescript6@6.0.2)(zod@4.4.3) airtable: specifier: ^0.12.2 version: 0.12.2 @@ -209,7 +380,7 @@ importers: version: 3.0.1(d3-selection@3.0.0) dappscout-iframe: specifier: 0.4.0 - version: 0.4.0(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76)) + version: 0.4.0(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3)) dayjs: specifier: ^1.11.21 version: 1.11.21 @@ -233,7 +404,7 @@ importers: version: https://codeload.github.com/blockscout/gradient-avatar/tar.gz/86810368c2581d2dc3aca089dc648336c8e17045 graphiql: specifier: ^4.1.2 - version: 4.1.2(@codemirror/language@6.0.0)(@emotion/is-prop-valid@1.4.0)(@types/node@20.16.7)(@types/react-dom@19.1.6(@types/react@19.1.7))(@types/react@19.1.7)(graphql-ws@5.16.2(graphql@16.14.0))(graphql@16.14.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(use-sync-external-store@1.4.0(react@19.1.4)) + version: 4.1.2(@codemirror/language@6.0.0)(@emotion/is-prop-valid@1.4.0)(@types/node@24.13.4)(@types/react-dom@19.1.6(@types/react@19.1.7))(@types/react@19.1.7)(graphql-ws@5.16.2(graphql@16.14.0))(graphql@16.14.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(use-sync-external-store@1.4.0(react@19.1.4)) graphql: specifier: ^16.14.0 version: 16.14.0 @@ -253,14 +424,14 @@ importers: specifier: 0.52.2 version: 0.52.2 next: - specifier: 16.3.1 - version: 16.3.1(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.57.0)(@types/node@20.16.7)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + specifier: 16.3.5 + version: 16.3.5(@babel/core@7.29.7(supports-color@10.2.2))(@opentelemetry/api@1.9.1)(@playwright/test@1.57.0)(@types/node@24.13.4)(babel-plugin-macros@3.1.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) next-themes: specifier: 0.4.4 version: 0.4.4(react-dom@19.1.4(react@19.1.4))(react@19.1.4) nextjs-routes: specifier: 2.2.5 - version: 2.2.5(next@16.3.1(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.57.0)(@types/node@20.16.7)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)) + version: 2.2.5(next@16.3.5(@babel/core@7.29.7(supports-color@10.2.2))(@opentelemetry/api@1.9.1)(@playwright/test@1.57.0)(@types/node@24.13.4)(babel-plugin-macros@3.1.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)) node-fetch: specifier: ^3.3.2 version: 3.3.2 @@ -279,6 +450,9 @@ importers: pino-pretty: specifier: ^9.4.1 version: 9.4.1 + posthog-js: + specifier: 1.434.15 + version: 1.434.15(@types/react@19.1.7)(react@19.1.4) prom-client: specifier: 15.1.1 version: 15.1.1 @@ -320,7 +494,7 @@ importers: version: 2.26.5 swagger-ui-react: specifier: 5.32.6 - version: 5.32.6(@types/react@19.1.7)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + version: 5.32.6(@types/react@19.1.7)(debug@4.4.3(supports-color@10.2.2))(react-dom@19.1.4(react@19.1.4))(react@19.1.4) use-font-face-observer: specifier: ^1.3.0 version: 1.3.0(react@19.1.4) @@ -329,20 +503,23 @@ importers: version: 1.2.0(@typescript/typescript6@6.0.2) viem: specifier: 2.41.2 - version: 2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) + version: 2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) wagmi: specifier: 2.19.5 - version: 2.19.5(@tanstack/query-core@5.55.4)(@tanstack/react-query@5.55.4(react@19.1.4))(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6)(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76))(zod@3.25.76) + version: 2.19.5(@tanstack/query-core@5.55.4)(@tanstack/react-query@5.55.4(react@19.1.4))(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(debug@4.4.3(supports-color@10.2.2))(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6))(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6)(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3))(zod@4.4.3) xss: specifier: ^1.0.15 version: 1.0.15 devDependencies: '@chakra-ui/cli': specifier: 3.36.1 - version: 3.36.1(@chakra-ui/react@3.36.1(@emotion/react@11.14.0(@types/react@19.1.7)(react@19.1.4))(react-dom@19.1.4(react@19.1.4))(react@19.1.4)) + version: 3.36.1(@chakra-ui/react@3.36.1(@emotion/react@11.14.0(@types/react@19.1.7)(react@19.1.4)(supports-color@10.2.2))(react-dom@19.1.4(react@19.1.4))(react@19.1.4))(supports-color@10.2.2) + '@eslint-react/eslint-plugin': + specifier: 5.19.0 + version: 5.19.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) '@eslint/compat': specifier: 1.2.2 - version: 1.2.2(eslint@9.39.2) + version: 1.2.2(eslint@9.39.2(supports-color@10.2.2)) '@eslint/js': specifier: 9.14.0 version: 9.14.0 @@ -351,19 +528,28 @@ importers: version: 16.2.6 '@playwright/experimental-ct-react': specifier: 1.57.0 - version: 1.57.0(@types/node@20.16.7)(terser@5.48.0)(vite@6.4.3(@types/node@20.16.7)(terser@5.48.0)(yaml@2.9.0))(yaml@2.9.0) + version: 1.57.0(@types/node@24.13.4)(supports-color@10.2.2)(terser@5.48.0)(vite@6.4.3(@types/node@24.13.4)(terser@5.48.0)(yaml@2.9.0))(yaml@2.9.0) '@playwright/test': specifier: 1.57.0 version: 1.57.0 + '@stryker-mutator/api': + specifier: 10.0.0 + version: 10.0.0 + '@stryker-mutator/core': + specifier: 10.0.0 + version: 10.0.0(@types/node@24.13.4) + '@stryker-mutator/vitest-runner': + specifier: 10.0.0 + version: 10.0.0(patch_hash=6ec965dcc2376b60fb1c0062fd95899901410eefe7508e286c1b1b0ff7c01159)(@stryker-mutator/core@10.0.0(@types/node@24.13.4))(vitest@5.0.0) '@stylistic/eslint-plugin': specifier: 5.2.3 - version: 5.2.3(eslint@9.39.2) + version: 5.2.3(eslint@9.39.2(supports-color@10.2.2)) '@svgr/webpack': specifier: ^6.5.1 - version: 6.5.1 + version: 6.5.1(supports-color@10.2.2) '@tanstack/eslint-plugin-query': specifier: 5.100.5 - version: 5.100.5(@typescript/typescript6@6.0.2)(eslint@9.39.2) + version: 5.100.5(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) '@testing-library/react': specifier: ^14.3.1 version: 14.3.1(react-dom@19.1.4(react@19.1.4))(react@19.1.4) @@ -392,8 +578,8 @@ importers: specifier: ^2.66.0 version: 2.66.0 '@types/node': - specifier: 20.16.7 - version: 20.16.7 + specifier: 24.13.4 + version: 24.13.4 '@types/phoenix': specifier: ^1.6.7 version: 1.6.7 @@ -415,57 +601,66 @@ importers: '@types/ws': specifier: ^8.18.1 version: 8.18.1 + '@typesafe-ai/sdk': + specifier: 0.6.0 + version: 0.6.0 '@typescript-eslint/eslint-plugin': specifier: ^5.62.0 - version: 5.62.0(@typescript-eslint/parser@8.59.2(@typescript/typescript6@6.0.2)(eslint@9.39.2))(@typescript/typescript6@6.0.2)(eslint@9.39.2) + version: 5.62.0(@typescript-eslint/parser@5.62.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2))(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) '@typescript/native': specifier: npm:typescript@7.0.2 version: typescript@7.0.2 '@vitejs/plugin-react': specifier: ^4.3.4 - version: 4.3.4(vite@6.4.3(@types/node@20.16.7)(terser@5.48.0)(yaml@2.9.0)) + version: 4.3.4(supports-color@10.2.2)(vite@6.4.3(@types/node@24.13.4)(terser@5.48.0)(yaml@2.9.0)) + '@vitest/coverage-v8': + specifier: 5.0.0 + version: 5.0.0(vitest@5.0.0) css-loader: specifier: ^6.11.0 - version: 6.11.0(webpack@5.107.2(esbuild@0.25.12)(postcss@8.5.15)) + version: 6.11.0(webpack@5.107.2(csso@5.0.5)(esbuild@0.25.12)(postcss@8.5.15)(uglify-js@3.19.3)) dotenv-cli: specifier: 10.0.0 version: 10.0.0 eslint: specifier: 9.39.2 - version: 9.39.2 + version: 9.39.2(supports-color@10.2.2) eslint-config-next: specifier: 16.2.6 - version: 16.2.6(@typescript-eslint/parser@8.59.2(@typescript/typescript6@6.0.2)(eslint@9.39.2))(@typescript/typescript6@6.0.2)(eslint@9.39.2) + version: 16.2.6(@typescript-eslint/parser@5.62.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2))(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) + eslint-import-resolver-node: + specifier: 0.3.10 + version: 0.3.10(supports-color@10.2.2) eslint-plugin-boundaries: specifier: 6.0.2 - version: 6.0.2(@typescript-eslint/parser@8.59.2(@typescript/typescript6@6.0.2)(eslint@9.39.2))(eslint@9.39.2) + version: 6.0.2(@typescript-eslint/parser@5.62.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) eslint-plugin-consistent-default-export-name: specifier: ^0.0.15 version: 0.0.15 eslint-plugin-import: specifier: 2.31.0 - version: 2.31.0(@typescript-eslint/parser@8.59.2(@typescript/typescript6@6.0.2)(eslint@9.39.2))(eslint@9.39.2) + version: 2.31.0(@typescript-eslint/parser@5.62.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) eslint-plugin-import-helpers: specifier: 2.0.1 - version: 2.0.1(eslint@9.39.2) + version: 2.0.1(eslint@9.39.2(supports-color@10.2.2)) eslint-plugin-jsx-a11y: specifier: 6.10.2 - version: 6.10.2(eslint@9.39.2) + version: 6.10.2(eslint@9.39.2(supports-color@10.2.2)) eslint-plugin-playwright: specifier: 2.0.1 - version: 2.0.1(eslint@9.39.2) + version: 2.0.1(eslint@9.39.2(supports-color@10.2.2)) eslint-plugin-react: specifier: 7.37.5 - version: 7.37.5(eslint@9.39.2) + version: 7.37.5(eslint@9.39.2(supports-color@10.2.2)) eslint-plugin-react-hooks: specifier: 5.0.0 - version: 5.0.0(eslint@9.39.2) + version: 5.0.0(eslint@9.39.2(supports-color@10.2.2)) eslint-plugin-regexp: specifier: 2.6.0 - version: 2.6.0(eslint@9.39.2) + version: 2.6.0(eslint@9.39.2(supports-color@10.2.2)) eslint-plugin-vitest: specifier: 0.5.4 - version: 0.5.4(@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@8.59.2(@typescript/typescript6@6.0.2)(eslint@9.39.2))(@typescript/typescript6@6.0.2)(eslint@9.39.2))(@typescript/typescript6@6.0.2)(eslint@9.39.2)(vitest@4.1.7(@opentelemetry/api@1.9.1)(@types/node@20.16.7)(jsdom@27.3.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(vite@6.4.3(@types/node@20.16.7)(terser@5.48.0)(yaml@2.9.0))) + version: 0.5.4(@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2))(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2))(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2)(vitest@5.0.0) globals: specifier: 15.12.0 version: 15.12.0 @@ -474,10 +669,10 @@ importers: version: 8.0.3 jsdom: specifier: 27.3.0 - version: 27.3.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) + version: 27.3.0(bufferutil@4.1.0)(supports-color@10.2.2)(utf-8-validate@6.0.6) license-report: specifier: 6.8.1 - version: 6.8.1 + version: 6.8.1(supports-color@10.2.2) license-report-check: specifier: 0.1.2 version: 0.1.2 @@ -492,7 +687,7 @@ importers: version: 2.0.0(@typescript/typescript6@6.0.2) style-loader: specifier: ^3.3.4 - version: 3.3.4(webpack@5.107.2(esbuild@0.25.12)(postcss@8.5.15)) + version: 3.3.4(webpack@5.107.2(csso@5.0.5)(esbuild@0.25.12)(postcss@8.5.15)(uglify-js@3.19.3)) svg-icons-cli: specifier: 0.0.8 version: 0.0.8 @@ -501,35 +696,32 @@ importers: version: 2.8.2 ts-node: specifier: ^10.9.2 - version: 10.9.2(@types/node@20.16.7)(@typescript/typescript6@6.0.2) + version: 10.9.2(@types/node@24.13.4)(@typescript/typescript6@6.0.2) typescript: specifier: npm:@typescript/typescript6@6.0.2 version: '@typescript/typescript6@6.0.2' typescript-eslint: specifier: 8.59.2 - version: 8.59.2(@typescript/typescript6@6.0.2)(eslint@9.39.2) + version: 8.59.2(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) + vite: + specifier: 6.4.3 + version: 6.4.3(@types/node@24.13.4)(terser@5.48.0)(yaml@2.9.0) vite-plugin-svgr: specifier: 5.2.0 - version: 5.2.0(@typescript/typescript6@6.0.2)(rollup@4.60.4)(vite@6.4.3(@types/node@20.16.7)(terser@5.48.0)(yaml@2.9.0)) + version: 5.2.0(@typescript/typescript6@6.0.2)(rollup@4.60.4)(supports-color@10.2.2)(vite@6.4.3(@types/node@24.13.4)(terser@5.48.0)(yaml@2.9.0)) vite-tsconfig-paths: specifier: 5.1.4 - version: 5.1.4(@typescript/typescript6@6.0.2)(vite@6.4.3(@types/node@20.16.7)(terser@5.48.0)(yaml@2.9.0)) + version: 5.1.4(@typescript/typescript6@6.0.2)(supports-color@10.2.2)(vite@6.4.3(@types/node@24.13.4)(terser@5.48.0)(yaml@2.9.0)) vitest: - specifier: 4.1.7 - version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@20.16.7)(jsdom@27.3.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(vite@6.4.3(@types/node@20.16.7)(terser@5.48.0)(yaml@2.9.0)) + specifier: 5.0.0 + version: 5.0.0(@opentelemetry/api@1.9.1)(@types/node@24.13.4)(@vitest/coverage-v8@5.0.0)(jsdom@27.3.0(bufferutil@4.1.0)(supports-color@10.2.2)(utf-8-validate@6.0.6))(vite@6.4.3(@types/node@24.13.4)(terser@5.48.0)(yaml@2.9.0)) vitest-fetch-mock: specifier: 0.4.5 - version: 0.4.5(vitest@4.1.7(@opentelemetry/api@1.9.1)(@types/node@20.16.7)(jsdom@27.3.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(vite@6.4.3(@types/node@20.16.7)(terser@5.48.0)(yaml@2.9.0))) + version: 0.4.5(vitest@5.0.0) ws: specifier: 8.21.0 version: 8.21.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) - deploy/tools/affected-tests: - dependencies: - dependency-tree: - specifier: 10.0.9 - version: 10.0.9 - deploy/tools/envs-validator: devDependencies: dotenv-cli: @@ -540,7 +732,7 @@ importers: version: 7.0.2 vite: specifier: 6.4.3 - version: 6.4.3(@types/node@25.9.2)(terser@5.48.0)(yaml@2.9.0) + version: 6.4.3(@types/node@24.13.4)(terser@5.48.0)(yaml@2.9.0) yup: specifier: ^1.7.1 version: 1.7.1 @@ -555,8 +747,8 @@ importers: version: 2.38.6(bufferutil@4.1.0)(typescript@7.0.2)(utf-8-validate@6.0.6)(zod@4.4.3) devDependencies: '@types/node': - specifier: 22.12.0 - version: 22.12.0 + specifier: 24.13.4 + version: 24.13.4 dotenv-cli: specifier: 10.0.0 version: 10.0.0 @@ -565,7 +757,7 @@ importers: version: 7.0.2 vite: specifier: 6.4.3 - version: 6.4.3(@types/node@22.12.0)(terser@5.48.0)(yaml@2.9.0) + version: 6.4.3(@types/node@24.13.4)(terser@5.48.0)(yaml@2.9.0) deploy/tools/favicon-generator: dependencies: @@ -576,8 +768,8 @@ importers: deploy/tools/feature-reporter: devDependencies: '@types/node': - specifier: 22.12.0 - version: 22.12.0 + specifier: 24.13.4 + version: 24.13.4 dotenv-cli: specifier: ^7.4.4 version: 7.4.4 @@ -586,7 +778,7 @@ importers: version: 7.0.2 vite: specifier: 6.4.3 - version: 6.4.3(@types/node@22.12.0)(terser@5.48.0)(yaml@2.9.0) + version: 6.4.3(@types/node@24.13.4)(terser@5.48.0)(yaml@2.9.0) deploy/tools/llms-txt-generator: dependencies: @@ -598,8 +790,8 @@ importers: specifier: 0.7.2 version: 0.7.2 '@types/node': - specifier: 22.12.0 - version: 22.12.0 + specifier: 24.13.4 + version: 24.13.4 dotenv-cli: specifier: 10.0.0 version: 10.0.0 @@ -608,34 +800,34 @@ importers: version: 7.0.2 vite: specifier: 6.4.3 - version: 6.4.3(@types/node@22.12.0)(terser@5.48.0)(yaml@2.9.0) + version: 6.4.3(@types/node@24.13.4)(terser@5.48.0)(yaml@2.9.0) deploy/tools/multichain-config-generator: devDependencies: '@types/node': - specifier: 22.12.0 - version: 22.12.0 + specifier: 24.13.4 + version: 24.13.4 typescript: specifier: 7.0.2 version: 7.0.2 vite: specifier: 6.4.3 - version: 6.4.3(@types/node@22.12.0)(terser@5.48.0)(yaml@2.9.0) + version: 6.4.3(@types/node@24.13.4)(terser@5.48.0)(yaml@2.9.0) deploy/tools/sitemap-generator: dependencies: next-sitemap: specifier: 4.2.3 - version: 4.2.3(next@16.3.1(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.57.0)(@types/node@20.16.7)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)) + version: 4.2.3(next@16.3.5(@babel/core@8.0.1)(@opentelemetry/api@1.9.1)(@playwright/test@1.57.0)(@types/node@24.13.4)(babel-plugin-macros@3.1.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)) src/toolkit/package: dependencies: '@chakra-ui/react': specifier: '>=3.36.1' - version: 3.36.1(@emotion/react@11.14.0(@types/react@18.3.12)(react@19.1.4))(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + version: 3.36.1(@emotion/react@11.14.0(@types/react@18.3.12)(react@19.1.4)(supports-color@10.2.2))(react-dom@19.1.4(react@19.1.4))(react@19.1.4) '@emotion/react': specifier: '>=11.14.0' - version: 11.14.0(@types/react@18.3.12)(react@19.1.4) + version: 11.14.0(@types/react@18.3.12)(react@19.1.4)(supports-color@10.2.2) '@internationalized/date': specifier: '>=3.12.2' version: 3.12.2 @@ -656,7 +848,7 @@ importers: version: 1.39.10 next: specifier: '>=16.2.6' - version: 16.2.6(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.57.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + version: 16.3.5(@babel/core@8.0.1)(@opentelemetry/api@1.9.1)(@playwright/test@1.57.0)(@types/node@24.13.4)(babel-plugin-macros@3.1.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) next-themes: specifier: '>=0.4.4' version: 0.4.4(react-dom@19.1.4(react@19.1.4))(react@19.1.4) @@ -672,10 +864,10 @@ importers: devDependencies: '@svgr/plugin-jsx': specifier: 8.1.0 - version: 8.1.0(@svgr/core@8.1.0(@typescript/typescript6@6.0.2)) + version: 8.1.0(@svgr/core@8.1.0(@typescript/typescript6@6.0.2)(supports-color@10.2.2))(supports-color@10.2.2) '@types/node': - specifier: 20.16.7 - version: 20.16.7 + specifier: 24.13.4 + version: 24.13.4 '@types/react': specifier: 18.3.12 version: 18.3.12 @@ -687,22 +879,22 @@ importers: version: typescript@7.0.2 '@vitejs/plugin-react': specifier: 4.3.4 - version: 4.3.4(vite@6.4.3(@types/node@20.16.7)(terser@5.48.0)(yaml@2.9.0)) + version: 4.3.4(supports-color@10.2.2)(vite@6.4.3(@types/node@24.13.4)(terser@5.48.0)(yaml@2.9.0)) typescript: specifier: npm:@typescript/typescript6@6.0.2 version: '@typescript/typescript6@6.0.2' vite: specifier: 6.4.3 - version: 6.4.3(@types/node@20.16.7)(terser@5.48.0)(yaml@2.9.0) + version: 6.4.3(@types/node@24.13.4)(terser@5.48.0)(yaml@2.9.0) vite-plugin-dts: specifier: 4.5.4 - version: 4.5.4(@types/node@20.16.7)(@typescript/typescript6@6.0.2)(rollup@4.60.4)(vite@6.4.3(@types/node@20.16.7)(terser@5.48.0)(yaml@2.9.0)) + version: 4.5.4(@types/node@24.13.4)(@typescript/typescript6@6.0.2)(rollup@4.60.4)(supports-color@10.2.2)(vite@6.4.3(@types/node@24.13.4)(terser@5.48.0)(yaml@2.9.0)) vite-plugin-svgr: specifier: 4.5.0 - version: 4.5.0(@typescript/typescript6@6.0.2)(rollup@4.60.4)(vite@6.4.3(@types/node@20.16.7)(terser@5.48.0)(yaml@2.9.0)) + version: 4.5.0(@typescript/typescript6@6.0.2)(rollup@4.60.4)(supports-color@10.2.2)(vite@6.4.3(@types/node@24.13.4)(terser@5.48.0)(yaml@2.9.0)) vite-tsconfig-paths: specifier: 5.1.4 - version: 5.1.4(@typescript/typescript6@6.0.2)(vite@6.4.3(@types/node@20.16.7)(terser@5.48.0)(yaml@2.9.0)) + version: 5.1.4(@typescript/typescript6@6.0.2)(supports-color@10.2.2)(vite@6.4.3(@types/node@24.13.4)(terser@5.48.0)(yaml@2.9.0)) packages: @@ -760,32 +952,62 @@ packages: resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==} engines: {node: '>=6.9.0'} + '@babel/code-frame@8.0.0': + resolution: {integrity: sha512-dYYg153EyN2Ekbqw2zAsbd6/JR+9N2SEoC7YV2GyyqMM7x9bLDTjBD6XBhSMLH0wtIVyJj03jWNriQhaN+eoCw==} + engines: {node: ^22.18.0 || >=24.11.0} + '@babel/compat-data@7.29.7': resolution: {integrity: sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==} engines: {node: '>=6.9.0'} + '@babel/compat-data@8.0.0': + resolution: {integrity: sha512-DOjnob/cXOUgDOozCDeq/aK2p5y8dUIVdf6tNhEV1HQRd6I8aQ4f4fbtHRVEvb6lP3BGomrKHiS8ICAASSVQSw==} + engines: {node: ^22.18.0 || >=24.11.0} + '@babel/core@7.29.7': resolution: {integrity: sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==} engines: {node: '>=6.9.0'} + '@babel/core@8.0.1': + resolution: {integrity: sha512-5FgxM4dLQpMJHSiVATk8foW263dVHQHBVpXYiimNECVWG01f4nFyEbQixeT6Mwvg7TayREJ2gpKl3o2RoMdnqw==} + engines: {node: ^22.18.0 || >=24.11.0} + '@babel/generator@7.29.7': resolution: {integrity: sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==} engines: {node: '>=6.9.0'} + '@babel/generator@8.0.0': + resolution: {integrity: sha512-NT9NrVwJsbSV6Y2FSstWa71EETOnzrjkL5/wX3D2mYHtKM+qvqB1DvR4D0Setb/gDBsHzRICifwEWMO8CnTF6g==} + engines: {node: ^22.18.0 || >=24.11.0} + '@babel/helper-annotate-as-pure@7.29.7': resolution: {integrity: sha512-OoK6239jHPuSQOoS0kfTVKn0b/rVTk0seKq4Gd2UMLtmOVLjDC0ki3e+c90Trqv2gMfvJFqkiljrr568+qddiw==} engines: {node: '>=6.9.0'} + '@babel/helper-annotate-as-pure@8.0.0': + resolution: {integrity: sha512-NSpMkMsvvZqzThJ0p1B02cbtA2ObEyfBvq950bmNkyxsxvcxwhvvCB036rKhlEnuBBo30bOrk13u3FzlKSoRrw==} + engines: {node: ^22.18.0 || >=24.11.0} + '@babel/helper-compilation-targets@7.29.7': resolution: {integrity: sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==} engines: {node: '>=6.9.0'} + '@babel/helper-compilation-targets@8.0.0': + resolution: {integrity: sha512-JwculLABZvyPvyLBpwU/E/IbH2uM3mnxNtIJpxnIfb24y1PrdVxK5Dqjle4DpgqpGRnwgC7G8IkzPdSXZrO1Ew==} + engines: {node: ^22.18.0 || >=24.11.0} + '@babel/helper-create-class-features-plugin@7.29.7': resolution: {integrity: sha512-IY3ZD9Tmooqr3TUhc3DUWxiuo8xx1DWLhd5M7hQ+ZWJamqM2BbalrBJb2MisSLoYorOj75U03qULCxQTY9r3hg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 + '@babel/helper-create-class-features-plugin@8.0.1': + resolution: {integrity: sha512-++t3ZktzlLmASAxIlxeXQK9Z2YwUafYGYcvGBFevqOqt16HozVHStUoQvWD09fzAZOb/uJGpUTBuGK41AJAuOA==} + engines: {node: ^22.18.0 || >=24.11.0} + peerDependencies: + '@babel/core': ^8.0.0 + '@babel/helper-create-regexp-features-plugin@7.29.7': resolution: {integrity: sha512-907Uymvqgg1dwUA+7IGwFAOSYzQOuzPXKNJ1yxzwPffzkYFg2q2eHi1fIOs6sXkG9NbIUMunnUlkYsfRFNvomg==} engines: {node: '>=6.9.0'} @@ -801,28 +1023,56 @@ packages: resolution: {integrity: sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==} engines: {node: '>=6.9.0'} + '@babel/helper-globals@8.0.0': + resolution: {integrity: sha512-lLozHOM6sWWlxNo8CYqHy4MBZeTvHXNgVPBfPOGsjPKUzHC2Az9QwB6gxdQmpwHl6GlQtbGgS+lj5887guDiLw==} + engines: {node: ^22.18.0 || >=24.11.0} + '@babel/helper-member-expression-to-functions@7.29.7': resolution: {integrity: sha512-j+7JYmk1JYDtACIGj0QJqqWZjoUpMoEikQGADMaHgCMCSDqd2+P32rfcibUNrGOMWrlzK1WJBdxrB3JJQZwWtg==} engines: {node: '>=6.9.0'} + '@babel/helper-member-expression-to-functions@8.0.0': + resolution: {integrity: sha512-xkXrMbtk87Gk7+oKBVmBc6EORg/Qwx++AHESldmHkpvG8wgccdhJJFwrzqlF382Fk8wfXhJHWE/g/43QvEGNPQ==} + engines: {node: ^22.18.0 || >=24.11.0} + '@babel/helper-module-imports@7.29.7': resolution: {integrity: sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==} engines: {node: '>=6.9.0'} + '@babel/helper-module-imports@8.0.0': + resolution: {integrity: sha512-NZ7mSS93o4ndX4KrbD7W8Sf3QT8Qe24PrnFyUcuOPDzK6faqDFKjY9RG7he7+I7FdiQ4llpnosFqzrXa+Vy3Ew==} + engines: {node: ^22.18.0 || >=24.11.0} + '@babel/helper-module-transforms@7.29.7': resolution: {integrity: sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 + '@babel/helper-module-transforms@8.0.1': + resolution: {integrity: sha512-UgAhl1kqiW5ciE0yCXqqvnb4H2n3IELJ7lIIQRezwDPilPEZX5i+Rvbja9MFTkwUn2biEiSMeV31aUzR4Lwakw==} + engines: {node: ^22.18.0 || >=24.11.0} + peerDependencies: + '@babel/core': ^8.0.0 + '@babel/helper-optimise-call-expression@7.29.7': resolution: {integrity: sha512-+kmGVjcT9RGYzoDwdwEqEvGgKe3BYq+O1iGzjFubaNgZHwYHP6lsF2Yghf4kEuv9BV7tYDZ913aBW9am6YKong==} engines: {node: '>=6.9.0'} + '@babel/helper-optimise-call-expression@8.0.0': + resolution: {integrity: sha512-3W6satvtPuCUkUx63S2jMoW9EQNYkADgs1HTfufmL7gCmAulHMKupA/12WNz4A0GMMFn/YnWWwqOT9IZrJHQjg==} + engines: {node: ^22.18.0 || >=24.11.0} + '@babel/helper-plugin-utils@7.29.7': resolution: {integrity: sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==} engines: {node: '>=6.9.0'} + '@babel/helper-plugin-utils@8.0.1': + resolution: {integrity: sha512-3PKFgjTyPlhFhorfP+SjKQxLViIL++zWjFOO4hGriYU+Bsm983DxEM1JmDRJVWXV0O9npu+xXRqz7Pbd3mh70g==} + engines: {node: ^22.18.0 || >=24.11.0} + peerDependencies: + '@babel/core': ^8.0.0 + '@babel/helper-remap-async-to-generator@7.29.7': resolution: {integrity: sha512-16AMiW26DbXWBbr3B8wNozKM0ydMLB892vaOaJW/fPJdnT8vJk5sdkQcU/isqUxyCE0cEoa8wZOcbgDuC4b6Og==} engines: {node: '>=6.9.0'} @@ -835,22 +1085,44 @@ packages: peerDependencies: '@babel/core': ^7.0.0 + '@babel/helper-replace-supers@8.0.1': + resolution: {integrity: sha512-B1SZADIcy3tmH8CmWvj4SHi/oAPom4UL3uknTc2QRNsPVLFk/sPnZvQL/8kj7Y5omvjMqie0vklvs6XM4OLW5Q==} + engines: {node: ^22.18.0 || >=24.11.0} + peerDependencies: + '@babel/core': ^8.0.0 + '@babel/helper-skip-transparent-expression-wrappers@7.29.7': resolution: {integrity: sha512-brcMGQaVzIeUb+6/bs1Av0f8YuNNjKY2JyvfRCsFuFsdKccEQ5Ges2y74D74NZ1Rz8lKJ9ksJkfqwQFJ/iNEyQ==} engines: {node: '>=6.9.0'} + '@babel/helper-skip-transparent-expression-wrappers@8.0.0': + resolution: {integrity: sha512-xmCA9kP3IhySsqhzwIdWGlDN/1A4cCKNBO/uwZx/3YzmDoMePwno2Q5/Bq0q+tYaKbeF940YiKV/kaW8Mzvpjw==} + engines: {node: ^22.18.0 || >=24.11.0} + '@babel/helper-string-parser@7.29.7': resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==} engines: {node: '>=6.9.0'} + '@babel/helper-string-parser@8.0.0': + resolution: {integrity: sha512-6mJgmFFFIIO82vvoLt9XtRC7/TkzXfts1t/SpRX4IHSzMgqoPYCWesVu1udUPUWioAE/2fcG6WuI8zrkE1gwrg==} + engines: {node: ^22.18.0 || >=24.11.0} + '@babel/helper-validator-identifier@7.29.7': resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} engines: {node: '>=6.9.0'} + '@babel/helper-validator-identifier@8.0.4': + resolution: {integrity: sha512-4wFaiLd0bVo4cIoTXI3zKI038NIWE/cr3jvBjejOVYVxV/m8Ltav1USiGzG1fmS5J2RhgEOgXNNK46cRPnRsrg==} + engines: {node: ^22.18.0 || >=24.11.0} + '@babel/helper-validator-option@7.29.7': resolution: {integrity: sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==} engines: {node: '>=6.9.0'} + '@babel/helper-validator-option@8.0.0': + resolution: {integrity: sha512-U4Dybxh4WESWHt5XhBeExi4DrY0/DNK1aHpQbsrQXCUbFHuMweT0TpLEWKvaraV2Y6fS+ZXunsZ8zIuZIgvF2Q==} + engines: {node: ^22.18.0 || >=24.11.0} + '@babel/helper-wrap-function@7.29.7': resolution: {integrity: sha512-iES0Skag9ERIF68aXadpO6dbXa03mNWK3sEqJaMnLNs/eC3l0lkImdfoy6Y09/SfkpawdAB4RjQ7PVA7TcVGdw==} engines: {node: '>=6.9.0'} @@ -859,11 +1131,20 @@ packages: resolution: {integrity: sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==} engines: {node: '>=6.9.0'} + '@babel/helpers@8.0.0': + resolution: {integrity: sha512-wfbi91pM3py96oIiJEz7qIpyXDytgr9zQC1HEWwlGNVRAEmItuU/0a41ZUKu1sJGyhhOIpc4t5vk4PYzt8wpsg==} + engines: {node: ^22.18.0 || >=24.11.0} + '@babel/parser@7.29.7': resolution: {integrity: sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==} engines: {node: '>=6.0.0'} hasBin: true + '@babel/parser@8.0.4': + resolution: {integrity: sha512-srpptsAkEbbNIC/q8nT7o+m6CQe8CJUTV/t7MYc9NnWlgYVtHOb7JH6SorxMhN0kuRJjVqXbKClG6xSbPtzz+g==} + engines: {node: ^22.18.0 || >=24.11.0} + hasBin: true + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.29.7': resolution: {integrity: sha512-j8SrR0zLZrRsC09DlszEx8FpMiwukKffYXMK0d5LmOglO7vGG6sz/BR/20yHqWH+Lnn31JTt2PE3hIWNgM2J6w==} engines: {node: '>=6.9.0'} @@ -900,12 +1181,24 @@ packages: peerDependencies: '@babel/core': ^7.0.0 + '@babel/plugin-proposal-decorators@8.0.2': + resolution: {integrity: sha512-+C6O6KKXU7BBq1GNaIkFJxrALUVGRcr+WeWm4OcuRl3h+l/CmNfcTLMrT2Lm3uvGBimBH/8pEBRrXJFLoO67Gg==} + engines: {node: ^22.18.0 || >=24.11.0} + peerDependencies: + '@babel/core': ^8.0.0 + '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2': resolution: {integrity: sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 + '@babel/plugin-syntax-decorators@8.0.1': + resolution: {integrity: sha512-NI+0S/6MvR6GlcQFwjDZ+WIc2qvG6TXN534lYs9llNldwW4b7Dh6KTtk030FA0xWdYGs4t1lWo+OEWN8wGB+Nw==} + engines: {node: ^22.18.0 || >=24.11.0} + peerDependencies: + '@babel/core': ^8.0.0 + '@babel/plugin-syntax-import-assertions@7.29.7': resolution: {integrity: sha512-/An1OCBN93thpBAGyfsK2pcf0jvju1SAtKkL2Ny++B5Sy6sqgzXDQH1cZxWbF96Wuk+bn41MDA9bLd4VVAw6rw==} engines: {node: '>=6.9.0'} @@ -924,12 +1217,24 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 + '@babel/plugin-syntax-jsx@8.0.1': + resolution: {integrity: sha512-n0jtCOxEovhU7METqSQjcZO9pX53nu9uNIjMS+hEt+Nt9jA7oOZoBIgbCxhhASmF6T6rPDGge5UAvh6Z4eFz/g==} + engines: {node: ^22.18.0 || >=24.11.0} + peerDependencies: + '@babel/core': ^8.0.0 + '@babel/plugin-syntax-typescript@7.29.7': resolution: {integrity: sha512-ngr+82Sh0xMz25TPCZi+nC2iTzjfCdWS2ONXTp/PtSCHCgaCNBpdMqgvJ2ccdLlClVZ7sisIgB914j/JFe+RZA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 + '@babel/plugin-syntax-typescript@8.0.3': + resolution: {integrity: sha512-jmTPwps7oSQSZaV1SxkQ3C12UWyufGysGc5OzDpZzvPAIX4mO7dJT3hoqkWVrSImvkcMiknir1iLN1SNV/CZzg==} + engines: {node: ^22.18.0 || >=24.11.0} + peerDependencies: + '@babel/core': ^8.0.0 + '@babel/plugin-syntax-unicode-sets-regex@7.18.6': resolution: {integrity: sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==} engines: {node: '>=6.9.0'} @@ -996,6 +1301,12 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 + '@babel/plugin-transform-destructuring@8.0.1': + resolution: {integrity: sha512-RtR8uLDl0QcCmqMNIkM8gmDeYZ3rS0ZH+sa+I6sfc09yFoqfp9AEPgBstq9KyfVb0lFCVSRFfJXCI70FIl5ccw==} + engines: {node: ^22.18.0 || >=24.11.0} + peerDependencies: + '@babel/core': ^8.0.0 + '@babel/plugin-transform-dotall-regex@7.29.7': resolution: {integrity: sha512-3qc18hsD2RdZiyJNDNc7HQpv6xbncwh8FYtxNFFzclSyh/trPD9KkVR9BDECUjDLvb7yJVF15GfYUuC+LMkkiQ==} engines: {node: '>=6.9.0'} @@ -1026,6 +1337,12 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 + '@babel/plugin-transform-explicit-resource-management@8.0.1': + resolution: {integrity: sha512-VzDIYwBlLCpV6mJfloRdJm8HmYnMqs7O+bGha8yfg2kP7jAdxeCw6yZBVBeaKKQUThtSU52iy+3lB7DhYsbOBA==} + engines: {node: ^22.18.0 || >=24.11.0} + peerDependencies: + '@babel/core': ^8.0.0 + '@babel/plugin-transform-exponentiation-operator@7.29.7': resolution: {integrity: sha512-zFpMOTLZBdW5LfObqcSbL6kefg4R4eLdmvS0wbN9M6D5Mym/sKm9toOoWyVOa+xDjvCnuWcHls2YonXwHvH3CQ==} engines: {node: '>=6.9.0'} @@ -1086,6 +1403,12 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 + '@babel/plugin-transform-modules-commonjs@8.0.1': + resolution: {integrity: sha512-PMuzulWrrzFNmY3lXSk/tV9NRb7y0eZZLJY4UEo2TKszroxvUZHAPPi+T9FDyrQhod+TQA+t+8/QYaaMpiEuhA==} + engines: {node: ^22.18.0 || >=24.11.0} + peerDependencies: + '@babel/core': ^8.0.0 + '@babel/plugin-transform-modules-systemjs@7.29.7': resolution: {integrity: sha512-TM2ZcQLoG2/y4HODiStCo10DibYhWhGWAwVv+EQKmG/7GFl0N+AAmUiXOMKM+aiJ9XBJ9AHVZBvTzMnJ2sM3cQ==} engines: {node: '>=6.9.0'} @@ -1182,12 +1505,24 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 + '@babel/plugin-transform-react-display-name@8.0.1': + resolution: {integrity: sha512-soLishXlkyu6jcICPyO3HEP7A3GCzKEnn7XfvYrImuWEOwFAz93qShmWSYPf5ww0ZkO4By0zsN2bVIDF54fSdA==} + engines: {node: ^22.18.0 || >=24.11.0} + peerDependencies: + '@babel/core': ^8.0.0 + '@babel/plugin-transform-react-jsx-development@7.29.7': resolution: {integrity: sha512-Xfy3UVMF04+ypnFbkhvfqtmvwfe92qwQdbGZVonhE+6v35GzlofmOnA1szaZqzb9xYWr0nl1e5EMmzi0DNON1g==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 + '@babel/plugin-transform-react-jsx-development@8.0.1': + resolution: {integrity: sha512-Hb+HUZpV9KFHjm+F+P3aLDMi8QXU9l3ROCQv20z18Me2sGyW5nNNR5YTevNlgHvCpFek3BnAwhDGq/BRndXViw==} + engines: {node: ^22.18.0 || >=24.11.0} + peerDependencies: + '@babel/core': ^8.0.0 + '@babel/plugin-transform-react-jsx-self@7.29.7': resolution: {integrity: sha512-TL0hMc9xzy86VD31nUiwzd5otRAcyEPcsegCxolO0PvcXuH1v0kECe/UIznYFihpkvU5wg/jk4v0TTEFfm53fw==} engines: {node: '>=6.9.0'} @@ -1206,12 +1541,24 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 + '@babel/plugin-transform-react-jsx@8.0.1': + resolution: {integrity: sha512-NgkoF7Uq+30TmOPDdNUimT0Nta02uVjqJRFNlVWKrbOCu/CkzfHa4aMnIs0lMpkMmZmWA1e42Va+F04i/pY1zw==} + engines: {node: ^22.18.0 || >=24.11.0} + peerDependencies: + '@babel/core': ^8.0.0 + '@babel/plugin-transform-react-pure-annotations@7.29.7': resolution: {integrity: sha512-H5E+HBgDpr6Q5t+Aj11tL7XkIui1jhbIoArVQnqjgXo5/3YxkN7ZEBcWF4RQlB0T4rrxJQbXS6kiFV6B7XTqUA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 + '@babel/plugin-transform-react-pure-annotations@8.0.1': + resolution: {integrity: sha512-7/8UwU8hoPBurXa9tUiTTC8aACTRy5tCqLUtqikHp2eGiWoEB57AduOdbQ71OOMTEvawKrGhv3WfzkDpI+/oSg==} + engines: {node: ^22.18.0 || >=24.11.0} + peerDependencies: + '@babel/core': ^8.0.0 + '@babel/plugin-transform-regenerator@7.29.7': resolution: {integrity: sha512-rNNFV0DBAJp988xW2DOntfDoYn1eR8GGF5AT5vYc+rjyfaQkM242c9tZUHHPe7KYaiJizXPWhQTzzdbXySyhBw==} engines: {node: '>=6.9.0'} @@ -1266,6 +1613,12 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 + '@babel/plugin-transform-typescript@8.0.1': + resolution: {integrity: sha512-0Svqp3413Eg0GElldykF/T7SNsxQO5YVGD70fZyAdZTnX8WRgcopmbiU7GTa5xY5ZnJcEpNbfns8/GjX+/1yeA==} + engines: {node: ^22.18.0 || >=24.11.0} + peerDependencies: + '@babel/core': ^8.0.0 + '@babel/plugin-transform-unicode-escapes@7.29.7': resolution: {integrity: sha512-jCfXxSjf94lf4E0hKE0AByxF6F3/pVFqRdUUNkDJhsY0m1ZKjnN6ZYyMeHNpzflxb/0q5b7t3p+BE+SLF1WOtA==} engines: {node: '>=6.9.0'} @@ -1307,12 +1660,24 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 + '@babel/preset-react@8.0.1': + resolution: {integrity: sha512-jrFuPp/pTddFZbtmWhdLNAYc6UMcpboeUPnw0BBrm4nOmcAko/1TRcFi1PzWCeOFRU+VaSiKmat87W1HvR7mIg==} + engines: {node: ^22.18.0 || >=24.11.0} + peerDependencies: + '@babel/core': ^8.0.0 + '@babel/preset-typescript@7.29.7': resolution: {integrity: sha512-/Foi8vKY2EVbed/1eZx0gJEEwHAIxogrySI7rULcRIvhZzbvoE/b5qG5Ghc0WKAFKOHA9SD1x7RsFlOYdutIiQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 + '@babel/preset-typescript@8.0.1': + resolution: {integrity: sha512-qrPhQIN1NLrPmzgazF9XKQqXrOcp/WJly+K+6ReFonn24FZqRJO7clxOJo6Ni75L+2vAqI3cHVU2OJLBxoPp5A==} + engines: {node: ^22.18.0 || >=24.11.0} + peerDependencies: + '@babel/core': ^8.0.0 + '@babel/runtime-corejs3@7.29.7': resolution: {integrity: sha512-ppj9ouYku+RX0ljtgZd+KMO5mkM2bCqg8H2PYAFWnLsHEIKIdRojqbJ2i3eVHrisuxy7nOFCmngTDdWtUCdXUQ==} engines: {node: '>=6.9.0'} @@ -1325,25 +1690,46 @@ packages: resolution: {integrity: sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==} engines: {node: '>=6.9.0'} + '@babel/template@8.0.0': + resolution: {integrity: sha512-eAD0QW/AlbamBbw0FeGiwasbCVPq5ncW0HNVyLP3B9czqLyh4gvw+5JTSNt6le9+ziAU7mqDZsKTHf3jTb4chQ==} + engines: {node: ^22.18.0 || >=24.11.0} + '@babel/traverse@7.29.7': resolution: {integrity: sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==} engines: {node: '>=6.9.0'} + '@babel/traverse@8.0.4': + resolution: {integrity: sha512-bZnmqzGG8UZneG1lLxBoWIH0G6Gr1D846Yu4/3XnY6FhCndMR49u26nTY08u/dAxWmLWF9vGQOuC+84FfIUoeg==} + engines: {node: ^22.18.0 || >=24.11.0} + '@babel/types@7.29.7': resolution: {integrity: sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==} engines: {node: '>=6.9.0'} + '@babel/types@8.0.4': + resolution: {integrity: sha512-eY+Yn3dCqTGmyiq2QRU66lA5FL8lqqqvecHt0fF3uHONIa7ToYsaCiWV8lOKqAs0Rb2SjixiKFROngnulPtt2g==} + engines: {node: ^22.18.0 || >=24.11.0} + '@base-org/account@1.1.1': resolution: {integrity: sha512-IfVJPrDPhHfqXRDb89472hXkpvJuQQR7FDI9isLPHEqSYt/45whIoBxSPgZ0ssTt379VhQo4+87PWI1DoLSfAQ==} '@base-org/account@2.4.0': resolution: {integrity: sha512-A4Umpi8B9/pqR78D1Yoze4xHyQaujioVRqqO3d6xuDFw9VRtjg6tK3bPlwE0aW+nVH/ntllCpPa2PbI8Rnjcug==} + '@bcoe/v8-coverage@1.0.2': + resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==} + engines: {node: '>=18'} + '@blockscout/admin-rs-types@1.5.1': resolution: {integrity: sha512-+wGwULmzzfwkASJKwPB1X09iqmNUeIPu7Pc5INQxL2XunllTs32bhyvji1XqyA+VmF5yOxxYhQuLhNqCeN4SfQ==} +<<<<<<< HEAD '@blockscout/api-types@0.0.1-beta.d261ddc': resolution: {integrity: sha512-9xB/r+C+MrtGhkielJojMPPfEQXQu+wpU09pctQoRfyfkdGVfWz5HCz8fqRY8D6CrBbR8P8+TKXYyF/NhwUJBA==} +======= + '@blockscout/api-types@0.0.1-beta.40c5c457b4': + resolution: {integrity: sha512-CF6Qc4rGsi1XT6n2dxMFaF+CqVWSOucw59KAeHCvpyVfhnk+1Jlqq78Vl7p4mmrmofQ7w+/K4dTFWeroOT+jHw==} +>>>>>>> v2.12.0 '@blockscout/bens-types@1.7.1': resolution: {integrity: sha512-MNIvYbj1I2vcU2rb6otlRnVMIpFG2B1WDqC7HicNrt10DbM5yPZNfBcsNM+Mhk/965Aeg529Fd+BkV9zhINqjA==} @@ -1351,17 +1737,17 @@ packages: '@blockscout/contracts-info-types@1.5.3': resolution: {integrity: sha512-J+n4C+H9418r2xcIJLPEpK6WJnhJVRGZVaRzpUpXmcxBkIqzsKqfxBMddaDt5uQX8OnQJyZY3fSkfoGAAfBQaw==} - '@blockscout/interchain-indexer-types@1.6.0': - resolution: {integrity: sha512-zWrL3tWqXqvQre9SURlheD6jxp447Ujv7tP35a94K0Ovs3bYCT/ydsUmLoZY3CrqKyRr6cTu8V/1qUql5l9Cpw==} + '@blockscout/interchain-indexer-types@1.11.0': + resolution: {integrity: sha512-vQvnAiqQJVJXmBGtRYeuz64Qlu+IgexSjkI+54VHldic0wIuxbgsgT3HWxYx4p2mZ3Yn/HpCKhcX9ijl2dWJQQ==} - '@blockscout/multichain-aggregator-types@2.1.6': - resolution: {integrity: sha512-qpotHqUXhp8NFZ9JioCn2B/gewWQ5stTV9QoHKnUbO/0HzXkLvJXdsOjqhkBlaVcOKEMFY08+T3t9O0oI0wQEA==} + '@blockscout/multichain-aggregator-types@2.1.8': + resolution: {integrity: sha512-j8dqb4h22FYQv3JdYa2pJp30P8YmLW556z/6u/JaT8rQyPylUY1oIZJ5944yhOtJsDUAgwzLsafFiPShTLgRog==} '@blockscout/points-types@1.4.1': resolution: {integrity: sha512-8aIE1od1zzXeVfQCIO8LU1aWT/CRx82t+1ZK4RbgFFrxWH8RX0Yo+I6haeraVj3qKjxiMkBda1xMbGohiKgXSg==} - '@blockscout/stats-types@2.11.1': - resolution: {integrity: sha512-Ti9RbekRfLR7dUnOp2dWU0VBu7uRkutnkFqKphvfxvbrY9fiXUJ/tT6Qg+OTNewK3RbuQ0O1ZPxbqMX5eRr0OQ==} + '@blockscout/stats-types@2.19.1': + resolution: {integrity: sha512-94PhDtR+WbylCuV01JhxW3f1jm4LfAIsY+yMgLEGam/iYcleZ4oMdCW9TtJj0X4HokyWWYTkUS2JGENn0WLE9Q==} '@blockscout/tac-operation-lifecycle-types@0.0.1-beta.71a05d5': resolution: {integrity: sha512-SFHCKG+Q1c+zn91HGcPdfVoLlxmk2uD55IiILJ6VIgfg5IhHLKLeu4+eoTwByq6fMirmyy1F/ywIdRhhQBFjUw==} @@ -1745,10 +2131,6 @@ packages: '@csstools/normalize.css@12.1.1': resolution: {integrity: sha512-YAYeJ+Xqh7fUou1d1j9XHl44BmsuThiTr4iNrgCQ3J27IbhXsxXDGZ1cXv8Qvs99d4rBbLiSKy3+WZiet32PcQ==} - '@dependents/detective-less@4.1.0': - resolution: {integrity: sha512-KrkT6qO5NxqNfy68sBl6CTSoJ4SNDIS5iQArkibhlbGU4LaDukZ3q2HIkh8aUKDio6o4itU4xDR7t82Y2eP1Bg==} - engines: {node: '>=14'} - '@discoveryjs/json-ext@0.5.7': resolution: {integrity: sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==} engines: {node: '>=10.0.0'} @@ -2338,6 +2720,55 @@ packages: resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + '@eslint-react/ast@5.19.0': + resolution: {integrity: sha512-6d9UY/CVz+zGMi6+aext7HSOVAj2Fa9eL7okBRnjBeMrRhmRAqtbm1oK7J+VlWrmUNLsiNXpyklhq36BsmYddg==} + engines: {node: '>=22.0.0'} + peerDependencies: + eslint: '*' + typescript: '*' + + '@eslint-react/core@5.19.0': + resolution: {integrity: sha512-PFhetWfvG6ZhLiJomkzjKKCLu6DVbKdyeYZ7HE7/rvAvmfsNi9iCGBqxkgXWfWpWEf24KrRUVpS3wLGJpPL8+g==} + engines: {node: '>=22.0.0'} + peerDependencies: + eslint: '*' + typescript: '*' + + '@eslint-react/eslint-plugin@5.19.0': + resolution: {integrity: sha512-SA3+Iz87sH3pq8hFHb+QhYrOpOyzut+k9YCx2WCJsapK3fu7odk73GRXB6J/W5f8tz0J0ARj1r+DsaSdGtF/zw==} + engines: {node: '>=22.0.0'} + peerDependencies: + eslint: '*' + typescript: '*' + + '@eslint-react/eslint@5.19.0': + resolution: {integrity: sha512-wod1Yd/atJbUD36naGirzztZ9w4BAWSxwIxF8/6qT7PaN/Q9r/vTB/T5rmmgvMa3C/hp0yI3uqr4zbO2mRPBOQ==} + engines: {node: '>=22.0.0'} + peerDependencies: + eslint: '*' + typescript: '*' + + '@eslint-react/jsx@5.19.0': + resolution: {integrity: sha512-iqauNb+qeNaEbG7VfDj+8taRLJFbEkptzdPx+YnIbyxhCVAnMaB3s/yeVyi4nszja7YwE/FccFInPZ4Qv8fVZw==} + engines: {node: '>=22.0.0'} + peerDependencies: + eslint: '*' + typescript: '*' + + '@eslint-react/shared@5.19.0': + resolution: {integrity: sha512-4x56YzT3Gph725Z8x1vMbOYUkOpbqp2v9NuFb9F7GqVmyZq9OxAQPEj5b7o+Fd2C1VIC3mHNCfL3FHVZ1YH8mA==} + engines: {node: '>=22.0.0'} + peerDependencies: + eslint: '*' + typescript: '*' + + '@eslint-react/var@5.19.0': + resolution: {integrity: sha512-dRRM/AYqQli1VNijY7n0+q4zUlNCGiOY1wGr1r+c5ajvlv/XwGibhTJjY6zGZWIjL9TUMIbIEH8aOdRaw2iNQg==} + engines: {node: '>=22.0.0'} + peerDependencies: + eslint: '*' + typescript: '*' + '@eslint/compat@1.2.2': resolution: {integrity: sha512-jhgiIrsw+tRfcBQ4BFl2C3vCrIUw2trCY0cnDvGZpwTtKCEDmZhAtMfrEUP/KpnwM6PrO0T+Ltm+ccW74olG3Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -2623,14 +3054,8 @@ packages: cpu: [arm64] os: [darwin] - '@img/sharp-darwin-arm64@0.34.5': - resolution: {integrity: sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [arm64] - os: [darwin] - - '@img/sharp-darwin-arm64@0.35.3': - resolution: {integrity: sha512-RMnFX7YQsMoh7lWfcM4NEHHymBX/rLuKNPVM84XE9ONPcaSCDgE7CHIHpSgPcO2xcRthgBy1HfNO319mwhIAkg==} + '@img/sharp-darwin-arm64@0.35.4': + resolution: {integrity: sha512-Uhfl4V4lhP2nbUVF9+hyH1+luj86f1gUFeo8ALYxFoULoU+G87D43BfeMP8XHsk9boxAnCY/bf2EHwhA7MuGsA==} engines: {node: '>=20.9.0'} cpu: [arm64] os: [darwin] @@ -2641,20 +3066,14 @@ packages: cpu: [x64] os: [darwin] - '@img/sharp-darwin-x64@0.34.5': - resolution: {integrity: sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [x64] - os: [darwin] - - '@img/sharp-darwin-x64@0.35.3': - resolution: {integrity: sha512-Xo+5uFBtLN0BKqieTxiFzFPQAUlBbbH5iBKyRX/z1JrbnYsHTfKJnUfL8+p2TPXr1pXqao4eeL4Rl144uDpK9w==} + '@img/sharp-darwin-x64@0.35.4': + resolution: {integrity: sha512-hWniXY3bG5qKpkKrAwPe4y+VTPmf086YQAnkxWh7uA1YrlRouWGa0M0Mxj3ZjnXFkv7/TD1bTy9lGUK26vRvWw==} engines: {node: '>=20.9.0'} cpu: [x64] os: [darwin] - '@img/sharp-freebsd-wasm32@0.35.3': - resolution: {integrity: sha512-lUxcqWIj2wMQ9BrwNjngcr1gWUr5xgaGThBRqPPalIC2n67Cqj1uPh8NnA/ZhAg8hUbKl+kVHKwgUIwe6ZYPrg==} + '@img/sharp-freebsd-wasm32@0.35.4': + resolution: {integrity: sha512-lIsKw/BU+kjB4eZjxrYrZmwOJYi3Ajrv66iAlBmUPyKc3HpnloevB1g3wxGD9P/5BbQ1brBGl65VRRrCvQDEqA==} engines: {node: '>=20.9.0'} os: [freebsd] @@ -2663,13 +3082,8 @@ packages: cpu: [arm64] os: [darwin] - '@img/sharp-libvips-darwin-arm64@1.2.4': - resolution: {integrity: sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==} - cpu: [arm64] - os: [darwin] - - '@img/sharp-libvips-darwin-arm64@1.3.2': - resolution: {integrity: sha512-9J6ypZFpQBj4YnePGoq/S38w6nz+vqg5WZLrLGY4YuSemdMq47GMLBPO42MzwdGwpg/agZ7xzZcFHa48xlywfg==} + '@img/sharp-libvips-darwin-arm64@1.3.3': + resolution: {integrity: sha512-suTBPTDGrI9WodccaDdwZItTSaBYASlBk1NSfElSHrUfzu3szG6lvIF58+WiFvnfzuK8ZBFS5zE00PxqxnRiPg==} cpu: [arm64] os: [darwin] @@ -2678,13 +3092,8 @@ packages: cpu: [x64] os: [darwin] - '@img/sharp-libvips-darwin-x64@1.2.4': - resolution: {integrity: sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==} - cpu: [x64] - os: [darwin] - - '@img/sharp-libvips-darwin-x64@1.3.2': - resolution: {integrity: sha512-m2pW1n6cns9VaubNwsZ+c3CRYjxNQWgJ5gPlnL1nbBcpkBvFm6SCFN5o0psFHI8w9n11NKhFkeEDns98tiqbEw==} + '@img/sharp-libvips-darwin-x64@1.3.3': + resolution: {integrity: sha512-FVJZ5mITMobmXIz/hPDTw0EintTW5H3WfrxwLqEqjiIihlu+hVRyGrFQ60xl0Lxn7Bt3zdpevPaQi0HEzqz9fw==} cpu: [x64] os: [darwin] @@ -2694,14 +3103,8 @@ packages: os: [linux] libc: [glibc] - '@img/sharp-libvips-linux-arm64@1.2.4': - resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==} - cpu: [arm64] - os: [linux] - libc: [glibc] - - '@img/sharp-libvips-linux-arm64@1.3.2': - resolution: {integrity: sha512-dqVSFynCox4C/J8kT16V7SIFAns0IjgLwkvYT7p8LQVmJ5OS5b6tI9IGflxTeuBS//zXeFIUbwt5dwxyZ17cnA==} + '@img/sharp-libvips-linux-arm64@1.3.3': + resolution: {integrity: sha512-0DaL0A6Xu6sQSQFwe4iVCrKWU2cCTItnRsYsCdxAMm9NF6twAA9BKnoqy4hqz4+azQ0JHuA26qiUKsf1XJ/v5A==} cpu: [arm64] os: [linux] libc: [glibc] @@ -2712,38 +3115,20 @@ packages: os: [linux] libc: [glibc] - '@img/sharp-libvips-linux-arm@1.2.4': - resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==} + '@img/sharp-libvips-linux-arm@1.3.3': + resolution: {integrity: sha512-3rbU4vqXXc3hY/OiXdl52xZvT0F1yEngWfvqudtPJg/KkyiaQw2DRsFrNzpmLvfavbwOq3qXn36GP8obHRULQA==} cpu: [arm] os: [linux] libc: [glibc] - '@img/sharp-libvips-linux-arm@1.3.2': - resolution: {integrity: sha512-1eMLzy92I4J6rmi4mAT8yC3HxOtniyGELlzGbNMLLeqe052ahFQ0h6LFq+lh5DsDIdYViIDst08abvSbcEdLXQ==} - cpu: [arm] - os: [linux] - libc: [glibc] - - '@img/sharp-libvips-linux-ppc64@1.2.4': - resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==} + '@img/sharp-libvips-linux-ppc64@1.3.3': + resolution: {integrity: sha512-cdn1OvUBwsXhbC0zSzJnNzf5MZ/mTrobawDvNXBTxe8VtqKAm0sRuEY2Evzovb/w9JMk4TvRxqt1mekSuJz64w==} cpu: [ppc64] os: [linux] libc: [glibc] - '@img/sharp-libvips-linux-ppc64@1.3.2': - resolution: {integrity: sha512-3z0NHDxD6n5I9gc05U1eW1AyRm+Gznzq3naMrthPNqE6oYykcogW0l/jfpJdjYnuNl8R7yI9pNbE1XiUeyq0Aw==} - cpu: [ppc64] - os: [linux] - libc: [glibc] - - '@img/sharp-libvips-linux-riscv64@1.2.4': - resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==} - cpu: [riscv64] - os: [linux] - libc: [glibc] - - '@img/sharp-libvips-linux-riscv64@1.3.2': - resolution: {integrity: sha512-bsb4rI+NldGOsXuej2r8OdSS8+zXDVaCWxyWrcv6kneTOlgAHtZABRzBBCwdsPiD90J4myNJuHpg6kA20ImW/w==} + '@img/sharp-libvips-linux-riscv64@1.3.3': + resolution: {integrity: sha512-HjPVx7yKz+0lqdhDlTw1tt90wamBoxhiXpvl1XZpJLiHH4RCJ5yDTqH+VlYPv2fwFs89JFw4c1IexYOcQUi4IQ==} cpu: [riscv64] os: [linux] libc: [glibc] @@ -2754,14 +3139,8 @@ packages: os: [linux] libc: [glibc] - '@img/sharp-libvips-linux-s390x@1.2.4': - resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==} - cpu: [s390x] - os: [linux] - libc: [glibc] - - '@img/sharp-libvips-linux-s390x@1.3.2': - resolution: {integrity: sha512-/ABshyj8gCpyIrNXnHn4LorDJ0HHm1VhXPBlxZ8zAtfVPAaSafXPGn+sUSIRiwaSBy0mmFjSjiXI5mkcwdChKQ==} + '@img/sharp-libvips-linux-s390x@1.3.3': + resolution: {integrity: sha512-neWLh+3yCNThxnfy3c4BbVBeGgt9aftno+XbT56iK28RgeDs3UOFWviLWlUu0bArYVYJaFDK+RRohbicUNCm8Q==} cpu: [s390x] os: [linux] libc: [glibc] @@ -2772,14 +3151,8 @@ packages: os: [linux] libc: [glibc] - '@img/sharp-libvips-linux-x64@1.2.4': - resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==} - cpu: [x64] - os: [linux] - libc: [glibc] - - '@img/sharp-libvips-linux-x64@1.3.2': - resolution: {integrity: sha512-ITPEtgffGJ0S6G9dRyw/366tJQqFRcHWPHhC+Stpg3Z8AEMrDrTr2lhdz4f/Y/HMbRh//7Z5mBzEpVdi62Oc3w==} + '@img/sharp-libvips-linux-x64@1.3.3': + resolution: {integrity: sha512-4vKmvAst9nrowcqquKFAyZJUDolUaIp8uRiN0mWFguJ1IplC9/pitXtlnnlU4aa/eJw3J7i67V+pwUL+wZGdsA==} cpu: [x64] os: [linux] libc: [glibc] @@ -2790,14 +3163,8 @@ packages: os: [linux] libc: [musl] - '@img/sharp-libvips-linuxmusl-arm64@1.2.4': - resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==} - cpu: [arm64] - os: [linux] - libc: [musl] - - '@img/sharp-libvips-linuxmusl-arm64@1.3.2': - resolution: {integrity: sha512-zE9EdiUzUmg5mDT5a1rk5fYJ6GWPloTwWBYDS14naqHsL+EaMpDj1AWnpLgh3u0YCORv2Tt50wrcrpYqkP97Kw==} + '@img/sharp-libvips-linuxmusl-arm64@1.3.3': + resolution: {integrity: sha512-Y9kQaLMuNoB0bPYOOdcZMaseNrFpPodIWWMrx+CZyydf2xn68j9WYc6sWWRrDwNkzCQjKYfc68L7jKjGlHMibw==} cpu: [arm64] os: [linux] libc: [musl] @@ -2808,14 +3175,8 @@ packages: os: [linux] libc: [musl] - '@img/sharp-libvips-linuxmusl-x64@1.2.4': - resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==} - cpu: [x64] - os: [linux] - libc: [musl] - - '@img/sharp-libvips-linuxmusl-x64@1.3.2': - resolution: {integrity: sha512-m0lrLiUt+lBYnCFr8qV/65yMR4E/c7/wf78I5eKTdkEakFAlZ9QlzEM3QIhhAwVeUhLAHLcCq7a7Vszq/oFNZQ==} + '@img/sharp-libvips-linuxmusl-x64@1.3.3': + resolution: {integrity: sha512-fj8Mv0HHfD1Rr+4I68+3agJynxDWtBFgicTbSOb9Bke6pIwzGcJ+RX/yHjmiEGFMCavY/dxvem7MyNaJF+wDiw==} cpu: [x64] os: [linux] libc: [musl] @@ -2827,15 +3188,8 @@ packages: os: [linux] libc: [glibc] - '@img/sharp-linux-arm64@0.34.5': - resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [arm64] - os: [linux] - libc: [glibc] - - '@img/sharp-linux-arm64@0.35.3': - resolution: {integrity: sha512-QgKDspHPnrU+GQ55XPhGwyhC8acLVOOSyAvo1oVfFmrIXLkDNmGWzAfDZ4xK8oSA1qBQrALcHX0G5UZni/SuFQ==} + '@img/sharp-linux-arm64@0.35.4': + resolution: {integrity: sha512-De4jpEnAU8Hd5oT0j1G3uL4ZvTuipVMn7YC6vPaJhy6/7EwEae0SVAoBrUMYQbkLGDm85taVWwuPc1a44LTzCQ==} engines: {node: '>=20.9.0'} cpu: [arm64] os: [linux] @@ -2848,43 +3202,22 @@ packages: os: [linux] libc: [glibc] - '@img/sharp-linux-arm@0.34.5': - resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [arm] - os: [linux] - libc: [glibc] - - '@img/sharp-linux-arm@0.35.3': - resolution: {integrity: sha512-affVWCTLooy8TSxbDx2qkzuDeaWLNVBA+P//FNBirHsXpP2fuBhk5AuboYUnrDnzoXes8GFjpTx0SBFOCRg+FA==} + '@img/sharp-linux-arm@0.35.4': + resolution: {integrity: sha512-7OAS8gI0EReKGVN2HssHlM6umJgxF5VI3xN0p9FA91p/YO+ou5hiNghLdZ5BEHztwaaK5+bLKRf8x/o2L2nk9A==} engines: {node: '>=20.9.0'} cpu: [arm] os: [linux] libc: [glibc] - '@img/sharp-linux-ppc64@0.34.5': - resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [ppc64] - os: [linux] - libc: [glibc] - - '@img/sharp-linux-ppc64@0.35.3': - resolution: {integrity: sha512-sMd8rDxmpLOwv/7N44klFjOD5DUO7FLdjiXDI0hoxYaf7Ar262dQIEkosE98bps+5HPLtp/EvNqeqQtOycP/IA==} + '@img/sharp-linux-ppc64@0.35.4': + resolution: {integrity: sha512-2oYZJeIl4kCcMGk4ouZVjnkCtFrpQFlNEtJ6GbxzhHQchwH0NH/qEb9ykmOl29dqwMq+JhFdZn+1ak2FKhI9fQ==} engines: {node: '>=20.9.0'} cpu: [ppc64] os: [linux] libc: [glibc] - '@img/sharp-linux-riscv64@0.34.5': - resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [riscv64] - os: [linux] - libc: [glibc] - - '@img/sharp-linux-riscv64@0.35.3': - resolution: {integrity: sha512-0Eob78yjlYPfL5vMNWAW55l3R9Y6BQS/gOfe0ZcP9mEz9ohhKSt4im1hayiknXgf8AWrFqMvJcKIdmLmEe7yeQ==} + '@img/sharp-linux-riscv64@0.35.4': + resolution: {integrity: sha512-cPbNChoRURAWdebDIHSenxRpgEdy7JkPydSnUxRm9VvKD7m0/xVaR/8Fzlu81pk5nHEvHH87UZUA7cTtwnbJSA==} engines: {node: '>=20.9.0'} cpu: [riscv64] os: [linux] @@ -2897,15 +3230,8 @@ packages: os: [linux] libc: [glibc] - '@img/sharp-linux-s390x@0.34.5': - resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [s390x] - os: [linux] - libc: [glibc] - - '@img/sharp-linux-s390x@0.35.3': - resolution: {integrity: sha512-KgAxQ0DxpNOq1rG2t5cgTgShJFGSuU7XO45cqC+1NVOuZnP6tlgZRuSYOfNupGkHID0o3cJOsw4DVeJpMovcGw==} + '@img/sharp-linux-s390x@0.35.4': + resolution: {integrity: sha512-RY0JFY8Fd6RonCBtHz+DvadaPkXDSI1AUn6yWL9TipqkZ1vY8w8evqdgyDFnkm4/K1ve1TvZiaePP5oSd4+WVQ==} engines: {node: '>=20.9.0'} cpu: [s390x] os: [linux] @@ -2918,15 +3244,8 @@ packages: os: [linux] libc: [glibc] - '@img/sharp-linux-x64@0.34.5': - resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [x64] - os: [linux] - libc: [glibc] - - '@img/sharp-linux-x64@0.35.3': - resolution: {integrity: sha512-8pqvxubL2PGdhlPy6GLqzDYMUjyRmKAwKHYKixpdJYBUK7PJ0C029XdsnpFIdgRZG68fZiGdHVWcKPvtiPB4cA==} + '@img/sharp-linux-x64@0.35.4': + resolution: {integrity: sha512-9qvvEAuk8k89TfWUoX2htWjbAMX8p+NxCppjpcg5k6xMsjhBQPTsoIh36h9Qde4WRuGpJeYnOjdosDn/cnv+OA==} engines: {node: '>=20.9.0'} cpu: [x64] os: [linux] @@ -2939,15 +3258,8 @@ packages: os: [linux] libc: [musl] - '@img/sharp-linuxmusl-arm64@0.34.5': - resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [arm64] - os: [linux] - libc: [musl] - - '@img/sharp-linuxmusl-arm64@0.35.3': - resolution: {integrity: sha512-Vz0iQjzzcSX3HCbfwFfCSG/9SCIqyO0mH2sXyiHaAYfBk0cRsCWXRyQYX0ovCK/PAQBbTzQ0dsPQHh5MAFL59w==} + '@img/sharp-linuxmusl-arm64@0.35.4': + resolution: {integrity: sha512-KB5jxpfWQTr0nc3xdHtWChdbifHrBGsd2SM62Eyxrl8afikm+f5qGBU75SJIZBT/S1MC8XyacdlXBMSWq6OURA==} engines: {node: '>=20.9.0'} cpu: [arm64] os: [linux] @@ -2960,15 +3272,8 @@ packages: os: [linux] libc: [musl] - '@img/sharp-linuxmusl-x64@0.34.5': - resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [x64] - os: [linux] - libc: [musl] - - '@img/sharp-linuxmusl-x64@0.35.3': - resolution: {integrity: sha512-6O1NPKcDVj9QEdg7Hx549EX8U0rp6yXQERqru6yRN7fGBn32UvIRJUlWnk+8xDCiG76hXVBbX82NZ/ZKr0euIg==} + '@img/sharp-linuxmusl-x64@0.35.4': + resolution: {integrity: sha512-f+eZJZIQNEEd26RPSW+76chwOf1XtA2Y/O+5ocVyLliHkeih3e+jhLVBdNTd2rS3IbNXK8+ug93Vf5ZXtF5Lxg==} engines: {node: '>=20.9.0'} cpu: [x64] os: [linux] @@ -2979,67 +3284,178 @@ packages: engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [wasm32] - '@img/sharp-wasm32@0.34.5': - resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + '@img/sharp-wasm32@0.35.4': + resolution: {integrity: sha512-zQnl4Kwp7Q6NHsENtU2T/00Zi+w3AQNwz3+UaTyVBy2FpXrzXzGjndpK61onhZjRtRpQXxCTeqw19bVyXOh7jA==} + engines: {node: '>=20.9.0'} + + '@img/sharp-webcontainers-wasm32@0.35.4': + resolution: {integrity: sha512-ESfNkywmCfPNyaZjxooddJQiQ+l/nTpGEOGthxiLnIHXC/CmcBixnfwUleX9mCz9ovrUUvKMap/pm8RYbzfwaA==} + engines: {node: '>=20.9.0'} cpu: [wasm32] - '@img/sharp-wasm32@0.35.3': - resolution: {integrity: sha512-cZ0XkcYGpHZkqW6iCkqTcmUC0CD9DhD5d/qeZlZkfRBn6GnHniZXLUo5+9xw8Iv76YE6LQFN9YNBlKREcCG76w==} - engines: {node: '>=20.9.0'} + '@img/sharp-win32-arm64@0.35.4': + resolution: {integrity: sha512-iNdlBX9gLVvqe2I3uIJSIKTq6wckP/DYxZtcqxm09x5Gi24DnFBmPAWZmr60ZyYMG0xlzo6goG3670ar+RXvRw==} + engines: {node: '>=20.9.0'} + cpu: [arm64] + os: [win32] + + '@img/sharp-win32-ia32@0.33.5': + resolution: {integrity: sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [ia32] + os: [win32] + + '@img/sharp-win32-ia32@0.35.4': + resolution: {integrity: sha512-kqRsbaa5CS6KHlpxnN7WhE6vAAugXyZButpRdvDWetlv6Qv4N9WTcrWzF7tXfB9T7MsoadqdI8hmwLq6UlLvtw==} + engines: {node: ^20.9.0} + cpu: [ia32] + os: [win32] + + '@img/sharp-win32-x64@0.33.5': + resolution: {integrity: sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [win32] + + '@img/sharp-win32-x64@0.35.4': + resolution: {integrity: sha512-XtmnYhBcrORsJ4XJngyzr/EWP0hRZLAZRFaApdKuviyqF78+ylxh2y06ZmtULAMOnObJ3ucpN0AcwSWnMowTRg==} + engines: {node: '>=20.9.0'} + cpu: [x64] + os: [win32] + + '@inquirer/ansi@2.0.7': + resolution: {integrity: sha512-3eTuUO1vH2cZm2ZKHeQxnOqlTi9EfZDGgIe3BL3I4u+rJHocr9Fz86M4fjYABPvFnQG/gGK551HqDiIcETwU6Q==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} + + '@inquirer/checkbox@5.2.2': + resolution: {integrity: sha512-Y5/bAScMy5Y+9isCx0SKbyJebMCaXXX5em0kxkj115eZNscgV9srOHrgyfS0e5xAVymIfOh9piYBKDILktsMMg==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/confirm@6.2.0': + resolution: {integrity: sha512-SKXarWrYhtpqOEctf9XGCGy29QjsvJAM0Aq9ZR9z4Ns94OmpqudOly+aSEfNqUf9SwsQaUgY9+Z8hyzG0xX8fw==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/core@12.0.0': + resolution: {integrity: sha512-+nnvFEXIB08CZNVXpvW3B+zHW96QXvGUjNKJ8NJIPqAZi5Kd4WhYt2S3C234ReepG1qw2HOlEUbjYVHBowXObA==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/editor@5.3.0': + resolution: {integrity: sha512-nnsP/IdJ8s83q7ZuObmgn12QM+uLCkab9E0Oordojbn62WUg1c+v9Ou/F/057pgh0ppX0W+Hj5bO/Dp5hsxQtA==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/expand@5.1.2': + resolution: {integrity: sha512-OWIH1IyyWqEKIyqC9Xy+Bnga7NkGMovFdo4atYZMUOTRqf6rO2WCv9E/1MyzvOErDBCxs+9UFliRUDc50xs/jw==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/external-editor@3.0.4': + resolution: {integrity: sha512-tZbbaK2ovq6vlrRBNQvjrypmrED/p5x2ncIHQ79cD55tei3dD96v5glMMA+6tiq7K104i/25DVYKWVPJuV6ptA==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true - '@img/sharp-webcontainers-wasm32@0.35.3': - resolution: {integrity: sha512-2rnq7bX3NzeR2T4YWgz8qiG4h3TSdMe+vN1iQXpJleSJ3SM5zQ8Fy2SyyXAWlbxpEZ2Y+Z4u1BePgJEYbSy80Q==} - engines: {node: '>=20.9.0'} - cpu: [wasm32] + '@inquirer/figures@2.0.8': + resolution: {integrity: sha512-tApbon79GM9ry56ja/Ud3SY2CL4TQsao9fIwDQbgTeNY55025GdMzQ2+UdegV/lx51VNGUB59M0v0nMpybYY4Q==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} - '@img/sharp-win32-arm64@0.34.5': - resolution: {integrity: sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [arm64] - os: [win32] + '@inquirer/input@5.1.3': + resolution: {integrity: sha512-F/BZHtyEzP+HO+IGVd4AjBRgvX/ywm42bx8S0+dENk2YclzE9tJ3X/15THwtT6ehApmKvdYDMsVTuyyDod0gOQ==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true - '@img/sharp-win32-arm64@0.35.3': - resolution: {integrity: sha512-4bPwFdMbeC4JQ8L8LOyWp6nsHcboP5fxkp6iPOXz2Vg49R42TuMs2whkJ5OAP4/Ul035qOzy0AecOF9VOscn4w==} - engines: {node: '>=20.9.0'} - cpu: [arm64] - os: [win32] + '@inquirer/number@4.2.0': + resolution: {integrity: sha512-ew+fSDijsQ/WhD4TV3XLb+if400cDuzTzHfGR8sTNBXkK9CYDWoGE8fhaO8GbT312pNv1AJEOsDxy/z/HVettA==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true - '@img/sharp-win32-ia32@0.33.5': - resolution: {integrity: sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [ia32] - os: [win32] + '@inquirer/password@5.1.2': + resolution: {integrity: sha512-nSdufycW8xynEVssFkNQEYIzTySilog0UlfOVRwh3pXzPSk4frXUT2jZWjHnKae6RU9PaoF9wfy1pGwewQuqGw==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true - '@img/sharp-win32-ia32@0.34.5': - resolution: {integrity: sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [ia32] - os: [win32] + '@inquirer/prompts@8.6.0': + resolution: {integrity: sha512-WgBVDRy3IQ4v9XMCpQ1YDGpso2PcMUxYJzZdH4Nt4t0eoXhEPmOCh5iZbXbR4GTbdUB9VPWBbJB12rkjbaGDCw==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true - '@img/sharp-win32-ia32@0.35.3': - resolution: {integrity: sha512-r53mXsBN6lFUDiST764SvgwUdHAqM4rPAiDzAmf4fLoB6X/rkfyTrLCg6+g17wJJiCmB3JYgHuUldCWUIRFSXw==} - engines: {node: ^20.9.0} - cpu: [ia32] - os: [win32] + '@inquirer/rawlist@5.3.2': + resolution: {integrity: sha512-oPSKrYK1X1bMkjXDzIKHUkJp195LFSfgbnVtXnjSKGFjrCbS6I+wyvfAZTwKE9BSt3HwWgfD7JfsXALBgCogzQ==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true - '@img/sharp-win32-x64@0.33.5': - resolution: {integrity: sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [x64] - os: [win32] + '@inquirer/search@4.3.0': + resolution: {integrity: sha512-HFxXE5w727ctSUcAwrDquftJGjMgu36OeV5SHEXMlr2j/ahzmRX9xSEeVolV8tzYnTf45cg6vGkdMMRdm3RPhQ==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true - '@img/sharp-win32-x64@0.34.5': - resolution: {integrity: sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [x64] - os: [win32] + '@inquirer/select@5.2.2': + resolution: {integrity: sha512-RkI8dRHWt+bh04oLixvF1kFzKC7e5rqJoHKkzcqSHATebBXFC6GmrT8ddbVkgSzLV0HnHs2cPFuBINr8otij8Q==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true - '@img/sharp-win32-x64@0.35.3': - resolution: {integrity: sha512-D4y1vNeZrIIJCN+uHaWVtH86B+aCrdMYYjicy9pXHvbGZeGYLLSd3wdVuC37FxVXlU1ARsk84eKWfWMXGYEqvA==} - engines: {node: '>=20.9.0'} - cpu: [x64] - os: [win32] + '@inquirer/type@4.0.7': + resolution: {integrity: sha512-t28inv14nMQ1PhKpsJPY+kEs/c00qzeCOS2gTNRyTjG5d6qsVA2fItxW4hkvGZ5lvanGLdtCzVIx5dwdRpN1+g==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true '@internationalized/date@3.12.2': resolution: {integrity: sha512-FY1Y+H64NDs+HAF6omlnWxm3mEpfgaCSWtL5l551ZZfImA+kGjPFgrnJrGjH6lfmLL0g8Z/mBu1R3kufeCp6Jw==} @@ -3106,6 +3522,9 @@ packages: '@jridgewell/sourcemap-codec@1.5.5': resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + '@jridgewell/sourcemap-codec@1.6.0': + resolution: {integrity: sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==} + '@jridgewell/trace-mapping@0.3.31': resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} @@ -3226,6 +3645,33 @@ packages: '@libp2p/websockets@9.2.19': resolution: {integrity: sha512-+g2qI9Lgvyofoc6GFztPoPVZV+z/lg9pIUfneHht6j88Y7tH3NrAQ7Ki+9lqS5XBX2h1O1bHULWbCaVYCj9TZg==} + '@lifi/widget-light@4.3.2': + resolution: {integrity: sha512-2TrQYZkMZjBSjbzyjy36bNSG+ZKuBclyxHNgrhYwEKCUU5xFLqUchxsJppDY/YIYJKdqjHcS5Yd0U6J3FeE7vQ==} + peerDependencies: + '@bigmi/client': ^0.10.1 + '@bigmi/react': ^0.9.1 + '@mysten/dapp-kit-react': ^2.1.3 + '@wagmi/core': ^3.5.0 + '@wallet-standard/base': ^1.1.0 + react: '>=19' + viem: '>=2.52.0' + wagmi: ^3.6.16 + peerDependenciesMeta: + '@bigmi/client': + optional: true + '@bigmi/react': + optional: true + '@mysten/dapp-kit-react': + optional: true + '@wagmi/core': + optional: true + '@wallet-standard/base': + optional: true + viem: + optional: true + wagmi: + optional: true + '@lit-labs/ssr-dom-shim@1.6.0': resolution: {integrity: sha512-VHb0ALPMTlgKjM6yIxxoQNnpKyUKLD04VzeQdsiXkMqkvYlAHxq9glGLmgbb889/1GsohSOAjvQYoiBppXFqrQ==} @@ -3480,115 +3926,60 @@ packages: '@next/env@13.5.11': resolution: {integrity: sha512-fbb2C7HChgM7CemdCY+y3N1n8pcTKdqtQLbC7/EQtPdLvlMUT9JX/dBYl8MMZAtYG4uVMyPFHXckb68q/NRwqg==} - '@next/env@16.2.6': - resolution: {integrity: sha512-gd8HoHN4ufj73WmR3JmVolrpJR47ILK6LouP5xElPglaVxir6e1a7VzvTvDWkOoPXT9rkkTzyCxBu4yeZfZwcw==} - - '@next/env@16.3.1': - resolution: {integrity: sha512-35G3xwkQUb2oETSDjFXGrVugknoayLFBh7vSE+yAcl9IP2zT9wyGwq7297AYHR11kJld807t5f8AJBs6WBzXsQ==} + '@next/env@16.3.5': + resolution: {integrity: sha512-NWEXVDMqoEo0ktmU6u0sE2Vg0LOcsD7NnOTJNo3/fEaTfsg+F1bMIxuDmQbda4e3yTIQwVdUREF2yIuMOusKtg==} '@next/eslint-plugin-next@16.2.6': resolution: {integrity: sha512-Z8l6o4JWKUl755x4R+wogD86KPeU+Ckw4K+SYG4kHeOJtRenDeK+OSbGcqZpDtbwn9DsJVdir2UxmwXuinUbUw==} - '@next/swc-darwin-arm64@16.2.6': - resolution: {integrity: sha512-ZJGkkcNfYgrrMkqOdZ7zoLa1TOy0qpcMfk/z4Mh/FKUz40gVO+HNQWqmLxf67Z5WB64DRp0dhEbyHfel+6sJUg==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [darwin] - - '@next/swc-darwin-arm64@16.3.1': - resolution: {integrity: sha512-ABMIu2zQ7cnNIHm5ivKGwZwUrm0pAai3yiJ/gK/rF1c1VP9UOnj7XECbMKFdVKp9I9eMYq9NoDs1WXOoowxzJw==} + '@next/swc-darwin-arm64@16.3.5': + resolution: {integrity: sha512-pMmGgETfKvElucLHtVaeiMRbp2zUbvKx7b1yGko0liBz3cw1mKSggWN/Rp/wPz8z+E1O82u3r4L1Co+ZS5hokQ==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] - '@next/swc-darwin-x64@16.2.6': - resolution: {integrity: sha512-v/YLBHIY132Ced3puBJ7YJKw1lqsCrgcNo2aRJlCEyQrrCeRJlvGlnmxhPxNQI3KE3N1DN5r9TPNPvka3nq5RQ==} + '@next/swc-darwin-x64@16.3.5': + resolution: {integrity: sha512-76VaGYvf6HPa5/w12yLkE3dXTn9AfdEviI79oEL3aZoAmRLc9rWitjWqyjViVysK/ht/y9YKzFkBrUdi/wGkow==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] - '@next/swc-darwin-x64@16.3.1': - resolution: {integrity: sha512-gNG21e/UnrroeScbY/QndUEdl0mF1FRibW7BBeYUz/5ABCepjqDdEdgr592vpzMtCn/m7FTjYq3TN4TpyDnutw==} - engines: {node: '>= 10'} - cpu: [x64] - os: [darwin] - - '@next/swc-linux-arm64-gnu@16.2.6': - resolution: {integrity: sha512-RPOvqlYBbcQjkz9VQQDZ2T2bARIjXZV1KFlt+V2Mr6SW/e4I9fcKsaA0hdyf2FHoTlsV2xnBd5Y912rP/1Ce6w==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [linux] - libc: [glibc] - - '@next/swc-linux-arm64-gnu@16.3.1': - resolution: {integrity: sha512-6B6Lw016iwNUQuaJoraMMTLh6TwHzFUtxipSScD1F3YyymcrRWkobodRS2ftIOkF5vrs4zNlyUrTC5YZQ9Lz5w==} + '@next/swc-linux-arm64-gnu@16.3.5': + resolution: {integrity: sha512-zKDELJ5jSQMHeO/hmXUQsAzagX4bQD4OiMi3pQ5FbUj+yK506oLVHnKA2YXMlbg1EHHqJYtyePOgByIDXD1lqw==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] libc: [glibc] - '@next/swc-linux-arm64-musl@16.2.6': - resolution: {integrity: sha512-URUTu1+dMkxJsPFgm+OeEvq9wf5sujw0EvgYy80TDGHTSLTnIHeqb0Eu8A3sC95IRgjejQL+kC4mw+4yPxiAXA==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [linux] - libc: [musl] - - '@next/swc-linux-arm64-musl@16.3.1': - resolution: {integrity: sha512-JUiPXZKK9wOhjf4MgDiH29GZLxfqOesbLtHq2pDxwH/WwscTRV2ToymnOTh1egzaZf0ueUf8T2+CeYTGHjW0Iw==} + '@next/swc-linux-arm64-musl@16.3.5': + resolution: {integrity: sha512-7Vql0pgzCoHagv6+FNOZoqmJqA52c6zeVbhtS/47qFozO1MSx4ms7x7GHiciY8R5CDsSMKMQjJEryoJLcsBIbA==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] libc: [musl] - '@next/swc-linux-x64-gnu@16.2.6': - resolution: {integrity: sha512-DOj182mPV8G3UkrayLoREM5YEYI+Dk5wv7Ox9xl1fFibAELEsFD0lDPfHIeILlutMMfdyhlzYPELG3peuKaurw==} - engines: {node: '>= 10'} - cpu: [x64] - os: [linux] - libc: [glibc] - - '@next/swc-linux-x64-gnu@16.3.1': - resolution: {integrity: sha512-Uog9jsrmIRIL/lfvIp9htmskSNC7JcQsMVucXL2V2YY1y/D9IUN3LPEafqy0zRJ2cIU1SQ0V6F6TlffQ+pLAGg==} + '@next/swc-linux-x64-gnu@16.3.5': + resolution: {integrity: sha512-NH/xzehyHEFWE2nlcZon7TB/0+H4shfWCi7S1zka815XCOhJDYZhoeJtOYy0dh0WVRWACVXSyGNFFytoMxUhRg==} engines: {node: '>= 10'} cpu: [x64] os: [linux] libc: [glibc] - '@next/swc-linux-x64-musl@16.2.6': - resolution: {integrity: sha512-HKQ5SP/V/ub73UvF7n/zeJlxk2kLmtL7Wzrg4WfmkjmNos5onJ2tKu7yZOPdL18A6Svfn3max29ym+ry7NkK4g==} - engines: {node: '>= 10'} - cpu: [x64] - os: [linux] - libc: [musl] - - '@next/swc-linux-x64-musl@16.3.1': - resolution: {integrity: sha512-6yy3FT13KgUFOj5H8bl8w/6nKiJwHIvbtwh1V+1acsu+7y4tJjnemSa6mhsh53BeoVrlozE+fMgZhXH46WmjMA==} + '@next/swc-linux-x64-musl@16.3.5': + resolution: {integrity: sha512-lV4+EhWMfS8jcC+EH2nn/Cm5cn6XsgbE07bU9tMH8fCo0tNAqhyzi1b5wQ/Tn6NGFTvKDY65w3ZH95EjwBRAnQ==} engines: {node: '>= 10'} cpu: [x64] os: [linux] libc: [musl] - '@next/swc-win32-arm64-msvc@16.2.6': - resolution: {integrity: sha512-LZXpTlPyS5v7HhSmnvsLGP3iIYgYOBnc8r8ArlT55sGHV89bR2HlDdBjWQ+PY6SJMmk8TuVGFuxalnP3k/0Dwg==} + '@next/swc-win32-arm64-msvc@16.3.5': + resolution: {integrity: sha512-/wKzAREX2RF++MhicjDbg8tGn2AiBIM0+EFeTFKoUEUbW5D6amCJehd5Z5G1H5/gxNdgnwoXMcHz24H/c2tGkQ==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] - '@next/swc-win32-arm64-msvc@16.3.1': - resolution: {integrity: sha512-iOoN1QecUoGNZik536U/vtK43YwgyrCsGIkth52yIkl612n+0C9MjSnJbQAikISpb+WYRooBVhaDlUW7iZoKog==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [win32] - - '@next/swc-win32-x64-msvc@16.2.6': - resolution: {integrity: sha512-F0+4i0h9J6C4eE3EAPWsoCk7UW/dbzOjyzxY0qnDUOYFu6FFmdZ6l97/XdV3/Nz3VYyO7UWjyEJUXkGqcoXfMA==} - engines: {node: '>= 10'} - cpu: [x64] - os: [win32] - - '@next/swc-win32-x64-msvc@16.3.1': - resolution: {integrity: sha512-d/k+PpAriUPaeMJJOG7HUSdqfEX46FEPWU1p3/nm2ACmXhj9hFEWdFODUBIpkuijXYkfL90qZzTqVPRp4BW/hw==} + '@next/swc-win32-x64-msvc@16.3.5': + resolution: {integrity: sha512-LNdCHzgLFc+UeqMS84LzXPaeBRKyqDN9OMyFAr1OrB0XrNw78IRrEVtZvvA7245W/HsaoeVOQX9jPjPk8jojwA==} engines: {node: '>= 10'} cpu: [x64] os: [win32] @@ -3682,10 +4073,6 @@ packages: wagmi: optional: true - '@opentelemetry/api-logs@0.208.0': - resolution: {integrity: sha512-CjruKY9V6NMssL/T1kAFgzosF1v9o6oeN+aX5JB/C/xPNtmgIJqcXHG7fA82Ou1zCpWGl4lROQUKwUNE1pMCyg==} - engines: {node: '>=8.0.0'} - '@opentelemetry/api-logs@0.217.0': resolution: {integrity: sha512-Cdq0jW2lknrNfrAm92MyEAvpe2cRsKjdnQLHUL6xRA4IVUnsWx6P65E7NcUO0Y+L4w1Aee5iV8FvjSwd+lrs9A==} engines: {node: '>=8.0.0'} @@ -3729,12 +4116,6 @@ packages: peerDependencies: '@opentelemetry/api': '>=1.0.0 <1.10.0' - '@opentelemetry/core@2.2.0': - resolution: {integrity: sha512-FuabnnUm8LflnieVxs6eP7Z383hgQU4W1e3KJS6aOG3RxWxcHyBxH8fDMHNgu/gFx/M2jvTOW/4/PHhLz6bjWw==} - engines: {node: ^18.19.0 || >=20.6.0} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.10.0' - '@opentelemetry/core@2.7.1': resolution: {integrity: sha512-QAqIj32AtK6+pEVNG7EOVxHdE06RP+FM5qpiEJ4RtDcFIqKUZHYhl7/7UY5efhwmwNAg7j8QbJVBLxMerc0+gw==} engines: {node: ^18.19.0 || >=20.6.0} @@ -3765,12 +4146,6 @@ packages: peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/exporter-logs-otlp-http@0.208.0': - resolution: {integrity: sha512-jOv40Bs9jy9bZVLo/i8FwUiuCvbjWDI+ZW13wimJm4LjnlwJxGgB+N/VWOZUTpM+ah/awXeQqKdNlpLf2EjvYg==} - engines: {node: ^18.19.0 || >=20.6.0} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - '@opentelemetry/exporter-logs-otlp-http@0.217.0': resolution: {integrity: sha512-KfLAdt1uilVE+3FxbgVnp2ZrzqbIawzcesnRoi+Kh9ckB5Ld5D8btUgoBvwTbdmuNx1j6b132Wsh72azq+pPNQ==} engines: {node: ^18.19.0 || >=20.6.0} @@ -4137,12 +4512,6 @@ packages: peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/otlp-exporter-base@0.208.0': - resolution: {integrity: sha512-gMd39gIfVb2OgxldxUtOwGJYSH8P1kVFFlJLuut32L6KgUC4gl1dMhn+YC2mGn0bDOiQYSk/uHOdSjuKp58vvA==} - engines: {node: ^18.19.0 || >=20.6.0} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - '@opentelemetry/otlp-exporter-base@0.217.0': resolution: {integrity: sha512-eYfqnB3UhKu/5frhd1R6+FprKygbhkomuaceMXDyzxbfXB9tKgZOVmjaJ02CkLA6Tdzumxl+e2H+vo2a8jiMPQ==} engines: {node: ^18.19.0 || >=20.6.0} @@ -4167,12 +4536,6 @@ packages: peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/otlp-transformer@0.208.0': - resolution: {integrity: sha512-DCFPY8C6lAQHUNkzcNT9R+qYExvsk6C5Bto2pbNxgicpcSWbe2WHShLxkOxIdNcBiYPdVHv/e7vH7K6TI+C+fQ==} - engines: {node: ^18.19.0 || >=20.6.0} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - '@opentelemetry/otlp-transformer@0.217.0': resolution: {integrity: sha512-MKK8UHKFUOGAvbZRWh90MhwHG+Fxm6OROBdjKPCF+HQobjuJ/Kuf8Chs8CR45X1aqotxrMj7OxTdsXe8sXuGVA==} engines: {node: ^18.19.0 || >=20.6.0} @@ -4231,12 +4594,6 @@ packages: peerDependencies: '@opentelemetry/api': ^1.0.0 - '@opentelemetry/resources@2.2.0': - resolution: {integrity: sha512-1pNQf/JazQTMA0BiO5NINUzH0cbLbbl7mntLa4aJNmCCXSj0q03T5ZXXL0zw4G55TjdL9Tz32cznGClf+8zr5A==} - engines: {node: ^18.19.0 || >=20.6.0} - peerDependencies: - '@opentelemetry/api': '>=1.3.0 <1.10.0' - '@opentelemetry/resources@2.7.1': resolution: {integrity: sha512-DeT6KKolmC4e/dRQvMQ/RwlnzhaqeiFOXY5ngoOPJ07GgVVKxZOg9EcrNZb5aTzUn+iCrJldAgOfQm1O/QfPAQ==} engines: {node: ^18.19.0 || >=20.6.0} @@ -4249,12 +4606,6 @@ packages: peerDependencies: '@opentelemetry/api': '>=1.3.0 <1.10.0' - '@opentelemetry/sdk-logs@0.208.0': - resolution: {integrity: sha512-QlAyL1jRpOeaqx7/leG1vJMp84g0xKP6gJmfELBpnI4O/9xPX+Hu5m1POk9Kl+veNkyth5t19hRlN6tNY1sjbA==} - engines: {node: ^18.19.0 || >=20.6.0} - peerDependencies: - '@opentelemetry/api': '>=1.4.0 <1.10.0' - '@opentelemetry/sdk-logs@0.217.0': resolution: {integrity: sha512-BB+PcHItcZDL63dPMW+mJvwN9rk37wuIDjRxbVlg6pPDvDR/7GL7UJHbGsllgoggOoTimsKgENaWPoGch/oE1A==} engines: {node: ^18.19.0 || >=20.6.0} @@ -4267,12 +4618,6 @@ packages: peerDependencies: '@opentelemetry/api': '>=1.4.0 <1.10.0' - '@opentelemetry/sdk-metrics@2.2.0': - resolution: {integrity: sha512-G5KYP6+VJMZzpGipQw7Giif48h6SGQ2PFKEYCybeXJsOCB4fp8azqMAAzE5lnnHK3ZVwYQrgmFbsUJO/zOnwGw==} - engines: {node: ^18.19.0 || >=20.6.0} - peerDependencies: - '@opentelemetry/api': '>=1.9.0 <1.10.0' - '@opentelemetry/sdk-metrics@2.7.1': resolution: {integrity: sha512-MpDJdkiFDs3Pm1RHO3KByuZbuBdJEXEAkiC0+yJdsZGVCdf1RpHR6n+LHDcS7ffmfrt5kVCzJSCfm4z2C7v0uQ==} engines: {node: ^18.19.0 || >=20.6.0} @@ -4297,12 +4642,6 @@ packages: peerDependencies: '@opentelemetry/api': '>=1.3.0 <1.10.0' - '@opentelemetry/sdk-trace-base@2.2.0': - resolution: {integrity: sha512-xWQgL0Bmctsalg6PaXExmzdedSp3gyKV8mQBwK/j9VGdCDu2fmXIb2gAehBKbkXCpJ4HPkgv3QfoJWRT4dHWbw==} - engines: {node: ^18.19.0 || >=20.6.0} - peerDependencies: - '@opentelemetry/api': '>=1.3.0 <1.10.0' - '@opentelemetry/sdk-trace-base@2.7.1': resolution: {integrity: sha512-NAYIlsF8MPUsKqJMiDQJTMPOmlbawC1Iz/omMLygZ1C9am8fTKYjTaI+OZM+WTY3t3Glo0wnOg/6/pac6RGPPw==} engines: {node: ^18.19.0 || >=20.6.0} @@ -4423,11 +4762,14 @@ packages: '@polka/url@1.0.0-next.29': resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} - '@posthog/core@1.29.11': - resolution: {integrity: sha512-/4EF7oxAFSWJgaXxppT8bdYp7MGAnWFnKz994+MetTz/T6CKbYpjqIXHCofQXtcOXjEclTYj91igA+IkVFKiSg==} + '@posthog/browser-common@0.9.0': + resolution: {integrity: sha512-EWoVzs1vVY7ihpn8yOjj3jyjriHUEEMVSBNL/TW3hk09fTriWRe76vJXrRg0G0yjTYxHmtlSkNn4mOV24ZEzzA==} - '@posthog/types@1.376.2': - resolution: {integrity: sha512-Y3ROpAxNqgcy2G0w6JoG5Gt+P6WNY2lkHTPMPzWqexRwemYbFegDi5AifDyD9/tstKTlOYKTTExtaJ5EBcghyQ==} + '@posthog/core@1.55.2': + resolution: {integrity: sha512-2mYxGbDmTLUs096VbcZqSWo0lrI48/BrX65r54NynxxFq409BOx9w73HxOAtOKZ1vblkdV6/GxQTe7KDcIQidg==} + + '@posthog/types@1.412.4': + resolution: {integrity: sha512-Q7lV9O9TbLngjOYw1ucm3bS3tn48xb1B4ZQdDy8gv7yfKe99hCTeWYG4xZ36nJM80VxpTF9TriiJt5hngDOkBg==} '@protobuf-ts/grpcweb-transport@2.11.1': resolution: {integrity: sha512-1W4utDdvOB+RHMFQ0soL4JdnxjXV+ddeGIUg08DvZrA8Ms6k5NN6GBFU2oHZdTOcJVpPrDJ02RJlqtaoCMNBtw==} @@ -5705,8 +6047,28 @@ packages: '@specify-sh/sdk@0.4.2': resolution: {integrity: sha512-vskPvwpE3SWkmrOPhfMgvmvxvC+XNL93FTTF4SD3cPNZDsry5qfPNIW6TU1OTh3DWk7JMl/3X605CJUUAdEYmw==} - '@standard-schema/spec@1.1.0': - resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} + '@stryker-mutator/api@10.0.0': + resolution: {integrity: sha512-ZtAJ0ZT3MVRCWJTBE2h90XB/6E+4lifHYtcTyNG6nU2nLekPgTo4gD5esjX6Okxo1b/JB4jJzyxYB54fwKAoJw==} + engines: {node: '>=22.0.0'} + + '@stryker-mutator/core@10.0.0': + resolution: {integrity: sha512-ZvMsRyaXQQ5e6Thcid9pkuODv6Fn9E3nrBQJUap+hcJuGJ4unm26afo3m6YKSjn8kinyxJ/3TXf0cTWRDaTxVw==} + engines: {node: '>=22.0.0'} + hasBin: true + + '@stryker-mutator/instrumenter@10.0.0': + resolution: {integrity: sha512-B7Wmn1KlEWyFeOz6D6oGvQGRfi5Xw3VemG6dEKvFQp4qLvxD9Mf4kcZghfxffgnYwXd3bFgqXsJ+ZGlhdfIOrQ==} + engines: {node: '>=22.0.0'} + + '@stryker-mutator/util@10.0.0': + resolution: {integrity: sha512-LzOpHiJaCp2ABQgnPMlrQQcsK43bd5Vo/2FGL78aN62yDoeRQ+4j3tzeuXxK5OAHdC3fUz6TDoy4IsoAuLAd3w==} + + '@stryker-mutator/vitest-runner@10.0.0': + resolution: {integrity: sha512-SHK2/vfvRUpiz7jXPnQMBnr6zLdm69DK03Mo5mPhaZWcRSygrKUqYsPqWsXsK+5ySHzlMTfCyFK5NQ/X9sJFFw==} + engines: {node: '>=22.0.0'} + peerDependencies: + '@stryker-mutator/core': 10.0.0 + vitest: '>=2.0.0' '@stylistic/eslint-plugin@5.2.3': resolution: {integrity: sha512-oY7GVkJGVMI5benlBDCaRrSC1qPasafyv5dOBLLv5MTilMGnErKhO6ziEfodDDIZbo5QxPUNW360VudJOFODMw==} @@ -5964,9 +6326,6 @@ packages: resolution: {integrity: sha512-qMx1nOrzoB+PF+pzb26Q4Tc2sOlrx9Ba2UBNX9hB31Omrq+QoZ2Gly0KLrQWw4Of1AQ4J9lnD+XOdwOdcdXqqw==} engines: {node: '>=12.20.0'} - '@swc/helpers@0.5.15': - resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} - '@swc/helpers@0.5.23': resolution: {integrity: sha512-5lSsMOTXURePglDfvuAQUqkGek9Hg2kksOYay2m0+XR++b2NWYL/4sWyuvVBIs8oKnJaxkdi9whaL/sqN13afw==} @@ -6297,6 +6656,9 @@ packages: '@types/filewriter@0.0.33': resolution: {integrity: sha512-xFU8ZXTw4gd358lb2jw25nxY9QAgqn2+bKKjKOYfNCzN4DKCFetK7sPtrlpg66Ywe3vWY9FNxprZawAh9wfJ3g==} + '@types/gensync@1.0.5': + resolution: {integrity: sha512-MbsRCT7mTikHwKZ0X+LVUTLRrZZRLipTuXEO9qOYO+zmjMVk81axyClMROf6uoPD9MRVu46bx8zoR0Ad9q3NAg==} + '@types/geojson@7946.0.16': resolution: {integrity: sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==} @@ -6321,6 +6683,9 @@ packages: '@types/js-cookie@3.0.6': resolution: {integrity: sha512-wkw9yd1kEXOPnvEeEV1Go1MmxtBJL0RR79aOTAApecWFVu7w0NNXNqhcWgvw2YgZDYadliXkl14pa3WXw5jlCQ==} + '@types/jsesc@2.5.1': + resolution: {integrity: sha512-9VN+6yxLOPLOav+7PwjZbxiID2bVaeq0ED4qSQmdQTdjnXJSaCVKTR58t15oqH1H5t8Ng2ZX1SabJVoN9Q34bw==} + '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} @@ -6358,11 +6723,8 @@ packages: '@types/node@17.0.45': resolution: {integrity: sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==} - '@types/node@20.16.7': - resolution: {integrity: sha512-QkDQjAY3gkvJNcZOWwzy3BN34RweT0OQ9zJyvLCU0kSK22dO2QYh/NHGfbEAYylPYzRB1/iXcojS79wOg5gFSw==} - - '@types/node@22.12.0': - resolution: {integrity: sha512-Fll2FZ1riMjNmlmJOdAyY5pUbkftXslB5DgEzlIuNaiWhXd00FhWxVC/r4yV/4wBb9JfImTu+jiSvXTkJ7F/gA==} + '@types/node@24.13.4': + resolution: {integrity: sha512-YJ7EqCstVTzIr0fMr7qul/977en+pQHrfmuKIo6Zr9i75Be21dr3MovcfvGtyvi2HAUrRerWps5sMO9I7WaxDw==} '@types/node@25.9.2': resolution: {integrity: sha512-G05zqtJhcDLb8uslf5EjCxXg9G1KQxiV8OS0R26IC//Eoyitzqe8z37I7cqvnZlrlSfgocQRfSn/AHBZJJFyGw==} @@ -6471,6 +6833,10 @@ packages: '@types/yargs@17.0.35': resolution: {integrity: sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==} + '@typesafe-ai/sdk@0.6.0': + resolution: {integrity: sha512-IddX+Q0XM+VagOUZFeP7wZjaO4SHMdvnh2zEBdrZZnXedWI3BNK1lKhMx3ayrkFWvVLbVcUHJy6AVZlY+e6Jaw==} + engines: {node: '>=20'} + '@typescript-eslint/eslint-plugin@5.62.0': resolution: {integrity: sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -6490,6 +6856,16 @@ packages: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' + '@typescript-eslint/parser@5.62.0': + resolution: {integrity: sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + '@typescript-eslint/parser@8.59.2': resolution: {integrity: sha512-plR3pp6D+SSUn1HM7xvSkx12/DhoHInI2YF35KAcVFNZvlC0gtrWqx7Qq1oH2Ssgi0vlFRCTbP+DZc7B9+TtsQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -6509,6 +6885,12 @@ packages: peerDependencies: typescript: '>=4.8.4 <6.1.0' + '@typescript-eslint/project-service@8.70.0': + resolution: {integrity: sha512-hFHbTNqhU9G+2eKFXCBVb1tjFT/LceiJ4+HfLO4pTpDI0KHi6iajpcFFkaSQ9gXmCh7n82A0PthaayEdN6mspQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + '@typescript-eslint/scope-manager@5.62.0': resolution: {integrity: sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -6525,6 +6907,10 @@ packages: resolution: {integrity: sha512-pFzqhllJMs+jghLQWzV00ds39xLzuyqPSev5pd8f4Ir0rtKR3ZLUB4/4dhjOFighWb9larvtfJvqL+4yKDI3Xw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/scope-manager@8.70.0': + resolution: {integrity: sha512-8nP3Kwh5hlgZ4FicGvmznAmJe8UL4sdU8tLukrPaMuQmDuk4Y8xYfzu/aYZW4xT2JCgc7H/TpDI5cGlxcWJSqQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/tsconfig-utils@8.59.2': resolution: {integrity: sha512-BKK4alN7oi4C/zv4VqHQ+uRU+lTa6JGIZ7s1juw7b3RHo9OfKB+bKX3u0iVZetdsUCBBkSbdWbarJbmN0fTeSw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -6537,6 +6923,12 @@ packages: peerDependencies: typescript: '>=4.8.4 <6.1.0' + '@typescript-eslint/tsconfig-utils@8.70.0': + resolution: {integrity: sha512-adnkeeNq9Sq1sUf4+FRVc0KdgYghzsgFpZSQVZVvY0LCuUuN0FnQgyGzCJeC4fW1cdXseBAjU2EOqUIjbNcZUw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + '@typescript-eslint/type-utils@5.62.0': resolution: {integrity: sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -6554,6 +6946,13 @@ packages: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' + '@typescript-eslint/type-utils@8.70.0': + resolution: {integrity: sha512-NUMKIhYVaVIVLnRL9CRt+VVcuLgSHUCpXn4/+K8wql+vdInUzvx8BjUO1oJ7cG9shjFJKtF8F8Hh2kCh3/KBVw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + '@typescript-eslint/types@5.62.0': resolution: {integrity: sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -6570,6 +6969,10 @@ packages: resolution: {integrity: sha512-AsE7x2XaAK+CVbeih0Fvbn+r1qHxtpLDJ3XUuFcIinT318T90yHMJC+Zgv+jUuDjQQd06HKwxnDu6sz1IcTilA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/types@8.70.0': + resolution: {integrity: sha512-asTOIYhDg4zdzOScCyaytrsV3cR6B4ecPQlXw/dJIm7J/MZTtCtfVII9JD8Geh4jTCrK/Xe6cg5UevoleMcoJQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/typescript-estree@5.62.0': resolution: {integrity: sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -6600,6 +7003,12 @@ packages: peerDependencies: typescript: '>=4.8.4 <6.1.0' + '@typescript-eslint/typescript-estree@8.70.0': + resolution: {integrity: sha512-d9NmHMPEKQ7QCLLm1jI3zmoQBwT5KwFYjXBJ9ymZfKCUU+5rmTRykKAFvH5Qn/ZCds3CEAFS9OC9M/jkl0X2bA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + '@typescript-eslint/utils@5.62.0': resolution: {integrity: sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -6626,6 +7035,13 @@ packages: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' + '@typescript-eslint/utils@8.70.0': + resolution: {integrity: sha512-oZmtKJz/4fufZ2p3+Cn3ijEojcdfR+1zYDH2xKYrEly0dR/Q/1xUPRCOlKGxod78nWlU2UnDe09GZ3TaknBFGA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + '@typescript-eslint/visitor-keys@5.62.0': resolution: {integrity: sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -6642,6 +7058,10 @@ packages: resolution: {integrity: sha512-9WI52t8ZGLVGrPMBet25yAftqY/n95+zmoUUtJBBQTKDSKUu7OsPTroT2op7U9JatkoRccL0YkWDNMFfC4Sjxg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/visitor-keys@8.70.0': + resolution: {integrity: sha512-BoC8PiO4Hkdo0TVJh9Ntxr5MxPDI7/oFsrygN5ADelFSeXG/qgNuucIGA+L5Z6JpPTE/uRfcTWtscjbUaufepQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript/typescript-aix-ppc64@7.0.2': resolution: {integrity: sha512-MTKKkWB7p/0E9xi1d1tHtZ5PiLkGEMIq88pK2CubZjOsLtYTLqhgIgi6zepFa+9GHZ6h05NMCkQxGKiPXMxXtQ==} engines: {node: '>=16.20.0'} @@ -6926,11 +7346,25 @@ packages: peerDependencies: vite: ^4.2.0 || ^5.0.0 || ^6.0.0 - '@vitest/expect@4.1.7': - resolution: {integrity: sha512-1R+tw0ortHEbZDGMymm+pN7/AFQ/RkFFdtd7EN+VBpynKmLbP8A3rpEXdshBJ7+8hQ9zBJh/i1s0yKNtxAnU7w==} + '@vitest/coverage-v8@5.0.0': + resolution: {integrity: sha512-toMg6PZGCIa/lQNCDoASrfb1ly4hsUKXFtFYC9kD4t78o5Y6LyNJU7AENt8eHPr3quYdxaxK7hj2mnbFfUk9NA==} + peerDependencies: + '@vitest/browser': 5.0.0 + vitest: 5.0.0 + peerDependenciesMeta: + '@vitest/browser': + optional: true + + '@vitest/istanbul-lib-coverage@1.0.1': + resolution: {integrity: sha512-k3DJZ8LhMBK9NS4SclF1ASD3OgXEWDorbIcPTRDK0/Zae6fRvu+fJRxtFdLfHsa9Y24beCdPnoNZ4LviTNstfA==} + engines: {node: '>=22'} + + '@vitest/istanbul-lib-report@1.0.1': + resolution: {integrity: sha512-1EOLRfsTMnyAr3+kEAsP4o9dhaDlGPpD7H5iLBBeq//YpNB1VIahkPhB+eRp9N2Dkfw8oySROjE3yf9XDeaIkQ==} + engines: {node: '>=22'} - '@vitest/mocker@4.1.7': - resolution: {integrity: sha512-vY7nuamKgfvpA1Koa3oYIw/k7D6kZnpGyNMZW8loow2bsBYla1TFdqTaXncWdRn4pgwNs+90RhnXhJScDwQeJA==} + '@vitest/mocker@5.0.0': + resolution: {integrity: sha512-66PGTMIiVJP3t4a5yxU9qPtf7MdTBs8jmToMvy+HVflB3Yy13WJZTtPePdvU+wjRV02SKK5doLbSA6o9pwOmiA==} peerDependencies: msw: ^2.4.9 vite: ^6.0.0 || ^7.0.0 || ^8.0.0 @@ -6940,20 +7374,8 @@ packages: vite: optional: true - '@vitest/pretty-format@4.1.7': - resolution: {integrity: sha512-umgCarTOYQWIaDMvGDRZij+6b9oVeLIyJzfN+AS88e0ZOU3QTgNNSTtjQOpcvWr3np1N0j4WgZj+sb3oYBDscw==} - - '@vitest/runner@4.1.7': - resolution: {integrity: sha512-BapjmAQ2aI78WdMEfeUWivnfVzB+VPGwWRQcJE0OUq7qEeEcBsCSf+0T5iREBNE5nBb4wA5Ya0W6IA+sghdEFw==} - - '@vitest/snapshot@4.1.7': - resolution: {integrity: sha512-ZacLzja+TmJeZ1h14xW2FB/WpeimUD3haBXQPyJqxvo8jQTmfeA8zv58mtjN2C7EHXZDYVcVYdYmAxjkWVvKCw==} - - '@vitest/spy@4.1.7': - resolution: {integrity: sha512-kbkI5LMWakyuTIvs6fUJ5qdIVb1XVKsYJAT4OJ938cHMROYMSfmoQdZy0aaAnjbbc8F61vkoTqz/Az+/HiIu5Q==} - - '@vitest/utils@4.1.7': - resolution: {integrity: sha512-T532WBu791cBxJlCl6SO+J14l81DQx6uQHm1bQbmCDY7nqlEIgkza/UFnSBNaUtSf41unldDFjdOBYEQC4b5Hw==} + '@vitest/spy@5.0.0': + resolution: {integrity: sha512-uy+luWBAPw9XfthoHi5AkfHUnuPYEESjl0p/r+meoBnU8bxg5GDQ3Ey8MjcJ6sqahkL4PFyrvfMJJBw7LbU06g==} '@volar/language-core@2.4.28': resolution: {integrity: sha512-w4qhIJ8ZSitgLAkVay6AbcnC7gP3glYM3fYwKV3srj8m494E3xtrCv6E+bWviiK/8hs6e6t1ij1s2Endql7vzQ==} @@ -7652,6 +8074,10 @@ packages: alien-signals@0.4.14: resolution: {integrity: sha512-itUAVzhczTmP2U5yX67xVpsbbOiquusbWVyA9N+sy6+r6YVbFkahXvNCeEPWEOMhwDYwbVbGHFkVL03N9I5g+Q==} + angular-html-parser@10.11.0: + resolution: {integrity: sha512-3vERzJ65UFDr3C7uozLJwsNcQS3FS784dSh583oDgDTTZMgXe3/pdyXgKndxiP5R2lvYRfW6gSl145Hnyf2OFA==} + engines: {node: '>= 14'} + anser@1.4.10: resolution: {integrity: sha512-hCv9AqTQ8ycjpSd3upOJd7vFwW1JaoYQ7tpham03GJ1ca8/65rqn0RpaWpItOAd6ylW9wAw6luXYPJIyPFVOww==} @@ -7693,9 +8119,6 @@ packages: apg-lite@1.0.5: resolution: {integrity: sha512-SlI+nLMQDzCZfS39ihzjGp3JNBQfJXyMi6cg9tkLOCPVErgFsUIAEdO9IezR7kbP5Xd0ozcPNQBkf9TO5cHgWw==} - app-module-path@2.2.0: - resolution: {integrity: sha512-gkco+qxENJV+8vFcDiiFhuoSvRXb2a/QPqpSoWhVz829VNJfOTnELbBmPmNKFxf3xdNnw4DWCkzkDaavcX/1YQ==} - arg@4.1.3: resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} @@ -7766,10 +8189,6 @@ packages: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} - ast-module-types@5.0.0: - resolution: {integrity: sha512-JvqziE0Wc0rXQfma0HZC/aY7URXHFuZV84fJRtP8u+lhp0JYCNd5wJzVXP45t0PH0Mej3ynlzvdyITYIu0G4LQ==} - engines: {node: '>=14'} - ast-types-flow@0.0.8: resolution: {integrity: sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==} @@ -7777,6 +8196,9 @@ packages: resolution: {integrity: sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg==} engines: {node: '>=4'} + ast-v8-to-istanbul@1.0.5: + resolution: {integrity: sha512-UPAgKJFSEGMWSDr3LX4tqnAb4f7KGT8O40Tyx8wbYmmZ/yn58lNCm8h3svs3eXgiGd5AXxz8NDOvXWvicq+rJA==} + async-function@1.0.0: resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} engines: {node: '>= 0.4'} @@ -7845,6 +8267,10 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + balanced-match@4.0.4: + resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} + engines: {node: 18 || 20 || >=22} + base-x@3.0.11: resolution: {integrity: sha512-xz7wQ8xDhdyP7tQxwdteLYeFfS68tSMNCZ/Y37WJ4bhGfKPpqEIlmIyueQHqOyoPhE6xNUqjzRr8ra0eF9VRvA==} @@ -7889,6 +8315,9 @@ packages: bintrees@1.0.2: resolution: {integrity: sha512-VOMgTMwjAaUG580SXn3LacVgjurrbMme7ZZNYGSSV7mmtY6QQRh0Eg3pwIcntQ77DErK1L0NxkbetjcoXzVwKw==} + birecord@0.1.2: + resolution: {integrity: sha512-5PAPTTmMpMEb+GuMb5DebfBkipRGyIW9+gtwEBSoDA9xkhHILm04+hZQ702pMksu3d8YAuGkmgTzQWcKqTPScA==} + bl@4.1.0: resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} @@ -7926,6 +8355,10 @@ packages: brace-expansion@1.1.12: resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} + brace-expansion@5.0.9: + resolution: {integrity: sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==} + engines: {node: 20 || >=22} + braces@3.0.3: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} @@ -8065,6 +8498,9 @@ packages: character-reference-invalid@2.0.1: resolution: {integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==} + chardet@2.2.0: + resolution: {integrity: sha512-rddelWYNPRrXq6PtNEN2S3f6t9ILzvqaN5pVgi4kqt9jHQaXIial9PznB5iSPVlQSLNaaH22ItWz3EJtQ10+OA==} + charenc@0.0.2: resolution: {integrity: sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==} @@ -8120,6 +8556,10 @@ packages: resolution: {integrity: sha512-xRwvIOMGrfOAnM1JYtqQImuaNtDEv9v6oIYAs4LIHwTiKee8uwvIi363igssOC0O5U04i4AlENs79LQLu9tEMw==} engines: {node: '>=20'} + cli-width@4.1.0: + resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} + engines: {node: '>= 12'} + client-only@0.0.1: resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} @@ -8159,6 +8599,9 @@ packages: codemirror@5.65.21: resolution: {integrity: sha512-6teYk0bA0nR3QP0ihGMoxuKzpl5W80FpnHpBJpgy66NK3cZv5b/d/HY8PnRvfSsCG1MTfr92u2WUl+wT0E40mQ==} + codemirror@6.0.2: + resolution: {integrity: sha512-VhydHotNW5w1UGK0Qj96BwSk/Zqbp9WbnyK2W/eVMv4QyF41INRGpjUhFJY7/uDNuudSc33a/PKr4iDqRduvHw==} + color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} @@ -8187,10 +8630,6 @@ packages: comma-separated-tokens@2.0.3: resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} - commander@10.0.1: - resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} - engines: {node: '>=14'} - commander@12.1.0: resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} engines: {node: '>=18'} @@ -8738,11 +9177,6 @@ packages: resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} engines: {node: '>= 0.8'} - dependency-tree@10.0.9: - resolution: {integrity: sha512-dwc59FRIsht+HfnTVM0BCjJaEWxdq2YAvEDy4/Hn6CwS3CBWMtFnL3aZGAkQn3XCYxk/YcTDE4jX2Q7bFTwCjA==} - engines: {node: '>=14'} - hasBin: true - dequal@2.0.3: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} @@ -8752,6 +9186,9 @@ packages: peerDependencies: valtio: '*' + des.js@1.1.0: + resolution: {integrity: sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg==} + destr@2.0.5: resolution: {integrity: sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==} @@ -8769,38 +9206,8 @@ packages: detect-node-es@1.1.0: resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} - detective-amd@5.0.2: - resolution: {integrity: sha512-XFd/VEQ76HSpym80zxM68ieB77unNuoMwopU2TFT/ErUk5n4KvUTwW4beafAVUugrjV48l4BmmR0rh2MglBaiA==} - engines: {node: '>=14'} - hasBin: true - - detective-cjs@5.0.1: - resolution: {integrity: sha512-6nTvAZtpomyz/2pmEmGX1sXNjaqgMplhQkskq2MLrar0ZAIkHMrDhLXkRiK2mvbu9wSWr0V5/IfiTrZqAQMrmQ==} - engines: {node: '>=14'} - - detective-es6@4.0.1: - resolution: {integrity: sha512-k3Z5tB4LQ8UVHkuMrFOlvb3GgFWdJ9NqAa2YLUU/jTaWJIm+JJnEh4PsMc+6dfT223Y8ACKOaC0qcj7diIhBKw==} - engines: {node: '>=14'} - - detective-postcss@6.1.3: - resolution: {integrity: sha512-7BRVvE5pPEvk2ukUWNQ+H2XOq43xENWbH0LcdCE14mwgTBEAMoAx+Fc1rdp76SmyZ4Sp48HlV7VedUnP6GA1Tw==} - engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - - detective-sass@5.0.3: - resolution: {integrity: sha512-YsYT2WuA8YIafp2RVF5CEfGhhyIVdPzlwQgxSjK+TUm3JoHP+Tcorbk3SfG0cNZ7D7+cYWa0ZBcvOaR0O8+LlA==} - engines: {node: '>=14'} - - detective-scss@4.0.3: - resolution: {integrity: sha512-VYI6cHcD0fLokwqqPFFtDQhhSnlFWvU614J42eY6G0s8c+MBhi9QAWycLwIOGxlmD8I/XvGSOUV1kIDhJ70ZPg==} - engines: {node: '>=14'} - - detective-stylus@4.0.0: - resolution: {integrity: sha512-TfPotjhszKLgFBzBhTOxNHDsutIxx9GTWjrL5Wh7Qx/ydxKhwUrlSFeLIn+ZaHPF+h0siVBkAQSuy6CADyTxgQ==} - engines: {node: '>=14'} - - detective-typescript@11.2.0: - resolution: {integrity: sha512-ARFxjzizOhPqs1fYC/2NMC3N4jrQ6HvVflnXBTRqNEqJuXwyKLRr9CrJwkRcV/SnZt1sNXgsF6FPm0x57Tq0rw==} - engines: {node: ^14.14.0 || >=16.0.0} + diff-match-patch@1.0.5: + resolution: {integrity: sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==} diff@4.0.4: resolution: {integrity: sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==} @@ -8852,6 +9259,9 @@ packages: resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} engines: {node: '>= 4'} + dompurify@3.4.16: + resolution: {integrity: sha512-sqo+pNp3qRhCIpbgRi1y8Tgk27Bo2Ry7w0dC1NBeNTdZChWjz9Xb/KOoZbRP/R6pQZ80Qw8YhXw13hWWBbMRnQ==} + dompurify@3.4.9: resolution: {integrity: sha512-4dPSRMRDqHvs0V4YDFCsaIZo4if5u0xM+llyxiM2fwuZFdKArUBAF3VtI2+n8NKg9P870WMdYk0UhqQNoWXbfQ==} @@ -8931,6 +9341,10 @@ packages: emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + empathic@2.0.1: + resolution: {integrity: sha512-YGRs8knHhKHVShLkFET/rWAU8kmHbOV5LwN938RHI0pljAJ1Gf6SzXsSmRaEzcXTtOOmVqJ5+WtQPL5uigY50Q==} + engines: {node: '>=14'} + encode-utf8@1.0.3: resolution: {integrity: sha512-ucAnuBEhUK4boH2HjVYG5Q2mQyPorvv0u/ocS+zhdw0S8AlHYY+GOFhP1Gio5z4icpP2ivFSvhtFjQi8+T9ppw==} @@ -8952,10 +9366,6 @@ packages: resolution: {integrity: sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==} engines: {node: '>=10.0.0'} - enhanced-resolve@5.22.0: - resolution: {integrity: sha512-xYcDWrpELkFzz9SpZ3PlI6Eu6eD93Yf0WLDRxikGhWJ3MAir2SNZTIVCVZqZ/NUyx8AdMc2gT9C0gPiw18kG+A==} - engines: {node: '>=10.13.0'} - enhanced-resolve@5.23.0: resolution: {integrity: sha512-yJN/BOOLxcOW2aQgeif9mSnaUB8KtvmMMp56oA1kx1CRfBKbhZm2pJ+NBY+3eOboHxix8lfjWpHE0Ei5U8RbSA==} engines: {node: '>=10.13.0'} @@ -9017,8 +9427,8 @@ packages: resolution: {integrity: sha512-HVLACW1TppGYjJ8H6/jqH/pqOtKRw6wMlrB23xfExmFWxFquAIWCmwoLsOyN96K4a5KbmOf5At9ZUO3GZbetAw==} engines: {node: '>= 0.4'} - es-module-lexer@2.1.0: - resolution: {integrity: sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==} + es-module-lexer@2.3.2: + resolution: {integrity: sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw==} es-object-atoms@1.1.2: resolution: {integrity: sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==} @@ -9072,11 +9482,6 @@ packages: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} - escodegen@2.1.0: - resolution: {integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==} - engines: {node: '>=6.0'} - hasBin: true - eslint-config-next@16.2.6: resolution: {integrity: sha512-z2ELYSkyrrJ6cuunTU8vhsT/RpouPkjaSah06nVW6Rg2Hpg0Vs8s497/e5s8G8qtdp4ccsiovz5P1rv+5VSW2Q==} peerDependencies: @@ -9173,6 +9578,13 @@ packages: peerDependencies: eslint: '>=8.40.0' + eslint-plugin-react-dom@5.19.0: + resolution: {integrity: sha512-QxWdjdPxQUI3SSMYR7WyrLew7pQzlOn5P3R5raWh2PiyavaBUbkfJhSJ307l1AaJOVLy5JeIB2Rl4bivwHAOIQ==} + engines: {node: '>=22.0.0'} + peerDependencies: + eslint: '*' + typescript: '*' + eslint-plugin-react-hooks@5.0.0: resolution: {integrity: sha512-hIOwI+5hYGpJEc4uPRmz2ulCjAGD/N13Lukkh8cLV0i2IRk/bdZDYjgLVHj+U9Z704kLIdIO6iueGvxNur0sgw==} engines: {node: '>=10'} @@ -9185,6 +9597,41 @@ packages: peerDependencies: eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 || ^10.0.0 + eslint-plugin-react-jsx@5.19.0: + resolution: {integrity: sha512-Upvpe50vpPi5jWvLqtBXoZEwaCz1pzszzoukK7qf0g1A6ZJ+XDJhi3yKmPQWBlp9c4GOwzwPuXGU5C2e8cHMbQ==} + engines: {node: '>=22.0.0'} + peerDependencies: + eslint: '*' + typescript: '*' + + eslint-plugin-react-naming-convention@5.19.0: + resolution: {integrity: sha512-H9ko2t+KYPBDOfr29D5hDAT6CXucHJKGQscI+/ndSmkBl9K/KaGWdhFiCzQPi5S2YwPkjrcuaSXIp/n8+PA5GA==} + engines: {node: '>=22.0.0'} + peerDependencies: + eslint: '*' + typescript: '*' + + eslint-plugin-react-rsc@5.19.0: + resolution: {integrity: sha512-XdfkB2XRS0b1WBkuxnRsWmhivNaBOLAIohBqWQItQycuket6wVliqSY+Q1NcTst4PdJ1bYzLuMgSXYsgwJMV7Q==} + engines: {node: '>=22.0.0'} + peerDependencies: + eslint: '*' + typescript: '*' + + eslint-plugin-react-web-api@5.19.0: + resolution: {integrity: sha512-wCNdYVHgdLmCikLicH6kfO6EE1SKzi63FlhLLGMGzzEAyvudvyfKI0+ZXDkFyjolvKXLQW6Thb8EeAFbHTZksw==} + engines: {node: '>=22.0.0'} + peerDependencies: + eslint: '*' + typescript: '*' + + eslint-plugin-react-x@5.19.0: + resolution: {integrity: sha512-HoweOyYhar8G2QEukOctSaK87Y3AzB3bn4ES+0VVhcNOOqBFphRaWqcvoNN8JE+5AKOhhpuzjVKY6DnGPCcAMw==} + engines: {node: '>=22.0.0'} + peerDependencies: + eslint: '*' + typescript: '*' + eslint-plugin-react@7.37.5: resolution: {integrity: sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==} engines: {node: '>=4'} @@ -9233,6 +9680,7 @@ packages: eslint@9.39.2: resolution: {integrity: sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. hasBin: true peerDependencies: jiti: '*' @@ -9327,12 +9775,16 @@ packages: resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} engines: {node: '>=0.8.x'} + execa@9.6.1: + resolution: {integrity: sha512-9Be3ZoN4LmYR90tUoVu2te2BsbzHfhJyfEiAVfz7N5/zv+jduIfLrV2xdQXOHbaD6KgpGdO9PRPM1Y4Q9QkPkA==} + engines: {node: ^18.19.0 || >=20.5.0} + expand-template@2.0.3: resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==} engines: {node: '>=6'} - expect-type@1.3.0: - resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} + expect-type@1.4.0: + resolution: {integrity: sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==} engines: {node: '>=12.0.0'} exponential-backoff@3.1.3: @@ -9448,6 +9900,10 @@ packages: fflate@0.8.3: resolution: {integrity: sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA==} + figures@6.1.0: + resolution: {integrity: sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==} + engines: {node: '>=18'} + file-entry-cache@8.0.0: resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} engines: {node: '>=16.0.0'} @@ -9459,11 +9915,6 @@ packages: file-uri-to-path@1.0.0: resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==} - filing-cabinet@4.2.0: - resolution: {integrity: sha512-YZ21ryzRcyqxpyKggdYSoXx//d3sCJzM3lsYoaeg/FyXdADGJrUl+BW1KIglaVLJN5BBcMtWylkygY8zBp2MrQ==} - engines: {node: '>=14'} - hasBin: true - fill-range@7.1.1: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} @@ -9634,10 +10085,6 @@ packages: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} engines: {node: '>=6.9.0'} - get-amd-module-type@5.0.1: - resolution: {integrity: sha512-jb65zDeHyDjFR1loOVk0HQGM5WNwoGB8aLWy3LKCieMKol0/ProHkhO2X1JxojuN10vbz1qNn09MJ7tNp7qMzw==} - engines: {node: '>=14'} - get-caller-file@2.0.5: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} @@ -9657,9 +10104,6 @@ packages: resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==} engines: {node: '>=6'} - get-own-enumerable-property-symbols@3.0.2: - resolution: {integrity: sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==} - get-port@7.2.0: resolution: {integrity: sha512-afP4W205ONCuMoPBqcR6PSXnzX35KTcJygfJfcp+QY+uwm3p20p1YczWXhlICIzGMCxYBQcySEcOgsJcrkyobg==} engines: {node: '>=16'} @@ -9706,10 +10150,6 @@ packages: deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me hasBin: true - glob@7.2.3: - resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} - deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me - glob@8.1.0: resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==} engines: {node: '>=12'} @@ -9750,11 +10190,6 @@ packages: globrex@0.1.2: resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} - gonzales-pe@4.3.0: - resolution: {integrity: sha512-otgSPpUmdWJ43VXyiNgEYE4luzHCL2pz4wQ0OnDluC6Eg4Ko3Vexy/SrSynglw/eR+OhkzmqFCZa/OFa/RgAOQ==} - engines: {node: '>=0.6.0'} - hasBin: true - google-logging-utils@1.1.3: resolution: {integrity: sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA==} engines: {node: '>=14'} @@ -9973,6 +10408,10 @@ packages: resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} engines: {node: '>= 14'} + human-signals@8.0.1: + resolution: {integrity: sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ==} + engines: {node: '>=18.18.0'} + humanize-duration@3.33.2: resolution: {integrity: sha512-K7Ny/ULO1hDm2nnhvAY+SJV1skxFb61fd073SG1IWJl+D44ULrruCuTyjHKjBVVcSuTlnY99DKtgEG39CM5QOQ==} @@ -9991,6 +10430,10 @@ packages: resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} engines: {node: '>=0.10.0'} + iconv-lite@0.7.3: + resolution: {integrity: sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==} + engines: {node: '>=0.10.0'} + icss-utils@5.1.0: resolution: {integrity: sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==} engines: {node: ^10 || ^12 || >= 14} @@ -10243,10 +10686,6 @@ packages: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} - is-obj@1.0.1: - resolution: {integrity: sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==} - engines: {node: '>=0.10.0'} - is-path-inside@4.0.0: resolution: {integrity: sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA==} engines: {node: '>=12'} @@ -10278,18 +10717,10 @@ packages: resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} engines: {node: '>= 0.4'} - is-regexp@1.0.0: - resolution: {integrity: sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==} - engines: {node: '>=0.10.0'} - is-regexp@3.1.0: resolution: {integrity: sha512-rbku49cWloU5bSMI+zaRaXdQHXnthP6DZ/vLnfdSKyL4zUzuWnomtOEiZZOd+ioQ+avFo/qau3KPTc7Fjy1uPA==} engines: {node: '>=12'} - is-relative-path@1.0.2: - resolution: {integrity: sha512-i1h+y50g+0hRbBD+dbnInl3JlJ702aar58snAeX+MxBAPvzXGej7sYoPMhlnykabt0ZzCJNBEyzMlekuQZN7fA==} - deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. - is-retry-allowed@2.2.0: resolution: {integrity: sha512-XVm7LOeLpTW4jV19QSH38vkswxoLud8sQ57YwJVTPWdiaI9I8keEhGFpBlslyVsgdQy4Opg8QOLb8YRgsyZiQg==} engines: {node: '>=10'} @@ -10326,12 +10757,9 @@ packages: resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} engines: {node: '>= 0.4'} - is-url-superb@4.0.0: - resolution: {integrity: sha512-GI+WjezhPPcbM+tqE9LnmsY5qqjwHzTvjJ36wxYX5ujNXefSUJ/T17r5bqDV8yLhcgB59KTPNOc9O9cmHTPWsA==} - engines: {node: '>=10'} - - is-url@1.2.4: - resolution: {integrity: sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==} + is-unicode-supported@2.1.0: + resolution: {integrity: sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==} + engines: {node: '>=18'} is-weakmap@2.0.2: resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} @@ -10535,9 +10963,15 @@ packages: js-file-download@0.4.12: resolution: {integrity: sha512-rML+NkoD08p5Dllpjo0ffy4jRHeY6Zsapvr/W86N7E0yuzAO6qa5X9+xog6zQNlH102J7IXljNY2FtS6Lj3ucg==} + js-md4@0.3.2: + resolution: {integrity: sha512-/GDnfQYsltsjRswQhN9fhv3EMw2sCpUdrdxyWDOUK7eyD++r3gRhzgiQgc/x4MAv2i1iuQ4lxO5mvqM3vj4bwA==} + js-sha3@0.8.0: resolution: {integrity: sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==} + js-tokens@10.0.0: + resolution: {integrity: sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==} + js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -10575,6 +11009,9 @@ packages: json-parse-even-better-errors@2.3.1: resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + json-rpc-2.0@1.7.1: + resolution: {integrity: sha512-JqZjhjAanbpkXIzFE7u8mE/iFblawwlXtONaCvRqI+pyABVz7B4M1EUNpyVW+dZjqgQ2L5HFmZCmOCgUKm00hg==} + json-rpc-engine@6.1.0: resolution: {integrity: sha512-NEdLrtrq1jUZyfjkr9OCz9EzCNhnRyWtt1PAnvnhwy6e8XETS0Dtc+ZNCO2gvuAoKsIn2+vCSowXTYE4CkgnAQ==} engines: {node: '>=10.0.0'} @@ -10710,6 +11147,9 @@ packages: lodash.debounce@4.0.8: resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} + lodash.groupby@4.6.0: + resolution: {integrity: sha512-5dcWxm23+VAoz+awKmBaiBvzox8+RqMgFhi7UvX9DHZr2HdxHXM/Wrf8cfKpsW37RNrvtPn6hSwNqurSILbmJw==} + lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} @@ -10778,6 +11218,12 @@ packages: magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + magic-string@1.3.1: + resolution: {integrity: sha512-rm91zr2Ou+XueDTohjQQjdQEcYM6zVi8KVUCG8Ec3vHwUEKrhSdCNyfuIywkA6hcCAteIn0ZOtAHA6eGpiX+Pg==} + + magicast@0.5.4: + resolution: {integrity: sha512-llBEhWm1SacoRwgHUoQJYtwp4PBLF4faQi5TCpIGyGs9n4y5+juI0tDgyKIfpqxckRHaHzouUEph3THklWh03w==} + main-event@1.0.4: resolution: {integrity: sha512-sKazUjIy2Jalv5lkQ446iOcrx8Q7TkaCuk6xfnzg5uUqMusMLDMPmRDmSNE2kjSVpSTJo4j1bQZusS+Ib7Bvrg==} @@ -11009,19 +11455,9 @@ packages: mockdate@3.0.5: resolution: {integrity: sha512-iniQP4rj1FhBdBYS/+eQv7j1tadJ9lJtdzgOpvsOHng/GbcDh2Fhdeq+ZRldrPYdXvCyfFUmFeEwEGXZB5I/AQ==} - module-definition@5.0.1: - resolution: {integrity: sha512-kvw3B4G19IXk+BOXnYq/D/VeO9qfHaapMeuS7w7sNUqmGaA6hywdFHMi+VWeR9wUScXM7XjoryTffCZ5B0/8IA==} - engines: {node: '>=14'} - hasBin: true - module-details-from-path@1.0.4: resolution: {integrity: sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w==} - module-lookup-amd@8.0.5: - resolution: {integrity: sha512-vc3rYLjDo5Frjox8NZpiyLXsNWJ5BWshztc/5KSOMzpg9k5cHH652YsJ7VKKmtM4SvaxuE9RkrYGhiSjH3Ehow==} - engines: {node: '>=14'} - hasBin: true - monaco-editor@0.52.2: resolution: {integrity: sha512-GEQWEZmfkOGLdd3XK8ryrfWz3AIP8YymVXiPHEdewrUq7mh0qrKrfHLNCXcbB6sTnMLnOZ3ztSiKcciFUkIJwQ==} @@ -11067,6 +11503,23 @@ packages: multiformats@9.9.0: resolution: {integrity: sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg==} + mutation-server-protocol@0.4.1: + resolution: {integrity: sha512-SBGK0j8hLDne7bktgThKI8kGvGTx3rY3LAeQTmOKZ5bVnL/7TorLMvcVF7dIPJCu5RNUWhkkuF53kurygYVt3g==} + engines: {node: '>=18'} + + mutation-testing-elements@3.8.4: + resolution: {integrity: sha512-5CF1SNa7at5ZH33vEr+21wNebTSrtNIVvnzaUlxortHajOrIPaSLczIWvg6sI/fsExekQ7jookwf2cHftkckqQ==} + + mutation-testing-metrics@3.8.4: + resolution: {integrity: sha512-DZcmndJBH6nrNs3tpiB3OcMVq9KkG2cHCpJSnDxSxPwi9qrafRmoec40xjhkbzOoX1n7/4UkDqg5tIj4A6nvCw==} + + mutation-testing-report-schema@3.8.4: + resolution: {integrity: sha512-s4G71R6Lt/PpZ0cqeglIcgyBdzLM8E+SeCHZAPg1wkSsPtRBa4XfPzAozYKdiJk/TLbNEEb7En9t0/bveuPuxA==} + + mute-stream@3.0.0: + resolution: {integrity: sha512-dkEJPVvun4FryqBmZ5KhDo0K9iDXAwn08tMLDinNdRBNPcYEDiWYysLcc6k3mjTMlbP9KyylvRpd4wFtwrT9rw==} + engines: {node: ^20.17.0 || >=22.9.0} + nanoclone@0.2.1: resolution: {integrity: sha512-wynEP02LmIbLpcYw8uBKpcfF6dmg2vcpKqxeH5UcoKEYdExslsdUA4ugFauuaeYdTB76ez6gJW8XAZ6CgkXYxA==} @@ -11127,29 +11580,8 @@ packages: react: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc react-dom: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc - next@16.2.6: - resolution: {integrity: sha512-qOVgKJg1+At15NpeUP+eJgCHvTCgXsogweq87Ri/Ix7PkqQHg4sdaXmSFqKlgaIXE4kW0g25LE68W87UANlHtw==} - engines: {node: '>=20.9.0'} - hasBin: true - peerDependencies: - '@opentelemetry/api': ^1.1.0 - '@playwright/test': ^1.51.1 - babel-plugin-react-compiler: '*' - react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 - react-dom: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 - sass: ^1.3.0 - peerDependenciesMeta: - '@opentelemetry/api': - optional: true - '@playwright/test': - optional: true - babel-plugin-react-compiler: - optional: true - sass: - optional: true - - next@16.3.1: - resolution: {integrity: sha512-hsAp0i7Rh+/dhe7DGIeN2YlpLM1DP4MNxti9EtDMtqcO612X81MvvEj388/oTce9U1EcEIOWDlGq0zRwrBKvuA==} + next@16.3.5: + resolution: {integrity: sha512-MdtsTgzyfCPRLC6uJ1mN8ao7lyJ4BB0U6Inhnx3gta1UcCIdHK3yxLG0E8OWQteWD8/Q0qb8A5o7wJaL8M9y2w==} engines: {node: '>=20.9.0'} hasBin: true peerDependencies: @@ -11238,10 +11670,6 @@ packages: resolution: {integrity: sha512-GYVXHE2KnrzAfsAjl4uP++evGFCrAU1jta4ubEjIG7YWt/64Gqv66a30yKwWczVjA6j3bM4nBwH7Pk1JmDHaxQ==} engines: {node: '>=18'} - node-source-walk@6.0.2: - resolution: {integrity: sha512-jn9vOIK/nfqoFCcpK89/VCVaLg1IHE6UVfDOzvqmANaJ/rWCTEdH8RZ1V278nv2jr36BJdyQXIAavBLXpzdlag==} - engines: {node: '>=14'} - normalize-path@3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} @@ -11254,6 +11682,10 @@ packages: resolution: {integrity: sha512-JYc0DPlpGWB40kH5g07gGTrYuMqV653k3uBKY6uITPWds3M0ov3GaWGp9lbE3Bzngx8+XkfzgvASb9vk9JDFXQ==} engines: {node: '>=14.16'} + npm-run-path@6.0.0: + resolution: {integrity: sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==} + engines: {node: '>=18'} + nth-check@2.1.1: resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} @@ -11310,6 +11742,10 @@ packages: obug@2.1.1: resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==} + obug@2.2.1: + resolution: {integrity: sha512-XrsrhT5sybtKI6wakr2SPOlGZWWYbUXZ7a0jT8/QOeAPau+1X/bSegNe5YR75oJmEZQbKningirmGOEJCIk61Q==} + engines: {node: '>=12.20.0'} + ofetch@1.5.1: resolution: {integrity: sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA==} @@ -11512,6 +11948,10 @@ packages: resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} engines: {node: '>=8'} + parse-ms@4.0.0: + resolution: {integrity: sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==} + engines: {node: '>=18'} + parse5@8.0.1: resolution: {integrity: sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==} @@ -11526,14 +11966,14 @@ packages: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} - path-is-absolute@1.0.1: - resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} - engines: {node: '>=0.10.0'} - path-key@3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} + path-key@4.0.0: + resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} + engines: {node: '>=12'} + path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} @@ -11579,6 +12019,10 @@ packages: resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} engines: {node: '>=12'} + picomatch@4.0.7: + resolution: {integrity: sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==} + engines: {node: '>=12'} + pify@3.0.0: resolution: {integrity: sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==} engines: {node: '>=4'} @@ -11722,16 +12166,6 @@ packages: postcss-value-parser@4.2.0: resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} - postcss-values-parser@6.0.2: - resolution: {integrity: sha512-YLJpK0N1brcNJrs9WatuJFtHaV9q5aAOj+S4DI5S7jgHlRfm0PIbDCAFRYMQD5SHq7Fy6xsDhyutgS0QOAs0qw==} - engines: {node: '>=10'} - peerDependencies: - postcss: ^8.2.9 - - postcss@8.4.31: - resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} - engines: {node: ^10 || ^12 || >=14} - postcss@8.5.15: resolution: {integrity: sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==} engines: {node: ^10 || ^12 || >=14} @@ -11756,8 +12190,16 @@ packages: resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==} engines: {node: '>=0.10.0'} - posthog-js@1.376.2: - resolution: {integrity: sha512-Anz2pCp7dcNbammTExiZpcKC08dxfrHYaJgaXH6rq5x3Zcfj/4FkcMJF2cGCrdQXel5Y4vftiVSseZda0HAQTQ==} + posthog-js@1.434.15: + resolution: {integrity: sha512-ZfBkNrQiFr1Q43G3dR0USzTRdfx+6gKPebyoWDmKnLUhG3yRgYjLH914J6ifEDRnH+RaGOx7rsWKxKcvaSJXHg==} + peerDependencies: + '@types/react': '>=16.8.0' + react: '>=16.8.0' + peerDependenciesMeta: + '@types/react': + optional: true + react: + optional: true preact@10.24.2: resolution: {integrity: sha512-1cSoF0aCC8uaARATfrlz4VCBqE8LwZwRfLgkxJOQwAlQt6ayTmi0D9OF7nXid1POI5SZidFuG9CnlXbDfLqY/Q==} @@ -11765,17 +12207,20 @@ packages: preact@10.29.2: resolution: {integrity: sha512-7tNmwg/7mzzAoB/8kSg6Hl37JraAZw3Z3A0JSY7VXlZwo82Xn0G7wKbNNs2qoF4ZEEsQGTwDAroNdqKs1ofJxQ==} + preact@10.29.8: + resolution: {integrity: sha512-ej2aVZ+vZ8WO7tvlQWRM9N63A0KzF9q4mWJfDUHgYaIofWY9hu74QdnQrjoPMmZi2/nZ5gN0bJCQF49xQqx09Q==} + peerDependencies: + preact-render-to-string: '>=5' + peerDependenciesMeta: + preact-render-to-string: + optional: true + prebuild-install@7.1.3: resolution: {integrity: sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==} engines: {node: '>=10'} deprecated: No longer maintained. Please contact the author of the relevant native addon; alternatives are available. hasBin: true - precinct@11.0.5: - resolution: {integrity: sha512-oHSWLC8cL/0znFhvln26D14KfCQFFn4KOLSw6hmLhd+LQ2SKt9Ljm89but76Pc7flM9Ty1TnXyrA2u16MfRV3w==} - engines: {node: ^14.14.0 || >=16.0.0} - hasBin: true - prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} @@ -11793,6 +12238,10 @@ packages: resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + pretty-ms@9.3.0: + resolution: {integrity: sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ==} + engines: {node: '>=18'} + prismjs@1.30.0: resolution: {integrity: sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==} engines: {node: '>=6'} @@ -11820,9 +12269,14 @@ packages: progress-events@1.1.0: resolution: {integrity: sha512-82DVc5tI36neVB3IjdXR11ztwGuoBc98em9ijzubeZKxI47OlV2Znq6mlPqE5xPDzO2Uw98GHiQSjj2favBCRQ==} + progress@2.0.3: + resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} + engines: {node: '>=0.4.0'} + prom-client@15.1.1: resolution: {integrity: sha512-GVA2H96QCg2q71rjc3VYvSrVG7OpnJxyryC7dMzvfJfpJJHzQVwF3TJLfHzKORcwJpElWs1TwXLthlJAFJxq2A==} engines: {node: ^16 || ^18 || >=20} + deprecated: prom-client has been replaced by @prometheus-io/client promise@8.3.0: resolution: {integrity: sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg==} @@ -11901,6 +12355,10 @@ packages: engines: {node: '>=10.13.0'} hasBin: true + qs@6.15.1: + resolution: {integrity: sha512-6YHEFRL9mfgcAvql/XhwTvf5jKcOiiupt2FiJxHkiX1z4j7WL8J/jRHYLluORvc1XxB5rV20KoeK00gVJamspg==} + engines: {node: '>=0.6'} + quansync@0.2.11: resolution: {integrity: sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==} @@ -11932,9 +12390,6 @@ packages: resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==} engines: {node: '>=10'} - quote-unquote@1.0.0: - resolution: {integrity: sha512-twwRO/ilhlG/FIgYeKGFqyHhoEhqgnKVkcmqMKi2r524gz3ZbDTcyFt38E9xjJI2vT+KbRNHVbnJ/e0I25Azwg==} - rabin-wasm@0.1.5: resolution: {integrity: sha512-uWgQTo7pim1Rnj5TuWcCewRDTf0PEFTSlaUjWP4eY9EbLV9em08v89oCz/WO+wRxpYuO36XEHp4wgYQnAgOHzA==} hasBin: true @@ -12325,15 +12780,6 @@ packages: require-main-filename@2.0.0: resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} - requirejs-config-file@4.0.0: - resolution: {integrity: sha512-jnIre8cbWOyvr8a5F2KuqBnY+SDA4NXr/hzEZJG79Mxm2WiFQz2dzhC8ibtPJS7zkmBEl1mxSwp5HhC1W4qpxw==} - engines: {node: '>=10.13.0'} - - requirejs@2.3.8: - resolution: {integrity: sha512-7/cTSLOdYkNBNJcDMWf+luFvMriVm7eYxp4BcFCsAX0wF421Vyce5SXP17c+Jd5otXKGNehIonFlyQXSowL6Mw==} - engines: {node: '>=0.4.0'} - hasBin: true - requires-port@1.0.0: resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} @@ -12343,10 +12789,6 @@ packages: resolve-alpn@1.2.1: resolution: {integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==} - resolve-dependency-path@3.0.2: - resolution: {integrity: sha512-Tz7zfjhLfsvR39ADOSk9us4421J/1ztVBo4rWUkF38hgHK5m0OCZ3NxFVpqHRkjctnwVa15igEUHFJp8MCS7vA==} - engines: {node: '>=14'} - resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} @@ -12430,6 +12872,9 @@ packages: rw@1.3.3: resolution: {integrity: sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==} + rxjs@7.8.2: + resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==} + safe-array-concat@1.1.4: resolution: {integrity: sha512-wtZlHyOje6OZTGqAoaDKxFkgRtkF9CnHAVnCHKfuj200wAgL+bSJhdsCD2l0Qx/2ekEXjPWcyKkfGb5CPboslg==} engines: {node: '>=0.4'} @@ -12458,11 +12903,6 @@ packages: sanitize-filename@1.6.4: resolution: {integrity: sha512-9ZyI08PsvdQl2r/bBIGubpVdR3RR9sY6RDiWFPreA21C/EFlQhmgo20UZlNjZMMZNubusLhAQozkA0Od5J21Eg==} - sass-lookup@5.0.1: - resolution: {integrity: sha512-t0X5PaizPc2H4+rCwszAqHZRtr4bugo4pgiCvrBFvIX0XFxnr29g77LJcpyj9A0DcKf7gXMLcgvRjsonYI6x4g==} - engines: {node: '>=14'} - hasBin: true - sax@1.6.0: resolution: {integrity: sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==} engines: {node: '>=11.0.0'} @@ -12519,11 +12959,6 @@ packages: engines: {node: '>=10'} hasBin: true - semver@7.8.4: - resolution: {integrity: sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==} - engines: {node: '>=10'} - hasBin: true - semver@7.8.5: resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==} engines: {node: '>=10'} @@ -12579,12 +13014,8 @@ packages: resolution: {integrity: sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - sharp@0.34.5: - resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - - sharp@0.35.3: - resolution: {integrity: sha512-ej0zVHuZGHCiABXcNxeYhpRnPNPAcvbG8RMdBAhDAxLKkCRVSpK3Iyu7qbqw3JMzoj0REeM6f3tJLtVwl0023Q==} + sharp@0.35.4: + resolution: {integrity: sha512-n++8XWcj+jCOr2IOl7h8LbKnGBDY4aPbmprMONBNFdn0ImXqpGVv5zliDs0V9HbmbCQLpbuo2ej9rAoOQTvMDA==} engines: {node: '>=20.9.0'} peerDependencies: '@types/node': '*' @@ -12705,6 +13136,10 @@ packages: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} + source-map@0.7.6: + resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==} + engines: {node: '>= 12'} + space-separated-tokens@2.0.2: resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} @@ -12750,8 +13185,8 @@ packages: resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} engines: {node: '>= 0.8'} - std-env@4.1.0: - resolution: {integrity: sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==} + std-env@4.2.0: + resolution: {integrity: sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==} stop-iteration-iterator@1.1.0: resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} @@ -12783,6 +13218,9 @@ packages: string-template@0.2.1: resolution: {integrity: sha512-Yptehjogou2xm4UJbxJ4CxgZx12HBfeystp0y3x7s4Dj32ltVVG1Gg8YhKjHZkHicuKpZX/ffilA8505VbUbpw==} + string-ts@2.3.1: + resolution: {integrity: sha512-xSJq+BS52SaFFAVxuStmx6n5aYZU571uYUnUrPXkPFCfdHyZMMlbP2v2Wx5sNBnAVzq/2+0+mcBLBa3Xa5ubYw==} + string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} @@ -12828,10 +13266,6 @@ packages: string_decoder@1.3.0: resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} - stringify-object@3.3.0: - resolution: {integrity: sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==} - engines: {node: '>=4'} - strip-ansi@6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} @@ -12844,6 +13278,10 @@ packages: resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} engines: {node: '>=4'} + strip-final-newline@4.0.0: + resolution: {integrity: sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==} + engines: {node: '>=18'} + strip-hex-prefix@1.0.0: resolution: {integrity: sha512-q8d4ue7JGEiVcypji1bALTos+0pWtyGlivAWyPuTkHzuTCJqrK9sWxYQZUq6Nq3cuyv3bm734IhHvHtGGURU6A==} engines: {node: '>=6.5.0', npm: '>=3'} @@ -12885,11 +13323,6 @@ packages: stylis@4.2.0: resolution: {integrity: sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==} - stylus-lookup@5.0.1: - resolution: {integrity: sha512-tLtJEd5AGvnVy4f9UHQMw4bkJJtaAcmo54N+ovQBjDY3DuWyK9Eltxzr5+KG0q4ew6v2EHyuWWNnHeiw/Eo7rQ==} - engines: {node: '>=14'} - hasBin: true - super-regex@0.2.0: resolution: {integrity: sha512-WZzIx3rC1CvbMDloLsVw0lkZVKJWbrkJ0k1ghKFmcnPrW1+jWbgTkTEWVtD9lMdmI4jZEz40+naBxl1dCUhXXw==} engines: {node: '>=14.16'} @@ -13070,13 +13503,18 @@ packages: tiny-warning@1.0.3: resolution: {integrity: sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==} - tinybench@2.9.0: - resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + tinybench@6.1.4: + resolution: {integrity: sha512-9APumHG7r4yOk4X4WlkmE71aZcv1gvin1czO3OQ1U9iJcFA5Ja/ygyb0vPOVHTthFozUYs8CLoLUlM8grb2lTQ==} + engines: {node: '>=20.0.0'} tinyexec@1.2.2: resolution: {integrity: sha512-M/Q0B2cp4K7kynaT/vnED1j8TlLY+Pp7C6Wl2bl/7u/F0mUVwdyOpwomQb8JpYLitHUssAJRmLZdMCGsrx7i+g==} engines: {node: '>=18'} + tinyexec@1.3.0: + resolution: {integrity: sha512-QKAl9m8gWWGHV8jZcPeym6j+XULi6tOf1mT83WYJ4Lk2ytW/uwAWkrP0uFsdoYMdueVJ0qs26wZ+23xeB4ibNQ==} + engines: {node: '>=18'} + tinyglobby@0.2.16: resolution: {integrity: sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==} engines: {node: '>=12.0.0'} @@ -13085,8 +13523,8 @@ packages: resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} engines: {node: '>=12.0.0'} - tinyrainbow@3.1.0: - resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==} + tinyrainbow@3.1.1: + resolution: {integrity: sha512-yau8yJdTt989Mm0Bd/236QnzEiPf2xLLTqUZRUJOo/3CB078LSwzei343DgtJVmfJKJE3TMINY1u42SQsP6mXw==} engines: {node: '>=14.0.0'} tldts-core@6.1.86: @@ -13154,6 +13592,10 @@ packages: resolution: {integrity: sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==} engines: {node: '>=20'} + tree-kill@1.2.2: + resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} + hasBin: true + tree-sitter-json@0.24.8: resolution: {integrity: sha512-Tc9ZZYwHyWZ3Tt1VEw7Pa2scu1YO7/d2BCBbKTx5hXwig3UfdQjsOPkPyLpDJOn/m1UBEWYAtSdGAwCSyagBqQ==} peerDependencies: @@ -13204,6 +13646,9 @@ packages: '@swc/wasm': optional: true + ts-pattern@5.9.0: + resolution: {integrity: sha512-6s5V71mX8qBUmlgbrfL33xDUwO0fq48rxAu2LBE11WBeGdpCPOsXksQbZJHvHwhrd3QjUusd3mAOM5Gg0mFBLg==} + ts-toolbelt@9.6.0: resolution: {integrity: sha512-nsZd8ZeNUzukXPlJmTBwUAuABDe/9qtVDelJeT/qW0ow3ZS3BsQJtNkan1802aM9Uf68/Y8ljw86Hu0h5IUW3w==} @@ -13221,10 +13666,6 @@ packages: tsconfig-paths@3.15.0: resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} - tsconfig-paths@4.2.0: - resolution: {integrity: sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==} - engines: {node: '>=6'} - tslib@1.14.1: resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} @@ -13247,6 +13688,10 @@ packages: tunnel-agent@0.6.0: resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==} + tunnel@0.0.6: + resolution: {integrity: sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==} + engines: {node: '>=0.6.11 <=0.7.0 || >=0.7.3'} + type-check@0.4.0: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} @@ -13283,6 +13728,14 @@ packages: resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==} engines: {node: '>= 0.4'} + typed-inject@5.0.0: + resolution: {integrity: sha512-0Ql2ORqBORLMdAW89TQKZsb1PQkFGImFfVmncXWe7a+AA3+7dh7Se9exxZowH4kbnlvKEFkMxUYdHUpjYWFJaA==} + engines: {node: '>=18'} + + typed-rest-client@2.3.1: + resolution: {integrity: sha512-k4kX5Up6qA68D0Cby2AK+6+vM5k3qTxe+/3FqhnHRExjY5cfbOnzjQZbP/LXleF8hVoDvDqxlgk9KK83HoBZlQ==} + engines: {node: '>= 16.0.0'} + types-ramda@0.30.1: resolution: {integrity: sha512-1HTsf5/QVRmLzcGfldPFvkVsAdi1db1BBKzi7iW3KBUlOICg/nKnFS+jGqDJS3YD8VsWbAh7JiHeBvbsw8RPxA==} @@ -13362,11 +13815,11 @@ packages: uncrypto@0.1.3: resolution: {integrity: sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==} - undici-types@6.19.8: - resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} + underscore@1.13.8: + resolution: {integrity: sha512-DXtD3ZtEQzc7M8m4cXotyHR+FAS18C64asBYY5vqZexfYryNNnDc02W4hKg3rdQuqOYas1jkseX0+nZXjTXnvQ==} - undici-types@6.20.0: - resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==} + undici-types@7.18.2: + resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==} undici-types@7.24.6: resolution: {integrity: sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==} @@ -13398,6 +13851,10 @@ packages: resolution: {integrity: sha512-hpbDzxUY9BFwX+UeBnxv3Sh1q7HFxj48DTmXchNgRa46lO8uj3/1iEn3MiNUYTg1g9ctIqXCCERn8gYZhHC5lQ==} engines: {node: '>=4'} + unicorn-magic@0.3.0: + resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==} + engines: {node: '>=18'} + unicorn-magic@0.4.0: resolution: {integrity: sha512-wH590V9VNgYH9g3lH9wWjTrUoKsjLF6sGLjhR4sH1LWpLmCOH0Zf7PukhDA8BiS7KHe4oPNkcTHqYkj7SOGUOw==} engines: {node: '>=20'} @@ -13774,23 +14231,23 @@ packages: peerDependencies: vitest: '>=2.0.0' - vitest@4.1.7: - resolution: {integrity: sha512-flYyaFd2CgoCoU+0UKt3pxksgC+S02iTDN0n3LtqaMeXsI9SBcdNujc2k0DeFLzUn/0k538yNjOSdwgCqcrwJA==} - engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} + vitest@5.0.0: + resolution: {integrity: sha512-gpsMNoRhMjMktVxPtstOH4/PJuPyovVaMDr4oDilXaGH1EcqM2OE96SoHT2VIQ6fTGtTjqmHDrEu2X9RQiXf8Q==} + engines: {node: ^22.12.0 || ^24.0.0 || >=26.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' '@opentelemetry/api': ^1.9.0 - '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 - '@vitest/browser-playwright': 4.1.7 - '@vitest/browser-preview': 4.1.7 - '@vitest/browser-webdriverio': 4.1.7 - '@vitest/coverage-istanbul': 4.1.7 - '@vitest/coverage-v8': 4.1.7 - '@vitest/ui': 4.1.7 + '@types/node': ^22.0.0 || >=24.0.0 + '@vitest/browser-playwright': 5.0.0 + '@vitest/browser-preview': 5.0.0 + '@vitest/browser-webdriverio': ^5.0.0-beta.5 || >=5.0.0 + '@vitest/coverage-istanbul': 5.0.0 + '@vitest/coverage-v8': 5.0.0 + '@vitest/ui': 5.0.0 happy-dom: '*' jsdom: '*' - vite: ^6.0.0 || ^7.0.0 || ^8.0.0 + vite: ^6.4.0 || ^7.0.0 || ^8.0.0 peerDependenciesMeta: '@edge-runtime/vm': optional: true @@ -13862,6 +14319,9 @@ packages: weald@1.1.3: resolution: {integrity: sha512-vMWtNbYuPb58NeG2+0sKA0Een4VMDwzf+3oHqh68buWRSOMUBlUeRb11LhV28czV+DUpJHRykifijZDuS9bInA==} + weapon-regex@2.0.4: + resolution: {integrity: sha512-ubuhY5Lo4phWcMsJqe8j62m9uhsuo/VpfK5XsSgYGRGDSt10hwVwOBTriPRd0dac+KYbGNXOrfXjM6xCp2NUKg==} + web-streams-polyfill@3.3.3: resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} engines: {node: '>= 8'} @@ -13869,8 +14329,11 @@ packages: web-tree-sitter@0.24.5: resolution: {integrity: sha512-+J/2VSHN8J47gQUAvF8KDadrfz6uFYVjxoxbKWDoXVsH2u7yLdarCnIURnrMA6uSRkgX3SdmqM5BOoQjPdSh5w==} - web-vitals@5.2.0: - resolution: {integrity: sha512-i2z98bEmaCqSDiHEDu+gHl/dmR4Q+TxFmG3/13KkMO+o8UxQzCqWaDRCiLgEa41nlO4VpXSI0ASa1xWmO9sBlA==} + web-vitals@6.2.1: + resolution: {integrity: sha512-rLcLXA2sx6+9dE88NHFubwTtGxpK4yYBLj6qHPdFoCaLr0cXGb4efOqtKLlm4loGA4OEKHIQKMKzZkKyOh5ctw==} + + web-vitals@6.2.2: + resolution: {integrity: sha512-oto5x6dLEgrRqfcWed+pZEUb2q6ikbFmqF54CRDhI/QGbn+qxC49b4C4OkbH+kb9C3a8shpFD3SgR9kRcs80ZQ==} web3-utils@1.10.4: resolution: {integrity: sha512-tsu8FiKJLk2PzhDl9fXbGUWTkkVXYhtTA+SmEFkKft+9BgwLxfCRpU96sWv7ICC8zixBNd3JURVoiR3dUXgP8A==} @@ -14100,6 +14563,10 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} + yoctocolors@2.2.0: + resolution: {integrity: sha512-xYqdZFUK/VYazNl/oCDYN+3WloWQwMfZxBoiNt6qNyk+xfOdi598muWE42rNZFp1kNOiqW936q5RhUdnpqElSg==} + engines: {node: '>=18'} + yup@0.32.11: resolution: {integrity: sha512-Z2Fe1bn+eLstG8DRR6FTavGD+MeAwyfmouhHsIUgaADz8jvFKbO/fXc2trJKZg+5EBjh4gGm3iU/t3onKlXHIg==} engines: {node: '>=10'} @@ -14323,28 +14790,54 @@ snapshots: js-tokens: 4.0.0 picocolors: 1.1.1 + '@babel/code-frame@8.0.0': + dependencies: + '@babel/helper-validator-identifier': 8.0.4 + js-tokens: 10.0.0 + '@babel/compat-data@7.29.7': {} - '@babel/core@7.29.7': + '@babel/compat-data@8.0.0': {} + + '@babel/core@7.29.7(supports-color@10.2.2)': dependencies: '@babel/code-frame': 7.29.7 '@babel/generator': 7.29.7 '@babel/helper-compilation-targets': 7.29.7 - '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7) + '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) '@babel/helpers': 7.29.7 '@babel/parser': 7.29.7 '@babel/template': 7.29.7 - '@babel/traverse': 7.29.7 + '@babel/traverse': 7.29.7(supports-color@10.2.2) '@babel/types': 7.29.7 '@jridgewell/remapping': 2.3.5 convert-source-map: 2.0.0 - debug: 4.4.3 + debug: 4.4.3(supports-color@10.2.2) gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 transitivePeerDependencies: - supports-color + '@babel/core@8.0.1': + dependencies: + '@babel/code-frame': 8.0.0 + '@babel/generator': 8.0.0 + '@babel/helper-compilation-targets': 8.0.0 + '@babel/helpers': 8.0.0 + '@babel/parser': 8.0.4 + '@babel/template': 8.0.0 + '@babel/traverse': 8.0.4 + '@babel/types': 8.0.4 + '@types/gensync': 1.0.5 + convert-source-map: 2.0.0 + empathic: 2.0.1 + gensync: 1.0.0-beta.2 + import-meta-resolve: 4.2.0 + json5: 2.2.3 + obug: 2.1.1 + semver: 7.8.5 + '@babel/generator@7.29.7': dependencies: '@babel/parser': 7.29.7 @@ -14353,10 +14846,23 @@ snapshots: '@jridgewell/trace-mapping': 0.3.31 jsesc: 3.1.0 + '@babel/generator@8.0.0': + dependencies: + '@babel/parser': 8.0.4 + '@babel/types': 8.0.4 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + '@types/jsesc': 2.5.1 + jsesc: 3.1.0 + '@babel/helper-annotate-as-pure@7.29.7': dependencies: '@babel/types': 7.29.7 + '@babel/helper-annotate-as-pure@8.0.0': + dependencies: + '@babel/types': 8.0.4 + '@babel/helper-compilation-targets@7.29.7': dependencies: '@babel/compat-data': 7.29.7 @@ -14365,32 +14871,51 @@ snapshots: lru-cache: 5.1.1 semver: 6.3.1 - '@babel/helper-create-class-features-plugin@7.29.7(@babel/core@7.29.7)': + '@babel/helper-compilation-targets@8.0.0': + dependencies: + '@babel/compat-data': 8.0.0 + '@babel/helper-validator-option': 8.0.0 + browserslist: 4.28.2 + lru-cache: 11.5.0 + semver: 7.8.5 + + '@babel/helper-create-class-features-plugin@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/helper-annotate-as-pure': 7.29.7 - '@babel/helper-member-expression-to-functions': 7.29.7 + '@babel/helper-member-expression-to-functions': 7.29.7(supports-color@10.2.2) '@babel/helper-optimise-call-expression': 7.29.7 - '@babel/helper-replace-supers': 7.29.7(@babel/core@7.29.7) - '@babel/helper-skip-transparent-expression-wrappers': 7.29.7 - '@babel/traverse': 7.29.7 + '@babel/helper-replace-supers': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/helper-skip-transparent-expression-wrappers': 7.29.7(supports-color@10.2.2) + '@babel/traverse': 7.29.7(supports-color@10.2.2) semver: 6.3.1 transitivePeerDependencies: - supports-color - '@babel/helper-create-regexp-features-plugin@7.29.7(@babel/core@7.29.7)': + '@babel/helper-create-class-features-plugin@8.0.1(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 + '@babel/helper-annotate-as-pure': 8.0.0 + '@babel/helper-member-expression-to-functions': 8.0.0 + '@babel/helper-optimise-call-expression': 8.0.0 + '@babel/helper-replace-supers': 8.0.1(@babel/core@8.0.1) + '@babel/helper-skip-transparent-expression-wrappers': 8.0.0 + '@babel/traverse': 8.0.4 + semver: 7.8.5 + + '@babel/helper-create-regexp-features-plugin@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/helper-annotate-as-pure': 7.29.7 regexpu-core: 6.4.0 semver: 6.3.1 - '@babel/helper-define-polyfill-provider@0.6.8(@babel/core@7.29.7)': + '@babel/helper-define-polyfill-provider@0.6.8(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/helper-compilation-targets': 7.29.7 '@babel/helper-plugin-utils': 7.29.7 - debug: 4.4.3 + debug: 4.4.3(supports-color@10.2.2) lodash.debounce: 4.0.8 resolve: 1.22.12 transitivePeerDependencies: @@ -14398,70 +14923,115 @@ snapshots: '@babel/helper-globals@7.29.7': {} - '@babel/helper-member-expression-to-functions@7.29.7': + '@babel/helper-globals@8.0.0': {} + + '@babel/helper-member-expression-to-functions@7.29.7(supports-color@10.2.2)': dependencies: - '@babel/traverse': 7.29.7 + '@babel/traverse': 7.29.7(supports-color@10.2.2) '@babel/types': 7.29.7 transitivePeerDependencies: - supports-color - '@babel/helper-module-imports@7.29.7': + '@babel/helper-member-expression-to-functions@8.0.0': dependencies: - '@babel/traverse': 7.29.7 + '@babel/traverse': 8.0.4 + '@babel/types': 8.0.4 + + '@babel/helper-module-imports@7.29.7(supports-color@10.2.2)': + dependencies: + '@babel/traverse': 7.29.7(supports-color@10.2.2) '@babel/types': 7.29.7 transitivePeerDependencies: - supports-color - '@babel/helper-module-transforms@7.29.7(@babel/core@7.29.7)': + '@babel/helper-module-imports@8.0.0': + dependencies: + '@babel/traverse': 8.0.4 + '@babel/types': 8.0.4 + + '@babel/helper-module-transforms@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: - '@babel/core': 7.29.7 - '@babel/helper-module-imports': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-module-imports': 7.29.7(supports-color@10.2.2) '@babel/helper-validator-identifier': 7.29.7 - '@babel/traverse': 7.29.7 + '@babel/traverse': 7.29.7(supports-color@10.2.2) transitivePeerDependencies: - supports-color + '@babel/helper-module-transforms@8.0.1(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 + '@babel/helper-module-imports': 8.0.0 + '@babel/helper-validator-identifier': 8.0.4 + '@babel/traverse': 8.0.4 + '@babel/helper-optimise-call-expression@7.29.7': dependencies: '@babel/types': 7.29.7 + '@babel/helper-optimise-call-expression@8.0.0': + dependencies: + '@babel/types': 8.0.4 + '@babel/helper-plugin-utils@7.29.7': {} - '@babel/helper-remap-async-to-generator@7.29.7(@babel/core@7.29.7)': + '@babel/helper-plugin-utils@8.0.1(@babel/core@8.0.1)': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 8.0.1 + + '@babel/helper-remap-async-to-generator@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/helper-annotate-as-pure': 7.29.7 - '@babel/helper-wrap-function': 7.29.7 - '@babel/traverse': 7.29.7 + '@babel/helper-wrap-function': 7.29.7(supports-color@10.2.2) + '@babel/traverse': 7.29.7(supports-color@10.2.2) transitivePeerDependencies: - supports-color - '@babel/helper-replace-supers@7.29.7(@babel/core@7.29.7)': + '@babel/helper-replace-supers@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: - '@babel/core': 7.29.7 - '@babel/helper-member-expression-to-functions': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-member-expression-to-functions': 7.29.7(supports-color@10.2.2) '@babel/helper-optimise-call-expression': 7.29.7 - '@babel/traverse': 7.29.7 + '@babel/traverse': 7.29.7(supports-color@10.2.2) transitivePeerDependencies: - supports-color - '@babel/helper-skip-transparent-expression-wrappers@7.29.7': + '@babel/helper-replace-supers@8.0.1(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 + '@babel/helper-member-expression-to-functions': 8.0.0 + '@babel/helper-optimise-call-expression': 8.0.0 + '@babel/traverse': 8.0.4 + + '@babel/helper-skip-transparent-expression-wrappers@7.29.7(supports-color@10.2.2)': dependencies: - '@babel/traverse': 7.29.7 + '@babel/traverse': 7.29.7(supports-color@10.2.2) '@babel/types': 7.29.7 transitivePeerDependencies: - supports-color + '@babel/helper-skip-transparent-expression-wrappers@8.0.0': + dependencies: + '@babel/traverse': 8.0.4 + '@babel/types': 8.0.4 + '@babel/helper-string-parser@7.29.7': {} + '@babel/helper-string-parser@8.0.0': {} + '@babel/helper-validator-identifier@7.29.7': {} + '@babel/helper-validator-identifier@8.0.4': {} + '@babel/helper-validator-option@7.29.7': {} - '@babel/helper-wrap-function@7.29.7': + '@babel/helper-validator-option@8.0.0': {} + + '@babel/helper-wrap-function@7.29.7(supports-color@10.2.2)': dependencies: '@babel/template': 7.29.7 - '@babel/traverse': 7.29.7 + '@babel/traverse': 7.29.7(supports-color@10.2.2) '@babel/types': 7.29.7 transitivePeerDependencies: - supports-color @@ -14471,578 +15041,678 @@ snapshots: '@babel/template': 7.29.7 '@babel/types': 7.29.7 + '@babel/helpers@8.0.0': + dependencies: + '@babel/template': 8.0.0 + '@babel/types': 8.0.4 + '@babel/parser@7.29.7': dependencies: '@babel/types': 7.29.7 - '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.29.7(@babel/core@7.29.7)': + '@babel/parser@8.0.4': + dependencies: + '@babel/types': 8.0.4 + + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/helper-plugin-utils': 7.29.7 - '@babel/traverse': 7.29.7 + '@babel/traverse': 7.29.7(supports-color@10.2.2) transitivePeerDependencies: - supports-color - '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-bugfix-safari-rest-destructuring-rhs-array@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-bugfix-safari-rest-destructuring-rhs-array@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/helper-plugin-utils': 7.29.7 - '@babel/helper-skip-transparent-expression-wrappers': 7.29.7 + '@babel/helper-skip-transparent-expression-wrappers': 7.29.7(supports-color@10.2.2) transitivePeerDependencies: - supports-color - '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/helper-plugin-utils': 7.29.7 - '@babel/helper-skip-transparent-expression-wrappers': 7.29.7 - '@babel/plugin-transform-optional-chaining': 7.29.7(@babel/core@7.29.7) + '@babel/helper-skip-transparent-expression-wrappers': 7.29.7(supports-color@10.2.2) + '@babel/plugin-transform-optional-chaining': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) transitivePeerDependencies: - supports-color - '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/helper-plugin-utils': 7.29.7 - '@babel/traverse': 7.29.7 + '@babel/traverse': 7.29.7(supports-color@10.2.2) transitivePeerDependencies: - supports-color - '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.29.7)': + '@babel/plugin-proposal-decorators@8.0.2(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 + '@babel/helper-create-class-features-plugin': 8.0.1(@babel/core@8.0.1) + '@babel/helper-plugin-utils': 8.0.1(@babel/core@8.0.1) + '@babel/plugin-syntax-decorators': 8.0.1(@babel/core@8.0.1) + + '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.29.7(supports-color@10.2.2))': + dependencies: + '@babel/core': 7.29.7(supports-color@10.2.2) + + '@babel/plugin-syntax-decorators@8.0.1(@babel/core@8.0.1)': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 8.0.1 + '@babel/helper-plugin-utils': 8.0.1(@babel/core@8.0.1) - '@babel/plugin-syntax-import-assertions@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-syntax-import-assertions@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-syntax-import-attributes@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-syntax-import-attributes@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-syntax-jsx@8.0.1(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 + '@babel/helper-plugin-utils': 8.0.1(@babel/core@8.0.1) + + '@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.29.7)': + '@babel/plugin-syntax-typescript@8.0.3(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 + '@babel/helper-plugin-utils': 8.0.1(@babel/core@8.0.1) + + '@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.7 - '@babel/helper-create-regexp-features-plugin': 7.29.7(@babel/core@7.29.7) + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-create-regexp-features-plugin': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-arrow-functions@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-transform-arrow-functions@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-async-generator-functions@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-transform-async-generator-functions@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/helper-plugin-utils': 7.29.7 - '@babel/helper-remap-async-to-generator': 7.29.7(@babel/core@7.29.7) - '@babel/traverse': 7.29.7 + '@babel/helper-remap-async-to-generator': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/traverse': 7.29.7(supports-color@10.2.2) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-async-to-generator@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-transform-async-to-generator@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: - '@babel/core': 7.29.7 - '@babel/helper-module-imports': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-module-imports': 7.29.7(supports-color@10.2.2) '@babel/helper-plugin-utils': 7.29.7 - '@babel/helper-remap-async-to-generator': 7.29.7(@babel/core@7.29.7) + '@babel/helper-remap-async-to-generator': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-block-scoped-functions@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-transform-block-scoped-functions@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-block-scoping@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-transform-block-scoping@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-class-properties@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-transform-class-properties@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: - '@babel/core': 7.29.7 - '@babel/helper-create-class-features-plugin': 7.29.7(@babel/core@7.29.7) + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-create-class-features-plugin': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) '@babel/helper-plugin-utils': 7.29.7 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-class-static-block@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-transform-class-static-block@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: - '@babel/core': 7.29.7 - '@babel/helper-create-class-features-plugin': 7.29.7(@babel/core@7.29.7) + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-create-class-features-plugin': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) '@babel/helper-plugin-utils': 7.29.7 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-classes@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-transform-classes@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/helper-annotate-as-pure': 7.29.7 '@babel/helper-compilation-targets': 7.29.7 '@babel/helper-globals': 7.29.7 '@babel/helper-plugin-utils': 7.29.7 - '@babel/helper-replace-supers': 7.29.7(@babel/core@7.29.7) - '@babel/traverse': 7.29.7 + '@babel/helper-replace-supers': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/traverse': 7.29.7(supports-color@10.2.2) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-computed-properties@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-transform-computed-properties@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/helper-plugin-utils': 7.29.7 '@babel/template': 7.29.7 - '@babel/plugin-transform-destructuring@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-transform-destructuring@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/helper-plugin-utils': 7.29.7 - '@babel/traverse': 7.29.7 + '@babel/traverse': 7.29.7(supports-color@10.2.2) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-dotall-regex@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-transform-destructuring@8.0.1(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 + '@babel/helper-plugin-utils': 8.0.1(@babel/core@8.0.1) + + '@babel/plugin-transform-dotall-regex@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.7 - '@babel/helper-create-regexp-features-plugin': 7.29.7(@babel/core@7.29.7) + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-create-regexp-features-plugin': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-duplicate-keys@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-transform-duplicate-keys@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.7 - '@babel/helper-create-regexp-features-plugin': 7.29.7(@babel/core@7.29.7) + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-create-regexp-features-plugin': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-dynamic-import@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-transform-dynamic-import@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-explicit-resource-management@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-transform-explicit-resource-management@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-destructuring': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-destructuring': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-exponentiation-operator@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-transform-explicit-resource-management@8.0.1(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 + '@babel/helper-plugin-utils': 8.0.1(@babel/core@8.0.1) + '@babel/plugin-transform-destructuring': 8.0.1(@babel/core@8.0.1) + + '@babel/plugin-transform-exponentiation-operator@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-export-namespace-from@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-transform-export-namespace-from@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-for-of@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-transform-for-of@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/helper-plugin-utils': 7.29.7 - '@babel/helper-skip-transparent-expression-wrappers': 7.29.7 + '@babel/helper-skip-transparent-expression-wrappers': 7.29.7(supports-color@10.2.2) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-function-name@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-transform-function-name@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/helper-compilation-targets': 7.29.7 '@babel/helper-plugin-utils': 7.29.7 - '@babel/traverse': 7.29.7 + '@babel/traverse': 7.29.7(supports-color@10.2.2) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-json-strings@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-transform-json-strings@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-literals@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-transform-literals@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-logical-assignment-operators@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-transform-logical-assignment-operators@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-member-expression-literals@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-transform-member-expression-literals@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-modules-amd@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-transform-modules-amd@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: - '@babel/core': 7.29.7 - '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7) + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) '@babel/helper-plugin-utils': 7.29.7 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-modules-commonjs@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-transform-modules-commonjs@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: - '@babel/core': 7.29.7 - '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7) + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) '@babel/helper-plugin-utils': 7.29.7 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-modules-systemjs@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-transform-modules-commonjs@8.0.1(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 + '@babel/helper-module-transforms': 8.0.1(@babel/core@8.0.1) + '@babel/helper-plugin-utils': 8.0.1(@babel/core@8.0.1) + + '@babel/plugin-transform-modules-systemjs@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: - '@babel/core': 7.29.7 - '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7) + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) '@babel/helper-plugin-utils': 7.29.7 '@babel/helper-validator-identifier': 7.29.7 - '@babel/traverse': 7.29.7 + '@babel/traverse': 7.29.7(supports-color@10.2.2) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-modules-umd@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-transform-modules-umd@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: - '@babel/core': 7.29.7 - '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7) + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) '@babel/helper-plugin-utils': 7.29.7 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-named-capturing-groups-regex@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-transform-named-capturing-groups-regex@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.7 - '@babel/helper-create-regexp-features-plugin': 7.29.7(@babel/core@7.29.7) + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-create-regexp-features-plugin': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-new-target@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-transform-new-target@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-nullish-coalescing-operator@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-transform-nullish-coalescing-operator@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-numeric-separator@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-transform-numeric-separator@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-object-rest-spread@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-transform-object-rest-spread@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/helper-compilation-targets': 7.29.7 '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-destructuring': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-parameters': 7.29.7(@babel/core@7.29.7) - '@babel/traverse': 7.29.7 + '@babel/plugin-transform-destructuring': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-parameters': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/traverse': 7.29.7(supports-color@10.2.2) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-object-super@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-transform-object-super@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/helper-plugin-utils': 7.29.7 - '@babel/helper-replace-supers': 7.29.7(@babel/core@7.29.7) + '@babel/helper-replace-supers': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-optional-catch-binding@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-transform-optional-catch-binding@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-optional-chaining@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-transform-optional-chaining@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/helper-plugin-utils': 7.29.7 - '@babel/helper-skip-transparent-expression-wrappers': 7.29.7 + '@babel/helper-skip-transparent-expression-wrappers': 7.29.7(supports-color@10.2.2) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-parameters@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-transform-parameters@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-private-methods@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-transform-private-methods@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: - '@babel/core': 7.29.7 - '@babel/helper-create-class-features-plugin': 7.29.7(@babel/core@7.29.7) + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-create-class-features-plugin': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) '@babel/helper-plugin-utils': 7.29.7 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-private-property-in-object@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-transform-private-property-in-object@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/helper-annotate-as-pure': 7.29.7 - '@babel/helper-create-class-features-plugin': 7.29.7(@babel/core@7.29.7) + '@babel/helper-create-class-features-plugin': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) '@babel/helper-plugin-utils': 7.29.7 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-property-literals@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-transform-property-literals@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-react-constant-elements@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-transform-react-constant-elements@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-react-display-name@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-transform-react-display-name@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-react-jsx-development@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-transform-react-display-name@8.0.1(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 + '@babel/helper-plugin-utils': 8.0.1(@babel/core@8.0.1) + + '@babel/plugin-transform-react-jsx-development@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: - '@babel/core': 7.29.7 - '@babel/plugin-transform-react-jsx': 7.29.7(@babel/core@7.29.7) + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/plugin-transform-react-jsx': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-react-jsx-self@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-transform-react-jsx-development@8.0.1(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 + '@babel/plugin-transform-react-jsx': 8.0.1(@babel/core@8.0.1) + + '@babel/plugin-transform-react-jsx-self@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-react-jsx-source@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-transform-react-jsx-source@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-react-jsx@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-transform-react-jsx@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/helper-annotate-as-pure': 7.29.7 - '@babel/helper-module-imports': 7.29.7 + '@babel/helper-module-imports': 7.29.7(supports-color@10.2.2) '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-syntax-jsx': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-syntax-jsx': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) '@babel/types': 7.29.7 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-react-pure-annotations@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-transform-react-jsx@8.0.1(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 + '@babel/helper-annotate-as-pure': 8.0.0 + '@babel/helper-module-imports': 8.0.0 + '@babel/helper-plugin-utils': 8.0.1(@babel/core@8.0.1) + '@babel/plugin-syntax-jsx': 8.0.1(@babel/core@8.0.1) + '@babel/types': 8.0.4 + + '@babel/plugin-transform-react-pure-annotations@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/helper-annotate-as-pure': 7.29.7 '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-regenerator@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-transform-react-pure-annotations@8.0.1(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 + '@babel/helper-annotate-as-pure': 8.0.0 + '@babel/helper-plugin-utils': 8.0.1(@babel/core@8.0.1) + + '@babel/plugin-transform-regenerator@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-regexp-modifiers@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-transform-regexp-modifiers@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.7 - '@babel/helper-create-regexp-features-plugin': 7.29.7(@babel/core@7.29.7) + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-create-regexp-features-plugin': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-reserved-words@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-transform-reserved-words@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-shorthand-properties@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-transform-shorthand-properties@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-spread@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-transform-spread@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/helper-plugin-utils': 7.29.7 - '@babel/helper-skip-transparent-expression-wrappers': 7.29.7 + '@babel/helper-skip-transparent-expression-wrappers': 7.29.7(supports-color@10.2.2) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-sticky-regex@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-transform-sticky-regex@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-template-literals@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-transform-template-literals@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-typeof-symbol@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-transform-typeof-symbol@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-typescript@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-transform-typescript@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/helper-annotate-as-pure': 7.29.7 - '@babel/helper-create-class-features-plugin': 7.29.7(@babel/core@7.29.7) + '@babel/helper-create-class-features-plugin': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) '@babel/helper-plugin-utils': 7.29.7 - '@babel/helper-skip-transparent-expression-wrappers': 7.29.7 - '@babel/plugin-syntax-typescript': 7.29.7(@babel/core@7.29.7) + '@babel/helper-skip-transparent-expression-wrappers': 7.29.7(supports-color@10.2.2) + '@babel/plugin-syntax-typescript': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-unicode-escapes@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-transform-typescript@8.0.1(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 + '@babel/helper-annotate-as-pure': 8.0.0 + '@babel/helper-create-class-features-plugin': 8.0.1(@babel/core@8.0.1) + '@babel/helper-plugin-utils': 8.0.1(@babel/core@8.0.1) + '@babel/helper-skip-transparent-expression-wrappers': 8.0.0 + '@babel/plugin-syntax-typescript': 8.0.3(@babel/core@8.0.1) + + '@babel/plugin-transform-unicode-escapes@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-unicode-property-regex@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-transform-unicode-property-regex@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.7 - '@babel/helper-create-regexp-features-plugin': 7.29.7(@babel/core@7.29.7) + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-create-regexp-features-plugin': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-unicode-regex@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-transform-unicode-regex@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.7 - '@babel/helper-create-regexp-features-plugin': 7.29.7(@babel/core@7.29.7) + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-create-regexp-features-plugin': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-unicode-sets-regex@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-transform-unicode-sets-regex@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.7 - '@babel/helper-create-regexp-features-plugin': 7.29.7(@babel/core@7.29.7) + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-create-regexp-features-plugin': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) '@babel/helper-plugin-utils': 7.29.7 - '@babel/preset-env@7.29.7(@babel/core@7.29.7)': + '@babel/preset-env@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: '@babel/compat-data': 7.29.7 - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/helper-compilation-targets': 7.29.7 '@babel/helper-plugin-utils': 7.29.7 '@babel/helper-validator-option': 7.29.7 - '@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-bugfix-safari-class-field-initializer-scope': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-bugfix-safari-rest-destructuring-rhs-array': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.29.7) - '@babel/plugin-syntax-import-assertions': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-syntax-import-attributes': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.29.7) - '@babel/plugin-transform-arrow-functions': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-async-generator-functions': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-async-to-generator': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-block-scoped-functions': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-block-scoping': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-class-properties': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-class-static-block': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-classes': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-computed-properties': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-destructuring': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-dotall-regex': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-duplicate-keys': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-duplicate-named-capturing-groups-regex': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-dynamic-import': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-explicit-resource-management': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-exponentiation-operator': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-export-namespace-from': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-for-of': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-function-name': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-json-strings': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-literals': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-logical-assignment-operators': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-member-expression-literals': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-modules-amd': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-modules-commonjs': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-modules-systemjs': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-modules-umd': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-named-capturing-groups-regex': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-new-target': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-nullish-coalescing-operator': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-numeric-separator': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-object-rest-spread': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-object-super': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-optional-catch-binding': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-optional-chaining': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-parameters': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-private-methods': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-private-property-in-object': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-property-literals': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-regenerator': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-regexp-modifiers': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-reserved-words': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-shorthand-properties': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-spread': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-sticky-regex': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-template-literals': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-typeof-symbol': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-unicode-escapes': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-unicode-property-regex': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-unicode-regex': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-unicode-sets-regex': 7.29.7(@babel/core@7.29.7) - '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.29.7) - babel-plugin-polyfill-corejs2: 0.4.17(@babel/core@7.29.7) - babel-plugin-polyfill-corejs3: 0.14.2(@babel/core@7.29.7) - babel-plugin-polyfill-regenerator: 0.6.8(@babel/core@7.29.7) + '@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-bugfix-safari-class-field-initializer-scope': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-bugfix-safari-rest-destructuring-rhs-array': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-syntax-import-assertions': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-syntax-import-attributes': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-arrow-functions': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-async-generator-functions': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-async-to-generator': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-block-scoped-functions': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-block-scoping': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-class-properties': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-class-static-block': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-classes': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-computed-properties': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-destructuring': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-dotall-regex': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-duplicate-keys': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-duplicate-named-capturing-groups-regex': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-dynamic-import': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-explicit-resource-management': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-exponentiation-operator': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-export-namespace-from': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-for-of': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-function-name': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-json-strings': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-literals': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-logical-assignment-operators': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-member-expression-literals': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-modules-amd': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-modules-commonjs': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-modules-systemjs': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-modules-umd': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-named-capturing-groups-regex': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-new-target': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-nullish-coalescing-operator': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-numeric-separator': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-object-rest-spread': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-object-super': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-optional-catch-binding': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-optional-chaining': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-parameters': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-private-methods': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-private-property-in-object': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-property-literals': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-regenerator': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-regexp-modifiers': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-reserved-words': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-shorthand-properties': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-spread': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-sticky-regex': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-template-literals': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-typeof-symbol': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-unicode-escapes': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-unicode-property-regex': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-unicode-regex': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-unicode-sets-regex': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.29.7(supports-color@10.2.2)) + babel-plugin-polyfill-corejs2: 0.4.17(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + babel-plugin-polyfill-corejs3: 0.14.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + babel-plugin-polyfill-regenerator: 0.6.8(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) core-js-compat: 3.49.0 semver: 6.3.1 transitivePeerDependencies: - supports-color - '@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.29.7)': + '@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/helper-plugin-utils': 7.29.7 '@babel/types': 7.29.7 esutils: 2.0.3 - '@babel/preset-react@7.29.7(@babel/core@7.29.7)': + '@babel/preset-react@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/helper-plugin-utils': 7.29.7 '@babel/helper-validator-option': 7.29.7 - '@babel/plugin-transform-react-display-name': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-react-jsx': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-react-jsx-development': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-react-pure-annotations': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-react-display-name': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-react-jsx': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-react-jsx-development': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-react-pure-annotations': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) transitivePeerDependencies: - supports-color - '@babel/preset-typescript@7.29.7(@babel/core@7.29.7)': + '@babel/preset-react@8.0.1(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 + '@babel/helper-plugin-utils': 8.0.1(@babel/core@8.0.1) + '@babel/helper-validator-option': 8.0.0 + '@babel/plugin-transform-react-display-name': 8.0.1(@babel/core@8.0.1) + '@babel/plugin-transform-react-jsx': 8.0.1(@babel/core@8.0.1) + '@babel/plugin-transform-react-jsx-development': 8.0.1(@babel/core@8.0.1) + '@babel/plugin-transform-react-pure-annotations': 8.0.1(@babel/core@8.0.1) + + '@babel/preset-typescript@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/helper-plugin-utils': 7.29.7 '@babel/helper-validator-option': 7.29.7 - '@babel/plugin-syntax-jsx': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-modules-commonjs': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-typescript': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-syntax-jsx': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-modules-commonjs': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/plugin-transform-typescript': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) transitivePeerDependencies: - supports-color + '@babel/preset-typescript@8.0.1(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 + '@babel/helper-plugin-utils': 8.0.1(@babel/core@8.0.1) + '@babel/helper-validator-option': 8.0.0 + '@babel/plugin-transform-modules-commonjs': 8.0.1(@babel/core@8.0.1) + '@babel/plugin-transform-typescript': 8.0.1(@babel/core@8.0.1) + '@babel/runtime-corejs3@7.29.7': dependencies: core-js-pure: 3.49.0 @@ -15055,7 +15725,13 @@ snapshots: '@babel/parser': 7.29.7 '@babel/types': 7.29.7 - '@babel/traverse@7.29.7': + '@babel/template@8.0.0': + dependencies: + '@babel/code-frame': 8.0.0 + '@babel/parser': 8.0.4 + '@babel/types': 8.0.4 + + '@babel/traverse@7.29.7(supports-color@10.2.2)': dependencies: '@babel/code-frame': 7.29.7 '@babel/generator': 7.29.7 @@ -15063,24 +15739,39 @@ snapshots: '@babel/parser': 7.29.7 '@babel/template': 7.29.7 '@babel/types': 7.29.7 - debug: 4.4.3 + debug: 4.4.3(supports-color@10.2.2) transitivePeerDependencies: - supports-color + '@babel/traverse@8.0.4': + dependencies: + '@babel/code-frame': 8.0.0 + '@babel/generator': 8.0.0 + '@babel/helper-globals': 8.0.0 + '@babel/parser': 8.0.4 + '@babel/template': 8.0.0 + '@babel/types': 8.0.4 + obug: 2.1.1 + '@babel/types@7.29.7': dependencies: '@babel/helper-string-parser': 7.29.7 '@babel/helper-validator-identifier': 7.29.7 - '@base-org/account@1.1.1(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(use-sync-external-store@1.4.0(react@19.1.4))(utf-8-validate@6.0.6)(zod@3.25.76)': + '@babel/types@8.0.4': + dependencies: + '@babel/helper-string-parser': 8.0.0 + '@babel/helper-validator-identifier': 8.0.4 + + '@base-org/account@1.1.1(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(use-sync-external-store@1.4.0(react@19.1.4))(utf-8-validate@6.0.6)(zod@4.4.3)': dependencies: '@noble/hashes': 1.8.0 clsx: 1.2.1 eventemitter3: 5.0.1 idb-keyval: 6.2.1 - ox: 0.6.9(@typescript/typescript6@6.0.2)(zod@3.25.76) + ox: 0.6.9(@typescript/typescript6@6.0.2)(zod@4.4.3) preact: 10.24.2 - viem: 2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) + viem: 2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) zustand: 5.0.3(@types/react@19.1.7)(react@19.1.4)(use-sync-external-store@1.4.0(react@19.1.4)) transitivePeerDependencies: - '@types/react' @@ -15092,16 +15783,16 @@ snapshots: - utf-8-validate - zod - '@base-org/account@2.4.0(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.1.4)(use-sync-external-store@1.4.0(react@19.1.4))(utf-8-validate@6.0.6)(zod@3.25.76)': + '@base-org/account@2.4.0(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(debug@4.4.3(supports-color@10.2.2))(fastestsmallesttextencoderdecoder@1.0.22)(react@19.1.4)(use-sync-external-store@1.4.0(react@19.1.4))(utf-8-validate@6.0.6)(zod@4.4.3)': dependencies: - '@coinbase/cdp-sdk': 1.50.0(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@6.0.6) + '@coinbase/cdp-sdk': 1.50.0(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(debug@4.4.3(supports-color@10.2.2))(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@6.0.6) '@noble/hashes': 1.8.0 clsx: 1.2.1 eventemitter3: 5.0.1 idb-keyval: 6.2.1 - ox: 0.6.9(@typescript/typescript6@6.0.2)(zod@3.25.76) + ox: 0.6.9(@typescript/typescript6@6.0.2)(zod@4.4.3) preact: 10.24.2 - viem: 2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) + viem: 2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) zustand: 5.0.3(@types/react@19.1.7)(react@19.1.4)(use-sync-external-store@1.4.0(react@19.1.4)) transitivePeerDependencies: - '@types/react' @@ -15115,21 +15806,27 @@ snapshots: - utf-8-validate - zod + '@bcoe/v8-coverage@1.0.2': {} + '@blockscout/admin-rs-types@1.5.1': {} +<<<<<<< HEAD '@blockscout/api-types@0.0.1-beta.d261ddc': {} +======= + '@blockscout/api-types@0.0.1-beta.40c5c457b4': {} +>>>>>>> v2.12.0 '@blockscout/bens-types@1.7.1': {} '@blockscout/contracts-info-types@1.5.3': {} - '@blockscout/interchain-indexer-types@1.6.0': {} + '@blockscout/interchain-indexer-types@1.11.0': {} - '@blockscout/multichain-aggregator-types@2.1.6': {} + '@blockscout/multichain-aggregator-types@2.1.8': {} '@blockscout/points-types@1.4.1': {} - '@blockscout/stats-types@2.11.1': {} + '@blockscout/stats-types@2.19.1': {} '@blockscout/tac-operation-lifecycle-types@0.0.1-beta.71a05d5': {} @@ -15139,10 +15836,10 @@ snapshots: '@borewit/text-codec@0.2.2': {} - '@boundaries/elements@2.0.1(@typescript-eslint/parser@8.59.2(@typescript/typescript6@6.0.2)(eslint@9.39.2))(eslint@9.39.2)': + '@boundaries/elements@2.0.1(@typescript-eslint/parser@5.62.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: - eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.59.2(@typescript/typescript6@6.0.2)(eslint@9.39.2))(eslint-import-resolver-node@0.3.9)(eslint@9.39.2) + eslint-import-resolver-node: 0.3.9(supports-color@10.2.2) + eslint-module-utils: 2.12.1(@typescript-eslint/parser@5.62.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2))(eslint-import-resolver-node@0.3.9(supports-color@10.2.2))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) handlebars: 4.7.9 is-core-module: 2.16.1 micromatch: 4.0.8 @@ -15194,12 +15891,12 @@ snapshots: dependencies: '@chainsafe/is-ip': 2.1.0 - '@chakra-ui/cli@3.36.1(@chakra-ui/react@3.36.1(@emotion/react@11.14.0(@types/react@19.1.7)(react@19.1.4))(react-dom@19.1.4(react@19.1.4))(react@19.1.4))': + '@chakra-ui/cli@3.36.1(@chakra-ui/react@3.36.1(@emotion/react@11.14.0(@types/react@19.1.7)(react@19.1.4)(supports-color@10.2.2))(react-dom@19.1.4(react@19.1.4))(react@19.1.4))(supports-color@10.2.2)': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/parser': 7.29.7 - '@babel/plugin-transform-typescript': 7.29.7(@babel/core@7.29.7) - '@chakra-ui/react': 3.36.1(@emotion/react@11.14.0(@types/react@19.1.7)(react@19.1.4))(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + '@babel/plugin-transform-typescript': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@chakra-ui/react': 3.36.1(@emotion/react@11.14.0(@types/react@19.1.7)(react@19.1.4)(supports-color@10.2.2))(react-dom@19.1.4(react@19.1.4))(react@19.1.4) '@clack/prompts': 1.5.1 '@pandacss/is-valid-prop': 1.11.1 '@types/babel__core': 7.20.5 @@ -15209,7 +15906,7 @@ snapshots: chokidar: 5.0.0 cli-table: 0.3.11 commander: 14.0.3 - debug: 4.4.3 + debug: 4.4.3(supports-color@10.2.2) dotenv: 17.3.1 esbuild: 0.28.1 get-tsconfig: 4.14.0 @@ -15224,11 +15921,11 @@ snapshots: transitivePeerDependencies: - supports-color - '@chakra-ui/react@3.36.1(@emotion/react@11.14.0(@types/react@18.3.12)(react@19.1.4))(react-dom@19.1.4(react@19.1.4))(react@19.1.4)': + '@chakra-ui/react@3.36.1(@emotion/react@11.14.0(@types/react@18.3.12)(react@19.1.4)(supports-color@10.2.2))(react-dom@19.1.4(react@19.1.4))(react@19.1.4)': dependencies: '@ark-ui/react': 5.37.2(react-dom@19.1.4(react@19.1.4))(react@19.1.4) '@emotion/is-prop-valid': 1.4.0 - '@emotion/react': 11.14.0(@types/react@18.3.12)(react@19.1.4) + '@emotion/react': 11.14.0(@types/react@18.3.12)(react@19.1.4)(supports-color@10.2.2) '@emotion/serialize': 1.3.3 '@emotion/use-insertion-effect-with-fallbacks': 1.2.0(react@19.1.4) '@emotion/utils': 1.4.2 @@ -15237,11 +15934,11 @@ snapshots: react: 19.1.4 react-dom: 19.1.4(react@19.1.4) - '@chakra-ui/react@3.36.1(@emotion/react@11.14.0(@types/react@19.1.7)(react@19.1.4))(react-dom@19.1.4(react@19.1.4))(react@19.1.4)': + '@chakra-ui/react@3.36.1(@emotion/react@11.14.0(@types/react@19.1.7)(react@19.1.4)(supports-color@10.2.2))(react-dom@19.1.4(react@19.1.4))(react@19.1.4)': dependencies: '@ark-ui/react': 5.37.2(react-dom@19.1.4(react@19.1.4))(react@19.1.4) '@emotion/is-prop-valid': 1.4.0 - '@emotion/react': 11.14.0(@types/react@19.1.7)(react@19.1.4) + '@emotion/react': 11.14.0(@types/react@19.1.7)(react@19.1.4)(supports-color@10.2.2) '@emotion/serialize': 1.3.3 '@emotion/use-insertion-effect-with-fallbacks': 1.2.0(react@19.1.4) '@emotion/utils': 1.4.2 @@ -15335,14 +16032,14 @@ snapshots: style-mod: 4.1.3 w3c-keyname: 2.2.8 - '@coinbase/cdp-sdk@1.50.0(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@6.0.6)': + '@coinbase/cdp-sdk@1.50.0(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(debug@4.4.3(supports-color@10.2.2))(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@6.0.6)': dependencies: '@solana-program/system': 0.10.0(@solana/kit@5.5.1(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@6.0.6)) '@solana-program/token': 0.9.0(@solana/kit@5.5.1(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@6.0.6)) '@solana/kit': 5.5.1(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@6.0.6) abitype: 1.0.6(@typescript/typescript6@6.0.2)(zod@3.25.76) - axios: 1.12.0(debug@4.4.3) - axios-retry: 4.5.0(axios@1.12.0) + axios: 1.12.0(debug@4.4.3(supports-color@10.2.2)) + axios-retry: 4.5.0(axios@1.12.0(debug@4.4.3(supports-color@10.2.2))) bs58: 6.0.0 jose: 6.2.3 md5: 2.3.0 @@ -15356,12 +16053,12 @@ snapshots: - typescript - utf-8-validate - '@coinbase/wallet-sdk@3.9.3': + '@coinbase/wallet-sdk@3.9.3(supports-color@10.2.2)': dependencies: bn.js: 5.2.3 buffer: 6.0.3 clsx: 1.2.1 - eth-block-tracker: 7.1.0 + eth-block-tracker: 7.1.0(supports-color@10.2.2) eth-json-rpc-filters: 6.0.1 eventemitter3: 5.0.4 keccak: 3.0.4 @@ -15370,15 +16067,15 @@ snapshots: transitivePeerDependencies: - supports-color - '@coinbase/wallet-sdk@4.3.6(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(use-sync-external-store@1.4.0(react@19.1.4))(utf-8-validate@6.0.6)(zod@3.25.76)': + '@coinbase/wallet-sdk@4.3.6(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(use-sync-external-store@1.4.0(react@19.1.4))(utf-8-validate@6.0.6)(zod@4.4.3)': dependencies: '@noble/hashes': 1.8.0 clsx: 1.2.1 eventemitter3: 5.0.1 idb-keyval: 6.2.1 - ox: 0.6.9(@typescript/typescript6@6.0.2)(zod@3.25.76) + ox: 0.6.9(@typescript/typescript6@6.0.2)(zod@4.4.3) preact: 10.24.2 - viem: 2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) + viem: 2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) zustand: 5.0.3(@types/react@19.1.7)(react@19.1.4)(use-sync-external-store@1.4.0(react@19.1.4)) transitivePeerDependencies: - '@types/react' @@ -15390,13 +16087,13 @@ snapshots: - utf-8-validate - zod - '@coinbase/wallet-sdk@4.3.7(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76)': + '@coinbase/wallet-sdk@4.3.7(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3)': dependencies: '@noble/hashes': 1.8.0 clsx: 1.2.1 eventemitter3: 5.0.4 preact: 10.29.2 - viem: 2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) + viem: 2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) transitivePeerDependencies: - bufferutil - typescript @@ -15657,11 +16354,6 @@ snapshots: '@csstools/normalize.css@12.1.1': {} - '@dependents/detective-less@4.1.0': - dependencies: - gonzales-pe: 4.3.0 - node-source-walk: 6.0.2 - '@discoveryjs/json-ext@0.5.7': {} '@dnsquery/dns-packet@6.1.1': @@ -15669,12 +16361,12 @@ snapshots: '@leichtgewicht/ip-codec': 2.0.5 utf8-codec: 1.0.0 - '@dynamic-labs-connectors/base-account-evm@4.6.9(@dynamic-labs/ethereum-core@4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6)(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76)))(@dynamic-labs/wallet-connector-core@4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6))(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(use-sync-external-store@1.4.0(react@19.1.4))(utf-8-validate@6.0.6)(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76))(zod@3.25.76)': + '@dynamic-labs-connectors/base-account-evm@4.6.9(30aff48d6969f56fd45e702fc8c5a887)': dependencies: - '@base-org/account': 1.1.1(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(use-sync-external-store@1.4.0(react@19.1.4))(utf-8-validate@6.0.6)(zod@3.25.76) - '@dynamic-labs/ethereum-core': 4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6)(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76)) - '@dynamic-labs/wallet-connector-core': 4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6) - viem: 2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) + '@base-org/account': 1.1.1(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(use-sync-external-store@1.4.0(react@19.1.4))(utf-8-validate@6.0.6)(zod@4.4.3) + '@dynamic-labs/ethereum-core': 4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(debug@4.4.3(supports-color@10.2.2))(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6)(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3)) + '@dynamic-labs/wallet-connector-core': 4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(debug@4.4.3(supports-color@10.2.2))(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6) + viem: 2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) transitivePeerDependencies: - '@types/react' - bufferutil @@ -15685,12 +16377,12 @@ snapshots: - utf-8-validate - zod - '@dynamic-labs-connectors/metamask-evm@4.6.9(fae979a47c6480d6087d4faa1da013ff)': + '@dynamic-labs-connectors/metamask-evm@4.6.9(e8ff8c7474d51c98957326bb7c6d4707)': dependencies: - '@dynamic-labs/ethereum': 4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4)(use-sync-external-store@1.4.0(react@19.1.4))(utf-8-validate@6.0.6)(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76))(zod@3.25.76) - '@dynamic-labs/ethereum-core': 4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6)(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76)) - '@dynamic-labs/wallet-connector-core': 4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6) - '@metamask/connect-evm': 1.3.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) + '@dynamic-labs/ethereum': 4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(debug@4.4.3(supports-color@10.2.2))(fastestsmallesttextencoderdecoder@1.0.22)(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6))(react@19.1.4)(supports-color@10.2.2)(use-sync-external-store@1.4.0(react@19.1.4))(utf-8-validate@6.0.6)(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3))(zod@4.4.3) + '@dynamic-labs/ethereum-core': 4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(debug@4.4.3(supports-color@10.2.2))(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6)(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3)) + '@dynamic-labs/wallet-connector-core': 4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(debug@4.4.3(supports-color@10.2.2))(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6) + '@metamask/connect-evm': 1.3.0(bufferutil@4.1.0)(supports-color@10.2.2)(utf-8-validate@6.0.6) transitivePeerDependencies: - '@react-native-async-storage/async-storage' - bufferutil @@ -15700,10 +16392,10 @@ snapshots: '@dynamic-labs-sdk/assert-package-version@1.12.1': {} - '@dynamic-labs-sdk/client@1.12.1(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6)': + '@dynamic-labs-sdk/client@1.12.1(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(debug@4.4.3(supports-color@10.2.2))(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6)': dependencies: '@dynamic-labs-sdk/assert-package-version': 1.12.1 - '@dynamic-labs-wallet/browser-wallet-client': 1.0.39(@dynamic-labs-wallet/forward-mpc-client@0.12.0(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(utf-8-validate@6.0.6)) + '@dynamic-labs-wallet/browser-wallet-client': 1.0.39(@dynamic-labs-wallet/forward-mpc-client@0.12.0(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(debug@4.4.3(supports-color@10.2.2)) '@dynamic-labs-wallet/forward-mpc-client': 0.12.0(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(utf-8-validate@6.0.6) '@dynamic-labs/sdk-api-core': 0.0.1046 '@simplewebauthn/browser': 13.1.0 @@ -15712,7 +16404,7 @@ snapshots: eventemitter3: 5.0.1 zod: 4.0.5 optionalDependencies: - react-native: 0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6) + react-native: 0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6) transitivePeerDependencies: - '@dynamic-labs-wallet/primitives' - bufferutil @@ -15721,40 +16413,40 @@ snapshots: - react-dom - utf-8-validate - '@dynamic-labs-wallet/browser-wallet-client@1.0.39(@dynamic-labs-wallet/forward-mpc-client@0.12.0(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(utf-8-validate@6.0.6))': + '@dynamic-labs-wallet/browser-wallet-client@1.0.39(@dynamic-labs-wallet/forward-mpc-client@0.12.0(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(debug@4.4.3(supports-color@10.2.2))': dependencies: - '@dynamic-labs-wallet/core': 1.0.39(@dynamic-labs-wallet/forward-mpc-client@0.12.0(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(utf-8-validate@6.0.6)) + '@dynamic-labs-wallet/core': 1.0.39(@dynamic-labs-wallet/forward-mpc-client@0.12.0(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(debug@4.4.3(supports-color@10.2.2)) '@dynamic-labs/logger': 4.84.0 '@dynamic-labs/message-transport': 4.84.0 transitivePeerDependencies: - '@dynamic-labs-wallet/forward-mpc-client' - debug - '@dynamic-labs-wallet/browser-wallet-client@1.0.48(@dynamic-labs-wallet/forward-mpc-client@0.12.0(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(utf-8-validate@6.0.6))': + '@dynamic-labs-wallet/browser-wallet-client@1.0.48(@dynamic-labs-wallet/forward-mpc-client@0.12.0(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(debug@4.4.3(supports-color@10.2.2))': dependencies: - '@dynamic-labs-wallet/core': 1.0.48(@dynamic-labs-wallet/forward-mpc-client@0.12.0(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(utf-8-validate@6.0.6)) + '@dynamic-labs-wallet/core': 1.0.48(@dynamic-labs-wallet/forward-mpc-client@0.12.0(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(debug@4.4.3(supports-color@10.2.2)) '@dynamic-labs/logger': 4.84.0 '@dynamic-labs/message-transport': 4.84.0 transitivePeerDependencies: - '@dynamic-labs-wallet/forward-mpc-client' - debug - '@dynamic-labs-wallet/core@1.0.39(@dynamic-labs-wallet/forward-mpc-client@0.12.0(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(utf-8-validate@6.0.6))': + '@dynamic-labs-wallet/core@1.0.39(@dynamic-labs-wallet/forward-mpc-client@0.12.0(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(debug@4.4.3(supports-color@10.2.2))': dependencies: '@dynamic-labs-wallet/forward-mpc-client': 0.12.0(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(utf-8-validate@6.0.6) '@dynamic-labs-wallet/primitives': 1.0.39 '@dynamic-labs/sdk-api-core': 0.0.984 - axios: 1.12.0(debug@4.4.3) + axios: 1.12.0(debug@4.4.3(supports-color@10.2.2)) uuid: 11.1.0 transitivePeerDependencies: - debug - '@dynamic-labs-wallet/core@1.0.48(@dynamic-labs-wallet/forward-mpc-client@0.12.0(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(utf-8-validate@6.0.6))': + '@dynamic-labs-wallet/core@1.0.48(@dynamic-labs-wallet/forward-mpc-client@0.12.0(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(debug@4.4.3(supports-color@10.2.2))': dependencies: '@dynamic-labs-wallet/forward-mpc-client': 0.12.0(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(utf-8-validate@6.0.6) '@dynamic-labs-wallet/primitives': 1.0.48 '@dynamic-labs/sdk-api-core': 0.0.984 - axios: 1.12.0(debug@4.4.3) + axios: 1.12.0(debug@4.4.3(supports-color@10.2.2)) uuid: 11.1.0 transitivePeerDependencies: - debug @@ -15794,22 +16486,22 @@ snapshots: dependencies: '@dynamic-labs/logger': 4.91.4 - '@dynamic-labs/embedded-wallet-evm@4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6)(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76))(zod@3.25.76)': + '@dynamic-labs/embedded-wallet-evm@4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(debug@4.4.3(supports-color@10.2.2))(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6)(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3))(zod@4.4.3)': dependencies: '@dynamic-labs/assert-package-version': 4.91.4 - '@dynamic-labs/embedded-wallet': 4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6) - '@dynamic-labs/ethereum-core': 4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6)(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76)) + '@dynamic-labs/embedded-wallet': 4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(debug@4.4.3(supports-color@10.2.2))(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6) + '@dynamic-labs/ethereum-core': 4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(debug@4.4.3(supports-color@10.2.2))(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6)(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3)) '@dynamic-labs/sdk-api-core': 0.0.1046 '@dynamic-labs/types': 4.91.4 '@dynamic-labs/utils': 4.91.4 '@dynamic-labs/wallet-book': 4.91.4(react-dom@19.1.4(react@19.1.4))(react@19.1.4) - '@dynamic-labs/wallet-connector-core': 4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6) + '@dynamic-labs/wallet-connector-core': 4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(debug@4.4.3(supports-color@10.2.2))(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6) '@dynamic-labs/webauthn': 4.91.4 '@turnkey/api-key-stamper': 0.4.7 '@turnkey/iframe-stamper': 2.5.0 - '@turnkey/viem': 0.13.0(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76))(zod@3.25.76) + '@turnkey/viem': 0.13.0(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3))(zod@4.4.3) '@turnkey/webauthn-stamper': 0.5.1 - viem: 2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) + viem: 2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) transitivePeerDependencies: - '@dynamic-labs-wallet/primitives' - '@react-native-async-storage/async-storage' @@ -15826,14 +16518,14 @@ snapshots: - utf-8-validate - zod - '@dynamic-labs/embedded-wallet@4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6)': + '@dynamic-labs/embedded-wallet@4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(debug@4.4.3(supports-color@10.2.2))(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6)': dependencies: '@dynamic-labs/assert-package-version': 4.91.4 '@dynamic-labs/logger': 4.91.4 '@dynamic-labs/sdk-api-core': 0.0.1046 '@dynamic-labs/utils': 4.91.4 '@dynamic-labs/wallet-book': 4.91.4(react-dom@19.1.4(react@19.1.4))(react@19.1.4) - '@dynamic-labs/wallet-connector-core': 4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6) + '@dynamic-labs/wallet-connector-core': 4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(debug@4.4.3(supports-color@10.2.2))(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6) '@dynamic-labs/webauthn': 4.91.4 '@turnkey/api-key-stamper': 0.4.7 '@turnkey/http': 3.10.0 @@ -15853,16 +16545,16 @@ snapshots: - react-native-passkey - utf-8-validate - '@dynamic-labs/ethereum-aa-core@4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6)(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76))': + '@dynamic-labs/ethereum-aa-core@4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(debug@4.4.3(supports-color@10.2.2))(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6)(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3))': dependencies: '@dynamic-labs/assert-package-version': 4.91.4 - '@dynamic-labs/ethereum-core': 4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6)(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76)) + '@dynamic-labs/ethereum-core': 4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(debug@4.4.3(supports-color@10.2.2))(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6)(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3)) '@dynamic-labs/sdk-api-core': 0.0.1046 '@dynamic-labs/types': 4.91.4 '@dynamic-labs/utils': 4.91.4 '@dynamic-labs/wallet-book': 4.91.4(react-dom@19.1.4(react@19.1.4))(react@19.1.4) - '@dynamic-labs/wallet-connector-core': 4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6) - viem: 2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) + '@dynamic-labs/wallet-connector-core': 4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(debug@4.4.3(supports-color@10.2.2))(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6) + viem: 2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) transitivePeerDependencies: - '@dynamic-labs-wallet/primitives' - '@react-native-async-storage/async-storage' @@ -15876,21 +16568,21 @@ snapshots: - react-native-passkey - utf-8-validate - '@dynamic-labs/ethereum-aa@4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(@zerodev/webauthn-key@5.5.0(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76)))(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6)(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76))': + '@dynamic-labs/ethereum-aa@4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(@zerodev/webauthn-key@5.5.0(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3)))(bufferutil@4.1.0)(debug@4.4.3(supports-color@10.2.2))(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6)(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3))': dependencies: '@dynamic-labs/assert-package-version': 4.91.4 - '@dynamic-labs/ethereum-aa-core': 4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6)(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76)) - '@dynamic-labs/ethereum-core': 4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6)(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76)) + '@dynamic-labs/ethereum-aa-core': 4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(debug@4.4.3(supports-color@10.2.2))(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6)(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3)) + '@dynamic-labs/ethereum-core': 4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(debug@4.4.3(supports-color@10.2.2))(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6)(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3)) '@dynamic-labs/logger': 4.91.4 '@dynamic-labs/sdk-api-core': 0.0.1046 '@dynamic-labs/types': 4.91.4 '@dynamic-labs/utils': 4.91.4 '@dynamic-labs/wallet-book': 4.91.4(react-dom@19.1.4(react@19.1.4))(react@19.1.4) - '@dynamic-labs/wallet-connector-core': 4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6) - '@zerodev/ecdsa-validator': 5.4.9(@zerodev/sdk@5.5.7(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76)))(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76)) - '@zerodev/multi-chain-ecdsa-validator': 5.4.5(@zerodev/sdk@5.5.7(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76)))(@zerodev/webauthn-key@5.5.0(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76)))(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76)) - '@zerodev/sdk': 5.5.7(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76)) - viem: 2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) + '@dynamic-labs/wallet-connector-core': 4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(debug@4.4.3(supports-color@10.2.2))(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6) + '@zerodev/ecdsa-validator': 5.4.9(@zerodev/sdk@5.5.7(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3)))(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3)) + '@zerodev/multi-chain-ecdsa-validator': 5.4.5(@zerodev/sdk@5.5.7(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3)))(@zerodev/webauthn-key@5.5.0(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3)))(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3)) + '@zerodev/sdk': 5.5.7(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3)) + viem: 2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) transitivePeerDependencies: - '@dynamic-labs-wallet/primitives' - '@react-native-async-storage/async-storage' @@ -15905,7 +16597,7 @@ snapshots: - react-native-passkey - utf-8-validate - '@dynamic-labs/ethereum-core@4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6)(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76))': + '@dynamic-labs/ethereum-core@4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(debug@4.4.3(supports-color@10.2.2))(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6)(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3))': dependencies: '@dynamic-labs/assert-package-version': 4.91.4 '@dynamic-labs/logger': 4.91.4 @@ -15914,8 +16606,8 @@ snapshots: '@dynamic-labs/types': 4.91.4 '@dynamic-labs/utils': 4.91.4 '@dynamic-labs/wallet-book': 4.91.4(react-dom@19.1.4(react@19.1.4))(react@19.1.4) - '@dynamic-labs/wallet-connector-core': 4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6) - viem: 2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) + '@dynamic-labs/wallet-connector-core': 4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(debug@4.4.3(supports-color@10.2.2))(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6) + viem: 2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) transitivePeerDependencies: - '@dynamic-labs-wallet/primitives' - '@react-native-async-storage/async-storage' @@ -15929,7 +16621,7 @@ snapshots: - react-native-passkey - utf-8-validate - '@dynamic-labs/ethereum-core@4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6)(viem@2.51.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76))': + '@dynamic-labs/ethereum-core@4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(debug@4.4.3(supports-color@10.2.2))(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6)(viem@2.51.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3))': dependencies: '@dynamic-labs/assert-package-version': 4.91.4 '@dynamic-labs/logger': 4.91.4 @@ -15938,8 +16630,8 @@ snapshots: '@dynamic-labs/types': 4.91.4 '@dynamic-labs/utils': 4.91.4 '@dynamic-labs/wallet-book': 4.91.4(react-dom@19.1.4(react@19.1.4))(react@19.1.4) - '@dynamic-labs/wallet-connector-core': 4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6) - viem: 2.51.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) + '@dynamic-labs/wallet-connector-core': 4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(debug@4.4.3(supports-color@10.2.2))(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6) + viem: 2.51.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) transitivePeerDependencies: - '@dynamic-labs-wallet/primitives' - '@react-native-async-storage/async-storage' @@ -15953,25 +16645,25 @@ snapshots: - react-native-passkey - utf-8-validate - '@dynamic-labs/ethereum@4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4)(use-sync-external-store@1.4.0(react@19.1.4))(utf-8-validate@6.0.6)(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76))(zod@3.25.76)': + '@dynamic-labs/ethereum@4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(debug@4.4.3(supports-color@10.2.2))(fastestsmallesttextencoderdecoder@1.0.22)(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6))(react@19.1.4)(supports-color@10.2.2)(use-sync-external-store@1.4.0(react@19.1.4))(utf-8-validate@6.0.6)(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3))(zod@4.4.3)': dependencies: - '@coinbase/wallet-sdk': 4.3.7(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) - '@dynamic-labs-connectors/base-account-evm': 4.6.9(@dynamic-labs/ethereum-core@4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6)(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76)))(@dynamic-labs/wallet-connector-core@4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6))(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(use-sync-external-store@1.4.0(react@19.1.4))(utf-8-validate@6.0.6)(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76))(zod@3.25.76) - '@dynamic-labs-connectors/metamask-evm': 4.6.9(fae979a47c6480d6087d4faa1da013ff) + '@coinbase/wallet-sdk': 4.3.7(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) + '@dynamic-labs-connectors/base-account-evm': 4.6.9(30aff48d6969f56fd45e702fc8c5a887) + '@dynamic-labs-connectors/metamask-evm': 4.6.9(e8ff8c7474d51c98957326bb7c6d4707) '@dynamic-labs/assert-package-version': 4.91.4 - '@dynamic-labs/embedded-wallet-evm': 4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6)(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76))(zod@3.25.76) - '@dynamic-labs/ethereum-core': 4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6)(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76)) + '@dynamic-labs/embedded-wallet-evm': 4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(debug@4.4.3(supports-color@10.2.2))(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6)(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3))(zod@4.4.3) + '@dynamic-labs/ethereum-core': 4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(debug@4.4.3(supports-color@10.2.2))(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6)(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3)) '@dynamic-labs/logger': 4.91.4 '@dynamic-labs/rpc-providers': 4.91.4 '@dynamic-labs/types': 4.91.4 '@dynamic-labs/utils': 4.91.4 - '@dynamic-labs/waas-evm': 4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6)(zod@3.25.76) + '@dynamic-labs/waas-evm': 4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(debug@4.4.3(supports-color@10.2.2))(fastestsmallesttextencoderdecoder@1.0.22)(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6)(zod@4.4.3) '@dynamic-labs/wallet-book': 4.91.4(react-dom@19.1.4(react@19.1.4))(react@19.1.4) - '@dynamic-labs/wallet-connector-core': 4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6) - '@walletconnect/ethereum-provider': 2.23.2(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@3.25.76) + '@dynamic-labs/wallet-connector-core': 4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(debug@4.4.3(supports-color@10.2.2))(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6) + '@walletconnect/ethereum-provider': 2.23.2(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@4.4.3) buffer: 6.0.3 eventemitter3: 5.0.1 - viem: 2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) + viem: 2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -16022,11 +16714,11 @@ snapshots: sharp: 0.33.5 url: 0.11.0 - '@dynamic-labs/locale@4.91.4(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4)': + '@dynamic-labs/locale@4.91.4(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6))(react@19.1.4)': dependencies: '@dynamic-labs/assert-package-version': 4.91.4 i18next: 23.4.6 - react-i18next: 13.5.0(i18next@23.4.6)(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4) + react-i18next: 13.5.0(i18next@23.4.6)(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6))(react@19.1.4) transitivePeerDependencies: - react - react-dom @@ -16048,7 +16740,7 @@ snapshots: '@vue/reactivity': 3.5.35 eventemitter3: 5.0.1 - '@dynamic-labs/multi-wallet@4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6)': + '@dynamic-labs/multi-wallet@4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(debug@4.4.3(supports-color@10.2.2))(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6)': dependencies: '@dynamic-labs/assert-package-version': 4.91.4 '@dynamic-labs/rpc-providers': 4.91.4 @@ -16056,7 +16748,7 @@ snapshots: '@dynamic-labs/types': 4.91.4 '@dynamic-labs/utils': 4.91.4 '@dynamic-labs/wallet-book': 4.91.4(react-dom@19.1.4(react@19.1.4))(react@19.1.4) - '@dynamic-labs/wallet-connector-core': 4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6) + '@dynamic-labs/wallet-connector-core': 4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(debug@4.4.3(supports-color@10.2.2))(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6) tslib: 2.4.1 transitivePeerDependencies: - '@dynamic-labs-wallet/primitives' @@ -16082,23 +16774,23 @@ snapshots: '@dynamic-labs/sdk-api-core@0.0.985': {} - '@dynamic-labs/sdk-react-core@4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(@types/react@19.1.7)(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6)': + '@dynamic-labs/sdk-react-core@4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(@types/react@19.1.7)(bufferutil@4.1.0)(debug@4.4.3(supports-color@10.2.2))(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6)': dependencies: - '@dynamic-labs-sdk/client': 1.12.1(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6) - '@dynamic-labs-wallet/browser-wallet-client': 1.0.48(@dynamic-labs-wallet/forward-mpc-client@0.12.0(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(utf-8-validate@6.0.6)) + '@dynamic-labs-sdk/client': 1.12.1(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(debug@4.4.3(supports-color@10.2.2))(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6) + '@dynamic-labs-wallet/browser-wallet-client': 1.0.48(@dynamic-labs-wallet/forward-mpc-client@0.12.0(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(debug@4.4.3(supports-color@10.2.2)) '@dynamic-labs-wallet/forward-mpc-client': 0.12.0(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(utf-8-validate@6.0.6) '@dynamic-labs/assert-package-version': 4.91.4 '@dynamic-labs/iconic': 4.91.4(react-dom@19.1.4(react@19.1.4))(react@19.1.4) - '@dynamic-labs/locale': 4.91.4(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4) + '@dynamic-labs/locale': 4.91.4(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6))(react@19.1.4) '@dynamic-labs/logger': 4.91.4 - '@dynamic-labs/multi-wallet': 4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6) + '@dynamic-labs/multi-wallet': 4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(debug@4.4.3(supports-color@10.2.2))(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6) '@dynamic-labs/rpc-providers': 4.91.4 '@dynamic-labs/sdk-api-core': 0.0.1046 '@dynamic-labs/store': 4.91.4 '@dynamic-labs/types': 4.91.4 '@dynamic-labs/utils': 4.91.4 '@dynamic-labs/wallet-book': 4.91.4(react-dom@19.1.4(react@19.1.4))(react@19.1.4) - '@dynamic-labs/wallet-connector-core': 4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6) + '@dynamic-labs/wallet-connector-core': 4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(debug@4.4.3(supports-color@10.2.2))(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6) '@hcaptcha/react-hcaptcha': 1.4.4(react-dom@19.1.4(react@19.1.4))(react@19.1.4) '@thumbmarkjs/thumbmarkjs': 0.16.0 bs58: 5.0.0 @@ -16110,7 +16802,7 @@ snapshots: react: 19.1.4 react-dom: 19.1.4(react@19.1.4) react-focus-lock: 2.13.6(@types/react@19.1.7)(react@19.1.4) - react-i18next: 13.5.0(i18next@23.4.6)(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4) + react-i18next: 13.5.0(i18next@23.4.6)(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6))(react@19.1.4) react-international-phone: 4.5.0(react@19.1.4) yup: 0.32.11 transitivePeerDependencies: @@ -16125,7 +16817,7 @@ snapshots: - react-native-passkey - utf-8-validate - '@dynamic-labs/solana-core@4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6)': + '@dynamic-labs/solana-core@4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(debug@4.4.3(supports-color@10.2.2))(fastestsmallesttextencoderdecoder@1.0.22)(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6)': dependencies: '@dynamic-labs/assert-package-version': 4.91.4 '@dynamic-labs/rpc-providers': 4.91.4 @@ -16133,7 +16825,7 @@ snapshots: '@dynamic-labs/types': 4.91.4 '@dynamic-labs/utils': 4.91.4 '@dynamic-labs/wallet-book': 4.91.4(react-dom@19.1.4(react@19.1.4))(react@19.1.4) - '@dynamic-labs/wallet-connector-core': 4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6) + '@dynamic-labs/wallet-connector-core': 4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(debug@4.4.3(supports-color@10.2.2))(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6) '@solana/spl-token': 0.4.14(@solana/web3.js@1.98.1(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(utf-8-validate@6.0.6) '@solana/web3.js': 1.98.1(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6) eventemitter3: 5.0.1 @@ -16158,7 +16850,7 @@ snapshots: '@dynamic-labs/assert-package-version': 4.91.4 '@dynamic-labs/logger': 4.91.4 - '@dynamic-labs/sui-core@4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6)': + '@dynamic-labs/sui-core@4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(debug@4.4.3(supports-color@10.2.2))(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6)': dependencies: '@dynamic-labs/assert-package-version': 4.91.4 '@dynamic-labs/logger': 4.91.4 @@ -16167,7 +16859,7 @@ snapshots: '@dynamic-labs/types': 4.91.4 '@dynamic-labs/utils': 4.91.4 '@dynamic-labs/wallet-book': 4.91.4(react-dom@19.1.4(react@19.1.4))(react@19.1.4) - '@dynamic-labs/wallet-connector-core': 4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6) + '@dynamic-labs/wallet-connector-core': 4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(debug@4.4.3(supports-color@10.2.2))(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6) '@mysten/sui': 1.45.2(@typescript/typescript6@6.0.2) '@mysten/wallet-standard': 0.19.9(@typescript/typescript6@6.0.2) text-encoding: 0.7.0 @@ -16217,17 +16909,17 @@ snapshots: eventemitter3: 5.0.1 tldts: 6.0.16 - '@dynamic-labs/waas-evm@4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6)(zod@3.25.76)': + '@dynamic-labs/waas-evm@4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(debug@4.4.3(supports-color@10.2.2))(fastestsmallesttextencoderdecoder@1.0.22)(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6)(zod@4.4.3)': dependencies: '@dynamic-labs/assert-package-version': 4.91.4 - '@dynamic-labs/ethereum-core': 4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6)(viem@2.51.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76)) + '@dynamic-labs/ethereum-core': 4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(debug@4.4.3(supports-color@10.2.2))(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6)(viem@2.51.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3)) '@dynamic-labs/logger': 4.91.4 '@dynamic-labs/sdk-api-core': 0.0.1046 '@dynamic-labs/types': 4.91.4 '@dynamic-labs/utils': 4.91.4 - '@dynamic-labs/waas': 4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6)(viem@2.51.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76)) - '@dynamic-labs/wallet-connector-core': 4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6) - viem: 2.51.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) + '@dynamic-labs/waas': 4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(debug@4.4.3(supports-color@10.2.2))(fastestsmallesttextencoderdecoder@1.0.22)(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6)(viem@2.51.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3)) + '@dynamic-labs/wallet-connector-core': 4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(debug@4.4.3(supports-color@10.2.2))(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6) + viem: 2.51.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) transitivePeerDependencies: - '@dynamic-labs-wallet/primitives' - '@gql.tada/svelte-support' @@ -16247,20 +16939,20 @@ snapshots: - utf-8-validate - zod - '@dynamic-labs/waas@4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6)(viem@2.51.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76))': + '@dynamic-labs/waas@4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(debug@4.4.3(supports-color@10.2.2))(fastestsmallesttextencoderdecoder@1.0.22)(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6)(viem@2.51.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3))': dependencies: - '@dynamic-labs-sdk/client': 1.12.1(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6) - '@dynamic-labs-wallet/browser-wallet-client': 1.0.48(@dynamic-labs-wallet/forward-mpc-client@0.12.0(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(utf-8-validate@6.0.6)) + '@dynamic-labs-sdk/client': 1.12.1(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(debug@4.4.3(supports-color@10.2.2))(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6) + '@dynamic-labs-wallet/browser-wallet-client': 1.0.48(@dynamic-labs-wallet/forward-mpc-client@0.12.0(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(debug@4.4.3(supports-color@10.2.2)) '@dynamic-labs-wallet/forward-mpc-client': 0.12.0(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(utf-8-validate@6.0.6) '@dynamic-labs/assert-package-version': 4.91.4 - '@dynamic-labs/ethereum-core': 4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6)(viem@2.51.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76)) + '@dynamic-labs/ethereum-core': 4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(debug@4.4.3(supports-color@10.2.2))(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6)(viem@2.51.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3)) '@dynamic-labs/logger': 4.91.4 '@dynamic-labs/sdk-api-core': 0.0.1046 - '@dynamic-labs/solana-core': 4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6) - '@dynamic-labs/sui-core': 4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6) + '@dynamic-labs/solana-core': 4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(debug@4.4.3(supports-color@10.2.2))(fastestsmallesttextencoderdecoder@1.0.22)(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6) + '@dynamic-labs/sui-core': 4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(debug@4.4.3(supports-color@10.2.2))(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6) '@dynamic-labs/utils': 4.91.4 '@dynamic-labs/wallet-book': 4.91.4(react-dom@19.1.4(react@19.1.4))(react@19.1.4) - '@dynamic-labs/wallet-connector-core': 4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6) + '@dynamic-labs/wallet-connector-core': 4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(debug@4.4.3(supports-color@10.2.2))(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6) transitivePeerDependencies: - '@dynamic-labs-wallet/primitives' - '@gql.tada/svelte-support' @@ -16280,20 +16972,20 @@ snapshots: - utf-8-validate - viem - '@dynamic-labs/wagmi-connector@4.91.4(cd9ebb1a64f614b48e624e4e0701fe2e)': + '@dynamic-labs/wagmi-connector@4.91.4(86bf20e8ca9767cde8328256d5f07007)': dependencies: '@dynamic-labs/assert-package-version': 4.91.4 - '@dynamic-labs/ethereum-core': 4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6)(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76)) + '@dynamic-labs/ethereum-core': 4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(debug@4.4.3(supports-color@10.2.2))(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6)(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3)) '@dynamic-labs/logger': 4.91.4 '@dynamic-labs/rpc-providers': 4.91.4 - '@dynamic-labs/sdk-react-core': 4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(@types/react@19.1.7)(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6) + '@dynamic-labs/sdk-react-core': 4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(@types/react@19.1.7)(bufferutil@4.1.0)(debug@4.4.3(supports-color@10.2.2))(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6) '@dynamic-labs/types': 4.91.4 - '@dynamic-labs/wallet-connector-core': 4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6) - '@wagmi/core': 2.22.1(@tanstack/query-core@5.55.4)(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(react@19.1.4)(use-sync-external-store@1.4.0(react@19.1.4))(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76)) + '@dynamic-labs/wallet-connector-core': 4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(debug@4.4.3(supports-color@10.2.2))(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6) + '@wagmi/core': 2.22.1(@tanstack/query-core@5.55.4)(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(react@19.1.4)(use-sync-external-store@1.4.0(react@19.1.4))(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3)) eventemitter3: 5.0.1 react: 19.1.4 - viem: 2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) - wagmi: 2.19.5(@tanstack/query-core@5.55.4)(@tanstack/react-query@5.55.4(react@19.1.4))(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6)(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76))(zod@3.25.76) + viem: 2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) + wagmi: 2.19.5(@tanstack/query-core@5.55.4)(@tanstack/react-query@5.55.4(react@19.1.4))(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(debug@4.4.3(supports-color@10.2.2))(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6))(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6)(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3))(zod@4.4.3) '@dynamic-labs/wallet-book@4.91.4(react-dom@19.1.4(react@19.1.4))(react@19.1.4)': dependencies: @@ -16307,10 +16999,10 @@ snapshots: util: 0.12.5 zod: 4.0.5 - '@dynamic-labs/wallet-connector-core@4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6)': + '@dynamic-labs/wallet-connector-core@4.91.4(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(debug@4.4.3(supports-color@10.2.2))(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6)': dependencies: - '@dynamic-labs-sdk/client': 1.12.1(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6) - '@dynamic-labs-wallet/browser-wallet-client': 1.0.48(@dynamic-labs-wallet/forward-mpc-client@0.12.0(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(utf-8-validate@6.0.6)) + '@dynamic-labs-sdk/client': 1.12.1(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(debug@4.4.3(supports-color@10.2.2))(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6) + '@dynamic-labs-wallet/browser-wallet-client': 1.0.48(@dynamic-labs-wallet/forward-mpc-client@0.12.0(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(utf-8-validate@6.0.6))(debug@4.4.3(supports-color@10.2.2)) '@dynamic-labs-wallet/forward-mpc-client': 0.12.0(@dynamic-labs-wallet/primitives@1.0.48)(bufferutil@4.1.0)(utf-8-validate@6.0.6) '@dynamic-labs/assert-package-version': 4.91.4 '@dynamic-labs/logger': 4.91.4 @@ -16365,9 +17057,9 @@ snapshots: tslib: 2.8.1 optional: true - '@emotion/babel-plugin@11.13.5': + '@emotion/babel-plugin@11.13.5(supports-color@10.2.2)': dependencies: - '@babel/helper-module-imports': 7.29.7 + '@babel/helper-module-imports': 7.29.7(supports-color@10.2.2) '@babel/runtime': 7.29.7 '@emotion/hash': 0.9.2 '@emotion/memoize': 0.9.0 @@ -16397,10 +17089,10 @@ snapshots: '@emotion/memoize@0.9.0': {} - '@emotion/react@11.14.0(@types/react@18.3.12)(react@19.1.4)': + '@emotion/react@11.14.0(@types/react@18.3.12)(react@19.1.4)(supports-color@10.2.2)': dependencies: '@babel/runtime': 7.29.7 - '@emotion/babel-plugin': 11.13.5 + '@emotion/babel-plugin': 11.13.5(supports-color@10.2.2) '@emotion/cache': 11.14.0 '@emotion/serialize': 1.3.3 '@emotion/use-insertion-effect-with-fallbacks': 1.2.0(react@19.1.4) @@ -16413,10 +17105,10 @@ snapshots: transitivePeerDependencies: - supports-color - '@emotion/react@11.14.0(@types/react@19.1.7)(react@19.1.4)': + '@emotion/react@11.14.0(@types/react@19.1.7)(react@19.1.4)(supports-color@10.2.2)': dependencies: '@babel/runtime': 7.29.7 - '@emotion/babel-plugin': 11.13.5 + '@emotion/babel-plugin': 11.13.5(supports-color@10.2.2) '@emotion/cache': 11.14.0 '@emotion/serialize': 1.3.3 '@emotion/use-insertion-effect-with-fallbacks': 1.2.0(react@19.1.4) @@ -16605,21 +17297,107 @@ snapshots: '@esbuild/win32-x64@0.28.1': optional: true - '@eslint-community/eslint-utils@4.9.1(eslint@9.39.2)': + '@eslint-community/eslint-utils@4.9.1(eslint@9.39.2(supports-color@10.2.2))': dependencies: - eslint: 9.39.2 + eslint: 9.39.2(supports-color@10.2.2) eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.12.2': {} - '@eslint/compat@1.2.2(eslint@9.39.2)': + '@eslint-react/ast@5.19.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2)': + dependencies: + '@typescript-eslint/types': 8.70.0 + '@typescript-eslint/typescript-estree': 8.70.0(@typescript/typescript6@6.0.2)(supports-color@10.2.2) + '@typescript-eslint/utils': 8.70.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) + eslint: 9.39.2(supports-color@10.2.2) + string-ts: 2.3.1 + typescript: '@typescript/typescript6@6.0.2' + transitivePeerDependencies: + - supports-color + + '@eslint-react/core@5.19.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2)': + dependencies: + '@eslint-react/ast': 5.19.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) + '@eslint-react/eslint': 5.19.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) + '@eslint-react/shared': 5.19.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) + '@eslint-react/var': 5.19.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) + '@typescript-eslint/scope-manager': 8.70.0 + '@typescript-eslint/types': 8.70.0 + '@typescript-eslint/utils': 8.70.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) + eslint: 9.39.2(supports-color@10.2.2) + ts-pattern: 5.9.0 + typescript: '@typescript/typescript6@6.0.2' + transitivePeerDependencies: + - supports-color + + '@eslint-react/eslint-plugin@5.19.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2)': + dependencies: + '@eslint-react/shared': 5.19.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) + eslint: 9.39.2(supports-color@10.2.2) + eslint-plugin-react-dom: 5.19.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) + eslint-plugin-react-jsx: 5.19.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) + eslint-plugin-react-naming-convention: 5.19.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) + eslint-plugin-react-rsc: 5.19.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) + eslint-plugin-react-web-api: 5.19.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) + eslint-plugin-react-x: 5.19.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) + typescript: '@typescript/typescript6@6.0.2' + transitivePeerDependencies: + - supports-color + + '@eslint-react/eslint@5.19.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2)': + dependencies: + '@typescript-eslint/utils': 8.70.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) + eslint: 9.39.2(supports-color@10.2.2) + typescript: '@typescript/typescript6@6.0.2' + transitivePeerDependencies: + - supports-color + + '@eslint-react/jsx@5.19.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2)': + dependencies: + '@eslint-react/ast': 5.19.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) + '@eslint-react/eslint': 5.19.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) + '@eslint-react/shared': 5.19.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) + '@eslint-react/var': 5.19.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) + '@typescript-eslint/types': 8.70.0 + '@typescript-eslint/utils': 8.70.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) + eslint: 9.39.2(supports-color@10.2.2) + ts-pattern: 5.9.0 + typescript: '@typescript/typescript6@6.0.2' + transitivePeerDependencies: + - supports-color + + '@eslint-react/shared@5.19.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2)': + dependencies: + '@eslint-react/eslint': 5.19.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) + '@typescript-eslint/utils': 8.70.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) + eslint: 9.39.2(supports-color@10.2.2) + ts-pattern: 5.9.0 + typescript: '@typescript/typescript6@6.0.2' + zod: 4.4.3 + transitivePeerDependencies: + - supports-color + + '@eslint-react/var@5.19.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2)': + dependencies: + '@eslint-react/ast': 5.19.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) + '@eslint-react/eslint': 5.19.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) + '@typescript-eslint/scope-manager': 8.70.0 + '@typescript-eslint/types': 8.70.0 + '@typescript-eslint/utils': 8.70.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) + eslint: 9.39.2(supports-color@10.2.2) + ts-pattern: 5.9.0 + typescript: '@typescript/typescript6@6.0.2' + transitivePeerDependencies: + - supports-color + + '@eslint/compat@1.2.2(eslint@9.39.2(supports-color@10.2.2))': optionalDependencies: - eslint: 9.39.2 + eslint: 9.39.2(supports-color@10.2.2) - '@eslint/config-array@0.21.2': + '@eslint/config-array@0.21.2(supports-color@10.2.2)': dependencies: '@eslint/object-schema': 2.1.7 - debug: 4.4.3 + debug: 4.4.3(supports-color@10.2.2) minimatch: 3.1.5 transitivePeerDependencies: - supports-color @@ -16632,10 +17410,10 @@ snapshots: dependencies: '@types/json-schema': 7.0.15 - '@eslint/eslintrc@3.3.5': + '@eslint/eslintrc@3.3.5(supports-color@10.2.2)': dependencies: ajv: 6.15.0 - debug: 4.4.3 + debug: 4.4.3(supports-color@10.2.2) espree: 10.4.0 globals: 14.0.0 ignore: 5.3.2 @@ -16756,11 +17534,11 @@ snapshots: '@formkit/auto-animate@0.8.4': {} - '@gemini-wallet/core@0.3.2(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76))': + '@gemini-wallet/core@0.3.2(supports-color@10.2.2)(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3))': dependencies: - '@metamask/rpc-errors': 7.0.2 + '@metamask/rpc-errors': 7.0.2(supports-color@10.2.2) eventemitter3: 5.0.1 - viem: 2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) + viem: 2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) transitivePeerDependencies: - supports-color @@ -16777,9 +17555,9 @@ snapshots: graphql: 16.14.0 typescript: '@typescript/typescript6@6.0.2' - '@graphiql/plugin-doc-explorer@0.2.2(@codemirror/language@6.0.0)(@emotion/is-prop-valid@1.4.0)(@types/node@20.16.7)(@types/react-dom@19.1.6(@types/react@19.1.7))(@types/react@19.1.7)(graphql-ws@5.16.2(graphql@16.14.0))(graphql@16.14.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(use-sync-external-store@1.4.0(react@19.1.4))': + '@graphiql/plugin-doc-explorer@0.2.2(@codemirror/language@6.0.0)(@emotion/is-prop-valid@1.4.0)(@types/node@24.13.4)(@types/react-dom@19.1.6(@types/react@19.1.7))(@types/react@19.1.7)(graphql-ws@5.16.2(graphql@16.14.0))(graphql@16.14.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(use-sync-external-store@1.4.0(react@19.1.4))': dependencies: - '@graphiql/react': 0.34.1(@codemirror/language@6.0.0)(@emotion/is-prop-valid@1.4.0)(@types/node@20.16.7)(@types/react-dom@19.1.6(@types/react@19.1.7))(@types/react@19.1.7)(graphql-ws@5.16.2(graphql@16.14.0))(graphql@16.14.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(use-sync-external-store@1.4.0(react@19.1.4)) + '@graphiql/react': 0.34.1(@codemirror/language@6.0.0)(@emotion/is-prop-valid@1.4.0)(@types/node@24.13.4)(@types/react-dom@19.1.6(@types/react@19.1.7))(@types/react@19.1.7)(graphql-ws@5.16.2(graphql@16.14.0))(graphql@16.14.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(use-sync-external-store@1.4.0(react@19.1.4)) '@headlessui/react': 2.2.10(react-dom@19.1.4(react@19.1.4))(react@19.1.4) graphql: 16.14.0 react: 19.1.4 @@ -16796,10 +17574,10 @@ snapshots: - immer - use-sync-external-store - '@graphiql/plugin-history@0.2.2(@codemirror/language@6.0.0)(@emotion/is-prop-valid@1.4.0)(@types/node@20.16.7)(@types/react-dom@19.1.6(@types/react@19.1.7))(@types/react@19.1.7)(graphql-ws@5.16.2(graphql@16.14.0))(graphql@16.14.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(use-sync-external-store@1.4.0(react@19.1.4))': + '@graphiql/plugin-history@0.2.2(@codemirror/language@6.0.0)(@emotion/is-prop-valid@1.4.0)(@types/node@24.13.4)(@types/react-dom@19.1.6(@types/react@19.1.7))(@types/react@19.1.7)(graphql-ws@5.16.2(graphql@16.14.0))(graphql@16.14.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(use-sync-external-store@1.4.0(react@19.1.4))': dependencies: - '@graphiql/react': 0.34.1(@codemirror/language@6.0.0)(@emotion/is-prop-valid@1.4.0)(@types/node@20.16.7)(@types/react-dom@19.1.6(@types/react@19.1.7))(@types/react@19.1.7)(graphql-ws@5.16.2(graphql@16.14.0))(graphql@16.14.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(use-sync-external-store@1.4.0(react@19.1.4)) - '@graphiql/toolkit': 0.11.3(@types/node@20.16.7)(graphql-ws@5.16.2(graphql@16.14.0))(graphql@16.14.0) + '@graphiql/react': 0.34.1(@codemirror/language@6.0.0)(@emotion/is-prop-valid@1.4.0)(@types/node@24.13.4)(@types/react-dom@19.1.6(@types/react@19.1.7))(@types/react@19.1.7)(graphql-ws@5.16.2(graphql@16.14.0))(graphql@16.14.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(use-sync-external-store@1.4.0(react@19.1.4)) + '@graphiql/toolkit': 0.11.3(@types/node@24.13.4)(graphql-ws@5.16.2(graphql@16.14.0))(graphql@16.14.0) react: 19.1.4 react-compiler-runtime: 19.1.0-rc.1(react@19.1.4) react-dom: 19.1.4(react@19.1.4) @@ -16815,9 +17593,9 @@ snapshots: - immer - use-sync-external-store - '@graphiql/react@0.34.1(@codemirror/language@6.0.0)(@emotion/is-prop-valid@1.4.0)(@types/node@20.16.7)(@types/react-dom@19.1.6(@types/react@19.1.7))(@types/react@19.1.7)(graphql-ws@5.16.2(graphql@16.14.0))(graphql@16.14.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(use-sync-external-store@1.4.0(react@19.1.4))': + '@graphiql/react@0.34.1(@codemirror/language@6.0.0)(@emotion/is-prop-valid@1.4.0)(@types/node@24.13.4)(@types/react-dom@19.1.6(@types/react@19.1.7))(@types/react@19.1.7)(graphql-ws@5.16.2(graphql@16.14.0))(graphql@16.14.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(use-sync-external-store@1.4.0(react@19.1.4))': dependencies: - '@graphiql/toolkit': 0.11.3(@types/node@20.16.7)(graphql-ws@5.16.2(graphql@16.14.0))(graphql@16.14.0) + '@graphiql/toolkit': 0.11.3(@types/node@24.13.4)(graphql-ws@5.16.2(graphql@16.14.0))(graphql@16.14.0) '@radix-ui/react-dialog': 1.1.15(@types/react-dom@19.1.6(@types/react@19.1.7))(@types/react@19.1.7)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) '@radix-ui/react-dropdown-menu': 2.1.16(@types/react-dom@19.1.6(@types/react@19.1.7))(@types/react@19.1.7)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) '@radix-ui/react-tooltip': 1.2.8(@types/react-dom@19.1.6(@types/react@19.1.7))(@types/react@19.1.7)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) @@ -16847,11 +17625,11 @@ snapshots: - immer - use-sync-external-store - '@graphiql/toolkit@0.11.3(@types/node@20.16.7)(graphql-ws@5.16.2(graphql@16.14.0))(graphql@16.14.0)': + '@graphiql/toolkit@0.11.3(@types/node@24.13.4)(graphql-ws@5.16.2(graphql@16.14.0))(graphql@16.14.0)': dependencies: '@n1ru4l/push-pull-async-iterable-iterator': 3.2.0 graphql: 16.14.0 - meros: 1.3.2(@types/node@20.16.7) + meros: 1.3.2(@types/node@24.13.4) optionalDependencies: graphql-ws: 5.16.2(graphql@16.14.0) transitivePeerDependencies: @@ -17011,7 +17789,7 @@ snapshots: multiformats: 13.4.2 uint8arrays: 5.1.1 - '@helia/unixfs@5.1.0': + '@helia/unixfs@5.1.0(supports-color@10.2.2)': dependencies: '@helia/interface': 5.4.0 '@ipld/dag-pb': 4.1.7 @@ -17023,7 +17801,7 @@ snapshots: interface-blockstore: 5.3.2 ipfs-unixfs: 11.2.5 ipfs-unixfs-exporter: 13.7.3 - ipfs-unixfs-importer: 15.4.0 + ipfs-unixfs-importer: 15.4.0(supports-color@10.2.2) it-all: 3.0.11 it-first: 3.0.11 it-glob: 3.0.6 @@ -17066,7 +17844,7 @@ snapshots: progress-events: 1.1.0 uint8arrays: 5.1.1 - '@helia/verified-fetch@2.6.12(bufferutil@4.1.0)(react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(utf-8-validate@6.0.6)': + '@helia/verified-fetch@2.6.12(bufferutil@4.1.0)(react-native@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6))(supports-color@10.2.2)(utf-8-validate@6.0.6)': dependencies: '@helia/block-brokers': 4.2.4 '@helia/car': 4.2.0 @@ -17074,7 +17852,7 @@ snapshots: '@helia/interface': 5.4.0 '@helia/ipns': 8.2.4 '@helia/routers': 3.1.3 - '@helia/unixfs': 5.1.0 + '@helia/unixfs': 5.1.0(supports-color@10.2.2) '@ipld/dag-cbor': 9.2.7 '@ipld/dag-json': 10.2.9 '@ipld/dag-pb': 4.1.7 @@ -17082,14 +17860,14 @@ snapshots: '@libp2p/kad-dht': 15.1.11 '@libp2p/logger': 5.2.0 '@libp2p/peer-id': 5.1.9 - '@libp2p/webrtc': 5.2.24(react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)) + '@libp2p/webrtc': 5.2.24(react-native@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6))(supports-color@10.2.2) '@libp2p/websockets': 9.2.19(bufferutil@4.1.0)(utf-8-validate@6.0.6) '@multiformats/dns': 1.0.13 abortable-iterator: 5.1.0 cborg: 4.5.8 - file-type: 20.5.0 + file-type: 20.5.0(supports-color@10.2.2) hashlru: 2.3.0 - helia: 5.5.1(bufferutil@4.1.0)(react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(utf-8-validate@6.0.6) + helia: 5.5.1(bufferutil@4.1.0)(react-native@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6))(supports-color@10.2.2)(utf-8-validate@6.0.6) interface-blockstore: 5.3.2 interface-datastore: 8.3.2 ipfs-unixfs-exporter: 13.7.3 @@ -17152,14 +17930,9 @@ snapshots: '@img/sharp-libvips-darwin-arm64': 1.0.4 optional: true - '@img/sharp-darwin-arm64@0.34.5': + '@img/sharp-darwin-arm64@0.35.4': optionalDependencies: - '@img/sharp-libvips-darwin-arm64': 1.2.4 - optional: true - - '@img/sharp-darwin-arm64@0.35.3': - optionalDependencies: - '@img/sharp-libvips-darwin-arm64': 1.3.2 + '@img/sharp-libvips-darwin-arm64': 1.3.3 optional: true '@img/sharp-darwin-x64@0.33.5': @@ -17167,103 +17940,68 @@ snapshots: '@img/sharp-libvips-darwin-x64': 1.0.4 optional: true - '@img/sharp-darwin-x64@0.34.5': - optionalDependencies: - '@img/sharp-libvips-darwin-x64': 1.2.4 - optional: true - - '@img/sharp-darwin-x64@0.35.3': + '@img/sharp-darwin-x64@0.35.4': optionalDependencies: - '@img/sharp-libvips-darwin-x64': 1.3.2 + '@img/sharp-libvips-darwin-x64': 1.3.3 optional: true - '@img/sharp-freebsd-wasm32@0.35.3': + '@img/sharp-freebsd-wasm32@0.35.4': dependencies: - '@img/sharp-wasm32': 0.35.3 + '@img/sharp-wasm32': 0.35.4 optional: true '@img/sharp-libvips-darwin-arm64@1.0.4': optional: true - '@img/sharp-libvips-darwin-arm64@1.2.4': - optional: true - - '@img/sharp-libvips-darwin-arm64@1.3.2': + '@img/sharp-libvips-darwin-arm64@1.3.3': optional: true '@img/sharp-libvips-darwin-x64@1.0.4': optional: true - '@img/sharp-libvips-darwin-x64@1.2.4': - optional: true - - '@img/sharp-libvips-darwin-x64@1.3.2': + '@img/sharp-libvips-darwin-x64@1.3.3': optional: true '@img/sharp-libvips-linux-arm64@1.0.4': optional: true - '@img/sharp-libvips-linux-arm64@1.2.4': - optional: true - - '@img/sharp-libvips-linux-arm64@1.3.2': + '@img/sharp-libvips-linux-arm64@1.3.3': optional: true '@img/sharp-libvips-linux-arm@1.0.5': optional: true - '@img/sharp-libvips-linux-arm@1.2.4': + '@img/sharp-libvips-linux-arm@1.3.3': optional: true - '@img/sharp-libvips-linux-arm@1.3.2': + '@img/sharp-libvips-linux-ppc64@1.3.3': optional: true - '@img/sharp-libvips-linux-ppc64@1.2.4': - optional: true - - '@img/sharp-libvips-linux-ppc64@1.3.2': - optional: true - - '@img/sharp-libvips-linux-riscv64@1.2.4': - optional: true - - '@img/sharp-libvips-linux-riscv64@1.3.2': + '@img/sharp-libvips-linux-riscv64@1.3.3': optional: true '@img/sharp-libvips-linux-s390x@1.0.4': optional: true - '@img/sharp-libvips-linux-s390x@1.2.4': - optional: true - - '@img/sharp-libvips-linux-s390x@1.3.2': + '@img/sharp-libvips-linux-s390x@1.3.3': optional: true '@img/sharp-libvips-linux-x64@1.0.4': optional: true - '@img/sharp-libvips-linux-x64@1.2.4': - optional: true - - '@img/sharp-libvips-linux-x64@1.3.2': + '@img/sharp-libvips-linux-x64@1.3.3': optional: true '@img/sharp-libvips-linuxmusl-arm64@1.0.4': optional: true - '@img/sharp-libvips-linuxmusl-arm64@1.2.4': - optional: true - - '@img/sharp-libvips-linuxmusl-arm64@1.3.2': + '@img/sharp-libvips-linuxmusl-arm64@1.3.3': optional: true '@img/sharp-libvips-linuxmusl-x64@1.0.4': optional: true - '@img/sharp-libvips-linuxmusl-x64@1.2.4': - optional: true - - '@img/sharp-libvips-linuxmusl-x64@1.3.2': + '@img/sharp-libvips-linuxmusl-x64@1.3.3': optional: true '@img/sharp-linux-arm64@0.33.5': @@ -17271,14 +18009,9 @@ snapshots: '@img/sharp-libvips-linux-arm64': 1.0.4 optional: true - '@img/sharp-linux-arm64@0.34.5': - optionalDependencies: - '@img/sharp-libvips-linux-arm64': 1.2.4 - optional: true - - '@img/sharp-linux-arm64@0.35.3': + '@img/sharp-linux-arm64@0.35.4': optionalDependencies: - '@img/sharp-libvips-linux-arm64': 1.3.2 + '@img/sharp-libvips-linux-arm64': 1.3.3 optional: true '@img/sharp-linux-arm@0.33.5': @@ -17286,34 +18019,19 @@ snapshots: '@img/sharp-libvips-linux-arm': 1.0.5 optional: true - '@img/sharp-linux-arm@0.34.5': - optionalDependencies: - '@img/sharp-libvips-linux-arm': 1.2.4 - optional: true - - '@img/sharp-linux-arm@0.35.3': - optionalDependencies: - '@img/sharp-libvips-linux-arm': 1.3.2 - optional: true - - '@img/sharp-linux-ppc64@0.34.5': - optionalDependencies: - '@img/sharp-libvips-linux-ppc64': 1.2.4 - optional: true - - '@img/sharp-linux-ppc64@0.35.3': + '@img/sharp-linux-arm@0.35.4': optionalDependencies: - '@img/sharp-libvips-linux-ppc64': 1.3.2 + '@img/sharp-libvips-linux-arm': 1.3.3 optional: true - '@img/sharp-linux-riscv64@0.34.5': + '@img/sharp-linux-ppc64@0.35.4': optionalDependencies: - '@img/sharp-libvips-linux-riscv64': 1.2.4 + '@img/sharp-libvips-linux-ppc64': 1.3.3 optional: true - '@img/sharp-linux-riscv64@0.35.3': + '@img/sharp-linux-riscv64@0.35.4': optionalDependencies: - '@img/sharp-libvips-linux-riscv64': 1.3.2 + '@img/sharp-libvips-linux-riscv64': 1.3.3 optional: true '@img/sharp-linux-s390x@0.33.5': @@ -17321,14 +18039,9 @@ snapshots: '@img/sharp-libvips-linux-s390x': 1.0.4 optional: true - '@img/sharp-linux-s390x@0.34.5': - optionalDependencies: - '@img/sharp-libvips-linux-s390x': 1.2.4 - optional: true - - '@img/sharp-linux-s390x@0.35.3': + '@img/sharp-linux-s390x@0.35.4': optionalDependencies: - '@img/sharp-libvips-linux-s390x': 1.3.2 + '@img/sharp-libvips-linux-s390x': 1.3.3 optional: true '@img/sharp-linux-x64@0.33.5': @@ -17336,14 +18049,9 @@ snapshots: '@img/sharp-libvips-linux-x64': 1.0.4 optional: true - '@img/sharp-linux-x64@0.34.5': - optionalDependencies: - '@img/sharp-libvips-linux-x64': 1.2.4 - optional: true - - '@img/sharp-linux-x64@0.35.3': + '@img/sharp-linux-x64@0.35.4': optionalDependencies: - '@img/sharp-libvips-linux-x64': 1.3.2 + '@img/sharp-libvips-linux-x64': 1.3.3 optional: true '@img/sharp-linuxmusl-arm64@0.33.5': @@ -17351,14 +18059,9 @@ snapshots: '@img/sharp-libvips-linuxmusl-arm64': 1.0.4 optional: true - '@img/sharp-linuxmusl-arm64@0.34.5': + '@img/sharp-linuxmusl-arm64@0.35.4': optionalDependencies: - '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 - optional: true - - '@img/sharp-linuxmusl-arm64@0.35.3': - optionalDependencies: - '@img/sharp-libvips-linuxmusl-arm64': 1.3.2 + '@img/sharp-libvips-linuxmusl-arm64': 1.3.3 optional: true '@img/sharp-linuxmusl-x64@0.33.5': @@ -17366,14 +18069,9 @@ snapshots: '@img/sharp-libvips-linuxmusl-x64': 1.0.4 optional: true - '@img/sharp-linuxmusl-x64@0.34.5': - optionalDependencies: - '@img/sharp-libvips-linuxmusl-x64': 1.2.4 - optional: true - - '@img/sharp-linuxmusl-x64@0.35.3': + '@img/sharp-linuxmusl-x64@0.35.4': optionalDependencies: - '@img/sharp-libvips-linuxmusl-x64': 1.3.2 + '@img/sharp-libvips-linuxmusl-x64': 1.3.3 optional: true '@img/sharp-wasm32@0.33.5': @@ -17381,44 +18079,149 @@ snapshots: '@emnapi/runtime': 1.10.0 optional: true - '@img/sharp-wasm32@0.34.5': - dependencies: - '@emnapi/runtime': 1.10.0 - optional: true - - '@img/sharp-wasm32@0.35.3': + '@img/sharp-wasm32@0.35.4': dependencies: '@emnapi/runtime': 1.11.3 optional: true - '@img/sharp-webcontainers-wasm32@0.35.3': + '@img/sharp-webcontainers-wasm32@0.35.4': dependencies: - '@img/sharp-wasm32': 0.35.3 - optional: true - - '@img/sharp-win32-arm64@0.34.5': + '@img/sharp-wasm32': 0.35.4 optional: true - '@img/sharp-win32-arm64@0.35.3': + '@img/sharp-win32-arm64@0.35.4': optional: true '@img/sharp-win32-ia32@0.33.5': optional: true - '@img/sharp-win32-ia32@0.34.5': - optional: true - - '@img/sharp-win32-ia32@0.35.3': + '@img/sharp-win32-ia32@0.35.4': optional: true '@img/sharp-win32-x64@0.33.5': optional: true - '@img/sharp-win32-x64@0.34.5': + '@img/sharp-win32-x64@0.35.4': optional: true - '@img/sharp-win32-x64@0.35.3': - optional: true + '@inquirer/ansi@2.0.7': {} + + '@inquirer/checkbox@5.2.2(@types/node@24.13.4)': + dependencies: + '@inquirer/ansi': 2.0.7 + '@inquirer/core': 12.0.0(@types/node@24.13.4) + '@inquirer/figures': 2.0.8 + '@inquirer/type': 4.0.7(@types/node@24.13.4) + optionalDependencies: + '@types/node': 24.13.4 + + '@inquirer/confirm@6.2.0(@types/node@24.13.4)': + dependencies: + '@inquirer/core': 12.0.0(@types/node@24.13.4) + '@inquirer/type': 4.0.7(@types/node@24.13.4) + optionalDependencies: + '@types/node': 24.13.4 + + '@inquirer/core@12.0.0(@types/node@24.13.4)': + dependencies: + '@inquirer/ansi': 2.0.7 + '@inquirer/figures': 2.0.8 + '@inquirer/type': 4.0.7(@types/node@24.13.4) + cli-width: 4.1.0 + fast-wrap-ansi: 0.2.2 + mute-stream: 3.0.0 + signal-exit: 4.1.0 + optionalDependencies: + '@types/node': 24.13.4 + + '@inquirer/editor@5.3.0(@types/node@24.13.4)': + dependencies: + '@inquirer/core': 12.0.0(@types/node@24.13.4) + '@inquirer/external-editor': 3.0.4(@types/node@24.13.4) + '@inquirer/type': 4.0.7(@types/node@24.13.4) + optionalDependencies: + '@types/node': 24.13.4 + + '@inquirer/expand@5.1.2(@types/node@24.13.4)': + dependencies: + '@inquirer/core': 12.0.0(@types/node@24.13.4) + '@inquirer/type': 4.0.7(@types/node@24.13.4) + optionalDependencies: + '@types/node': 24.13.4 + + '@inquirer/external-editor@3.0.4(@types/node@24.13.4)': + dependencies: + chardet: 2.2.0 + iconv-lite: 0.7.3 + optionalDependencies: + '@types/node': 24.13.4 + + '@inquirer/figures@2.0.8': {} + + '@inquirer/input@5.1.3(@types/node@24.13.4)': + dependencies: + '@inquirer/core': 12.0.0(@types/node@24.13.4) + '@inquirer/type': 4.0.7(@types/node@24.13.4) + optionalDependencies: + '@types/node': 24.13.4 + + '@inquirer/number@4.2.0(@types/node@24.13.4)': + dependencies: + '@inquirer/core': 12.0.0(@types/node@24.13.4) + '@inquirer/type': 4.0.7(@types/node@24.13.4) + optionalDependencies: + '@types/node': 24.13.4 + + '@inquirer/password@5.1.2(@types/node@24.13.4)': + dependencies: + '@inquirer/ansi': 2.0.7 + '@inquirer/core': 12.0.0(@types/node@24.13.4) + '@inquirer/type': 4.0.7(@types/node@24.13.4) + optionalDependencies: + '@types/node': 24.13.4 + + '@inquirer/prompts@8.6.0(@types/node@24.13.4)': + dependencies: + '@inquirer/checkbox': 5.2.2(@types/node@24.13.4) + '@inquirer/confirm': 6.2.0(@types/node@24.13.4) + '@inquirer/editor': 5.3.0(@types/node@24.13.4) + '@inquirer/expand': 5.1.2(@types/node@24.13.4) + '@inquirer/input': 5.1.3(@types/node@24.13.4) + '@inquirer/number': 4.2.0(@types/node@24.13.4) + '@inquirer/password': 5.1.2(@types/node@24.13.4) + '@inquirer/rawlist': 5.3.2(@types/node@24.13.4) + '@inquirer/search': 4.3.0(@types/node@24.13.4) + '@inquirer/select': 5.2.2(@types/node@24.13.4) + optionalDependencies: + '@types/node': 24.13.4 + + '@inquirer/rawlist@5.3.2(@types/node@24.13.4)': + dependencies: + '@inquirer/core': 12.0.0(@types/node@24.13.4) + '@inquirer/type': 4.0.7(@types/node@24.13.4) + optionalDependencies: + '@types/node': 24.13.4 + + '@inquirer/search@4.3.0(@types/node@24.13.4)': + dependencies: + '@inquirer/core': 12.0.0(@types/node@24.13.4) + '@inquirer/figures': 2.0.8 + '@inquirer/type': 4.0.7(@types/node@24.13.4) + optionalDependencies: + '@types/node': 24.13.4 + + '@inquirer/select@5.2.2(@types/node@24.13.4)': + dependencies: + '@inquirer/ansi': 2.0.7 + '@inquirer/core': 12.0.0(@types/node@24.13.4) + '@inquirer/figures': 2.0.8 + '@inquirer/type': 4.0.7(@types/node@24.13.4) + optionalDependencies: + '@types/node': 24.13.4 + + '@inquirer/type@4.0.7(@types/node@24.13.4)': + optionalDependencies: + '@types/node': 24.13.4 '@internationalized/date@3.12.2': dependencies: @@ -17458,7 +18261,7 @@ snapshots: dependencies: multiformats: 14.0.0 - '@ipshipyard/libp2p-auto-tls@1.0.0': + '@ipshipyard/libp2p-auto-tls@1.0.0(supports-color@10.2.2)': dependencies: '@chainsafe/is-ip': 2.1.0 '@libp2p/crypto': 5.1.18 @@ -17470,7 +18273,7 @@ snapshots: '@multiformats/multiaddr': 12.5.1 '@multiformats/multiaddr-matcher': 1.8.0 '@peculiar/x509': 1.14.3 - acme-client: 5.4.0 + acme-client: 5.4.0(supports-color@10.2.2) any-signal: 4.2.0 delay: 6.0.0 interface-datastore: 8.3.2 @@ -17503,7 +18306,7 @@ snapshots: '@jest/schemas': 29.6.3 '@types/istanbul-lib-coverage': 2.0.6 '@types/istanbul-reports': 3.0.4 - '@types/node': 22.12.0 + '@types/node': 24.13.4 '@types/yargs': 17.0.35 chalk: 4.1.2 @@ -17526,6 +18329,8 @@ snapshots: '@jridgewell/sourcemap-codec@1.5.5': {} + '@jridgewell/sourcemap-codec@1.6.0': {} + '@jridgewell/trace-mapping@0.3.31': dependencies: '@jridgewell/resolve-uri': 3.1.2 @@ -17930,7 +18735,7 @@ snapshots: uint8arraylist: 2.4.9 uint8arrays: 5.1.1 - '@libp2p/webrtc@5.2.24(react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))': + '@libp2p/webrtc@5.2.24(react-native@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6))(supports-color@10.2.2)': dependencies: '@chainsafe/is-ip': 2.1.0 '@chainsafe/libp2p-noise': 16.1.5 @@ -17962,7 +18767,7 @@ snapshots: protons-runtime: 5.6.0 race-event: 1.6.1 race-signal: 1.1.3 - react-native-webrtc: 124.0.7(react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)) + react-native-webrtc: 124.0.7(react-native@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6))(supports-color@10.2.2) uint8-varint: 2.0.5 uint8arraylist: 2.4.9 uint8arrays: 5.1.1 @@ -17989,6 +18794,15 @@ snapshots: - bufferutil - utf-8-validate + ? '@lifi/widget-light@4.3.2(patch_hash=8ee9f6665560f5ce16422afed5a38522d274ad98ca518fe83619f682b53ed378)(@wagmi/core@2.22.1(@tanstack/query-core@5.55.4)(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(react@19.1.4)(use-sync-external-store@1.4.0(react@19.1.4))(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3)))(@wallet-standard/base@1.1.0)(react@19.1.4)(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3))(wagmi@2.19.5(@tanstack/query-core@5.55.4)(@tanstack/react-query@5.55.4(react@19.1.4))(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(debug@4.4.3(supports-color@10.2.2))(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6))(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6)(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3))(zod@4.4.3))' + : dependencies: + react: 19.1.4 + optionalDependencies: + '@wagmi/core': 2.22.1(@tanstack/query-core@5.55.4)(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(react@19.1.4)(use-sync-external-store@1.4.0(react@19.1.4))(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3)) + '@wallet-standard/base': 1.1.0 + viem: 2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) + wagmi: 2.19.5(@tanstack/query-core@5.55.4)(@tanstack/react-query@5.55.4(react@19.1.4))(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(debug@4.4.3(supports-color@10.2.2))(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6))(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6)(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3))(zod@4.4.3) + '@lit-labs/ssr-dom-shim@1.6.0': {} '@lit/react@1.0.8(@types/react@19.1.7)': @@ -18024,11 +18838,11 @@ snapshots: dependencies: openapi-fetch: 0.13.8 - '@metamask/connect-evm@1.3.0(bufferutil@4.1.0)(utf-8-validate@6.0.6)': + '@metamask/connect-evm@1.3.0(bufferutil@4.1.0)(supports-color@10.2.2)(utf-8-validate@6.0.6)': dependencies: '@metamask/analytics': 0.5.0 - '@metamask/connect-multichain': 0.14.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) - '@metamask/utils': 11.11.0 + '@metamask/connect-multichain': 0.14.0(bufferutil@4.1.0)(supports-color@10.2.2)(utf-8-validate@6.0.6) + '@metamask/utils': 11.11.0(supports-color@10.2.2) transitivePeerDependencies: - '@react-native-async-storage/async-storage' - bufferutil @@ -18036,16 +18850,16 @@ snapshots: - supports-color - utf-8-validate - '@metamask/connect-multichain@0.14.0(bufferutil@4.1.0)(utf-8-validate@6.0.6)': + '@metamask/connect-multichain@0.14.0(bufferutil@4.1.0)(supports-color@10.2.2)(utf-8-validate@6.0.6)': dependencies: '@metamask/analytics': 0.5.0 '@metamask/mobile-wallet-protocol-core': 0.4.0 - '@metamask/mobile-wallet-protocol-dapp-client': 0.3.0 + '@metamask/mobile-wallet-protocol-dapp-client': 0.3.0(supports-color@10.2.2) '@metamask/multichain-api-client': 0.10.1 '@metamask/multichain-ui': 0.4.1 '@metamask/onboarding': 1.0.1 - '@metamask/rpc-errors': 7.0.3 - '@metamask/utils': 11.11.0 + '@metamask/rpc-errors': 7.0.3(supports-color@10.2.2) + '@metamask/utils': 11.11.0(supports-color@10.2.2) '@paulmillr/qr': 0.2.1 bowser: 2.14.1 buffer: 6.0.3 @@ -18061,35 +18875,35 @@ snapshots: - supports-color - utf-8-validate - '@metamask/eth-json-rpc-provider@1.0.1': + '@metamask/eth-json-rpc-provider@1.0.1(supports-color@10.2.2)': dependencies: - '@metamask/json-rpc-engine': 7.3.3 + '@metamask/json-rpc-engine': 7.3.3(supports-color@10.2.2) '@metamask/safe-event-emitter': 3.1.2 - '@metamask/utils': 5.0.2 + '@metamask/utils': 5.0.2(supports-color@10.2.2) transitivePeerDependencies: - supports-color - '@metamask/json-rpc-engine@7.3.3': + '@metamask/json-rpc-engine@7.3.3(supports-color@10.2.2)': dependencies: - '@metamask/rpc-errors': 6.4.0 + '@metamask/rpc-errors': 6.4.0(supports-color@10.2.2) '@metamask/safe-event-emitter': 3.1.2 - '@metamask/utils': 8.5.0 + '@metamask/utils': 8.5.0(supports-color@10.2.2) transitivePeerDependencies: - supports-color - '@metamask/json-rpc-engine@8.0.2': + '@metamask/json-rpc-engine@8.0.2(supports-color@10.2.2)': dependencies: - '@metamask/rpc-errors': 6.4.0 + '@metamask/rpc-errors': 6.4.0(supports-color@10.2.2) '@metamask/safe-event-emitter': 3.1.2 - '@metamask/utils': 8.5.0 + '@metamask/utils': 8.5.0(supports-color@10.2.2) transitivePeerDependencies: - supports-color - '@metamask/json-rpc-middleware-stream@7.0.2': + '@metamask/json-rpc-middleware-stream@7.0.2(supports-color@10.2.2)': dependencies: - '@metamask/json-rpc-engine': 8.0.2 + '@metamask/json-rpc-engine': 8.0.2(supports-color@10.2.2) '@metamask/safe-event-emitter': 3.1.2 - '@metamask/utils': 8.5.0 + '@metamask/utils': 8.5.0(supports-color@10.2.2) readable-stream: 3.6.2 transitivePeerDependencies: - supports-color @@ -18101,10 +18915,10 @@ snapshots: eventemitter3: 5.0.4 uuid: 11.1.0 - '@metamask/mobile-wallet-protocol-dapp-client@0.3.0': + '@metamask/mobile-wallet-protocol-dapp-client@0.3.0(supports-color@10.2.2)': dependencies: '@metamask/mobile-wallet-protocol-core': 0.4.0 - '@metamask/utils': 9.3.0 + '@metamask/utils': 9.3.0(supports-color@10.2.2) uuid: 11.1.0 transitivePeerDependencies: - supports-color @@ -18131,9 +18945,9 @@ snapshots: dependencies: bowser: 2.14.1 - '@metamask/post-message-stream@7.0.0': + '@metamask/post-message-stream@7.0.0(supports-color@10.2.2)': dependencies: - '@metamask/utils': 5.0.2 + '@metamask/utils': 5.0.2(supports-color@10.2.2) readable-stream: 3.6.2 transitivePeerDependencies: - supports-color @@ -18153,14 +18967,14 @@ snapshots: pump: 3.0.4 webextension-polyfill-ts: 0.25.0 - '@metamask/providers@16.1.0': + '@metamask/providers@16.1.0(supports-color@10.2.2)': dependencies: - '@metamask/json-rpc-engine': 8.0.2 - '@metamask/json-rpc-middleware-stream': 7.0.2 + '@metamask/json-rpc-engine': 8.0.2(supports-color@10.2.2) + '@metamask/json-rpc-middleware-stream': 7.0.2(supports-color@10.2.2) '@metamask/object-multiplex': 2.1.0 - '@metamask/rpc-errors': 6.4.0 + '@metamask/rpc-errors': 6.4.0(supports-color@10.2.2) '@metamask/safe-event-emitter': 3.1.2 - '@metamask/utils': 8.5.0 + '@metamask/utils': 8.5.0(supports-color@10.2.2) detect-browser: 5.3.0 extension-port-stream: 3.0.0 fast-deep-equal: 3.1.3 @@ -18170,23 +18984,23 @@ snapshots: transitivePeerDependencies: - supports-color - '@metamask/rpc-errors@6.4.0': + '@metamask/rpc-errors@6.4.0(supports-color@10.2.2)': dependencies: - '@metamask/utils': 9.3.0 + '@metamask/utils': 9.3.0(supports-color@10.2.2) fast-safe-stringify: 2.1.1 transitivePeerDependencies: - supports-color - '@metamask/rpc-errors@7.0.2': + '@metamask/rpc-errors@7.0.2(supports-color@10.2.2)': dependencies: - '@metamask/utils': 11.11.0 + '@metamask/utils': 11.11.0(supports-color@10.2.2) fast-safe-stringify: 2.1.1 transitivePeerDependencies: - supports-color - '@metamask/rpc-errors@7.0.3': + '@metamask/rpc-errors@7.0.3(supports-color@10.2.2)': dependencies: - '@metamask/utils': 11.11.0 + '@metamask/utils': 11.11.0(supports-color@10.2.2) fast-safe-stringify: 2.1.1 transitivePeerDependencies: - supports-color @@ -18199,17 +19013,17 @@ snapshots: dependencies: openapi-fetch: 0.13.8 - '@metamask/sdk-communication-layer@0.33.1(cross-fetch@4.1.0)(eciesjs@0.4.18)(eventemitter2@6.4.9)(readable-stream@3.6.2)(socket.io-client@4.8.3(bufferutil@4.1.0)(utf-8-validate@6.0.6))': + '@metamask/sdk-communication-layer@0.33.1(cross-fetch@4.1.0)(eciesjs@0.4.18)(eventemitter2@6.4.9)(readable-stream@3.6.2)(socket.io-client@4.8.3(bufferutil@4.1.0)(supports-color@10.2.2)(utf-8-validate@6.0.6))(supports-color@10.2.2)': dependencies: '@metamask/sdk-analytics': 0.0.5 bufferutil: 4.1.0 cross-fetch: 4.1.0 date-fns: 2.30.0 - debug: 4.3.4 + debug: 4.3.4(supports-color@10.2.2) eciesjs: 0.4.18 eventemitter2: 6.4.9 readable-stream: 3.6.2 - socket.io-client: 4.8.3(bufferutil@4.1.0)(utf-8-validate@6.0.6) + socket.io-client: 4.8.3(bufferutil@4.1.0)(supports-color@10.2.2)(utf-8-validate@6.0.6) utf-8-validate: 5.0.10 uuid: 8.3.2 transitivePeerDependencies: @@ -18219,25 +19033,25 @@ snapshots: dependencies: '@paulmillr/qr': 0.2.1 - '@metamask/sdk@0.33.1(bufferutil@4.1.0)(utf-8-validate@6.0.6)': + '@metamask/sdk@0.33.1(bufferutil@4.1.0)(supports-color@10.2.2)(utf-8-validate@6.0.6)': dependencies: '@babel/runtime': 7.29.7 '@metamask/onboarding': 1.0.1 - '@metamask/providers': 16.1.0 + '@metamask/providers': 16.1.0(supports-color@10.2.2) '@metamask/sdk-analytics': 0.0.5 - '@metamask/sdk-communication-layer': 0.33.1(cross-fetch@4.1.0)(eciesjs@0.4.18)(eventemitter2@6.4.9)(readable-stream@3.6.2)(socket.io-client@4.8.3(bufferutil@4.1.0)(utf-8-validate@6.0.6)) + '@metamask/sdk-communication-layer': 0.33.1(cross-fetch@4.1.0)(eciesjs@0.4.18)(eventemitter2@6.4.9)(readable-stream@3.6.2)(socket.io-client@4.8.3(bufferutil@4.1.0)(supports-color@10.2.2)(utf-8-validate@6.0.6))(supports-color@10.2.2) '@metamask/sdk-install-modal-web': 0.32.1 '@paulmillr/qr': 0.2.1 bowser: 2.14.1 cross-fetch: 4.1.0 - debug: 4.3.4 + debug: 4.3.4(supports-color@10.2.2) eciesjs: 0.4.18 eth-rpc-errors: 4.0.3 eventemitter2: 6.4.9 obj-multiplex: 1.0.0 pump: 3.0.4 readable-stream: 3.6.2 - socket.io-client: 4.8.3(bufferutil@4.1.0)(utf-8-validate@6.0.6) + socket.io-client: 4.8.3(bufferutil@4.1.0)(supports-color@10.2.2)(utf-8-validate@6.0.6) tslib: 2.8.1 util: 0.12.5 uuid: 8.3.2 @@ -18249,7 +19063,7 @@ snapshots: '@metamask/superstruct@3.2.1': {} - '@metamask/utils@11.11.0': + '@metamask/utils@11.11.0(supports-color@10.2.2)': dependencies: '@ethereumjs/tx': 4.2.0 '@metamask/superstruct': 3.2.1 @@ -18257,69 +19071,69 @@ snapshots: '@scure/base': 1.1.9 '@types/debug': 4.1.13 '@types/lodash': 4.17.24 - debug: 4.4.3 + debug: 4.4.3(supports-color@10.2.2) lodash: 4.18.1 pony-cause: 2.1.11 - semver: 7.8.4 + semver: 7.8.5 uuid: 9.0.1 transitivePeerDependencies: - supports-color - '@metamask/utils@5.0.2': + '@metamask/utils@5.0.2(supports-color@10.2.2)': dependencies: '@ethereumjs/tx': 4.2.0 '@types/debug': 4.1.13 - debug: 4.4.3 - semver: 7.8.1 + debug: 4.4.3(supports-color@10.2.2) + semver: 7.8.5 superstruct: 1.0.4 transitivePeerDependencies: - supports-color - '@metamask/utils@8.5.0': + '@metamask/utils@8.5.0(supports-color@10.2.2)': dependencies: '@ethereumjs/tx': 4.2.0 '@metamask/superstruct': 3.2.1 '@noble/hashes': 1.8.0 '@scure/base': 1.1.9 '@types/debug': 4.1.13 - debug: 4.4.3 + debug: 4.4.3(supports-color@10.2.2) pony-cause: 2.1.11 - semver: 7.8.4 + semver: 7.8.5 uuid: 9.0.1 transitivePeerDependencies: - supports-color - '@metamask/utils@9.3.0': + '@metamask/utils@9.3.0(supports-color@10.2.2)': dependencies: '@ethereumjs/tx': 4.2.0 '@metamask/superstruct': 3.2.1 '@noble/hashes': 1.8.0 '@scure/base': 1.1.9 '@types/debug': 4.1.13 - debug: 4.4.3 + debug: 4.4.3(supports-color@10.2.2) pony-cause: 2.1.11 - semver: 7.8.4 + semver: 7.8.5 uuid: 9.0.1 transitivePeerDependencies: - supports-color - '@microsoft/api-extractor-model@7.33.8(@types/node@20.16.7)': + '@microsoft/api-extractor-model@7.33.8(@types/node@24.13.4)': dependencies: '@microsoft/tsdoc': 0.16.0 '@microsoft/tsdoc-config': 0.18.1 - '@rushstack/node-core-library': 5.23.1(@types/node@20.16.7) + '@rushstack/node-core-library': 5.23.1(@types/node@24.13.4) transitivePeerDependencies: - '@types/node' - '@microsoft/api-extractor@7.58.7(@types/node@20.16.7)': + '@microsoft/api-extractor@7.58.7(@types/node@24.13.4)': dependencies: - '@microsoft/api-extractor-model': 7.33.8(@types/node@20.16.7) + '@microsoft/api-extractor-model': 7.33.8(@types/node@24.13.4) '@microsoft/tsdoc': 0.16.0 '@microsoft/tsdoc-config': 0.18.1 - '@rushstack/node-core-library': 5.23.1(@types/node@20.16.7) + '@rushstack/node-core-library': 5.23.1(@types/node@24.13.4) '@rushstack/rig-package': 0.7.3 - '@rushstack/terminal': 0.24.0(@types/node@20.16.7) - '@rushstack/ts-command-line': 5.3.9(@types/node@20.16.7) + '@rushstack/terminal': 0.24.0(@types/node@24.13.4) + '@rushstack/ts-command-line': 5.3.9(@types/node@24.13.4) diff: 8.0.4 minimatch: 10.2.3 resolve: 1.22.12 @@ -18404,19 +19218,19 @@ snapshots: '@multiformats/multiaddr': 12.5.1 is-ip: 5.0.1 - '@multisender.app/multisender-react-widget@0.2.3(b9d9ab1f9c537c8f45225bdc5044ccc9)': + '@multisender.app/multisender-react-widget@0.2.3(4c18776c2376832d04d8e6a63f20812b)': dependencies: '@codemirror/commands': 6.10.3 '@csstools/normalize.css': 12.1.1 '@formkit/auto-animate': 0.8.4 '@mantine/core': 8.3.18(@mantine/hooks@8.3.18(react@19.1.4))(@types/react@19.1.7)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) '@mantine/hooks': 8.3.18(react@19.1.4) - '@reown/appkit': 1.7.20(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@3.25.76) - '@reown/appkit-adapter-wagmi': 1.7.20(09e58c13ebbf9932f0a56e13a5b340ff) + '@reown/appkit': 1.7.20(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@4.4.3) + '@reown/appkit-adapter-wagmi': 1.7.20(df3f0d3afef4414b7f4264d6db7d6a30) '@tabler/icons-react': 3.44.0(react@19.1.4) '@tanstack/react-query': 5.55.4(react@19.1.4) - '@uiw/react-codemirror': 4.25.10(@babel/runtime@7.29.7)(@codemirror/autocomplete@6.20.2)(@codemirror/language@6.0.0)(@codemirror/lint@6.9.6)(@codemirror/search@6.7.0)(@codemirror/state@6.6.0)(@codemirror/theme-one-dark@6.1.3)(@codemirror/view@6.43.0)(codemirror@5.65.21)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) - axios: 1.12.0(debug@4.4.3) + '@uiw/react-codemirror': 4.25.10(@babel/runtime@7.29.7)(@codemirror/autocomplete@6.20.2)(@codemirror/language@6.0.0)(@codemirror/lint@6.9.6)(@codemirror/search@6.7.0)(@codemirror/state@6.6.0)(@codemirror/theme-one-dark@6.1.3)(@codemirror/view@6.43.0)(codemirror@6.0.2)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + axios: 1.12.0(debug@4.4.3(supports-color@10.2.2)) clsx: 2.1.1 es-toolkit: 1.39.10 from-exponential: 1.1.1 @@ -18424,12 +19238,12 @@ snapshots: idb: 8.0.3 luxon: 3.7.2 nanoid: 5.1.11 - posthog-js: 1.376.2 + posthog-js: 1.434.15(@types/react@19.1.7)(react@19.1.4) react: 19.1.4 react-dom: 19.1.4(react@19.1.4) react-papaparse: 4.4.0 - viem: 2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) - wagmi: 2.19.5(@tanstack/query-core@5.55.4)(@tanstack/react-query@5.55.4(react@19.1.4))(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6)(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76))(zod@3.25.76) + viem: 2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) + wagmi: 2.19.5(@tanstack/query-core@5.55.4)(@tanstack/react-query@5.55.4(react@19.1.4))(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(debug@4.4.3(supports-color@10.2.2))(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6))(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6)(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3))(zod@4.4.3) transitivePeerDependencies: - '@babel/runtime' - '@codemirror/autocomplete' @@ -18442,6 +19256,7 @@ snapshots: - '@types/react' - codemirror - debug + - preact-render-to-string '@mysten/bcs@1.9.2': dependencies: @@ -18501,60 +19316,34 @@ snapshots: '@next/env@13.5.11': {} - '@next/env@16.2.6': {} - - '@next/env@16.3.1': {} + '@next/env@16.3.5': {} '@next/eslint-plugin-next@16.2.6': dependencies: fast-glob: 3.3.1 - '@next/swc-darwin-arm64@16.2.6': - optional: true - - '@next/swc-darwin-arm64@16.3.1': - optional: true - - '@next/swc-darwin-x64@16.2.6': - optional: true - - '@next/swc-darwin-x64@16.3.1': - optional: true - - '@next/swc-linux-arm64-gnu@16.2.6': - optional: true - - '@next/swc-linux-arm64-gnu@16.3.1': - optional: true - - '@next/swc-linux-arm64-musl@16.2.6': - optional: true - - '@next/swc-linux-arm64-musl@16.3.1': - optional: true - - '@next/swc-linux-x64-gnu@16.2.6': + '@next/swc-darwin-arm64@16.3.5': optional: true - '@next/swc-linux-x64-gnu@16.3.1': + '@next/swc-darwin-x64@16.3.5': optional: true - '@next/swc-linux-x64-musl@16.2.6': + '@next/swc-linux-arm64-gnu@16.3.5': optional: true - '@next/swc-linux-x64-musl@16.3.1': + '@next/swc-linux-arm64-musl@16.3.5': optional: true - '@next/swc-win32-arm64-msvc@16.2.6': + '@next/swc-linux-x64-gnu@16.3.5': optional: true - '@next/swc-win32-arm64-msvc@16.3.1': + '@next/swc-linux-x64-musl@16.3.5': optional: true - '@next/swc-win32-x64-msvc@16.2.6': + '@next/swc-win32-arm64-msvc@16.3.5': optional: true - '@next/swc-win32-x64-msvc@16.3.1': + '@next/swc-win32-x64-msvc@16.3.5': optional: true '@noble/ciphers@0.4.1': {} @@ -18629,16 +19418,12 @@ snapshots: '@ethersproject/bignumber': 5.8.0 '@ethersproject/solidity': 5.8.0 - '@nouns/sdk@1.2.0(@tanstack/react-query@5.55.4(react@19.1.4))(@wagmi/core@2.22.1(@tanstack/query-core@5.55.4)(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(react@19.1.4)(use-sync-external-store@1.4.0(react@19.1.4))(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76)))(wagmi@2.19.5(@tanstack/query-core@5.55.4)(@tanstack/react-query@5.55.4(react@19.1.4))(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6)(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76))(zod@3.25.76))': + '@nouns/sdk@1.2.0(@tanstack/react-query@5.55.4(react@19.1.4))(@wagmi/core@2.22.1(@tanstack/query-core@5.55.4)(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(react@19.1.4)(use-sync-external-store@1.4.0(react@19.1.4))(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3)))(wagmi@2.19.5(@tanstack/query-core@5.55.4)(@tanstack/react-query@5.55.4(react@19.1.4))(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(debug@4.4.3(supports-color@10.2.2))(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6))(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6)(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3))(zod@4.4.3))': dependencies: - '@wagmi/core': 2.22.1(@tanstack/query-core@5.55.4)(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(react@19.1.4)(use-sync-external-store@1.4.0(react@19.1.4))(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76)) + '@wagmi/core': 2.22.1(@tanstack/query-core@5.55.4)(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(react@19.1.4)(use-sync-external-store@1.4.0(react@19.1.4))(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3)) optionalDependencies: '@tanstack/react-query': 5.55.4(react@19.1.4) - wagmi: 2.19.5(@tanstack/query-core@5.55.4)(@tanstack/react-query@5.55.4(react@19.1.4))(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6)(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76))(zod@3.25.76) - - '@opentelemetry/api-logs@0.208.0': - dependencies: - '@opentelemetry/api': 1.9.1 + wagmi: 2.19.5(@tanstack/query-core@5.55.4)(@tanstack/react-query@5.55.4(react@19.1.4))(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(debug@4.4.3(supports-color@10.2.2))(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6))(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6)(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3))(zod@4.4.3) '@opentelemetry/api-logs@0.217.0': dependencies: @@ -18650,58 +19435,58 @@ snapshots: '@opentelemetry/api@1.9.1': {} - '@opentelemetry/auto-instrumentations-node@0.75.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.8.0(@opentelemetry/api@1.9.1))': + '@opentelemetry/auto-instrumentations-node@0.75.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.8.0(@opentelemetry/api@1.9.1))(supports-color@10.2.2)': dependencies: '@opentelemetry/api': 1.9.1 '@opentelemetry/core': 2.8.0(@opentelemetry/api@1.9.1) - '@opentelemetry/instrumentation': 0.217.0(@opentelemetry/api@1.9.1) - '@opentelemetry/instrumentation-amqplib': 0.64.0(@opentelemetry/api@1.9.1) - '@opentelemetry/instrumentation-aws-lambda': 0.69.0(@opentelemetry/api@1.9.1) - '@opentelemetry/instrumentation-aws-sdk': 0.72.0(@opentelemetry/api@1.9.1) - '@opentelemetry/instrumentation-bunyan': 0.62.0(@opentelemetry/api@1.9.1) - '@opentelemetry/instrumentation-cassandra-driver': 0.62.0(@opentelemetry/api@1.9.1) - '@opentelemetry/instrumentation-connect': 0.60.0(@opentelemetry/api@1.9.1) - '@opentelemetry/instrumentation-cucumber': 0.33.0(@opentelemetry/api@1.9.1) - '@opentelemetry/instrumentation-dataloader': 0.34.0(@opentelemetry/api@1.9.1) - '@opentelemetry/instrumentation-dns': 0.60.0(@opentelemetry/api@1.9.1) - '@opentelemetry/instrumentation-express': 0.65.0(@opentelemetry/api@1.9.1) - '@opentelemetry/instrumentation-fs': 0.36.0(@opentelemetry/api@1.9.1) - '@opentelemetry/instrumentation-generic-pool': 0.60.0(@opentelemetry/api@1.9.1) - '@opentelemetry/instrumentation-graphql': 0.65.0(@opentelemetry/api@1.9.1) - '@opentelemetry/instrumentation-grpc': 0.217.0(@opentelemetry/api@1.9.1) - '@opentelemetry/instrumentation-hapi': 0.63.0(@opentelemetry/api@1.9.1) - '@opentelemetry/instrumentation-http': 0.217.0(@opentelemetry/api@1.9.1) - '@opentelemetry/instrumentation-ioredis': 0.65.0(@opentelemetry/api@1.9.1) - '@opentelemetry/instrumentation-kafkajs': 0.26.0(@opentelemetry/api@1.9.1) - '@opentelemetry/instrumentation-knex': 0.61.0(@opentelemetry/api@1.9.1) - '@opentelemetry/instrumentation-koa': 0.65.0(@opentelemetry/api@1.9.1) - '@opentelemetry/instrumentation-lru-memoizer': 0.61.0(@opentelemetry/api@1.9.1) - '@opentelemetry/instrumentation-memcached': 0.60.0(@opentelemetry/api@1.9.1) - '@opentelemetry/instrumentation-mongodb': 0.70.0(@opentelemetry/api@1.9.1) - '@opentelemetry/instrumentation-mongoose': 0.63.0(@opentelemetry/api@1.9.1) - '@opentelemetry/instrumentation-mysql': 0.63.0(@opentelemetry/api@1.9.1) - '@opentelemetry/instrumentation-mysql2': 0.63.0(@opentelemetry/api@1.9.1) - '@opentelemetry/instrumentation-nestjs-core': 0.63.0(@opentelemetry/api@1.9.1) - '@opentelemetry/instrumentation-net': 0.61.0(@opentelemetry/api@1.9.1) - '@opentelemetry/instrumentation-openai': 0.15.0(@opentelemetry/api@1.9.1) - '@opentelemetry/instrumentation-oracledb': 0.42.0(@opentelemetry/api@1.9.1) - '@opentelemetry/instrumentation-pg': 0.69.0(@opentelemetry/api@1.9.1) - '@opentelemetry/instrumentation-pino': 0.63.0(@opentelemetry/api@1.9.1) - '@opentelemetry/instrumentation-redis': 0.65.0(@opentelemetry/api@1.9.1) - '@opentelemetry/instrumentation-restify': 0.62.0(@opentelemetry/api@1.9.1) - '@opentelemetry/instrumentation-router': 0.61.0(@opentelemetry/api@1.9.1) - '@opentelemetry/instrumentation-runtime-node': 0.30.0(@opentelemetry/api@1.9.1) - '@opentelemetry/instrumentation-socket.io': 0.64.0(@opentelemetry/api@1.9.1) - '@opentelemetry/instrumentation-tedious': 0.36.0(@opentelemetry/api@1.9.1) - '@opentelemetry/instrumentation-undici': 0.27.0(@opentelemetry/api@1.9.1) - '@opentelemetry/instrumentation-winston': 0.61.0(@opentelemetry/api@1.9.1) + '@opentelemetry/instrumentation': 0.217.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) + '@opentelemetry/instrumentation-amqplib': 0.64.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) + '@opentelemetry/instrumentation-aws-lambda': 0.69.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) + '@opentelemetry/instrumentation-aws-sdk': 0.72.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) + '@opentelemetry/instrumentation-bunyan': 0.62.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) + '@opentelemetry/instrumentation-cassandra-driver': 0.62.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) + '@opentelemetry/instrumentation-connect': 0.60.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) + '@opentelemetry/instrumentation-cucumber': 0.33.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) + '@opentelemetry/instrumentation-dataloader': 0.34.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) + '@opentelemetry/instrumentation-dns': 0.60.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) + '@opentelemetry/instrumentation-express': 0.65.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) + '@opentelemetry/instrumentation-fs': 0.36.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) + '@opentelemetry/instrumentation-generic-pool': 0.60.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) + '@opentelemetry/instrumentation-graphql': 0.65.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) + '@opentelemetry/instrumentation-grpc': 0.217.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) + '@opentelemetry/instrumentation-hapi': 0.63.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) + '@opentelemetry/instrumentation-http': 0.217.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) + '@opentelemetry/instrumentation-ioredis': 0.65.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) + '@opentelemetry/instrumentation-kafkajs': 0.26.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) + '@opentelemetry/instrumentation-knex': 0.61.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) + '@opentelemetry/instrumentation-koa': 0.65.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) + '@opentelemetry/instrumentation-lru-memoizer': 0.61.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) + '@opentelemetry/instrumentation-memcached': 0.60.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) + '@opentelemetry/instrumentation-mongodb': 0.70.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) + '@opentelemetry/instrumentation-mongoose': 0.63.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) + '@opentelemetry/instrumentation-mysql': 0.63.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) + '@opentelemetry/instrumentation-mysql2': 0.63.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) + '@opentelemetry/instrumentation-nestjs-core': 0.63.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) + '@opentelemetry/instrumentation-net': 0.61.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) + '@opentelemetry/instrumentation-openai': 0.15.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) + '@opentelemetry/instrumentation-oracledb': 0.42.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) + '@opentelemetry/instrumentation-pg': 0.69.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) + '@opentelemetry/instrumentation-pino': 0.63.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) + '@opentelemetry/instrumentation-redis': 0.65.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) + '@opentelemetry/instrumentation-restify': 0.62.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) + '@opentelemetry/instrumentation-router': 0.61.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) + '@opentelemetry/instrumentation-runtime-node': 0.30.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) + '@opentelemetry/instrumentation-socket.io': 0.64.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) + '@opentelemetry/instrumentation-tedious': 0.36.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) + '@opentelemetry/instrumentation-undici': 0.27.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) + '@opentelemetry/instrumentation-winston': 0.61.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) '@opentelemetry/resource-detector-alibaba-cloud': 0.33.8(@opentelemetry/api@1.9.1) '@opentelemetry/resource-detector-aws': 2.18.0(@opentelemetry/api@1.9.1) '@opentelemetry/resource-detector-azure': 0.25.0(@opentelemetry/api@1.9.1) '@opentelemetry/resource-detector-container': 0.8.9(@opentelemetry/api@1.9.1) - '@opentelemetry/resource-detector-gcp': 0.52.0(@opentelemetry/api@1.9.1) + '@opentelemetry/resource-detector-gcp': 0.52.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) '@opentelemetry/resources': 2.7.1(@opentelemetry/api@1.9.1) - '@opentelemetry/sdk-node': 0.217.0(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-node': 0.217.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) transitivePeerDependencies: - supports-color @@ -18725,11 +19510,6 @@ snapshots: dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.1)': - dependencies: - '@opentelemetry/api': 1.9.1 - '@opentelemetry/semantic-conventions': 1.41.1 - '@opentelemetry/core@2.7.1(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 @@ -18768,15 +19548,6 @@ snapshots: '@opentelemetry/otlp-transformer': 0.218.0(@opentelemetry/api@1.9.1) '@opentelemetry/sdk-logs': 0.218.0(@opentelemetry/api@1.9.1) - '@opentelemetry/exporter-logs-otlp-http@0.208.0(@opentelemetry/api@1.9.1)': - dependencies: - '@opentelemetry/api': 1.9.1 - '@opentelemetry/api-logs': 0.208.0 - '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.1) - '@opentelemetry/otlp-exporter-base': 0.208.0(@opentelemetry/api@1.9.1) - '@opentelemetry/otlp-transformer': 0.208.0(@opentelemetry/api@1.9.1) - '@opentelemetry/sdk-logs': 0.208.0(@opentelemetry/api@1.9.1) - '@opentelemetry/exporter-logs-otlp-http@0.217.0(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 @@ -18961,264 +19732,264 @@ snapshots: '@opentelemetry/sdk-trace-base': 2.7.1(@opentelemetry/api@1.9.1) '@opentelemetry/semantic-conventions': 1.41.1 - '@opentelemetry/instrumentation-amqplib@0.64.0(@opentelemetry/api@1.9.1)': + '@opentelemetry/instrumentation-amqplib@0.64.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2)': dependencies: '@opentelemetry/api': 1.9.1 '@opentelemetry/core': 2.8.0(@opentelemetry/api@1.9.1) - '@opentelemetry/instrumentation': 0.217.0(@opentelemetry/api@1.9.1) + '@opentelemetry/instrumentation': 0.217.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) '@opentelemetry/semantic-conventions': 1.41.1 transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-aws-lambda@0.69.0(@opentelemetry/api@1.9.1)': + '@opentelemetry/instrumentation-aws-lambda@0.69.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/instrumentation': 0.217.0(@opentelemetry/api@1.9.1) + '@opentelemetry/instrumentation': 0.217.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) '@opentelemetry/semantic-conventions': 1.41.1 '@types/aws-lambda': 8.10.162 transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-aws-sdk@0.72.0(@opentelemetry/api@1.9.1)': + '@opentelemetry/instrumentation-aws-sdk@0.72.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2)': dependencies: '@opentelemetry/api': 1.9.1 '@opentelemetry/core': 2.8.0(@opentelemetry/api@1.9.1) - '@opentelemetry/instrumentation': 0.217.0(@opentelemetry/api@1.9.1) + '@opentelemetry/instrumentation': 0.217.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) '@opentelemetry/semantic-conventions': 1.41.1 transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-bunyan@0.62.0(@opentelemetry/api@1.9.1)': + '@opentelemetry/instrumentation-bunyan@0.62.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2)': dependencies: '@opentelemetry/api': 1.9.1 '@opentelemetry/api-logs': 0.217.0 - '@opentelemetry/instrumentation': 0.217.0(@opentelemetry/api@1.9.1) + '@opentelemetry/instrumentation': 0.217.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) '@types/bunyan': 1.8.11 transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-cassandra-driver@0.62.0(@opentelemetry/api@1.9.1)': + '@opentelemetry/instrumentation-cassandra-driver@0.62.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/instrumentation': 0.217.0(@opentelemetry/api@1.9.1) + '@opentelemetry/instrumentation': 0.217.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) '@opentelemetry/semantic-conventions': 1.41.1 transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-connect@0.60.0(@opentelemetry/api@1.9.1)': + '@opentelemetry/instrumentation-connect@0.60.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2)': dependencies: '@opentelemetry/api': 1.9.1 '@opentelemetry/core': 2.8.0(@opentelemetry/api@1.9.1) - '@opentelemetry/instrumentation': 0.217.0(@opentelemetry/api@1.9.1) + '@opentelemetry/instrumentation': 0.217.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) '@opentelemetry/semantic-conventions': 1.41.1 '@types/connect': 3.4.38 transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-cucumber@0.33.0(@opentelemetry/api@1.9.1)': + '@opentelemetry/instrumentation-cucumber@0.33.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/instrumentation': 0.217.0(@opentelemetry/api@1.9.1) + '@opentelemetry/instrumentation': 0.217.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) '@opentelemetry/semantic-conventions': 1.41.1 transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-dataloader@0.34.0(@opentelemetry/api@1.9.1)': + '@opentelemetry/instrumentation-dataloader@0.34.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/instrumentation': 0.217.0(@opentelemetry/api@1.9.1) + '@opentelemetry/instrumentation': 0.217.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-dns@0.60.0(@opentelemetry/api@1.9.1)': + '@opentelemetry/instrumentation-dns@0.60.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/instrumentation': 0.217.0(@opentelemetry/api@1.9.1) + '@opentelemetry/instrumentation': 0.217.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-express@0.65.0(@opentelemetry/api@1.9.1)': + '@opentelemetry/instrumentation-express@0.65.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2)': dependencies: '@opentelemetry/api': 1.9.1 '@opentelemetry/core': 2.8.0(@opentelemetry/api@1.9.1) - '@opentelemetry/instrumentation': 0.217.0(@opentelemetry/api@1.9.1) + '@opentelemetry/instrumentation': 0.217.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) '@opentelemetry/semantic-conventions': 1.41.1 transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-fs@0.36.0(@opentelemetry/api@1.9.1)': + '@opentelemetry/instrumentation-fs@0.36.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2)': dependencies: '@opentelemetry/api': 1.9.1 '@opentelemetry/core': 2.8.0(@opentelemetry/api@1.9.1) - '@opentelemetry/instrumentation': 0.217.0(@opentelemetry/api@1.9.1) + '@opentelemetry/instrumentation': 0.217.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-generic-pool@0.60.0(@opentelemetry/api@1.9.1)': + '@opentelemetry/instrumentation-generic-pool@0.60.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/instrumentation': 0.217.0(@opentelemetry/api@1.9.1) + '@opentelemetry/instrumentation': 0.217.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-graphql@0.65.0(@opentelemetry/api@1.9.1)': + '@opentelemetry/instrumentation-graphql@0.65.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/instrumentation': 0.217.0(@opentelemetry/api@1.9.1) + '@opentelemetry/instrumentation': 0.217.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-grpc@0.217.0(@opentelemetry/api@1.9.1)': + '@opentelemetry/instrumentation-grpc@0.217.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/instrumentation': 0.217.0(@opentelemetry/api@1.9.1) + '@opentelemetry/instrumentation': 0.217.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) '@opentelemetry/semantic-conventions': 1.41.1 transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-hapi@0.63.0(@opentelemetry/api@1.9.1)': + '@opentelemetry/instrumentation-hapi@0.63.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2)': dependencies: '@opentelemetry/api': 1.9.1 '@opentelemetry/core': 2.8.0(@opentelemetry/api@1.9.1) - '@opentelemetry/instrumentation': 0.217.0(@opentelemetry/api@1.9.1) + '@opentelemetry/instrumentation': 0.217.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) '@opentelemetry/semantic-conventions': 1.41.1 transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-http@0.217.0(@opentelemetry/api@1.9.1)': + '@opentelemetry/instrumentation-http@0.217.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2)': dependencies: '@opentelemetry/api': 1.9.1 '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.1) - '@opentelemetry/instrumentation': 0.217.0(@opentelemetry/api@1.9.1) + '@opentelemetry/instrumentation': 0.217.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) '@opentelemetry/semantic-conventions': 1.41.1 forwarded-parse: 2.1.2 transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-ioredis@0.65.0(@opentelemetry/api@1.9.1)': + '@opentelemetry/instrumentation-ioredis@0.65.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/instrumentation': 0.217.0(@opentelemetry/api@1.9.1) + '@opentelemetry/instrumentation': 0.217.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) '@opentelemetry/redis-common': 0.38.3 '@opentelemetry/semantic-conventions': 1.41.1 transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-kafkajs@0.26.0(@opentelemetry/api@1.9.1)': + '@opentelemetry/instrumentation-kafkajs@0.26.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/instrumentation': 0.217.0(@opentelemetry/api@1.9.1) + '@opentelemetry/instrumentation': 0.217.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) '@opentelemetry/semantic-conventions': 1.41.1 transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-knex@0.61.0(@opentelemetry/api@1.9.1)': + '@opentelemetry/instrumentation-knex@0.61.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/instrumentation': 0.217.0(@opentelemetry/api@1.9.1) + '@opentelemetry/instrumentation': 0.217.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) '@opentelemetry/semantic-conventions': 1.41.1 transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-koa@0.65.0(@opentelemetry/api@1.9.1)': + '@opentelemetry/instrumentation-koa@0.65.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2)': dependencies: '@opentelemetry/api': 1.9.1 '@opentelemetry/core': 2.8.0(@opentelemetry/api@1.9.1) - '@opentelemetry/instrumentation': 0.217.0(@opentelemetry/api@1.9.1) + '@opentelemetry/instrumentation': 0.217.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) '@opentelemetry/semantic-conventions': 1.41.1 transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-lru-memoizer@0.61.0(@opentelemetry/api@1.9.1)': + '@opentelemetry/instrumentation-lru-memoizer@0.61.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/instrumentation': 0.217.0(@opentelemetry/api@1.9.1) + '@opentelemetry/instrumentation': 0.217.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-memcached@0.60.0(@opentelemetry/api@1.9.1)': + '@opentelemetry/instrumentation-memcached@0.60.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/instrumentation': 0.217.0(@opentelemetry/api@1.9.1) + '@opentelemetry/instrumentation': 0.217.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) '@opentelemetry/semantic-conventions': 1.41.1 '@types/memcached': 2.2.10 transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-mongodb@0.70.0(@opentelemetry/api@1.9.1)': + '@opentelemetry/instrumentation-mongodb@0.70.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/instrumentation': 0.217.0(@opentelemetry/api@1.9.1) + '@opentelemetry/instrumentation': 0.217.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) '@opentelemetry/semantic-conventions': 1.41.1 transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-mongoose@0.63.0(@opentelemetry/api@1.9.1)': + '@opentelemetry/instrumentation-mongoose@0.63.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2)': dependencies: '@opentelemetry/api': 1.9.1 '@opentelemetry/core': 2.8.0(@opentelemetry/api@1.9.1) - '@opentelemetry/instrumentation': 0.217.0(@opentelemetry/api@1.9.1) + '@opentelemetry/instrumentation': 0.217.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) '@opentelemetry/semantic-conventions': 1.41.1 transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-mysql2@0.63.0(@opentelemetry/api@1.9.1)': + '@opentelemetry/instrumentation-mysql2@0.63.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/instrumentation': 0.217.0(@opentelemetry/api@1.9.1) + '@opentelemetry/instrumentation': 0.217.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) '@opentelemetry/semantic-conventions': 1.41.1 '@opentelemetry/sql-common': 0.41.2(@opentelemetry/api@1.9.1) transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-mysql@0.63.0(@opentelemetry/api@1.9.1)': + '@opentelemetry/instrumentation-mysql@0.63.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/instrumentation': 0.217.0(@opentelemetry/api@1.9.1) + '@opentelemetry/instrumentation': 0.217.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) '@opentelemetry/semantic-conventions': 1.41.1 '@types/mysql': 2.15.27 transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-nestjs-core@0.63.0(@opentelemetry/api@1.9.1)': + '@opentelemetry/instrumentation-nestjs-core@0.63.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/instrumentation': 0.217.0(@opentelemetry/api@1.9.1) + '@opentelemetry/instrumentation': 0.217.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) '@opentelemetry/semantic-conventions': 1.41.1 transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-net@0.61.0(@opentelemetry/api@1.9.1)': + '@opentelemetry/instrumentation-net@0.61.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/instrumentation': 0.217.0(@opentelemetry/api@1.9.1) + '@opentelemetry/instrumentation': 0.217.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) '@opentelemetry/semantic-conventions': 1.41.1 transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-openai@0.15.0(@opentelemetry/api@1.9.1)': + '@opentelemetry/instrumentation-openai@0.15.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2)': dependencies: '@opentelemetry/api': 1.9.1 '@opentelemetry/api-logs': 0.217.0 - '@opentelemetry/instrumentation': 0.217.0(@opentelemetry/api@1.9.1) + '@opentelemetry/instrumentation': 0.217.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) '@opentelemetry/semantic-conventions': 1.41.1 transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-oracledb@0.42.0(@opentelemetry/api@1.9.1)': + '@opentelemetry/instrumentation-oracledb@0.42.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/instrumentation': 0.217.0(@opentelemetry/api@1.9.1) + '@opentelemetry/instrumentation': 0.217.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) '@opentelemetry/semantic-conventions': 1.41.1 '@types/oracledb': 6.5.2 transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-pg@0.69.0(@opentelemetry/api@1.9.1)': + '@opentelemetry/instrumentation-pg@0.69.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2)': dependencies: '@opentelemetry/api': 1.9.1 '@opentelemetry/core': 2.8.0(@opentelemetry/api@1.9.1) - '@opentelemetry/instrumentation': 0.217.0(@opentelemetry/api@1.9.1) + '@opentelemetry/instrumentation': 0.217.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) '@opentelemetry/semantic-conventions': 1.41.1 '@opentelemetry/sql-common': 0.41.2(@opentelemetry/api@1.9.1) '@types/pg': 8.15.6 @@ -19226,105 +19997,99 @@ snapshots: transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-pino@0.63.0(@opentelemetry/api@1.9.1)': + '@opentelemetry/instrumentation-pino@0.63.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2)': dependencies: '@opentelemetry/api': 1.9.1 '@opentelemetry/api-logs': 0.217.0 '@opentelemetry/core': 2.8.0(@opentelemetry/api@1.9.1) - '@opentelemetry/instrumentation': 0.217.0(@opentelemetry/api@1.9.1) + '@opentelemetry/instrumentation': 0.217.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-redis@0.65.0(@opentelemetry/api@1.9.1)': + '@opentelemetry/instrumentation-redis@0.65.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/instrumentation': 0.217.0(@opentelemetry/api@1.9.1) + '@opentelemetry/instrumentation': 0.217.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) '@opentelemetry/redis-common': 0.38.3 '@opentelemetry/semantic-conventions': 1.41.1 transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-restify@0.62.0(@opentelemetry/api@1.9.1)': + '@opentelemetry/instrumentation-restify@0.62.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2)': dependencies: '@opentelemetry/api': 1.9.1 '@opentelemetry/core': 2.8.0(@opentelemetry/api@1.9.1) - '@opentelemetry/instrumentation': 0.217.0(@opentelemetry/api@1.9.1) + '@opentelemetry/instrumentation': 0.217.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) '@opentelemetry/semantic-conventions': 1.41.1 transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-router@0.61.0(@opentelemetry/api@1.9.1)': + '@opentelemetry/instrumentation-router@0.61.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/instrumentation': 0.217.0(@opentelemetry/api@1.9.1) + '@opentelemetry/instrumentation': 0.217.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) '@opentelemetry/semantic-conventions': 1.41.1 transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-runtime-node@0.30.0(@opentelemetry/api@1.9.1)': + '@opentelemetry/instrumentation-runtime-node@0.30.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/instrumentation': 0.217.0(@opentelemetry/api@1.9.1) + '@opentelemetry/instrumentation': 0.217.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-socket.io@0.64.0(@opentelemetry/api@1.9.1)': + '@opentelemetry/instrumentation-socket.io@0.64.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/instrumentation': 0.217.0(@opentelemetry/api@1.9.1) + '@opentelemetry/instrumentation': 0.217.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-tedious@0.36.0(@opentelemetry/api@1.9.1)': + '@opentelemetry/instrumentation-tedious@0.36.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/instrumentation': 0.217.0(@opentelemetry/api@1.9.1) + '@opentelemetry/instrumentation': 0.217.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) '@opentelemetry/semantic-conventions': 1.41.1 '@types/tedious': 4.0.14 transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-undici@0.27.0(@opentelemetry/api@1.9.1)': + '@opentelemetry/instrumentation-undici@0.27.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2)': dependencies: '@opentelemetry/api': 1.9.1 '@opentelemetry/core': 2.8.0(@opentelemetry/api@1.9.1) - '@opentelemetry/instrumentation': 0.217.0(@opentelemetry/api@1.9.1) + '@opentelemetry/instrumentation': 0.217.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) '@opentelemetry/semantic-conventions': 1.41.1 transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-winston@0.61.0(@opentelemetry/api@1.9.1)': + '@opentelemetry/instrumentation-winston@0.61.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2)': dependencies: '@opentelemetry/api': 1.9.1 '@opentelemetry/api-logs': 0.217.0 - '@opentelemetry/instrumentation': 0.217.0(@opentelemetry/api@1.9.1) + '@opentelemetry/instrumentation': 0.217.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation@0.217.0(@opentelemetry/api@1.9.1)': + '@opentelemetry/instrumentation@0.217.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2)': dependencies: '@opentelemetry/api': 1.9.1 '@opentelemetry/api-logs': 0.217.0 import-in-the-middle: 3.0.2 - require-in-the-middle: 8.0.1 + require-in-the-middle: 8.0.1(supports-color@10.2.2) transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation@0.218.0(@opentelemetry/api@1.9.1)': + '@opentelemetry/instrumentation@0.218.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2)': dependencies: '@opentelemetry/api': 1.9.1 '@opentelemetry/api-logs': 0.218.0 import-in-the-middle: 3.0.2 - require-in-the-middle: 8.0.1 + require-in-the-middle: 8.0.1(supports-color@10.2.2) transitivePeerDependencies: - supports-color - '@opentelemetry/otlp-exporter-base@0.208.0(@opentelemetry/api@1.9.1)': - dependencies: - '@opentelemetry/api': 1.9.1 - '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.1) - '@opentelemetry/otlp-transformer': 0.208.0(@opentelemetry/api@1.9.1) - '@opentelemetry/otlp-exporter-base@0.217.0(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 @@ -19353,17 +20118,6 @@ snapshots: '@opentelemetry/otlp-exporter-base': 0.218.0(@opentelemetry/api@1.9.1) '@opentelemetry/otlp-transformer': 0.218.0(@opentelemetry/api@1.9.1) - '@opentelemetry/otlp-transformer@0.208.0(@opentelemetry/api@1.9.1)': - dependencies: - '@opentelemetry/api': 1.9.1 - '@opentelemetry/api-logs': 0.208.0 - '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.1) - '@opentelemetry/resources': 2.2.0(@opentelemetry/api@1.9.1) - '@opentelemetry/sdk-logs': 0.208.0(@opentelemetry/api@1.9.1) - '@opentelemetry/sdk-metrics': 2.2.0(@opentelemetry/api@1.9.1) - '@opentelemetry/sdk-trace-base': 2.2.0(@opentelemetry/api@1.9.1) - protobufjs: 7.5.5 - '@opentelemetry/otlp-transformer@0.217.0(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 @@ -19423,21 +20177,15 @@ snapshots: '@opentelemetry/core': 2.8.0(@opentelemetry/api@1.9.1) '@opentelemetry/resources': 2.7.1(@opentelemetry/api@1.9.1) - '@opentelemetry/resource-detector-gcp@0.52.0(@opentelemetry/api@1.9.1)': + '@opentelemetry/resource-detector-gcp@0.52.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2)': dependencies: '@opentelemetry/api': 1.9.1 '@opentelemetry/core': 2.8.0(@opentelemetry/api@1.9.1) '@opentelemetry/resources': 2.7.1(@opentelemetry/api@1.9.1) - gcp-metadata: 8.1.3 + gcp-metadata: 8.1.3(supports-color@10.2.2) transitivePeerDependencies: - supports-color - '@opentelemetry/resources@2.2.0(@opentelemetry/api@1.9.1)': - dependencies: - '@opentelemetry/api': 1.9.1 - '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.1) - '@opentelemetry/semantic-conventions': 1.41.1 - '@opentelemetry/resources@2.7.1(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 @@ -19450,13 +20198,6 @@ snapshots: '@opentelemetry/core': 2.8.0(@opentelemetry/api@1.9.1) '@opentelemetry/semantic-conventions': 1.41.1 - '@opentelemetry/sdk-logs@0.208.0(@opentelemetry/api@1.9.1)': - dependencies: - '@opentelemetry/api': 1.9.1 - '@opentelemetry/api-logs': 0.208.0 - '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.1) - '@opentelemetry/resources': 2.2.0(@opentelemetry/api@1.9.1) - '@opentelemetry/sdk-logs@0.217.0(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 @@ -19473,12 +20214,6 @@ snapshots: '@opentelemetry/resources': 2.7.1(@opentelemetry/api@1.9.1) '@opentelemetry/semantic-conventions': 1.41.1 - '@opentelemetry/sdk-metrics@2.2.0(@opentelemetry/api@1.9.1)': - dependencies: - '@opentelemetry/api': 1.9.1 - '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.1) - '@opentelemetry/resources': 2.2.0(@opentelemetry/api@1.9.1) - '@opentelemetry/sdk-metrics@2.7.1(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 @@ -19491,7 +20226,7 @@ snapshots: '@opentelemetry/core': 2.8.0(@opentelemetry/api@1.9.1) '@opentelemetry/resources': 2.8.0(@opentelemetry/api@1.9.1) - '@opentelemetry/sdk-node@0.217.0(@opentelemetry/api@1.9.1)': + '@opentelemetry/sdk-node@0.217.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2)': dependencies: '@opentelemetry/api': 1.9.1 '@opentelemetry/api-logs': 0.217.0 @@ -19509,7 +20244,7 @@ snapshots: '@opentelemetry/exporter-trace-otlp-http': 0.217.0(@opentelemetry/api@1.9.1) '@opentelemetry/exporter-trace-otlp-proto': 0.217.0(@opentelemetry/api@1.9.1) '@opentelemetry/exporter-zipkin': 2.7.1(@opentelemetry/api@1.9.1) - '@opentelemetry/instrumentation': 0.217.0(@opentelemetry/api@1.9.1) + '@opentelemetry/instrumentation': 0.217.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) '@opentelemetry/otlp-exporter-base': 0.217.0(@opentelemetry/api@1.9.1) '@opentelemetry/propagator-b3': 2.7.1(@opentelemetry/api@1.9.1) '@opentelemetry/propagator-jaeger': 2.7.1(@opentelemetry/api@1.9.1) @@ -19522,7 +20257,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@opentelemetry/sdk-node@0.218.0(@opentelemetry/api@1.9.1)': + '@opentelemetry/sdk-node@0.218.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2)': dependencies: '@opentelemetry/api': 1.9.1 '@opentelemetry/api-logs': 0.218.0 @@ -19540,7 +20275,7 @@ snapshots: '@opentelemetry/exporter-trace-otlp-http': 0.218.0(@opentelemetry/api@1.9.1) '@opentelemetry/exporter-trace-otlp-proto': 0.218.0(@opentelemetry/api@1.9.1) '@opentelemetry/exporter-zipkin': 2.7.1(@opentelemetry/api@1.9.1) - '@opentelemetry/instrumentation': 0.218.0(@opentelemetry/api@1.9.1) + '@opentelemetry/instrumentation': 0.218.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) '@opentelemetry/otlp-exporter-base': 0.218.0(@opentelemetry/api@1.9.1) '@opentelemetry/propagator-b3': 2.7.1(@opentelemetry/api@1.9.1) '@opentelemetry/propagator-jaeger': 2.7.1(@opentelemetry/api@1.9.1) @@ -19553,13 +20288,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.1)': - dependencies: - '@opentelemetry/api': 1.9.1 - '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.1) - '@opentelemetry/resources': 2.2.0(@opentelemetry/api@1.9.1) - '@opentelemetry/semantic-conventions': 1.41.1 - '@opentelemetry/sdk-trace-base@2.7.1(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 @@ -19718,11 +20446,11 @@ snapshots: '@pkgjs/parseargs@0.11.0': optional: true - '@playwright/experimental-ct-core@1.57.0(@types/node@20.16.7)(terser@5.48.0)(yaml@2.9.0)': + '@playwright/experimental-ct-core@1.57.0(@types/node@24.13.4)(terser@5.48.0)(yaml@2.9.0)': dependencies: playwright: 1.57.0 playwright-core: 1.57.0 - vite: 6.4.3(@types/node@20.16.7)(terser@5.48.0)(yaml@2.9.0) + vite: 6.4.3(@types/node@24.13.4)(terser@5.48.0)(yaml@2.9.0) transitivePeerDependencies: - '@types/node' - jiti @@ -19736,10 +20464,10 @@ snapshots: - tsx - yaml - '@playwright/experimental-ct-react@1.57.0(@types/node@20.16.7)(terser@5.48.0)(vite@6.4.3(@types/node@20.16.7)(terser@5.48.0)(yaml@2.9.0))(yaml@2.9.0)': + '@playwright/experimental-ct-react@1.57.0(@types/node@24.13.4)(supports-color@10.2.2)(terser@5.48.0)(vite@6.4.3(@types/node@24.13.4)(terser@5.48.0)(yaml@2.9.0))(yaml@2.9.0)': dependencies: - '@playwright/experimental-ct-core': 1.57.0(@types/node@20.16.7)(terser@5.48.0)(yaml@2.9.0) - '@vitejs/plugin-react': 4.3.4(vite@6.4.3(@types/node@20.16.7)(terser@5.48.0)(yaml@2.9.0)) + '@playwright/experimental-ct-core': 1.57.0(@types/node@24.13.4)(terser@5.48.0)(yaml@2.9.0) + '@vitejs/plugin-react': 4.3.4(supports-color@10.2.2)(vite@6.4.3(@types/node@24.13.4)(terser@5.48.0)(yaml@2.9.0)) transitivePeerDependencies: - '@types/node' - jiti @@ -19761,11 +20489,16 @@ snapshots: '@polka/url@1.0.0-next.29': {} - '@posthog/core@1.29.11': + '@posthog/browser-common@0.9.0': + dependencies: + '@posthog/core': 1.55.2 + '@posthog/types': 1.412.4 + + '@posthog/core@1.55.2': dependencies: - '@posthog/types': 1.376.2 + '@posthog/types': 1.412.4 - '@posthog/types@1.376.2': {} + '@posthog/types@1.412.4': {} '@protobuf-ts/grpcweb-transport@2.11.1': dependencies: @@ -20133,9 +20866,9 @@ snapshots: '@react-native/assets-registry@0.85.3': {} - '@react-native/codegen@0.85.3(@babel/core@7.29.7)': + '@react-native/codegen@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/parser': 7.29.7 hermes-parser: 0.33.3 invariant: 2.2.4 @@ -20143,13 +20876,13 @@ snapshots: tinyglobby: 0.2.17 yargs: 17.7.2 - '@react-native/community-cli-plugin@0.85.3(bufferutil@4.1.0)(utf-8-validate@6.0.6)': + '@react-native/community-cli-plugin@0.85.3(bufferutil@4.1.0)(supports-color@10.2.2)(utf-8-validate@6.0.6)': dependencies: - '@react-native/dev-middleware': 0.85.3(bufferutil@4.1.0)(utf-8-validate@6.0.6) - debug: 4.4.3 + '@react-native/dev-middleware': 0.85.3(bufferutil@4.1.0)(supports-color@10.2.2)(utf-8-validate@6.0.6) + debug: 4.4.3(supports-color@10.2.2) invariant: 2.2.4 - metro: 0.84.4(bufferutil@4.1.0)(utf-8-validate@6.0.6) - metro-config: 0.84.4(bufferutil@4.1.0)(utf-8-validate@6.0.6) + metro: 0.84.4(bufferutil@4.1.0)(supports-color@10.2.2)(utf-8-validate@6.0.6) + metro-config: 0.84.4(bufferutil@4.1.0)(supports-color@10.2.2)(utf-8-validate@6.0.6) metro-core: 0.84.4 semver: 7.8.5 transitivePeerDependencies: @@ -20159,27 +20892,27 @@ snapshots: '@react-native/debugger-frontend@0.85.3': {} - '@react-native/debugger-shell@0.85.3': + '@react-native/debugger-shell@0.85.3(supports-color@10.2.2)': dependencies: cross-spawn: 7.0.6 - debug: 4.4.3 + debug: 4.4.3(supports-color@10.2.2) fb-dotslash: 0.5.8 transitivePeerDependencies: - supports-color - '@react-native/dev-middleware@0.85.3(bufferutil@4.1.0)(utf-8-validate@6.0.6)': + '@react-native/dev-middleware@0.85.3(bufferutil@4.1.0)(supports-color@10.2.2)(utf-8-validate@6.0.6)': dependencies: '@isaacs/ttlcache': 1.4.1 '@react-native/debugger-frontend': 0.85.3 - '@react-native/debugger-shell': 0.85.3 - chrome-launcher: 0.15.2 - chromium-edge-launcher: 0.3.0 - connect: 3.7.0 - debug: 4.4.3 + '@react-native/debugger-shell': 0.85.3(supports-color@10.2.2) + chrome-launcher: 0.15.2(supports-color@10.2.2) + chromium-edge-launcher: 0.3.0(supports-color@10.2.2) + connect: 3.7.0(supports-color@10.2.2) + debug: 4.4.3(supports-color@10.2.2) invariant: 2.2.4 nullthrows: 1.1.1 open: 7.4.2 - serve-static: 1.16.3 + serve-static: 1.16.3(supports-color@10.2.2) ws: 8.21.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) transitivePeerDependencies: - bufferutil @@ -20192,12 +20925,12 @@ snapshots: '@react-native/normalize-colors@0.85.3': {} - '@react-native/virtualized-lists@0.85.3(@types/react@19.1.7)(react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4)': + '@react-native/virtualized-lists@0.85.3(@types/react@19.1.7)(react-native@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6))(react@19.1.4)': dependencies: invariant: 2.2.4 nullthrows: 1.1.1 react: 19.1.4 - react-native: 0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6) + react-native: 0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6) optionalDependencies: '@types/react': 19.1.7 @@ -20205,22 +20938,22 @@ snapshots: dependencies: react: 19.1.4 - '@reown/appkit-adapter-wagmi@1.7.20(09e58c13ebbf9932f0a56e13a5b340ff)': + '@reown/appkit-adapter-wagmi@1.7.20(df3f0d3afef4414b7f4264d6db7d6a30)': dependencies: - '@reown/appkit': 1.7.20(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@3.25.76) - '@reown/appkit-common': 1.7.20(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) - '@reown/appkit-controllers': 1.7.20(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@3.25.76) + '@reown/appkit': 1.7.20(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@4.4.3) + '@reown/appkit-common': 1.7.20(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) + '@reown/appkit-controllers': 1.7.20(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@4.4.3) '@reown/appkit-polyfills': 1.7.20 - '@reown/appkit-scaffold-ui': 1.7.20(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(valtio@2.1.5(@types/react@19.1.7)(react@19.1.4))(zod@3.25.76) - '@reown/appkit-utils': 1.7.20(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(valtio@2.1.5(@types/react@19.1.7)(react@19.1.4))(zod@3.25.76) + '@reown/appkit-scaffold-ui': 1.7.20(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(valtio@2.1.5(@types/react@19.1.7)(react@19.1.4))(zod@4.4.3) + '@reown/appkit-utils': 1.7.20(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(valtio@2.1.5(@types/react@19.1.7)(react@19.1.4))(zod@4.4.3) '@reown/appkit-wallet': 1.7.20(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6) - '@wagmi/core': 2.22.1(@tanstack/query-core@5.55.4)(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(react@19.1.4)(use-sync-external-store@1.4.0(react@19.1.4))(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76)) - '@walletconnect/universal-provider': 2.21.7(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) + '@wagmi/core': 2.22.1(@tanstack/query-core@5.55.4)(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(react@19.1.4)(use-sync-external-store@1.4.0(react@19.1.4))(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3)) + '@walletconnect/universal-provider': 2.21.7(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) valtio: 2.1.5(@types/react@19.1.7)(react@19.1.4) - viem: 2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) - wagmi: 2.19.5(@tanstack/query-core@5.55.4)(@tanstack/react-query@5.55.4(react@19.1.4))(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6)(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76))(zod@3.25.76) + viem: 2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) + wagmi: 2.19.5(@tanstack/query-core@5.55.4)(@tanstack/react-query@5.55.4(react@19.1.4))(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(debug@4.4.3(supports-color@10.2.2))(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6))(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6)(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3))(zod@4.4.3) optionalDependencies: - '@wagmi/connectors': 6.2.0(09e58c13ebbf9932f0a56e13a5b340ff) + '@wagmi/connectors': 6.2.0(df3f0d3afef4414b7f4264d6db7d6a30) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -20270,11 +21003,11 @@ snapshots: - utf-8-validate - zod - '@reown/appkit-common@1.7.20(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76)': + '@reown/appkit-common@1.7.20(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3)': dependencies: big.js: 6.2.2 dayjs: 1.11.13 - viem: 2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) + viem: 2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) transitivePeerDependencies: - bufferutil - typescript @@ -20292,11 +21025,11 @@ snapshots: - utf-8-validate - zod - '@reown/appkit-common@1.7.8(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76)': + '@reown/appkit-common@1.7.8(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3)': dependencies: big.js: 6.2.2 dayjs: 1.11.13 - viem: 2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) + viem: 2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) transitivePeerDependencies: - bufferutil - typescript @@ -20314,24 +21047,24 @@ snapshots: - utf-8-validate - zod - '@reown/appkit-common@1.8.11(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76)': + '@reown/appkit-common@1.8.11(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3)': dependencies: big.js: 6.2.2 dayjs: 1.11.13 - viem: 2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) + viem: 2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) transitivePeerDependencies: - bufferutil - typescript - utf-8-validate - zod - '@reown/appkit-controllers@1.7.20(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@3.25.76)': + '@reown/appkit-controllers@1.7.20(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@4.4.3)': dependencies: - '@reown/appkit-common': 1.7.20(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) + '@reown/appkit-common': 1.7.20(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) '@reown/appkit-wallet': 1.7.20(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6) - '@walletconnect/universal-provider': 2.21.7(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) + '@walletconnect/universal-provider': 2.21.7(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) valtio: 2.1.5(@types/react@19.1.7)(react@19.1.4) - viem: 2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) + viem: 2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -20360,13 +21093,13 @@ snapshots: - utf-8-validate - zod - '@reown/appkit-controllers@1.7.8(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@3.25.76)': + '@reown/appkit-controllers@1.7.8(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@4.4.3)': dependencies: - '@reown/appkit-common': 1.7.8(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) + '@reown/appkit-common': 1.7.8(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) '@reown/appkit-wallet': 1.7.8(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6) - '@walletconnect/universal-provider': 2.21.0(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) + '@walletconnect/universal-provider': 2.21.0(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) valtio: 1.13.2(@types/react@19.1.7)(react@19.1.4) - viem: 2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) + viem: 2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -20395,13 +21128,13 @@ snapshots: - utf-8-validate - zod - '@reown/appkit-controllers@1.8.11(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@3.25.76)': + '@reown/appkit-controllers@1.8.11(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@4.4.3)': dependencies: - '@reown/appkit-common': 1.8.11(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) + '@reown/appkit-common': 1.8.11(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) '@reown/appkit-wallet': 1.8.11(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6) - '@walletconnect/universal-provider': 2.22.4(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) + '@walletconnect/universal-provider': 2.22.4(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) valtio: 2.1.7(@types/react@19.1.7)(react@19.1.4) - viem: 2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) + viem: 2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -20430,12 +21163,12 @@ snapshots: - utf-8-validate - zod - '@reown/appkit-pay@1.7.20(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@3.25.76)': + '@reown/appkit-pay@1.7.20(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@4.4.3)': dependencies: - '@reown/appkit-common': 1.7.20(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) - '@reown/appkit-controllers': 1.7.20(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@3.25.76) - '@reown/appkit-ui': 1.7.20(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@3.25.76) - '@reown/appkit-utils': 1.7.20(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(valtio@2.1.5(@types/react@19.1.7)(react@19.1.4))(zod@3.25.76) + '@reown/appkit-common': 1.7.20(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) + '@reown/appkit-controllers': 1.7.20(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@4.4.3) + '@reown/appkit-ui': 1.7.20(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@4.4.3) + '@reown/appkit-utils': 1.7.20(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(valtio@2.1.5(@types/react@19.1.7)(react@19.1.4))(zod@4.4.3) lit: 3.3.0 valtio: 2.1.5(@types/react@19.1.7)(react@19.1.4) transitivePeerDependencies: @@ -20466,12 +21199,12 @@ snapshots: - utf-8-validate - zod - '@reown/appkit-pay@1.7.8(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@3.25.76)': + '@reown/appkit-pay@1.7.8(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@4.4.3)': dependencies: - '@reown/appkit-common': 1.7.8(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) - '@reown/appkit-controllers': 1.7.8(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@3.25.76) - '@reown/appkit-ui': 1.7.8(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@3.25.76) - '@reown/appkit-utils': 1.7.8(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(valtio@1.13.2(@types/react@19.1.7)(react@19.1.4))(zod@3.25.76) + '@reown/appkit-common': 1.7.8(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) + '@reown/appkit-controllers': 1.7.8(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@4.4.3) + '@reown/appkit-ui': 1.7.8(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@4.4.3) + '@reown/appkit-utils': 1.7.8(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(valtio@1.13.2(@types/react@19.1.7)(react@19.1.4))(zod@4.4.3) lit: 3.3.0 valtio: 1.13.2(@types/react@19.1.7)(react@19.1.4) transitivePeerDependencies: @@ -20502,12 +21235,12 @@ snapshots: - utf-8-validate - zod - '@reown/appkit-pay@1.8.11(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@3.25.76)': + '@reown/appkit-pay@1.8.11(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@4.4.3)': dependencies: - '@reown/appkit-common': 1.8.11(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) - '@reown/appkit-controllers': 1.8.11(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@3.25.76) - '@reown/appkit-ui': 1.8.11(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@3.25.76) - '@reown/appkit-utils': 1.8.11(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(valtio@2.1.7(@types/react@19.1.7)(react@19.1.4))(zod@3.25.76) + '@reown/appkit-common': 1.8.11(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) + '@reown/appkit-controllers': 1.8.11(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@4.4.3) + '@reown/appkit-ui': 1.8.11(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@4.4.3) + '@reown/appkit-utils': 1.8.11(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(valtio@2.1.7(@types/react@19.1.7)(react@19.1.4))(zod@4.4.3) lit: 3.3.0 valtio: 2.1.7(@types/react@19.1.7)(react@19.1.4) transitivePeerDependencies: @@ -20550,12 +21283,12 @@ snapshots: dependencies: buffer: 6.0.3 - '@reown/appkit-scaffold-ui@1.7.20(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(valtio@2.1.5(@types/react@19.1.7)(react@19.1.4))(zod@3.25.76)': + '@reown/appkit-scaffold-ui@1.7.20(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(valtio@2.1.5(@types/react@19.1.7)(react@19.1.4))(zod@4.4.3)': dependencies: - '@reown/appkit-common': 1.7.20(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) - '@reown/appkit-controllers': 1.7.20(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@3.25.76) - '@reown/appkit-ui': 1.7.20(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@3.25.76) - '@reown/appkit-utils': 1.7.20(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(valtio@2.1.5(@types/react@19.1.7)(react@19.1.4))(zod@3.25.76) + '@reown/appkit-common': 1.7.20(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) + '@reown/appkit-controllers': 1.7.20(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@4.4.3) + '@reown/appkit-ui': 1.7.20(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@4.4.3) + '@reown/appkit-utils': 1.7.20(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(valtio@2.1.5(@types/react@19.1.7)(react@19.1.4))(zod@4.4.3) '@reown/appkit-wallet': 1.7.20(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6) lit: 3.3.0 transitivePeerDependencies: @@ -20587,12 +21320,12 @@ snapshots: - valtio - zod - '@reown/appkit-scaffold-ui@1.7.8(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(valtio@1.13.2(@types/react@19.1.7)(react@19.1.4))(zod@3.25.76)': + '@reown/appkit-scaffold-ui@1.7.8(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(valtio@1.13.2(@types/react@19.1.7)(react@19.1.4))(zod@4.4.3)': dependencies: - '@reown/appkit-common': 1.7.8(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) - '@reown/appkit-controllers': 1.7.8(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@3.25.76) - '@reown/appkit-ui': 1.7.8(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@3.25.76) - '@reown/appkit-utils': 1.7.8(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(valtio@1.13.2(@types/react@19.1.7)(react@19.1.4))(zod@3.25.76) + '@reown/appkit-common': 1.7.8(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) + '@reown/appkit-controllers': 1.7.8(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@4.4.3) + '@reown/appkit-ui': 1.7.8(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@4.4.3) + '@reown/appkit-utils': 1.7.8(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(valtio@1.13.2(@types/react@19.1.7)(react@19.1.4))(zod@4.4.3) '@reown/appkit-wallet': 1.7.8(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6) lit: 3.3.0 transitivePeerDependencies: @@ -20624,12 +21357,12 @@ snapshots: - valtio - zod - '@reown/appkit-scaffold-ui@1.8.11(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(valtio@2.1.7(@types/react@19.1.7)(react@19.1.4))(zod@3.25.76)': + '@reown/appkit-scaffold-ui@1.8.11(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(valtio@2.1.7(@types/react@19.1.7)(react@19.1.4))(zod@4.4.3)': dependencies: - '@reown/appkit-common': 1.8.11(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) - '@reown/appkit-controllers': 1.8.11(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@3.25.76) - '@reown/appkit-ui': 1.8.11(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@3.25.76) - '@reown/appkit-utils': 1.8.11(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(valtio@2.1.7(@types/react@19.1.7)(react@19.1.4))(zod@3.25.76) + '@reown/appkit-common': 1.8.11(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) + '@reown/appkit-controllers': 1.8.11(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@4.4.3) + '@reown/appkit-ui': 1.8.11(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@4.4.3) + '@reown/appkit-utils': 1.8.11(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(valtio@2.1.7(@types/react@19.1.7)(react@19.1.4))(zod@4.4.3) '@reown/appkit-wallet': 1.8.11(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6) lit: 3.3.0 transitivePeerDependencies: @@ -20661,10 +21394,10 @@ snapshots: - valtio - zod - '@reown/appkit-ui@1.7.20(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@3.25.76)': + '@reown/appkit-ui@1.7.20(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@4.4.3)': dependencies: - '@reown/appkit-common': 1.7.20(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) - '@reown/appkit-controllers': 1.7.20(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@3.25.76) + '@reown/appkit-common': 1.7.20(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) + '@reown/appkit-controllers': 1.7.20(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@4.4.3) '@reown/appkit-wallet': 1.7.20(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6) lit: 3.3.0 qrcode: 1.5.3 @@ -20696,10 +21429,10 @@ snapshots: - utf-8-validate - zod - '@reown/appkit-ui@1.7.8(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@3.25.76)': + '@reown/appkit-ui@1.7.8(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@4.4.3)': dependencies: - '@reown/appkit-common': 1.7.8(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) - '@reown/appkit-controllers': 1.7.8(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@3.25.76) + '@reown/appkit-common': 1.7.8(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) + '@reown/appkit-controllers': 1.7.8(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@4.4.3) '@reown/appkit-wallet': 1.7.8(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6) lit: 3.3.0 qrcode: 1.5.3 @@ -20731,11 +21464,11 @@ snapshots: - utf-8-validate - zod - '@reown/appkit-ui@1.8.11(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@3.25.76)': + '@reown/appkit-ui@1.8.11(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@4.4.3)': dependencies: '@phosphor-icons/webcomponents': 2.1.5 - '@reown/appkit-common': 1.8.11(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) - '@reown/appkit-controllers': 1.8.11(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@3.25.76) + '@reown/appkit-common': 1.8.11(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) + '@reown/appkit-controllers': 1.8.11(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@4.4.3) '@reown/appkit-wallet': 1.8.11(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6) lit: 3.3.0 qrcode: 1.5.3 @@ -20767,17 +21500,17 @@ snapshots: - utf-8-validate - zod - '@reown/appkit-utils@1.7.20(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(valtio@2.1.5(@types/react@19.1.7)(react@19.1.4))(zod@3.25.76)': + '@reown/appkit-utils@1.7.20(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(valtio@2.1.5(@types/react@19.1.7)(react@19.1.4))(zod@4.4.3)': dependencies: - '@reown/appkit-common': 1.7.20(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) - '@reown/appkit-controllers': 1.7.20(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@3.25.76) + '@reown/appkit-common': 1.7.20(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) + '@reown/appkit-controllers': 1.7.20(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@4.4.3) '@reown/appkit-polyfills': 1.7.20 '@reown/appkit-wallet': 1.7.20(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6) '@wallet-standard/wallet': 1.1.0 '@walletconnect/logger': 2.1.2 - '@walletconnect/universal-provider': 2.21.7(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) + '@walletconnect/universal-provider': 2.21.7(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) valtio: 2.1.5(@types/react@19.1.7)(react@19.1.4) - viem: 2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) + viem: 2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -20806,16 +21539,16 @@ snapshots: - utf-8-validate - zod - '@reown/appkit-utils@1.7.8(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(valtio@1.13.2(@types/react@19.1.7)(react@19.1.4))(zod@3.25.76)': + '@reown/appkit-utils@1.7.8(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(valtio@1.13.2(@types/react@19.1.7)(react@19.1.4))(zod@4.4.3)': dependencies: - '@reown/appkit-common': 1.7.8(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) - '@reown/appkit-controllers': 1.7.8(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@3.25.76) + '@reown/appkit-common': 1.7.8(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) + '@reown/appkit-controllers': 1.7.8(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@4.4.3) '@reown/appkit-polyfills': 1.7.8 '@reown/appkit-wallet': 1.7.8(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6) '@walletconnect/logger': 2.1.2 - '@walletconnect/universal-provider': 2.21.0(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) + '@walletconnect/universal-provider': 2.21.0(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) valtio: 1.13.2(@types/react@19.1.7)(react@19.1.4) - viem: 2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) + viem: 2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -20844,17 +21577,17 @@ snapshots: - utf-8-validate - zod - '@reown/appkit-utils@1.8.11(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(valtio@2.1.7(@types/react@19.1.7)(react@19.1.4))(zod@3.25.76)': + '@reown/appkit-utils@1.8.11(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(valtio@2.1.7(@types/react@19.1.7)(react@19.1.4))(zod@4.4.3)': dependencies: - '@reown/appkit-common': 1.8.11(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) - '@reown/appkit-controllers': 1.8.11(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@3.25.76) + '@reown/appkit-common': 1.8.11(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) + '@reown/appkit-controllers': 1.8.11(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@4.4.3) '@reown/appkit-polyfills': 1.8.11 '@reown/appkit-wallet': 1.8.11(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6) '@wallet-standard/wallet': 1.1.0 '@walletconnect/logger': 3.0.2 - '@walletconnect/universal-provider': 2.22.4(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) + '@walletconnect/universal-provider': 2.22.4(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) valtio: 2.1.7(@types/react@19.1.7)(react@19.1.4) - viem: 2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) + viem: 2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -20916,21 +21649,21 @@ snapshots: - typescript - utf-8-validate - '@reown/appkit@1.7.20(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@3.25.76)': + '@reown/appkit@1.7.20(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@4.4.3)': dependencies: - '@reown/appkit-common': 1.7.20(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) - '@reown/appkit-controllers': 1.7.20(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@3.25.76) - '@reown/appkit-pay': 1.7.20(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@3.25.76) + '@reown/appkit-common': 1.7.20(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) + '@reown/appkit-controllers': 1.7.20(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@4.4.3) + '@reown/appkit-pay': 1.7.20(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@4.4.3) '@reown/appkit-polyfills': 1.7.20 - '@reown/appkit-scaffold-ui': 1.7.20(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(valtio@2.1.5(@types/react@19.1.7)(react@19.1.4))(zod@3.25.76) - '@reown/appkit-ui': 1.7.20(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@3.25.76) - '@reown/appkit-utils': 1.7.20(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(valtio@2.1.5(@types/react@19.1.7)(react@19.1.4))(zod@3.25.76) + '@reown/appkit-scaffold-ui': 1.7.20(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(valtio@2.1.5(@types/react@19.1.7)(react@19.1.4))(zod@4.4.3) + '@reown/appkit-ui': 1.7.20(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@4.4.3) + '@reown/appkit-utils': 1.7.20(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(valtio@2.1.5(@types/react@19.1.7)(react@19.1.4))(zod@4.4.3) '@reown/appkit-wallet': 1.7.20(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6) - '@walletconnect/universal-provider': 2.21.7(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) + '@walletconnect/universal-provider': 2.21.7(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) bs58: 6.0.0 semver: 7.7.2 valtio: 2.1.5(@types/react@19.1.7)(react@19.1.4) - viem: 2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) + viem: 2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) optionalDependencies: '@lit/react': 1.0.8(@types/react@19.1.7) transitivePeerDependencies: @@ -20961,21 +21694,21 @@ snapshots: - utf-8-validate - zod - '@reown/appkit@1.7.8(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@3.25.76)': + '@reown/appkit@1.7.8(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@4.4.3)': dependencies: - '@reown/appkit-common': 1.7.8(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) - '@reown/appkit-controllers': 1.7.8(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@3.25.76) - '@reown/appkit-pay': 1.7.8(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@3.25.76) + '@reown/appkit-common': 1.7.8(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) + '@reown/appkit-controllers': 1.7.8(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@4.4.3) + '@reown/appkit-pay': 1.7.8(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@4.4.3) '@reown/appkit-polyfills': 1.7.8 - '@reown/appkit-scaffold-ui': 1.7.8(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(valtio@1.13.2(@types/react@19.1.7)(react@19.1.4))(zod@3.25.76) - '@reown/appkit-ui': 1.7.8(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@3.25.76) - '@reown/appkit-utils': 1.7.8(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(valtio@1.13.2(@types/react@19.1.7)(react@19.1.4))(zod@3.25.76) + '@reown/appkit-scaffold-ui': 1.7.8(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(valtio@1.13.2(@types/react@19.1.7)(react@19.1.4))(zod@4.4.3) + '@reown/appkit-ui': 1.7.8(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@4.4.3) + '@reown/appkit-utils': 1.7.8(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(valtio@1.13.2(@types/react@19.1.7)(react@19.1.4))(zod@4.4.3) '@reown/appkit-wallet': 1.7.8(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6) '@walletconnect/types': 2.21.0 - '@walletconnect/universal-provider': 2.21.0(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) + '@walletconnect/universal-provider': 2.21.0(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) bs58: 6.0.0 valtio: 1.13.2(@types/react@19.1.7)(react@19.1.4) - viem: 2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) + viem: 2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -21004,21 +21737,21 @@ snapshots: - utf-8-validate - zod - '@reown/appkit@1.8.11(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@3.25.76)': + '@reown/appkit@1.8.11(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@4.4.3)': dependencies: - '@reown/appkit-common': 1.8.11(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) - '@reown/appkit-controllers': 1.8.11(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@3.25.76) - '@reown/appkit-pay': 1.8.11(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@3.25.76) + '@reown/appkit-common': 1.8.11(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) + '@reown/appkit-controllers': 1.8.11(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@4.4.3) + '@reown/appkit-pay': 1.8.11(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@4.4.3) '@reown/appkit-polyfills': 1.8.11 - '@reown/appkit-scaffold-ui': 1.8.11(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(valtio@2.1.7(@types/react@19.1.7)(react@19.1.4))(zod@3.25.76) - '@reown/appkit-ui': 1.8.11(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@3.25.76) - '@reown/appkit-utils': 1.8.11(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(valtio@2.1.7(@types/react@19.1.7)(react@19.1.4))(zod@3.25.76) + '@reown/appkit-scaffold-ui': 1.8.11(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(valtio@2.1.7(@types/react@19.1.7)(react@19.1.4))(zod@4.4.3) + '@reown/appkit-ui': 1.8.11(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@4.4.3) + '@reown/appkit-utils': 1.8.11(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(valtio@2.1.7(@types/react@19.1.7)(react@19.1.4))(zod@4.4.3) '@reown/appkit-wallet': 1.8.11(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6) - '@walletconnect/universal-provider': 2.22.4(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) + '@walletconnect/universal-provider': 2.22.4(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) bs58: 6.0.0 semver: 7.7.2 valtio: 2.1.7(@types/react@19.1.7)(react@19.1.4) - viem: 2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) + viem: 2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) optionalDependencies: '@lit/react': 1.0.8(@types/react@19.1.7) transitivePeerDependencies: @@ -21138,7 +21871,7 @@ snapshots: '@rtsao/scc@1.1.0': {} - '@rushstack/node-core-library@5.23.1(@types/node@20.16.7)': + '@rushstack/node-core-library@5.23.1(@types/node@24.13.4)': dependencies: ajv: 8.18.0 ajv-draft-04: 1.0.0(ajv@8.18.0) @@ -21149,37 +21882,37 @@ snapshots: resolve: 1.22.12 semver: 7.7.4 optionalDependencies: - '@types/node': 20.16.7 + '@types/node': 24.13.4 - '@rushstack/problem-matcher@0.2.1(@types/node@20.16.7)': + '@rushstack/problem-matcher@0.2.1(@types/node@24.13.4)': optionalDependencies: - '@types/node': 20.16.7 + '@types/node': 24.13.4 '@rushstack/rig-package@0.7.3': dependencies: jju: 1.4.0 resolve: 1.22.12 - '@rushstack/terminal@0.24.0(@types/node@20.16.7)': + '@rushstack/terminal@0.24.0(@types/node@24.13.4)': dependencies: - '@rushstack/node-core-library': 5.23.1(@types/node@20.16.7) - '@rushstack/problem-matcher': 0.2.1(@types/node@20.16.7) + '@rushstack/node-core-library': 5.23.1(@types/node@24.13.4) + '@rushstack/problem-matcher': 0.2.1(@types/node@24.13.4) supports-color: 8.1.1 optionalDependencies: - '@types/node': 20.16.7 + '@types/node': 24.13.4 - '@rushstack/ts-command-line@5.3.9(@types/node@20.16.7)': + '@rushstack/ts-command-line@5.3.9(@types/node@24.13.4)': dependencies: - '@rushstack/terminal': 0.24.0(@types/node@20.16.7) + '@rushstack/terminal': 0.24.0(@types/node@24.13.4) '@types/argparse': 1.0.38 argparse: 1.0.10 string-argv: 0.3.2 transitivePeerDependencies: - '@types/node' - '@safe-global/safe-apps-provider@0.18.6(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76)': + '@safe-global/safe-apps-provider@0.18.6(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3)': dependencies: - '@safe-global/safe-apps-sdk': 9.1.0(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) + '@safe-global/safe-apps-sdk': 9.1.0(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) events: 3.3.0 transitivePeerDependencies: - bufferutil @@ -21187,10 +21920,10 @@ snapshots: - utf-8-validate - zod - '@safe-global/safe-apps-sdk@9.1.0(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76)': + '@safe-global/safe-apps-sdk@9.1.0(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3)': dependencies: '@safe-global/safe-gateway-typescript-sdk': 3.23.1 - viem: 2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) + viem: 2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) transitivePeerDependencies: - bufferutil - typescript @@ -21854,112 +22587,176 @@ snapshots: transitivePeerDependencies: - encoding - '@standard-schema/spec@1.1.0': {} + '@stryker-mutator/api@10.0.0': + dependencies: + mutation-testing-metrics: 3.8.4 + mutation-testing-report-schema: 3.8.4 + tslib: 2.8.1 + typed-inject: 5.0.0 + + '@stryker-mutator/core@10.0.0(@types/node@24.13.4)': + dependencies: + '@inquirer/prompts': 8.6.0(@types/node@24.13.4) + '@stryker-mutator/api': 10.0.0 + '@stryker-mutator/instrumenter': 10.0.0 + '@stryker-mutator/util': 10.0.0 + ajv: 8.20.0 + chalk: 5.6.2 + commander: 14.0.3 + diff-match-patch: 1.0.5 + emoji-regex: 10.6.0 + execa: 9.6.1 + json-rpc-2.0: 1.7.1 + lodash.groupby: 4.6.0 + minimatch: 10.2.5 + mutation-server-protocol: 0.4.1 + mutation-testing-elements: 3.8.4 + mutation-testing-metrics: 3.8.4 + mutation-testing-report-schema: 3.8.4 + npm-run-path: 6.0.0 + progress: 2.0.3 + rxjs: 7.8.2 + semver: 7.8.5 + source-map: 0.7.6 + tree-kill: 1.2.2 + tslib: 2.8.1 + typed-inject: 5.0.0 + typed-rest-client: 2.3.1 + transitivePeerDependencies: + - '@types/node' + + '@stryker-mutator/instrumenter@10.0.0': + dependencies: + '@babel/core': 8.0.1 + '@babel/generator': 8.0.0 + '@babel/parser': 8.0.4 + '@babel/plugin-proposal-decorators': 8.0.2(@babel/core@8.0.1) + '@babel/plugin-transform-explicit-resource-management': 8.0.1(@babel/core@8.0.1) + '@babel/preset-react': 8.0.1(@babel/core@8.0.1) + '@babel/preset-typescript': 8.0.1(@babel/core@8.0.1) + '@babel/traverse': 8.0.4 + '@stryker-mutator/api': 10.0.0 + '@stryker-mutator/util': 10.0.0 + angular-html-parser: 10.11.0 + semver: 7.8.5 + tslib: 2.8.1 + weapon-regex: 2.0.4 + + '@stryker-mutator/util@10.0.0': {} + + '@stryker-mutator/vitest-runner@10.0.0(patch_hash=6ec965dcc2376b60fb1c0062fd95899901410eefe7508e286c1b1b0ff7c01159)(@stryker-mutator/core@10.0.0(@types/node@24.13.4))(vitest@5.0.0)': + dependencies: + '@stryker-mutator/api': 10.0.0 + '@stryker-mutator/core': 10.0.0(@types/node@24.13.4) + '@stryker-mutator/util': 10.0.0 + semver: 7.8.5 + tslib: 2.8.1 + vitest: 5.0.0(@opentelemetry/api@1.9.1)(@types/node@24.13.4)(@vitest/coverage-v8@5.0.0)(jsdom@27.3.0(bufferutil@4.1.0)(supports-color@10.2.2)(utf-8-validate@6.0.6))(vite@6.4.3(@types/node@24.13.4)(terser@5.48.0)(yaml@2.9.0)) - '@stylistic/eslint-plugin@5.2.3(eslint@9.39.2)': + '@stylistic/eslint-plugin@5.2.3(eslint@9.39.2(supports-color@10.2.2))': dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2) + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2(supports-color@10.2.2)) '@typescript-eslint/types': 8.60.0 - eslint: 9.39.2 + eslint: 9.39.2(supports-color@10.2.2) eslint-visitor-keys: 4.2.1 espree: 10.4.0 estraverse: 5.3.0 picomatch: 4.0.4 - '@svgr/babel-plugin-add-jsx-attribute@6.5.1(@babel/core@7.29.7)': + '@svgr/babel-plugin-add-jsx-attribute@6.5.1(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) - '@svgr/babel-plugin-add-jsx-attribute@8.0.0(@babel/core@7.29.7)': + '@svgr/babel-plugin-add-jsx-attribute@8.0.0(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) - '@svgr/babel-plugin-remove-jsx-attribute@8.0.0(@babel/core@7.29.7)': + '@svgr/babel-plugin-remove-jsx-attribute@8.0.0(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) - '@svgr/babel-plugin-remove-jsx-empty-expression@8.0.0(@babel/core@7.29.7)': + '@svgr/babel-plugin-remove-jsx-empty-expression@8.0.0(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) - '@svgr/babel-plugin-replace-jsx-attribute-value@6.5.1(@babel/core@7.29.7)': + '@svgr/babel-plugin-replace-jsx-attribute-value@6.5.1(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) - '@svgr/babel-plugin-replace-jsx-attribute-value@8.0.0(@babel/core@7.29.7)': + '@svgr/babel-plugin-replace-jsx-attribute-value@8.0.0(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) - '@svgr/babel-plugin-svg-dynamic-title@6.5.1(@babel/core@7.29.7)': + '@svgr/babel-plugin-svg-dynamic-title@6.5.1(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) - '@svgr/babel-plugin-svg-dynamic-title@8.0.0(@babel/core@7.29.7)': + '@svgr/babel-plugin-svg-dynamic-title@8.0.0(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) - '@svgr/babel-plugin-svg-em-dimensions@6.5.1(@babel/core@7.29.7)': + '@svgr/babel-plugin-svg-em-dimensions@6.5.1(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) - '@svgr/babel-plugin-svg-em-dimensions@8.0.0(@babel/core@7.29.7)': + '@svgr/babel-plugin-svg-em-dimensions@8.0.0(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) - '@svgr/babel-plugin-transform-react-native-svg@6.5.1(@babel/core@7.29.7)': + '@svgr/babel-plugin-transform-react-native-svg@6.5.1(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) - '@svgr/babel-plugin-transform-react-native-svg@8.1.0(@babel/core@7.29.7)': + '@svgr/babel-plugin-transform-react-native-svg@8.1.0(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) - '@svgr/babel-plugin-transform-svg-component@6.5.1(@babel/core@7.29.7)': + '@svgr/babel-plugin-transform-svg-component@6.5.1(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) - '@svgr/babel-plugin-transform-svg-component@8.0.0(@babel/core@7.29.7)': + '@svgr/babel-plugin-transform-svg-component@8.0.0(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) - '@svgr/babel-preset@6.5.1(@babel/core@7.29.7)': + '@svgr/babel-preset@6.5.1(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.7 - '@svgr/babel-plugin-add-jsx-attribute': 6.5.1(@babel/core@7.29.7) - '@svgr/babel-plugin-remove-jsx-attribute': 8.0.0(@babel/core@7.29.7) - '@svgr/babel-plugin-remove-jsx-empty-expression': 8.0.0(@babel/core@7.29.7) - '@svgr/babel-plugin-replace-jsx-attribute-value': 6.5.1(@babel/core@7.29.7) - '@svgr/babel-plugin-svg-dynamic-title': 6.5.1(@babel/core@7.29.7) - '@svgr/babel-plugin-svg-em-dimensions': 6.5.1(@babel/core@7.29.7) - '@svgr/babel-plugin-transform-react-native-svg': 6.5.1(@babel/core@7.29.7) - '@svgr/babel-plugin-transform-svg-component': 6.5.1(@babel/core@7.29.7) + '@babel/core': 7.29.7(supports-color@10.2.2) + '@svgr/babel-plugin-add-jsx-attribute': 6.5.1(@babel/core@7.29.7(supports-color@10.2.2)) + '@svgr/babel-plugin-remove-jsx-attribute': 8.0.0(@babel/core@7.29.7(supports-color@10.2.2)) + '@svgr/babel-plugin-remove-jsx-empty-expression': 8.0.0(@babel/core@7.29.7(supports-color@10.2.2)) + '@svgr/babel-plugin-replace-jsx-attribute-value': 6.5.1(@babel/core@7.29.7(supports-color@10.2.2)) + '@svgr/babel-plugin-svg-dynamic-title': 6.5.1(@babel/core@7.29.7(supports-color@10.2.2)) + '@svgr/babel-plugin-svg-em-dimensions': 6.5.1(@babel/core@7.29.7(supports-color@10.2.2)) + '@svgr/babel-plugin-transform-react-native-svg': 6.5.1(@babel/core@7.29.7(supports-color@10.2.2)) + '@svgr/babel-plugin-transform-svg-component': 6.5.1(@babel/core@7.29.7(supports-color@10.2.2)) - '@svgr/babel-preset@8.1.0(@babel/core@7.29.7)': + '@svgr/babel-preset@8.1.0(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.7 - '@svgr/babel-plugin-add-jsx-attribute': 8.0.0(@babel/core@7.29.7) - '@svgr/babel-plugin-remove-jsx-attribute': 8.0.0(@babel/core@7.29.7) - '@svgr/babel-plugin-remove-jsx-empty-expression': 8.0.0(@babel/core@7.29.7) - '@svgr/babel-plugin-replace-jsx-attribute-value': 8.0.0(@babel/core@7.29.7) - '@svgr/babel-plugin-svg-dynamic-title': 8.0.0(@babel/core@7.29.7) - '@svgr/babel-plugin-svg-em-dimensions': 8.0.0(@babel/core@7.29.7) - '@svgr/babel-plugin-transform-react-native-svg': 8.1.0(@babel/core@7.29.7) - '@svgr/babel-plugin-transform-svg-component': 8.0.0(@babel/core@7.29.7) + '@babel/core': 7.29.7(supports-color@10.2.2) + '@svgr/babel-plugin-add-jsx-attribute': 8.0.0(@babel/core@7.29.7(supports-color@10.2.2)) + '@svgr/babel-plugin-remove-jsx-attribute': 8.0.0(@babel/core@7.29.7(supports-color@10.2.2)) + '@svgr/babel-plugin-remove-jsx-empty-expression': 8.0.0(@babel/core@7.29.7(supports-color@10.2.2)) + '@svgr/babel-plugin-replace-jsx-attribute-value': 8.0.0(@babel/core@7.29.7(supports-color@10.2.2)) + '@svgr/babel-plugin-svg-dynamic-title': 8.0.0(@babel/core@7.29.7(supports-color@10.2.2)) + '@svgr/babel-plugin-svg-em-dimensions': 8.0.0(@babel/core@7.29.7(supports-color@10.2.2)) + '@svgr/babel-plugin-transform-react-native-svg': 8.1.0(@babel/core@7.29.7(supports-color@10.2.2)) + '@svgr/babel-plugin-transform-svg-component': 8.0.0(@babel/core@7.29.7(supports-color@10.2.2)) - '@svgr/core@6.5.1': + '@svgr/core@6.5.1(supports-color@10.2.2)': dependencies: - '@babel/core': 7.29.7 - '@svgr/babel-preset': 6.5.1(@babel/core@7.29.7) - '@svgr/plugin-jsx': 6.5.1(@svgr/core@6.5.1) + '@babel/core': 7.29.7(supports-color@10.2.2) + '@svgr/babel-preset': 6.5.1(@babel/core@7.29.7(supports-color@10.2.2)) + '@svgr/plugin-jsx': 6.5.1(@svgr/core@6.5.1(supports-color@10.2.2))(supports-color@10.2.2) camelcase: 6.3.0 cosmiconfig: 7.1.0 transitivePeerDependencies: - supports-color - '@svgr/core@8.1.0(@typescript/typescript6@6.0.2)': + '@svgr/core@8.1.0(@typescript/typescript6@6.0.2)(supports-color@10.2.2)': dependencies: - '@babel/core': 7.29.7 - '@svgr/babel-preset': 8.1.0(@babel/core@7.29.7) + '@babel/core': 7.29.7(supports-color@10.2.2) + '@svgr/babel-preset': 8.1.0(@babel/core@7.29.7(supports-color@10.2.2)) camelcase: 6.3.0 cosmiconfig: 8.3.6(@typescript/typescript6@6.0.2) snake-case: 3.0.4 @@ -21977,43 +22774,43 @@ snapshots: '@babel/types': 7.29.7 entities: 4.5.0 - '@svgr/plugin-jsx@6.5.1(@svgr/core@6.5.1)': + '@svgr/plugin-jsx@6.5.1(@svgr/core@6.5.1(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: - '@babel/core': 7.29.7 - '@svgr/babel-preset': 6.5.1(@babel/core@7.29.7) - '@svgr/core': 6.5.1 + '@babel/core': 7.29.7(supports-color@10.2.2) + '@svgr/babel-preset': 6.5.1(@babel/core@7.29.7(supports-color@10.2.2)) + '@svgr/core': 6.5.1(supports-color@10.2.2) '@svgr/hast-util-to-babel-ast': 6.5.1 svg-parser: 2.0.4 transitivePeerDependencies: - supports-color - '@svgr/plugin-jsx@8.1.0(@svgr/core@8.1.0(@typescript/typescript6@6.0.2))': + '@svgr/plugin-jsx@8.1.0(@svgr/core@8.1.0(@typescript/typescript6@6.0.2)(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: - '@babel/core': 7.29.7 - '@svgr/babel-preset': 8.1.0(@babel/core@7.29.7) - '@svgr/core': 8.1.0(@typescript/typescript6@6.0.2) + '@babel/core': 7.29.7(supports-color@10.2.2) + '@svgr/babel-preset': 8.1.0(@babel/core@7.29.7(supports-color@10.2.2)) + '@svgr/core': 8.1.0(@typescript/typescript6@6.0.2)(supports-color@10.2.2) '@svgr/hast-util-to-babel-ast': 8.0.0 svg-parser: 2.0.4 transitivePeerDependencies: - supports-color - '@svgr/plugin-svgo@6.5.1(@svgr/core@6.5.1)': + '@svgr/plugin-svgo@6.5.1(@svgr/core@6.5.1(supports-color@10.2.2))': dependencies: - '@svgr/core': 6.5.1 + '@svgr/core': 6.5.1(supports-color@10.2.2) cosmiconfig: 7.1.0 deepmerge: 4.3.1 svgo: 2.8.2 - '@svgr/webpack@6.5.1': + '@svgr/webpack@6.5.1(supports-color@10.2.2)': dependencies: - '@babel/core': 7.29.7 - '@babel/plugin-transform-react-constant-elements': 7.29.7(@babel/core@7.29.7) - '@babel/preset-env': 7.29.7(@babel/core@7.29.7) - '@babel/preset-react': 7.29.7(@babel/core@7.29.7) - '@babel/preset-typescript': 7.29.7(@babel/core@7.29.7) - '@svgr/core': 6.5.1 - '@svgr/plugin-jsx': 6.5.1(@svgr/core@6.5.1) - '@svgr/plugin-svgo': 6.5.1(@svgr/core@6.5.1) + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/plugin-transform-react-constant-elements': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/preset-env': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/preset-react': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@babel/preset-typescript': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) + '@svgr/core': 6.5.1(supports-color@10.2.2) + '@svgr/plugin-jsx': 6.5.1(@svgr/core@6.5.1(supports-color@10.2.2))(supports-color@10.2.2) + '@svgr/plugin-svgo': 6.5.1(@svgr/core@6.5.1(supports-color@10.2.2)) transitivePeerDependencies: - supports-color @@ -22402,13 +23199,13 @@ snapshots: web-tree-sitter: 0.24.5 optional: true - '@swagger-api/apidom-reference@1.11.1': + '@swagger-api/apidom-reference@1.11.1(debug@4.4.3(supports-color@10.2.2))': dependencies: '@babel/runtime-corejs3': 7.29.7 '@swagger-api/apidom-core': 1.11.1 '@swagger-api/apidom-error': 1.11.1 '@types/ramda': 0.30.2 - axios: 1.12.0(debug@4.4.3) + axios: 1.12.0(debug@4.4.3(supports-color@10.2.2)) minimatch: 10.2.5 ramda: 0.30.1 ramda-adjunct: 5.1.0(ramda@0.30.1) @@ -22447,11 +23244,7 @@ snapshots: '@swaggerexpert/json-pointer@2.10.2': dependencies: - apg-lite: 1.0.5 - - '@swc/helpers@0.5.15': - dependencies: - tslib: 2.8.1 + apg-lite: 1.0.5 '@swc/helpers@0.5.23': dependencies: @@ -22468,10 +23261,10 @@ snapshots: '@tabler/icons@3.44.0': {} - '@tanstack/eslint-plugin-query@5.100.5(@typescript/typescript6@6.0.2)(eslint@9.39.2)': + '@tanstack/eslint-plugin-query@5.100.5(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: - '@typescript-eslint/utils': 8.60.0(@typescript/typescript6@6.0.2)(eslint@9.39.2) - eslint: 9.39.2 + '@typescript-eslint/utils': 8.60.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) + eslint: 9.39.2(supports-color@10.2.2) optionalDependencies: typescript: '@typescript/typescript6@6.0.2' transitivePeerDependencies: @@ -22521,9 +23314,9 @@ snapshots: '@thumbmarkjs/thumbmarkjs@0.16.0': {} - '@tokenizer/inflate@0.2.7': + '@tokenizer/inflate@0.2.7(supports-color@10.2.2)': dependencies: - debug: 4.4.3 + debug: 4.4.3(supports-color@10.2.2) fflate: 0.8.3 token-types: 6.1.2 transitivePeerDependencies: @@ -22582,7 +23375,7 @@ snapshots: '@turnkey/api-key-stamper': 0.4.7 '@turnkey/encoding': 0.5.0 - '@turnkey/sdk-browser@5.8.0(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76)': + '@turnkey/sdk-browser@5.8.0(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3)': dependencies: '@turnkey/api-key-stamper': 0.4.7 '@turnkey/crypto': 2.5.0 @@ -22591,7 +23384,7 @@ snapshots: '@turnkey/iframe-stamper': 2.5.0 '@turnkey/indexed-db-stamper': 1.1.1 '@turnkey/sdk-types': 0.3.0 - '@turnkey/wallet-stamper': 1.0.8(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) + '@turnkey/wallet-stamper': 1.0.8(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) '@turnkey/webauthn-stamper': 0.5.1 bs58check: 4.0.0 buffer: 6.0.3 @@ -22604,11 +23397,11 @@ snapshots: - utf-8-validate - zod - '@turnkey/sdk-server@4.7.0(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76)': + '@turnkey/sdk-server@4.7.0(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3)': dependencies: '@turnkey/api-key-stamper': 0.4.7 '@turnkey/http': 3.10.0 - '@turnkey/wallet-stamper': 1.0.8(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) + '@turnkey/wallet-stamper': 1.0.8(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) buffer: 6.0.3 cross-fetch: 3.2.0 transitivePeerDependencies: @@ -22620,16 +23413,16 @@ snapshots: '@turnkey/sdk-types@0.3.0': {} - '@turnkey/viem@0.13.0(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76))(zod@3.25.76)': + '@turnkey/viem@0.13.0(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3))(zod@4.4.3)': dependencies: '@noble/curves': 1.8.0 '@openzeppelin/contracts': 4.9.6 '@turnkey/api-key-stamper': 0.4.7 '@turnkey/http': 3.10.0 - '@turnkey/sdk-browser': 5.8.0(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) - '@turnkey/sdk-server': 4.7.0(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) + '@turnkey/sdk-browser': 5.8.0(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) + '@turnkey/sdk-server': 4.7.0(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) cross-fetch: 4.1.0 - viem: 2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) + viem: 2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) transitivePeerDependencies: - bufferutil - encoding @@ -22637,12 +23430,12 @@ snapshots: - utf-8-validate - zod - '@turnkey/wallet-stamper@1.0.8(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76)': + '@turnkey/wallet-stamper@1.0.8(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3)': dependencies: '@turnkey/crypto': 2.5.0 '@turnkey/encoding': 0.5.0 optionalDependencies: - viem: 2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) + viem: 2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) transitivePeerDependencies: - bufferutil - typescript @@ -22687,13 +23480,13 @@ snapshots: '@types/bunyan@1.8.11': dependencies: - '@types/node': 22.12.0 + '@types/node': 25.9.2 '@types/cacheable-request@6.0.3': dependencies: '@types/http-cache-semantics': 4.2.0 '@types/keyv': 3.1.4 - '@types/node': 22.12.0 + '@types/node': 25.9.2 '@types/responselike': 1.0.3 '@types/chai@5.2.3': @@ -22718,7 +23511,7 @@ snapshots: '@types/connect@3.4.38': dependencies: - '@types/node': 22.12.0 + '@types/node': 25.9.2 '@types/crypto-js@4.2.2': {} @@ -22863,7 +23656,7 @@ snapshots: '@types/dns-packet@5.6.5': dependencies: - '@types/node': 22.12.0 + '@types/node': 25.9.2 '@types/dom-to-image@2.6.7': {} @@ -22877,6 +23670,8 @@ snapshots: '@types/filewriter@0.0.33': {} + '@types/gensync@1.0.5': {} + '@types/geojson@7946.0.16': {} '@types/har-format@1.2.16': {} @@ -22899,19 +23694,21 @@ snapshots: '@types/js-cookie@3.0.6': {} + '@types/jsesc@2.5.1': {} + '@types/json-schema@7.0.15': {} '@types/json5@0.0.29': {} '@types/keyv@3.1.4': dependencies: - '@types/node': 22.12.0 + '@types/node': 25.9.2 '@types/lodash@4.17.24': {} '@types/memcached@2.2.10': dependencies: - '@types/node': 22.12.0 + '@types/node': 25.9.2 '@types/mixpanel-browser@2.66.0': dependencies: @@ -22922,11 +23719,11 @@ snapshots: '@types/multicast-dns@7.2.4': dependencies: '@types/dns-packet': 5.6.5 - '@types/node': 22.12.0 + '@types/node': 25.9.2 '@types/mysql@2.15.27': dependencies: - '@types/node': 22.12.0 + '@types/node': 25.9.2 '@types/node@12.20.55': {} @@ -22934,26 +23731,21 @@ snapshots: '@types/node@17.0.45': {} - '@types/node@20.16.7': - dependencies: - undici-types: 6.19.8 - - '@types/node@22.12.0': + '@types/node@24.13.4': dependencies: - undici-types: 6.20.0 + undici-types: 7.18.2 '@types/node@25.9.2': dependencies: undici-types: 7.24.6 - optional: true '@types/oracledb@6.5.2': dependencies: - '@types/node': 22.12.0 + '@types/node': 25.9.2 '@types/papaparse@5.5.2': dependencies: - '@types/node': 22.12.0 + '@types/node': 25.9.2 '@types/parse-json@4.0.2': {} @@ -22963,13 +23755,13 @@ snapshots: '@types/pg@8.15.6': dependencies: - '@types/node': 22.12.0 + '@types/node': 25.9.2 pg-protocol: 1.14.0 pg-types: 2.2.0 '@types/pg@8.6.1': dependencies: - '@types/node': 22.12.0 + '@types/node': 25.9.2 pg-protocol: 1.14.0 pg-types: 2.2.0 @@ -22981,7 +23773,7 @@ snapshots: '@types/qrcode@1.5.6': dependencies: - '@types/node': 22.12.0 + '@types/node': 25.9.2 '@types/ramda@0.30.2': dependencies: @@ -23014,7 +23806,7 @@ snapshots: '@types/responselike@1.0.3': dependencies: - '@types/node': 22.12.0 + '@types/node': 25.9.2 '@types/retry@0.12.2': {} @@ -23032,7 +23824,7 @@ snapshots: '@types/tedious@4.0.14': dependencies: - '@types/node': 22.12.0 + '@types/node': 25.9.2 '@types/tern@0.23.9': dependencies: @@ -23050,11 +23842,11 @@ snapshots: '@types/ws@7.4.7': dependencies: - '@types/node': 22.12.0 + '@types/node': 25.9.2 '@types/ws@8.18.1': dependencies: - '@types/node': 22.12.0 + '@types/node': 25.9.2 '@types/yargs-parser@21.0.3': {} @@ -23062,15 +23854,17 @@ snapshots: dependencies: '@types/yargs-parser': 21.0.3 - '@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@8.59.2(@typescript/typescript6@6.0.2)(eslint@9.39.2))(@typescript/typescript6@6.0.2)(eslint@9.39.2)': + '@typesafe-ai/sdk@0.6.0': {} + + '@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2))(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.59.2(@typescript/typescript6@6.0.2)(eslint@9.39.2) + '@typescript-eslint/parser': 5.62.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) '@typescript-eslint/scope-manager': 5.62.0 - '@typescript-eslint/type-utils': 5.62.0(@typescript/typescript6@6.0.2)(eslint@9.39.2) - '@typescript-eslint/utils': 5.62.0(@typescript/typescript6@6.0.2)(eslint@9.39.2) - debug: 4.4.3 - eslint: 9.39.2 + '@typescript-eslint/type-utils': 5.62.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) + '@typescript-eslint/utils': 5.62.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) + debug: 4.4.3(supports-color@10.2.2) + eslint: 9.39.2(supports-color@10.2.2) graphemer: 1.4.0 ignore: 5.3.2 natural-compare-lite: 1.4.0 @@ -23081,15 +23875,15 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/eslint-plugin@8.59.2(@typescript-eslint/parser@8.59.2(@typescript/typescript6@6.0.2)(eslint@9.39.2))(@typescript/typescript6@6.0.2)(eslint@9.39.2)': + '@typescript-eslint/eslint-plugin@8.59.2(@typescript-eslint/parser@8.59.2(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2))(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.59.2(@typescript/typescript6@6.0.2)(eslint@9.39.2) + '@typescript-eslint/parser': 8.59.2(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) '@typescript-eslint/scope-manager': 8.59.2 - '@typescript-eslint/type-utils': 8.59.2(@typescript/typescript6@6.0.2)(eslint@9.39.2) - '@typescript-eslint/utils': 8.59.2(@typescript/typescript6@6.0.2)(eslint@9.39.2) + '@typescript-eslint/type-utils': 8.59.2(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) + '@typescript-eslint/utils': 8.59.2(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) '@typescript-eslint/visitor-keys': 8.59.2 - eslint: 9.39.2 + eslint: 9.39.2(supports-color@10.2.2) ignore: 7.0.5 natural-compare: 1.4.0 ts-api-utils: 2.5.0(@typescript/typescript6@6.0.2) @@ -23097,32 +23891,53 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.59.2(@typescript/typescript6@6.0.2)(eslint@9.39.2)': + '@typescript-eslint/parser@5.62.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2)': + dependencies: + '@typescript-eslint/scope-manager': 5.62.0 + '@typescript-eslint/types': 5.62.0 + '@typescript-eslint/typescript-estree': 5.62.0(@typescript/typescript6@6.0.2)(supports-color@10.2.2) + debug: 4.4.3(supports-color@10.2.2) + eslint: 9.39.2(supports-color@10.2.2) + optionalDependencies: + typescript: '@typescript/typescript6@6.0.2' + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@8.59.2(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: '@typescript-eslint/scope-manager': 8.59.2 '@typescript-eslint/types': 8.59.2 - '@typescript-eslint/typescript-estree': 8.59.2(@typescript/typescript6@6.0.2) + '@typescript-eslint/typescript-estree': 8.59.2(@typescript/typescript6@6.0.2)(supports-color@10.2.2) '@typescript-eslint/visitor-keys': 8.59.2 - debug: 4.4.3 - eslint: 9.39.2 + debug: 4.4.3(supports-color@10.2.2) + eslint: 9.39.2(supports-color@10.2.2) typescript: '@typescript/typescript6@6.0.2' transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.59.2(@typescript/typescript6@6.0.2)': + '@typescript-eslint/project-service@8.59.2(@typescript/typescript6@6.0.2)(supports-color@10.2.2)': dependencies: '@typescript-eslint/tsconfig-utils': 8.59.2(@typescript/typescript6@6.0.2) '@typescript-eslint/types': 8.59.2 - debug: 4.4.3 + debug: 4.4.3(supports-color@10.2.2) typescript: '@typescript/typescript6@6.0.2' transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.60.0(@typescript/typescript6@6.0.2)': + '@typescript-eslint/project-service@8.60.0(@typescript/typescript6@6.0.2)(supports-color@10.2.2)': dependencies: '@typescript-eslint/tsconfig-utils': 8.60.0(@typescript/typescript6@6.0.2) '@typescript-eslint/types': 8.60.0 - debug: 4.4.3 + debug: 4.4.3(supports-color@10.2.2) + typescript: '@typescript/typescript6@6.0.2' + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/project-service@8.70.0(@typescript/typescript6@6.0.2)(supports-color@10.2.2)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.70.0(@typescript/typescript6@6.0.2) + '@typescript-eslint/types': 8.70.0 + debug: 4.4.3(supports-color@10.2.2) typescript: '@typescript/typescript6@6.0.2' transitivePeerDependencies: - supports-color @@ -23147,6 +23962,11 @@ snapshots: '@typescript-eslint/types': 8.60.0 '@typescript-eslint/visitor-keys': 8.60.0 + '@typescript-eslint/scope-manager@8.70.0': + dependencies: + '@typescript-eslint/types': 8.70.0 + '@typescript-eslint/visitor-keys': 8.70.0 + '@typescript-eslint/tsconfig-utils@8.59.2(@typescript/typescript6@6.0.2)': dependencies: typescript: '@typescript/typescript6@6.0.2' @@ -23155,25 +23975,41 @@ snapshots: dependencies: typescript: '@typescript/typescript6@6.0.2' - '@typescript-eslint/type-utils@5.62.0(@typescript/typescript6@6.0.2)(eslint@9.39.2)': + '@typescript-eslint/tsconfig-utils@8.70.0(@typescript/typescript6@6.0.2)': + dependencies: + typescript: '@typescript/typescript6@6.0.2' + + '@typescript-eslint/type-utils@5.62.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: - '@typescript-eslint/typescript-estree': 5.62.0(@typescript/typescript6@6.0.2) - '@typescript-eslint/utils': 5.62.0(@typescript/typescript6@6.0.2)(eslint@9.39.2) - debug: 4.4.3 - eslint: 9.39.2 + '@typescript-eslint/typescript-estree': 5.62.0(@typescript/typescript6@6.0.2)(supports-color@10.2.2) + '@typescript-eslint/utils': 5.62.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) + debug: 4.4.3(supports-color@10.2.2) + eslint: 9.39.2(supports-color@10.2.2) tsutils: 3.21.0(@typescript/typescript6@6.0.2) optionalDependencies: typescript: '@typescript/typescript6@6.0.2' transitivePeerDependencies: - supports-color - '@typescript-eslint/type-utils@8.59.2(@typescript/typescript6@6.0.2)(eslint@9.39.2)': + '@typescript-eslint/type-utils@8.59.2(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: '@typescript-eslint/types': 8.59.2 - '@typescript-eslint/typescript-estree': 8.59.2(@typescript/typescript6@6.0.2) - '@typescript-eslint/utils': 8.59.2(@typescript/typescript6@6.0.2)(eslint@9.39.2) - debug: 4.4.3 - eslint: 9.39.2 + '@typescript-eslint/typescript-estree': 8.59.2(@typescript/typescript6@6.0.2)(supports-color@10.2.2) + '@typescript-eslint/utils': 8.59.2(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) + debug: 4.4.3(supports-color@10.2.2) + eslint: 9.39.2(supports-color@10.2.2) + ts-api-utils: 2.5.0(@typescript/typescript6@6.0.2) + typescript: '@typescript/typescript6@6.0.2' + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/type-utils@8.70.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2)': + dependencies: + '@typescript-eslint/types': 8.70.0 + '@typescript-eslint/typescript-estree': 8.70.0(@typescript/typescript6@6.0.2)(supports-color@10.2.2) + '@typescript-eslint/utils': 8.70.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) + debug: 4.4.3(supports-color@10.2.2) + eslint: 9.39.2(supports-color@10.2.2) ts-api-utils: 2.5.0(@typescript/typescript6@6.0.2) typescript: '@typescript/typescript6@6.0.2' transitivePeerDependencies: @@ -23187,123 +24023,137 @@ snapshots: '@typescript-eslint/types@8.60.0': {} - '@typescript-eslint/typescript-estree@5.62.0(@typescript/typescript6@6.0.2)': + '@typescript-eslint/types@8.70.0': {} + + '@typescript-eslint/typescript-estree@5.62.0(@typescript/typescript6@6.0.2)(supports-color@10.2.2)': dependencies: '@typescript-eslint/types': 5.62.0 '@typescript-eslint/visitor-keys': 5.62.0 - debug: 4.4.3 + debug: 4.4.3(supports-color@10.2.2) globby: 11.1.0 is-glob: 4.0.3 - semver: 7.8.1 + semver: 7.8.5 tsutils: 3.21.0(@typescript/typescript6@6.0.2) optionalDependencies: typescript: '@typescript/typescript6@6.0.2' transitivePeerDependencies: - supports-color - '@typescript-eslint/typescript-estree@5.62.0(typescript@5.9.3)': - dependencies: - '@typescript-eslint/types': 5.62.0 - '@typescript-eslint/visitor-keys': 5.62.0 - debug: 4.4.3 - globby: 11.1.0 - is-glob: 4.0.3 - semver: 7.8.1 - tsutils: 3.21.0(typescript@5.9.3) - optionalDependencies: - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/typescript-estree@7.18.0(@typescript/typescript6@6.0.2)': + '@typescript-eslint/typescript-estree@7.18.0(@typescript/typescript6@6.0.2)(supports-color@10.2.2)': dependencies: '@typescript-eslint/types': 7.18.0 '@typescript-eslint/visitor-keys': 7.18.0 - debug: 4.4.3 + debug: 4.4.3(supports-color@10.2.2) globby: 11.1.0 is-glob: 4.0.3 minimatch: 9.0.9 - semver: 7.8.1 + semver: 7.8.5 ts-api-utils: 1.4.3(@typescript/typescript6@6.0.2) optionalDependencies: typescript: '@typescript/typescript6@6.0.2' transitivePeerDependencies: - supports-color - '@typescript-eslint/typescript-estree@8.59.2(@typescript/typescript6@6.0.2)': + '@typescript-eslint/typescript-estree@8.59.2(@typescript/typescript6@6.0.2)(supports-color@10.2.2)': dependencies: - '@typescript-eslint/project-service': 8.59.2(@typescript/typescript6@6.0.2) + '@typescript-eslint/project-service': 8.59.2(@typescript/typescript6@6.0.2)(supports-color@10.2.2) '@typescript-eslint/tsconfig-utils': 8.59.2(@typescript/typescript6@6.0.2) '@typescript-eslint/types': 8.59.2 '@typescript-eslint/visitor-keys': 8.59.2 - debug: 4.4.3 + debug: 4.4.3(supports-color@10.2.2) minimatch: 10.2.5 - semver: 7.8.1 + semver: 7.8.5 tinyglobby: 0.2.16 ts-api-utils: 2.5.0(@typescript/typescript6@6.0.2) typescript: '@typescript/typescript6@6.0.2' transitivePeerDependencies: - supports-color - '@typescript-eslint/typescript-estree@8.60.0(@typescript/typescript6@6.0.2)': + '@typescript-eslint/typescript-estree@8.60.0(@typescript/typescript6@6.0.2)(supports-color@10.2.2)': dependencies: - '@typescript-eslint/project-service': 8.60.0(@typescript/typescript6@6.0.2) + '@typescript-eslint/project-service': 8.60.0(@typescript/typescript6@6.0.2)(supports-color@10.2.2) '@typescript-eslint/tsconfig-utils': 8.60.0(@typescript/typescript6@6.0.2) '@typescript-eslint/types': 8.60.0 '@typescript-eslint/visitor-keys': 8.60.0 - debug: 4.4.3 + debug: 4.4.3(supports-color@10.2.2) minimatch: 10.2.5 - semver: 7.8.1 + semver: 7.8.5 tinyglobby: 0.2.16 ts-api-utils: 2.5.0(@typescript/typescript6@6.0.2) typescript: '@typescript/typescript6@6.0.2' transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@5.62.0(@typescript/typescript6@6.0.2)(eslint@9.39.2)': + '@typescript-eslint/typescript-estree@8.70.0(@typescript/typescript6@6.0.2)(supports-color@10.2.2)': + dependencies: + '@typescript-eslint/project-service': 8.70.0(@typescript/typescript6@6.0.2)(supports-color@10.2.2) + '@typescript-eslint/tsconfig-utils': 8.70.0(@typescript/typescript6@6.0.2) + '@typescript-eslint/types': 8.70.0 + '@typescript-eslint/visitor-keys': 8.70.0 + debug: 4.4.3(supports-color@10.2.2) + minimatch: 10.2.5 + semver: 7.8.5 + tinyglobby: 0.2.17 + ts-api-utils: 2.5.0(@typescript/typescript6@6.0.2) + typescript: '@typescript/typescript6@6.0.2' + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@5.62.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2) + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2(supports-color@10.2.2)) '@types/json-schema': 7.0.15 '@types/semver': 7.7.1 '@typescript-eslint/scope-manager': 5.62.0 '@typescript-eslint/types': 5.62.0 - '@typescript-eslint/typescript-estree': 5.62.0(@typescript/typescript6@6.0.2) - eslint: 9.39.2 + '@typescript-eslint/typescript-estree': 5.62.0(@typescript/typescript6@6.0.2)(supports-color@10.2.2) + eslint: 9.39.2(supports-color@10.2.2) eslint-scope: 5.1.1 - semver: 7.8.1 + semver: 7.8.5 transitivePeerDependencies: - supports-color - typescript - '@typescript-eslint/utils@7.18.0(@typescript/typescript6@6.0.2)(eslint@9.39.2)': + '@typescript-eslint/utils@7.18.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2) + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2(supports-color@10.2.2)) '@typescript-eslint/scope-manager': 7.18.0 '@typescript-eslint/types': 7.18.0 - '@typescript-eslint/typescript-estree': 7.18.0(@typescript/typescript6@6.0.2) - eslint: 9.39.2 + '@typescript-eslint/typescript-estree': 7.18.0(@typescript/typescript6@6.0.2)(supports-color@10.2.2) + eslint: 9.39.2(supports-color@10.2.2) transitivePeerDependencies: - supports-color - typescript - '@typescript-eslint/utils@8.59.2(@typescript/typescript6@6.0.2)(eslint@9.39.2)': + '@typescript-eslint/utils@8.59.2(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2) + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2(supports-color@10.2.2)) '@typescript-eslint/scope-manager': 8.59.2 '@typescript-eslint/types': 8.59.2 - '@typescript-eslint/typescript-estree': 8.59.2(@typescript/typescript6@6.0.2) - eslint: 9.39.2 + '@typescript-eslint/typescript-estree': 8.59.2(@typescript/typescript6@6.0.2)(supports-color@10.2.2) + eslint: 9.39.2(supports-color@10.2.2) typescript: '@typescript/typescript6@6.0.2' transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.60.0(@typescript/typescript6@6.0.2)(eslint@9.39.2)': + '@typescript-eslint/utils@8.60.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2) + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2(supports-color@10.2.2)) '@typescript-eslint/scope-manager': 8.60.0 '@typescript-eslint/types': 8.60.0 - '@typescript-eslint/typescript-estree': 8.60.0(@typescript/typescript6@6.0.2) - eslint: 9.39.2 + '@typescript-eslint/typescript-estree': 8.60.0(@typescript/typescript6@6.0.2)(supports-color@10.2.2) + eslint: 9.39.2(supports-color@10.2.2) + typescript: '@typescript/typescript6@6.0.2' + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@8.70.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2)': + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2(supports-color@10.2.2)) + '@typescript-eslint/scope-manager': 8.70.0 + '@typescript-eslint/types': 8.70.0 + '@typescript-eslint/typescript-estree': 8.70.0(@typescript/typescript6@6.0.2)(supports-color@10.2.2) + eslint: 9.39.2(supports-color@10.2.2) typescript: '@typescript/typescript6@6.0.2' transitivePeerDependencies: - supports-color @@ -23328,6 +24178,11 @@ snapshots: '@typescript-eslint/types': 8.60.0 eslint-visitor-keys: 5.0.1 + '@typescript-eslint/visitor-keys@8.70.0': + dependencies: + '@typescript-eslint/types': 8.70.0 + eslint-visitor-keys: 5.0.1 + '@typescript/typescript-aix-ppc64@7.0.2': optional: true @@ -23407,7 +24262,7 @@ snapshots: '@codemirror/state': 6.6.0 '@codemirror/view': 6.43.0 - '@uiw/react-codemirror@4.25.10(@babel/runtime@7.29.7)(@codemirror/autocomplete@6.20.2)(@codemirror/language@6.0.0)(@codemirror/lint@6.9.6)(@codemirror/search@6.7.0)(@codemirror/state@6.6.0)(@codemirror/theme-one-dark@6.1.3)(@codemirror/view@6.43.0)(codemirror@5.65.21)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)': + '@uiw/react-codemirror@4.25.10(@babel/runtime@7.29.7)(@codemirror/autocomplete@6.20.2)(@codemirror/language@6.0.0)(@codemirror/lint@6.9.6)(@codemirror/search@6.7.0)(@codemirror/state@6.6.0)(@codemirror/theme-one-dark@6.1.3)(@codemirror/view@6.43.0)(codemirror@6.0.2)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)': dependencies: '@babel/runtime': 7.29.7 '@codemirror/commands': 6.10.3 @@ -23415,7 +24270,7 @@ snapshots: '@codemirror/theme-one-dark': 6.1.3 '@codemirror/view': 6.43.0 '@uiw/codemirror-extensions-basic-setup': 4.25.10(@codemirror/autocomplete@6.20.2)(@codemirror/commands@6.10.3)(@codemirror/language@6.0.0)(@codemirror/lint@6.9.6)(@codemirror/search@6.7.0)(@codemirror/state@6.6.0)(@codemirror/view@6.43.0) - codemirror: 5.65.21 + codemirror: 6.0.2 react: 19.1.4 react-dom: 19.1.4(react@19.1.4) transitivePeerDependencies: @@ -23496,57 +24351,45 @@ snapshots: '@visulima/boxen@2.0.10': {} - '@vitejs/plugin-react@4.3.4(vite@6.4.3(@types/node@20.16.7)(terser@5.48.0)(yaml@2.9.0))': + '@vitejs/plugin-react@4.3.4(supports-color@10.2.2)(vite@6.4.3(@types/node@24.13.4)(terser@5.48.0)(yaml@2.9.0))': dependencies: - '@babel/core': 7.29.7 - '@babel/plugin-transform-react-jsx-self': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-react-jsx-source': 7.29.7(@babel/core@7.29.7) + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/plugin-transform-react-jsx-self': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-react-jsx-source': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) '@types/babel__core': 7.20.5 react-refresh: 0.14.2 - vite: 6.4.3(@types/node@20.16.7)(terser@5.48.0)(yaml@2.9.0) + vite: 6.4.3(@types/node@24.13.4)(terser@5.48.0)(yaml@2.9.0) transitivePeerDependencies: - supports-color - '@vitest/expect@4.1.7': - dependencies: - '@standard-schema/spec': 1.1.0 - '@types/chai': 5.2.3 - '@vitest/spy': 4.1.7 - '@vitest/utils': 4.1.7 - chai: 6.2.2 - tinyrainbow: 3.1.0 - - '@vitest/mocker@4.1.7(vite@6.4.3(@types/node@20.16.7)(terser@5.48.0)(yaml@2.9.0))': + '@vitest/coverage-v8@5.0.0(vitest@5.0.0)': dependencies: - '@vitest/spy': 4.1.7 - estree-walker: 3.0.3 - magic-string: 0.30.21 - optionalDependencies: - vite: 6.4.3(@types/node@20.16.7)(terser@5.48.0)(yaml@2.9.0) + '@bcoe/v8-coverage': 1.0.2 + '@vitest/istanbul-lib-coverage': 1.0.1 + '@vitest/istanbul-lib-report': 1.0.1 + ast-v8-to-istanbul: 1.0.5 + magicast: 0.5.4 + obug: 2.2.1 + std-env: 4.2.0 + tinyrainbow: 3.1.1 + vitest: 5.0.0(@opentelemetry/api@1.9.1)(@types/node@24.13.4)(@vitest/coverage-v8@5.0.0)(jsdom@27.3.0(bufferutil@4.1.0)(supports-color@10.2.2)(utf-8-validate@6.0.6))(vite@6.4.3(@types/node@24.13.4)(terser@5.48.0)(yaml@2.9.0)) - '@vitest/pretty-format@4.1.7': - dependencies: - tinyrainbow: 3.1.0 + '@vitest/istanbul-lib-coverage@1.0.1': {} - '@vitest/runner@4.1.7': + '@vitest/istanbul-lib-report@1.0.1': dependencies: - '@vitest/utils': 4.1.7 - pathe: 2.0.3 + '@vitest/istanbul-lib-coverage': 1.0.1 - '@vitest/snapshot@4.1.7': + '@vitest/mocker@5.0.0(vite@6.4.3(@types/node@24.13.4)(terser@5.48.0)(yaml@2.9.0))': dependencies: - '@vitest/pretty-format': 4.1.7 - '@vitest/utils': 4.1.7 - magic-string: 0.30.21 - pathe: 2.0.3 - - '@vitest/spy@4.1.7': {} + '@jridgewell/trace-mapping': 0.3.31 + '@vitest/spy': 5.0.0 + estree-walker: 3.0.3 + magic-string: 1.3.1 + optionalDependencies: + vite: 6.4.3(@types/node@24.13.4)(terser@5.48.0)(yaml@2.9.0) - '@vitest/utils@4.1.7': - dependencies: - '@vitest/pretty-format': 4.1.7 - convert-source-map: 2.0.0 - tinyrainbow: 3.1.0 + '@vitest/spy@5.0.0': {} '@volar/language-core@2.4.28': dependencies: @@ -23597,19 +24440,19 @@ snapshots: '@vue/shared@3.5.35': {} - '@wagmi/connectors@6.2.0(09e58c13ebbf9932f0a56e13a5b340ff)': - dependencies: - '@base-org/account': 2.4.0(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.1.4)(use-sync-external-store@1.4.0(react@19.1.4))(utf-8-validate@6.0.6)(zod@3.25.76) - '@coinbase/wallet-sdk': 4.3.6(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(use-sync-external-store@1.4.0(react@19.1.4))(utf-8-validate@6.0.6)(zod@3.25.76) - '@gemini-wallet/core': 0.3.2(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76)) - '@metamask/sdk': 0.33.1(bufferutil@4.1.0)(utf-8-validate@6.0.6) - '@safe-global/safe-apps-provider': 0.18.6(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) - '@safe-global/safe-apps-sdk': 9.1.0(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) - '@wagmi/core': 2.22.1(@tanstack/query-core@5.55.4)(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(react@19.1.4)(use-sync-external-store@1.4.0(react@19.1.4))(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76)) - '@walletconnect/ethereum-provider': 2.21.1(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@3.25.76) - cbw-sdk: '@coinbase/wallet-sdk@3.9.3' - porto: 0.2.35(71352da693451397413658bdd9d79753) - viem: 2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) + '@wagmi/connectors@6.2.0(df3f0d3afef4414b7f4264d6db7d6a30)': + dependencies: + '@base-org/account': 2.4.0(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(debug@4.4.3(supports-color@10.2.2))(fastestsmallesttextencoderdecoder@1.0.22)(react@19.1.4)(use-sync-external-store@1.4.0(react@19.1.4))(utf-8-validate@6.0.6)(zod@4.4.3) + '@coinbase/wallet-sdk': 4.3.6(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(use-sync-external-store@1.4.0(react@19.1.4))(utf-8-validate@6.0.6)(zod@4.4.3) + '@gemini-wallet/core': 0.3.2(supports-color@10.2.2)(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3)) + '@metamask/sdk': 0.33.1(bufferutil@4.1.0)(supports-color@10.2.2)(utf-8-validate@6.0.6) + '@safe-global/safe-apps-provider': 0.18.6(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) + '@safe-global/safe-apps-sdk': 9.1.0(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) + '@wagmi/core': 2.22.1(@tanstack/query-core@5.55.4)(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(react@19.1.4)(use-sync-external-store@1.4.0(react@19.1.4))(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3)) + '@walletconnect/ethereum-provider': 2.21.1(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@4.4.3) + cbw-sdk: '@coinbase/wallet-sdk@3.9.3(supports-color@10.2.2)' + porto: 0.2.35(8095bfa4b9e61ff0a46bb10857184fb4) + viem: 2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) optionalDependencies: typescript: '@typescript/typescript6@6.0.2' transitivePeerDependencies: @@ -23650,11 +24493,11 @@ snapshots: - wagmi - zod - '@wagmi/core@2.22.1(@tanstack/query-core@5.55.4)(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(react@19.1.4)(use-sync-external-store@1.4.0(react@19.1.4))(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76))': + '@wagmi/core@2.22.1(@tanstack/query-core@5.55.4)(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(react@19.1.4)(use-sync-external-store@1.4.0(react@19.1.4))(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3))': dependencies: eventemitter3: 5.0.1 mipd: 0.0.7(@typescript/typescript6@6.0.2) - viem: 2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) + viem: 2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) zustand: 5.0.0(@types/react@19.1.7)(react@19.1.4)(use-sync-external-store@1.4.0(react@19.1.4)) optionalDependencies: '@tanstack/query-core': 5.55.4 @@ -23692,7 +24535,7 @@ snapshots: dependencies: '@wallet-standard/base': 1.1.0 - '@walletconnect/core@2.21.0(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76)': + '@walletconnect/core@2.21.0(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3)': dependencies: '@walletconnect/heartbeat': 1.2.2 '@walletconnect/jsonrpc-provider': 1.0.14 @@ -23706,7 +24549,7 @@ snapshots: '@walletconnect/safe-json': 1.0.2 '@walletconnect/time': 1.0.2 '@walletconnect/types': 2.21.0 - '@walletconnect/utils': 2.21.0(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) + '@walletconnect/utils': 2.21.0(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) '@walletconnect/window-getters': 1.0.1 es-toolkit: 1.33.0 events: 3.3.0 @@ -23736,7 +24579,7 @@ snapshots: - utf-8-validate - zod - '@walletconnect/core@2.21.1(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76)': + '@walletconnect/core@2.21.1(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3)': dependencies: '@walletconnect/heartbeat': 1.2.2 '@walletconnect/jsonrpc-provider': 1.0.14 @@ -23750,7 +24593,7 @@ snapshots: '@walletconnect/safe-json': 1.0.2 '@walletconnect/time': 1.0.2 '@walletconnect/types': 2.21.1 - '@walletconnect/utils': 2.21.1(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) + '@walletconnect/utils': 2.21.1(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) '@walletconnect/window-getters': 1.0.1 es-toolkit: 1.33.0 events: 3.3.0 @@ -23780,7 +24623,7 @@ snapshots: - utf-8-validate - zod - '@walletconnect/core@2.21.7(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76)': + '@walletconnect/core@2.21.7(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3)': dependencies: '@walletconnect/heartbeat': 1.2.2 '@walletconnect/jsonrpc-provider': 1.0.14 @@ -23794,7 +24637,7 @@ snapshots: '@walletconnect/safe-json': 1.0.2 '@walletconnect/time': 1.0.2 '@walletconnect/types': 2.21.7 - '@walletconnect/utils': 2.21.7(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) + '@walletconnect/utils': 2.21.7(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) '@walletconnect/window-getters': 1.0.1 es-toolkit: 1.39.3 events: 3.3.0 @@ -23824,7 +24667,7 @@ snapshots: - utf-8-validate - zod - '@walletconnect/core@2.22.4(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76)': + '@walletconnect/core@2.22.4(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3)': dependencies: '@walletconnect/heartbeat': 1.2.2 '@walletconnect/jsonrpc-provider': 1.0.14 @@ -23838,7 +24681,7 @@ snapshots: '@walletconnect/safe-json': 1.0.2 '@walletconnect/time': 1.0.2 '@walletconnect/types': 2.22.4 - '@walletconnect/utils': 2.22.4(@typescript/typescript6@6.0.2)(zod@3.25.76) + '@walletconnect/utils': 2.22.4(@typescript/typescript6@6.0.2)(zod@4.4.3) '@walletconnect/window-getters': 1.0.1 es-toolkit: 1.39.3 events: 3.3.0 @@ -23868,7 +24711,7 @@ snapshots: - utf-8-validate - zod - '@walletconnect/core@2.23.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76)': + '@walletconnect/core@2.23.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3)': dependencies: '@walletconnect/heartbeat': 1.2.2 '@walletconnect/jsonrpc-provider': 1.0.14 @@ -23882,7 +24725,7 @@ snapshots: '@walletconnect/safe-json': 1.0.2 '@walletconnect/time': 1.0.2 '@walletconnect/types': 2.23.2 - '@walletconnect/utils': 2.23.2(@typescript/typescript6@6.0.2)(zod@3.25.76) + '@walletconnect/utils': 2.23.2(@typescript/typescript6@6.0.2)(zod@4.4.3) '@walletconnect/window-getters': 1.0.1 es-toolkit: 1.39.3 events: 3.3.0 @@ -23916,18 +24759,18 @@ snapshots: dependencies: tslib: 1.14.1 - '@walletconnect/ethereum-provider@2.21.1(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@3.25.76)': + '@walletconnect/ethereum-provider@2.21.1(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@4.4.3)': dependencies: - '@reown/appkit': 1.7.8(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@3.25.76) + '@reown/appkit': 1.7.8(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@4.4.3) '@walletconnect/jsonrpc-http-connection': 1.0.8 '@walletconnect/jsonrpc-provider': 1.0.14 '@walletconnect/jsonrpc-types': 1.0.4 '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/keyvaluestorage': 1.1.1 - '@walletconnect/sign-client': 2.21.1(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) + '@walletconnect/sign-client': 2.21.1(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) '@walletconnect/types': 2.21.1 - '@walletconnect/universal-provider': 2.21.1(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) - '@walletconnect/utils': 2.21.1(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) + '@walletconnect/universal-provider': 2.21.1(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) + '@walletconnect/utils': 2.21.1(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) events: 3.3.0 transitivePeerDependencies: - '@azure/app-configuration' @@ -23957,19 +24800,19 @@ snapshots: - utf-8-validate - zod - '@walletconnect/ethereum-provider@2.23.2(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@3.25.76)': + '@walletconnect/ethereum-provider@2.23.2(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@4.4.3)': dependencies: - '@reown/appkit': 1.8.11(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@3.25.76) + '@reown/appkit': 1.8.11(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)(zod@4.4.3) '@walletconnect/jsonrpc-http-connection': 1.0.8 '@walletconnect/jsonrpc-provider': 1.0.14 '@walletconnect/jsonrpc-types': 1.0.4 '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/keyvaluestorage': 1.1.1 '@walletconnect/logger': 3.0.2 - '@walletconnect/sign-client': 2.23.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) + '@walletconnect/sign-client': 2.23.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) '@walletconnect/types': 2.23.2 - '@walletconnect/universal-provider': 2.23.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) - '@walletconnect/utils': 2.23.2(@typescript/typescript6@6.0.2)(zod@3.25.76) + '@walletconnect/universal-provider': 2.23.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) + '@walletconnect/utils': 2.23.2(@typescript/typescript6@6.0.2)(zod@4.4.3) events: 3.3.0 transitivePeerDependencies: - '@azure/app-configuration' @@ -24102,16 +24945,16 @@ snapshots: dependencies: tslib: 1.14.1 - '@walletconnect/sign-client@2.21.0(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76)': + '@walletconnect/sign-client@2.21.0(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3)': dependencies: - '@walletconnect/core': 2.21.0(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) + '@walletconnect/core': 2.21.0(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) '@walletconnect/events': 1.0.1 '@walletconnect/heartbeat': 1.2.2 '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/logger': 2.1.2 '@walletconnect/time': 1.0.2 '@walletconnect/types': 2.21.0 - '@walletconnect/utils': 2.21.0(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) + '@walletconnect/utils': 2.21.0(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) events: 3.3.0 transitivePeerDependencies: - '@azure/app-configuration' @@ -24138,16 +24981,16 @@ snapshots: - utf-8-validate - zod - '@walletconnect/sign-client@2.21.1(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76)': + '@walletconnect/sign-client@2.21.1(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3)': dependencies: - '@walletconnect/core': 2.21.1(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) + '@walletconnect/core': 2.21.1(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) '@walletconnect/events': 1.0.1 '@walletconnect/heartbeat': 1.2.2 '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/logger': 2.1.2 '@walletconnect/time': 1.0.2 '@walletconnect/types': 2.21.1 - '@walletconnect/utils': 2.21.1(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) + '@walletconnect/utils': 2.21.1(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) events: 3.3.0 transitivePeerDependencies: - '@azure/app-configuration' @@ -24174,16 +25017,16 @@ snapshots: - utf-8-validate - zod - '@walletconnect/sign-client@2.21.7(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76)': + '@walletconnect/sign-client@2.21.7(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3)': dependencies: - '@walletconnect/core': 2.21.7(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) + '@walletconnect/core': 2.21.7(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) '@walletconnect/events': 1.0.1 '@walletconnect/heartbeat': 1.2.2 '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/logger': 2.1.2 '@walletconnect/time': 1.0.2 '@walletconnect/types': 2.21.7 - '@walletconnect/utils': 2.21.7(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) + '@walletconnect/utils': 2.21.7(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) events: 3.3.0 transitivePeerDependencies: - '@azure/app-configuration' @@ -24210,16 +25053,16 @@ snapshots: - utf-8-validate - zod - '@walletconnect/sign-client@2.22.4(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76)': + '@walletconnect/sign-client@2.22.4(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3)': dependencies: - '@walletconnect/core': 2.22.4(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) + '@walletconnect/core': 2.22.4(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) '@walletconnect/events': 1.0.1 '@walletconnect/heartbeat': 1.2.2 '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/logger': 3.0.0 '@walletconnect/time': 1.0.2 '@walletconnect/types': 2.22.4 - '@walletconnect/utils': 2.22.4(@typescript/typescript6@6.0.2)(zod@3.25.76) + '@walletconnect/utils': 2.22.4(@typescript/typescript6@6.0.2)(zod@4.4.3) events: 3.3.0 transitivePeerDependencies: - '@azure/app-configuration' @@ -24246,16 +25089,16 @@ snapshots: - utf-8-validate - zod - '@walletconnect/sign-client@2.23.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76)': + '@walletconnect/sign-client@2.23.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3)': dependencies: - '@walletconnect/core': 2.23.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) + '@walletconnect/core': 2.23.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) '@walletconnect/events': 1.0.1 '@walletconnect/heartbeat': 1.2.2 '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/logger': 3.0.2 '@walletconnect/time': 1.0.2 '@walletconnect/types': 2.23.2 - '@walletconnect/utils': 2.23.2(@typescript/typescript6@6.0.2)(zod@3.25.76) + '@walletconnect/utils': 2.23.2(@typescript/typescript6@6.0.2)(zod@4.4.3) events: 3.3.0 transitivePeerDependencies: - '@azure/app-configuration' @@ -24431,7 +25274,7 @@ snapshots: - ioredis - uploadthing - '@walletconnect/universal-provider@2.21.0(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76)': + '@walletconnect/universal-provider@2.21.0(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3)': dependencies: '@walletconnect/events': 1.0.1 '@walletconnect/jsonrpc-http-connection': 1.0.8 @@ -24440,9 +25283,9 @@ snapshots: '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/keyvaluestorage': 1.1.1 '@walletconnect/logger': 2.1.2 - '@walletconnect/sign-client': 2.21.0(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) + '@walletconnect/sign-client': 2.21.0(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) '@walletconnect/types': 2.21.0 - '@walletconnect/utils': 2.21.0(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) + '@walletconnect/utils': 2.21.0(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) es-toolkit: 1.33.0 events: 3.3.0 transitivePeerDependencies: @@ -24471,7 +25314,7 @@ snapshots: - utf-8-validate - zod - '@walletconnect/universal-provider@2.21.1(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76)': + '@walletconnect/universal-provider@2.21.1(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3)': dependencies: '@walletconnect/events': 1.0.1 '@walletconnect/jsonrpc-http-connection': 1.0.8 @@ -24480,9 +25323,9 @@ snapshots: '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/keyvaluestorage': 1.1.1 '@walletconnect/logger': 2.1.2 - '@walletconnect/sign-client': 2.21.1(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) + '@walletconnect/sign-client': 2.21.1(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) '@walletconnect/types': 2.21.1 - '@walletconnect/utils': 2.21.1(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) + '@walletconnect/utils': 2.21.1(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) es-toolkit: 1.33.0 events: 3.3.0 transitivePeerDependencies: @@ -24511,7 +25354,7 @@ snapshots: - utf-8-validate - zod - '@walletconnect/universal-provider@2.21.7(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76)': + '@walletconnect/universal-provider@2.21.7(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3)': dependencies: '@walletconnect/events': 1.0.1 '@walletconnect/jsonrpc-http-connection': 1.0.8 @@ -24520,9 +25363,9 @@ snapshots: '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/keyvaluestorage': 1.1.1 '@walletconnect/logger': 2.1.2 - '@walletconnect/sign-client': 2.21.7(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) + '@walletconnect/sign-client': 2.21.7(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) '@walletconnect/types': 2.21.7 - '@walletconnect/utils': 2.21.7(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) + '@walletconnect/utils': 2.21.7(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) es-toolkit: 1.39.3 events: 3.3.0 transitivePeerDependencies: @@ -24551,7 +25394,7 @@ snapshots: - utf-8-validate - zod - '@walletconnect/universal-provider@2.22.4(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76)': + '@walletconnect/universal-provider@2.22.4(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3)': dependencies: '@walletconnect/events': 1.0.1 '@walletconnect/jsonrpc-http-connection': 1.0.8 @@ -24560,9 +25403,9 @@ snapshots: '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/keyvaluestorage': 1.1.1 '@walletconnect/logger': 3.0.0 - '@walletconnect/sign-client': 2.22.4(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) + '@walletconnect/sign-client': 2.22.4(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) '@walletconnect/types': 2.22.4 - '@walletconnect/utils': 2.22.4(@typescript/typescript6@6.0.2)(zod@3.25.76) + '@walletconnect/utils': 2.22.4(@typescript/typescript6@6.0.2)(zod@4.4.3) es-toolkit: 1.39.3 events: 3.3.0 transitivePeerDependencies: @@ -24591,7 +25434,7 @@ snapshots: - utf-8-validate - zod - '@walletconnect/universal-provider@2.23.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76)': + '@walletconnect/universal-provider@2.23.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3)': dependencies: '@walletconnect/events': 1.0.1 '@walletconnect/jsonrpc-http-connection': 1.0.8 @@ -24600,9 +25443,9 @@ snapshots: '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/keyvaluestorage': 1.1.1 '@walletconnect/logger': 3.0.2 - '@walletconnect/sign-client': 2.23.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) + '@walletconnect/sign-client': 2.23.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) '@walletconnect/types': 2.23.2 - '@walletconnect/utils': 2.23.2(@typescript/typescript6@6.0.2)(zod@3.25.76) + '@walletconnect/utils': 2.23.2(@typescript/typescript6@6.0.2)(zod@4.4.3) es-toolkit: 1.39.3 events: 3.3.0 transitivePeerDependencies: @@ -24631,7 +25474,7 @@ snapshots: - utf-8-validate - zod - '@walletconnect/utils@2.21.0(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76)': + '@walletconnect/utils@2.21.0(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3)': dependencies: '@noble/ciphers': 1.2.1 '@noble/curves': 1.8.1 @@ -24649,7 +25492,7 @@ snapshots: detect-browser: 5.3.0 query-string: 7.1.3 uint8arrays: 3.1.0 - viem: 2.23.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) + viem: 2.23.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -24675,7 +25518,7 @@ snapshots: - utf-8-validate - zod - '@walletconnect/utils@2.21.1(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76)': + '@walletconnect/utils@2.21.1(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3)': dependencies: '@noble/ciphers': 1.2.1 '@noble/curves': 1.8.1 @@ -24693,7 +25536,7 @@ snapshots: detect-browser: 5.3.0 query-string: 7.1.3 uint8arrays: 3.1.0 - viem: 2.23.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) + viem: 2.23.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -24719,7 +25562,7 @@ snapshots: - utf-8-validate - zod - '@walletconnect/utils@2.21.7(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76)': + '@walletconnect/utils@2.21.7(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3)': dependencies: '@msgpack/msgpack': 3.1.2 '@noble/ciphers': 1.3.0 @@ -24740,7 +25583,7 @@ snapshots: detect-browser: 5.3.0 query-string: 7.1.3 uint8arrays: 3.1.1 - viem: 2.31.0(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) + viem: 2.31.0(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -24766,7 +25609,7 @@ snapshots: - utf-8-validate - zod - '@walletconnect/utils@2.22.4(@typescript/typescript6@6.0.2)(zod@3.25.76)': + '@walletconnect/utils@2.22.4(@typescript/typescript6@6.0.2)(zod@4.4.3)': dependencies: '@msgpack/msgpack': 3.1.2 '@noble/ciphers': 1.3.0 @@ -24786,7 +25629,7 @@ snapshots: blakejs: 1.2.1 bs58: 6.0.0 detect-browser: 5.3.0 - ox: 0.9.3(@typescript/typescript6@6.0.2)(zod@3.25.76) + ox: 0.9.3(@typescript/typescript6@6.0.2)(zod@4.4.3) uint8arrays: 3.1.1 transitivePeerDependencies: - '@azure/app-configuration' @@ -24811,7 +25654,7 @@ snapshots: - uploadthing - zod - '@walletconnect/utils@2.23.2(@typescript/typescript6@6.0.2)(zod@3.25.76)': + '@walletconnect/utils@2.23.2(@typescript/typescript6@6.0.2)(zod@4.4.3)': dependencies: '@msgpack/msgpack': 3.1.2 '@noble/ciphers': 1.3.0 @@ -24831,7 +25674,7 @@ snapshots: blakejs: 1.2.1 bs58: 6.0.0 detect-browser: 5.3.0 - ox: 0.9.3(@typescript/typescript6@6.0.2)(zod@3.25.76) + ox: 0.9.3(@typescript/typescript6@6.0.2)(zod@4.4.3) uint8arrays: 3.1.1 transitivePeerDependencies: - '@azure/app-configuration' @@ -25514,51 +26357,51 @@ snapshots: '@zag-js/utils@1.41.2': {} - '@zerodev/ecdsa-validator@5.4.9(@zerodev/sdk@5.5.7(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76)))(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76))': + '@zerodev/ecdsa-validator@5.4.9(@zerodev/sdk@5.5.7(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3)))(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3))': dependencies: - '@zerodev/sdk': 5.5.7(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76)) - viem: 2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) + '@zerodev/sdk': 5.5.7(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3)) + viem: 2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) - '@zerodev/multi-chain-ecdsa-validator@5.4.5(@zerodev/sdk@5.5.7(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76)))(@zerodev/webauthn-key@5.5.0(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76)))(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76))': + '@zerodev/multi-chain-ecdsa-validator@5.4.5(@zerodev/sdk@5.5.7(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3)))(@zerodev/webauthn-key@5.5.0(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3)))(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3))': dependencies: '@simplewebauthn/browser': 9.0.1 '@simplewebauthn/typescript-types': 8.3.4 - '@zerodev/sdk': 5.5.7(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76)) - '@zerodev/webauthn-key': 5.5.0(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76)) + '@zerodev/sdk': 5.5.7(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3)) + '@zerodev/webauthn-key': 5.5.0(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3)) merkletreejs: 0.3.11 - viem: 2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) + viem: 2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) - '@zerodev/sdk@5.5.7(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76))': + '@zerodev/sdk@5.5.7(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3))': dependencies: - semver: 7.8.4 - viem: 2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) + semver: 7.8.5 + viem: 2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) - '@zerodev/webauthn-key@5.5.0(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76))': + '@zerodev/webauthn-key@5.5.0(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3))': dependencies: '@noble/curves': 1.9.7 '@simplewebauthn/browser': 8.3.7 '@simplewebauthn/types': 12.0.0 - viem: 2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) + viem: 2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) abitype@1.0.6(@typescript/typescript6@6.0.2)(zod@3.25.76): optionalDependencies: typescript: '@typescript/typescript6@6.0.2' zod: 3.25.76 - abitype@1.0.8(@typescript/typescript6@6.0.2)(zod@3.25.76): + abitype@1.0.8(@typescript/typescript6@6.0.2)(zod@4.4.3): optionalDependencies: typescript: '@typescript/typescript6@6.0.2' - zod: 3.25.76 + zod: 4.4.3 abitype@1.1.0(@typescript/typescript6@6.0.2)(zod@3.22.4): optionalDependencies: typescript: '@typescript/typescript6@6.0.2' zod: 3.22.4 - abitype@1.1.0(@typescript/typescript6@6.0.2)(zod@3.25.76): + abitype@1.1.0(@typescript/typescript6@6.0.2)(zod@4.4.3): optionalDependencies: typescript: '@typescript/typescript6@6.0.2' - zod: 3.25.76 + zod: 4.4.3 abitype@1.1.0(typescript@7.0.2)(zod@4.4.3): optionalDependencies: @@ -25618,12 +26461,12 @@ snapshots: mime-types: 3.0.2 negotiator: 1.0.0 - acme-client@5.4.0: + acme-client@5.4.0(supports-color@10.2.2): dependencies: '@peculiar/x509': 1.14.3 asn1js: 3.0.10 - axios: 1.12.0(debug@4.4.3) - debug: 4.4.3 + axios: 1.12.0(debug@4.4.3(supports-color@10.2.2)) + debug: 4.4.3(supports-color@10.2.2) node-forge: 1.3.2 transitivePeerDependencies: - supports-color @@ -25702,6 +26545,8 @@ snapshots: alien-signals@0.4.14: {} + angular-html-parser@10.11.0: {} + anser@1.4.10: {} ansi-color@0.2.2: {} @@ -25731,8 +26576,6 @@ snapshots: apg-lite@1.0.5: {} - app-module-path@2.2.0: {} - arg@4.1.3: {} argparse@1.0.10: @@ -25832,14 +26675,18 @@ snapshots: assertion-error@2.0.1: {} - ast-module-types@5.0.0: {} - ast-types-flow@0.0.8: {} ast-types@0.16.1: dependencies: tslib: 2.8.1 + ast-v8-to-istanbul@1.0.5: + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + estree-walker: 3.0.3 + js-tokens: 10.0.0 + async-function@1.0.0: {} async-mutex@0.2.6: @@ -25866,14 +26713,14 @@ snapshots: axe-core@4.11.4: {} - axios-retry@4.5.0(axios@1.12.0): + axios-retry@4.5.0(axios@1.12.0(debug@4.4.3(supports-color@10.2.2))): dependencies: - axios: 1.12.0(debug@4.4.3) + axios: 1.12.0(debug@4.4.3(supports-color@10.2.2)) is-retry-allowed: 2.2.0 - axios@1.12.0(debug@4.4.3): + axios@1.12.0(debug@4.4.3(supports-color@10.2.2)): dependencies: - follow-redirects: 1.16.0(debug@4.4.3) + follow-redirects: 1.16.0(debug@4.4.3(supports-color@10.2.2)) form-data: 4.0.6 proxy-from-env: 1.1.0 transitivePeerDependencies: @@ -25887,27 +26734,27 @@ snapshots: cosmiconfig: 7.1.0 resolve: 1.22.12 - babel-plugin-polyfill-corejs2@0.4.17(@babel/core@7.29.7): + babel-plugin-polyfill-corejs2@0.4.17(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2): dependencies: '@babel/compat-data': 7.29.7 - '@babel/core': 7.29.7 - '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.29.7) + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) semver: 6.3.1 transitivePeerDependencies: - supports-color - babel-plugin-polyfill-corejs3@0.14.2(@babel/core@7.29.7): + babel-plugin-polyfill-corejs3@0.14.2(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2): dependencies: - '@babel/core': 7.29.7 - '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.29.7) + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) core-js-compat: 3.49.0 transitivePeerDependencies: - supports-color - babel-plugin-polyfill-regenerator@0.6.8(@babel/core@7.29.7): + babel-plugin-polyfill-regenerator@0.6.8(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2): dependencies: - '@babel/core': 7.29.7 - '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.29.7) + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) transitivePeerDependencies: - supports-color @@ -25917,6 +26764,8 @@ snapshots: balanced-match@1.0.2: {} + balanced-match@4.0.4: {} + base-x@3.0.11: dependencies: safe-buffer: 5.2.1 @@ -25951,6 +26800,8 @@ snapshots: bintrees@1.0.2: {} + birecord@0.1.2: {} + bl@4.1.0: dependencies: buffer: 5.7.1 @@ -26000,6 +26851,10 @@ snapshots: balanced-match: 1.0.2 concat-map: 0.0.1 + brace-expansion@5.0.9: + dependencies: + balanced-match: 4.0.4 + braces@3.0.3: dependencies: fill-range: 7.1.1 @@ -26148,6 +27003,8 @@ snapshots: character-reference-invalid@2.0.1: {} + chardet@2.2.0: {} + charenc@0.0.2: {} chokidar@4.0.3: @@ -26160,23 +27017,23 @@ snapshots: chownr@1.1.4: {} - chrome-launcher@0.15.2: + chrome-launcher@0.15.2(supports-color@10.2.2): dependencies: - '@types/node': 22.12.0 + '@types/node': 24.13.4 escape-string-regexp: 4.0.0 is-wsl: 2.2.0 - lighthouse-logger: 1.4.2 + lighthouse-logger: 1.4.2(supports-color@10.2.2) transitivePeerDependencies: - supports-color chrome-trace-event@1.0.4: {} - chromium-edge-launcher@0.3.0: + chromium-edge-launcher@0.3.0(supports-color@10.2.2): dependencies: - '@types/node': 22.12.0 + '@types/node': 24.13.4 escape-string-regexp: 4.0.0 is-wsl: 2.2.0 - lighthouse-logger: 1.4.2 + lighthouse-logger: 1.4.2(supports-color@10.2.2) mkdirp: 1.0.4 transitivePeerDependencies: - supports-color @@ -26207,6 +27064,8 @@ snapshots: slice-ansi: 8.0.0 string-width: 8.2.1 + cli-width@4.1.0: {} + client-only@0.0.1: {} cliui@6.0.0: @@ -26246,6 +27105,16 @@ snapshots: codemirror@5.65.21: {} + codemirror@6.0.2: + dependencies: + '@codemirror/autocomplete': 6.20.2 + '@codemirror/commands': 6.10.3 + '@codemirror/language': 6.12.3 + '@codemirror/lint': 6.9.6 + '@codemirror/search': 6.7.0 + '@codemirror/state': 6.6.0 + '@codemirror/view': 6.43.0 + color-convert@2.0.1: dependencies: color-name: 1.1.4 @@ -26272,8 +27141,6 @@ snapshots: comma-separated-tokens@2.0.3: {} - commander@10.0.1: {} - commander@12.1.0: {} commander@13.1.0: {} @@ -26301,10 +27168,10 @@ snapshots: confbox@0.2.4: {} - connect@3.7.0: + connect@3.7.0(supports-color@10.2.2): dependencies: - debug: 2.6.9 - finalhandler: 1.1.2 + debug: 2.6.9(supports-color@10.2.2) + finalhandler: 1.1.2(supports-color@10.2.2) parseurl: 1.3.3 utils-merge: 1.0.1 transitivePeerDependencies: @@ -26482,7 +27349,7 @@ snapshots: semver: 7.8.1 tinyglobby: 0.2.16 - css-loader@6.11.0(webpack@5.107.2(esbuild@0.25.12)(postcss@8.5.15)): + css-loader@6.11.0(webpack@5.107.2(csso@5.0.5)(esbuild@0.25.12)(postcss@8.5.15)(uglify-js@3.19.3)): dependencies: icss-utils: 5.1.0(postcss@8.5.15) postcss: 8.5.15 @@ -26493,7 +27360,7 @@ snapshots: postcss-value-parser: 4.2.0 semver: 7.8.1 optionalDependencies: - webpack: 5.107.2(esbuild@0.25.12)(postcss@8.5.15) + webpack: 5.107.2(csso@5.0.5)(esbuild@0.25.12)(postcss@8.5.15)(uglify-js@3.19.3) css-select@4.3.0: dependencies: @@ -26725,11 +27592,11 @@ snapshots: damerau-levenshtein@1.0.8: {} - dappscout-iframe@0.4.0(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76)): + dappscout-iframe@0.4.0(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3)): dependencies: react: 19.1.4 react-dom: 19.1.4(react@19.1.4) - viem: 2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) + viem: 2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) data-uri-to-buffer@4.0.1: {} @@ -26785,21 +27652,29 @@ snapshots: debounce@1.2.1: {} - debug@2.6.9: + debug@2.6.9(supports-color@10.2.2): dependencies: ms: 2.0.0 + optionalDependencies: + supports-color: 10.2.2 - debug@3.2.7: + debug@3.2.7(supports-color@10.2.2): dependencies: ms: 2.1.3 + optionalDependencies: + supports-color: 10.2.2 - debug@4.3.4: + debug@4.3.4(supports-color@10.2.2): dependencies: ms: 2.1.2 + optionalDependencies: + supports-color: 10.2.2 - debug@4.4.3: + debug@4.4.3(supports-color@10.2.2): dependencies: ms: 2.1.3 + optionalDependencies: + supports-color: 10.2.2 decamelize@1.2.0: {} @@ -26885,21 +27760,17 @@ snapshots: depd@2.0.0: {} - dependency-tree@10.0.9: - dependencies: - commander: 10.0.1 - filing-cabinet: 4.2.0 - precinct: 11.0.5 - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - dequal@2.0.3: {} derive-valtio@0.1.0(valtio@1.13.2(@types/react@19.1.7)(react@19.1.4)): dependencies: valtio: 1.13.2(@types/react@19.1.7)(react@19.1.4) + des.js@1.1.0: + dependencies: + inherits: 2.0.4 + minimalistic-assert: 1.0.1 + destr@2.0.5: {} destroy@1.2.0: {} @@ -26910,48 +27781,7 @@ snapshots: detect-node-es@1.1.0: {} - detective-amd@5.0.2: - dependencies: - ast-module-types: 5.0.0 - escodegen: 2.1.0 - get-amd-module-type: 5.0.1 - node-source-walk: 6.0.2 - - detective-cjs@5.0.1: - dependencies: - ast-module-types: 5.0.0 - node-source-walk: 6.0.2 - - detective-es6@4.0.1: - dependencies: - node-source-walk: 6.0.2 - - detective-postcss@6.1.3: - dependencies: - is-url: 1.2.4 - postcss: 8.5.15 - postcss-values-parser: 6.0.2(postcss@8.5.15) - - detective-sass@5.0.3: - dependencies: - gonzales-pe: 4.3.0 - node-source-walk: 6.0.2 - - detective-scss@4.0.3: - dependencies: - gonzales-pe: 4.3.0 - node-source-walk: 6.0.2 - - detective-stylus@4.0.0: {} - - detective-typescript@11.2.0: - dependencies: - '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.9.3) - ast-module-types: 5.0.0 - node-source-walk: 6.0.2 - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color + diff-match-patch@1.0.5: {} diff@4.0.4: {} @@ -26999,6 +27829,10 @@ snapshots: dependencies: domelementtype: 2.3.0 + dompurify@3.4.16: + optionalDependencies: + '@types/trusted-types': 2.0.7 + dompurify@3.4.9: optionalDependencies: '@types/trusted-types': 2.0.7 @@ -27093,6 +27927,8 @@ snapshots: emoji-regex@9.2.2: {} + empathic@2.0.1: {} + encode-utf8@1.0.3: {} encodeurl@1.0.2: {} @@ -27103,10 +27939,10 @@ snapshots: dependencies: once: 1.4.0 - engine.io-client@6.6.5(bufferutil@4.1.0)(utf-8-validate@6.0.6): + engine.io-client@6.6.5(bufferutil@4.1.0)(supports-color@10.2.2)(utf-8-validate@6.0.6): dependencies: '@socket.io/component-emitter': 3.1.2 - debug: 4.4.3 + debug: 4.4.3(supports-color@10.2.2) engine.io-parser: 5.2.3 ws: 8.21.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) xmlhttprequest-ssl: 2.1.2 @@ -27117,11 +27953,6 @@ snapshots: engine.io-parser@5.2.3: {} - enhanced-resolve@5.22.0: - dependencies: - graceful-fs: 4.2.11 - tapable: 2.3.3 - enhanced-resolve@5.23.0: dependencies: graceful-fs: 4.2.11 @@ -27250,7 +28081,7 @@ snapshots: iterator.prototype: 1.1.5 math-intrinsics: 1.1.0 - es-module-lexer@2.1.0: {} + es-module-lexer@2.3.2: {} es-object-atoms@1.1.2: dependencies: @@ -27349,26 +28180,18 @@ snapshots: escape-string-regexp@4.0.0: {} - escodegen@2.1.0: - dependencies: - esprima: 4.0.1 - estraverse: 5.3.0 - esutils: 2.0.3 - optionalDependencies: - source-map: 0.6.1 - - eslint-config-next@16.2.6(@typescript-eslint/parser@8.59.2(@typescript/typescript6@6.0.2)(eslint@9.39.2))(@typescript/typescript6@6.0.2)(eslint@9.39.2): + eslint-config-next@16.2.6(@typescript-eslint/parser@5.62.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2))(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2): dependencies: '@next/eslint-plugin-next': 16.2.6 - eslint: 9.39.2 - eslint-import-resolver-node: 0.3.10 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.2) - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.59.2(@typescript/typescript6@6.0.2)(eslint@9.39.2))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2) - eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.2) - eslint-plugin-react: 7.37.5(eslint@9.39.2) - eslint-plugin-react-hooks: 7.1.1(eslint@9.39.2) + eslint: 9.39.2(supports-color@10.2.2) + eslint-import-resolver-node: 0.3.10(supports-color@10.2.2) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@5.62.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) + eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.2(supports-color@10.2.2)) + eslint-plugin-react: 7.37.5(eslint@9.39.2(supports-color@10.2.2)) + eslint-plugin-react-hooks: 7.1.1(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) globals: 16.4.0 - typescript-eslint: 8.59.2(@typescript/typescript6@6.0.2)(eslint@9.39.2) + typescript-eslint: 8.59.2(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) optionalDependencies: typescript: '@typescript/typescript6@6.0.2' transitivePeerDependencies: @@ -27377,65 +28200,81 @@ snapshots: - eslint-plugin-import-x - supports-color - eslint-import-resolver-node@0.3.10: + eslint-import-resolver-node@0.3.10(supports-color@10.2.2): dependencies: - debug: 3.2.7 + debug: 3.2.7(supports-color@10.2.2) is-core-module: 2.16.2 resolve: 2.0.0-next.7 transitivePeerDependencies: - supports-color - eslint-import-resolver-node@0.3.9: + eslint-import-resolver-node@0.3.9(supports-color@10.2.2): dependencies: - debug: 3.2.7 + debug: 3.2.7(supports-color@10.2.2) is-core-module: 2.16.2 resolve: 1.22.12 transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.2): + eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2): + dependencies: + '@nolyfill/is-core-module': 1.0.39 + debug: 4.4.3(supports-color@10.2.2) + eslint: 9.39.2(supports-color@10.2.2) + get-tsconfig: 4.14.0 + is-bun-module: 2.0.0 + stable-hash: 0.0.5 + tinyglobby: 0.2.16 + unrs-resolver: 1.12.2 + optionalDependencies: + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@5.62.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) + transitivePeerDependencies: + - supports-color + + eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2): dependencies: '@nolyfill/is-core-module': 1.0.39 - debug: 4.4.3 - eslint: 9.39.2 + debug: 4.4.3(supports-color@10.2.2) + eslint: 9.39.2(supports-color@10.2.2) get-tsconfig: 4.14.0 is-bun-module: 2.0.0 stable-hash: 0.0.5 tinyglobby: 0.2.16 unrs-resolver: 1.12.2 optionalDependencies: - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.59.2(@typescript/typescript6@6.0.2)(eslint@9.39.2))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@5.62.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.1(@typescript-eslint/parser@8.59.2(@typescript/typescript6@6.0.2)(eslint@9.39.2))(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2): + eslint-module-utils@2.12.1(@typescript-eslint/parser@5.62.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2))(eslint-import-resolver-node@0.3.10(supports-color@10.2.2))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2): dependencies: - debug: 3.2.7 + debug: 3.2.7(supports-color@10.2.2) optionalDependencies: - '@typescript-eslint/parser': 8.59.2(@typescript/typescript6@6.0.2)(eslint@9.39.2) - eslint: 9.39.2 - eslint-import-resolver-node: 0.3.10 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.2) + '@typescript-eslint/parser': 5.62.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) + eslint: 9.39.2(supports-color@10.2.2) + eslint-import-resolver-node: 0.3.10(supports-color@10.2.2) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.1(@typescript-eslint/parser@8.59.2(@typescript/typescript6@6.0.2)(eslint@9.39.2))(eslint-import-resolver-node@0.3.9)(eslint@9.39.2): + eslint-module-utils@2.12.1(@typescript-eslint/parser@5.62.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2))(eslint-import-resolver-node@0.3.9(supports-color@10.2.2))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2): dependencies: - debug: 3.2.7 + debug: 3.2.7(supports-color@10.2.2) optionalDependencies: - '@typescript-eslint/parser': 8.59.2(@typescript/typescript6@6.0.2)(eslint@9.39.2) - eslint: 9.39.2 - eslint-import-resolver-node: 0.3.9 + '@typescript-eslint/parser': 5.62.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) + eslint: 9.39.2(supports-color@10.2.2) + eslint-import-resolver-node: 0.3.9(supports-color@10.2.2) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) transitivePeerDependencies: - supports-color - eslint-plugin-boundaries@6.0.2(@typescript-eslint/parser@8.59.2(@typescript/typescript6@6.0.2)(eslint@9.39.2))(eslint@9.39.2): + eslint-plugin-boundaries@6.0.2(@typescript-eslint/parser@5.62.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2): dependencies: - '@boundaries/elements': 2.0.1(@typescript-eslint/parser@8.59.2(@typescript/typescript6@6.0.2)(eslint@9.39.2))(eslint@9.39.2) + '@boundaries/elements': 2.0.1(@typescript-eslint/parser@5.62.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) chalk: 4.1.2 - eslint: 9.39.2 - eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.59.2(@typescript/typescript6@6.0.2)(eslint@9.39.2))(eslint-import-resolver-node@0.3.9)(eslint@9.39.2) + eslint: 9.39.2(supports-color@10.2.2) + eslint-import-resolver-node: 0.3.9(supports-color@10.2.2) + eslint-module-utils: 2.12.1(@typescript-eslint/parser@5.62.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2))(eslint-import-resolver-node@0.3.9(supports-color@10.2.2))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) handlebars: 4.7.9 micromatch: 4.0.8 transitivePeerDependencies: @@ -27449,22 +28288,22 @@ snapshots: lodash: 4.18.1 pkg-dir: 5.0.0 - eslint-plugin-import-helpers@2.0.1(eslint@9.39.2): + eslint-plugin-import-helpers@2.0.1(eslint@9.39.2(supports-color@10.2.2)): dependencies: - eslint: 9.39.2 + eslint: 9.39.2(supports-color@10.2.2) - eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.59.2(@typescript/typescript6@6.0.2)(eslint@9.39.2))(eslint@9.39.2): + eslint-plugin-import@2.31.0(@typescript-eslint/parser@5.62.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.9 array.prototype.findlastindex: 1.2.6 array.prototype.flat: 1.3.3 array.prototype.flatmap: 1.3.3 - debug: 3.2.7 + debug: 3.2.7(supports-color@10.2.2) doctrine: 2.1.0 - eslint: 9.39.2 - eslint-import-resolver-node: 0.3.10 - eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.59.2(@typescript/typescript6@6.0.2)(eslint@9.39.2))(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2) + eslint: 9.39.2(supports-color@10.2.2) + eslint-import-resolver-node: 0.3.10(supports-color@10.2.2) + eslint-module-utils: 2.12.1(@typescript-eslint/parser@5.62.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2))(eslint-import-resolver-node@0.3.10(supports-color@10.2.2))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) hasown: 2.0.3 is-core-module: 2.16.2 is-glob: 4.0.3 @@ -27476,24 +28315,24 @@ snapshots: string.prototype.trimend: 1.0.9 tsconfig-paths: 3.15.0 optionalDependencies: - '@typescript-eslint/parser': 8.59.2(@typescript/typescript6@6.0.2)(eslint@9.39.2) + '@typescript-eslint/parser': 5.62.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack - supports-color - eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.59.2(@typescript/typescript6@6.0.2)(eslint@9.39.2))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2): + eslint-plugin-import@2.32.0(@typescript-eslint/parser@5.62.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.9 array.prototype.findlastindex: 1.2.6 array.prototype.flat: 1.3.3 array.prototype.flatmap: 1.3.3 - debug: 3.2.7 + debug: 3.2.7(supports-color@10.2.2) doctrine: 2.1.0 - eslint: 9.39.2 - eslint-import-resolver-node: 0.3.10 - eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.59.2(@typescript/typescript6@6.0.2)(eslint@9.39.2))(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2) + eslint: 9.39.2(supports-color@10.2.2) + eslint-import-resolver-node: 0.3.10(supports-color@10.2.2) + eslint-module-utils: 2.12.1(@typescript-eslint/parser@5.62.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2))(eslint-import-resolver-node@0.3.10(supports-color@10.2.2))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) hasown: 2.0.3 is-core-module: 2.16.2 is-glob: 4.0.3 @@ -27505,13 +28344,13 @@ snapshots: string.prototype.trimend: 1.0.9 tsconfig-paths: 3.15.0 optionalDependencies: - '@typescript-eslint/parser': 8.59.2(@typescript/typescript6@6.0.2)(eslint@9.39.2) + '@typescript-eslint/parser': 5.62.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack - supports-color - eslint-plugin-jsx-a11y@6.10.2(eslint@9.39.2): + eslint-plugin-jsx-a11y@6.10.2(eslint@9.39.2(supports-color@10.2.2)): dependencies: aria-query: 5.3.2 array-includes: 3.1.9 @@ -27521,7 +28360,7 @@ snapshots: axobject-query: 4.1.0 damerau-levenshtein: 1.0.8 emoji-regex: 9.2.2 - eslint: 9.39.2 + eslint: 9.39.2(supports-color@10.2.2) hasown: 2.0.3 jsx-ast-utils: 3.3.5 language-tags: 1.0.9 @@ -27530,27 +28369,122 @@ snapshots: safe-regex-test: 1.1.0 string.prototype.includes: 2.0.1 - eslint-plugin-playwright@2.0.1(eslint@9.39.2): + eslint-plugin-playwright@2.0.1(eslint@9.39.2(supports-color@10.2.2)): dependencies: - eslint: 9.39.2 + eslint: 9.39.2(supports-color@10.2.2) globals: 13.24.0 - eslint-plugin-react-hooks@5.0.0(eslint@9.39.2): + eslint-plugin-react-dom@5.19.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2): + dependencies: + '@eslint-react/ast': 5.19.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) + '@eslint-react/eslint': 5.19.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) + '@eslint-react/jsx': 5.19.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) + '@eslint-react/shared': 5.19.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) + '@typescript-eslint/types': 8.70.0 + '@typescript-eslint/utils': 8.70.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) + compare-versions: 6.1.1 + eslint: 9.39.2(supports-color@10.2.2) + typescript: '@typescript/typescript6@6.0.2' + transitivePeerDependencies: + - supports-color + + eslint-plugin-react-hooks@5.0.0(eslint@9.39.2(supports-color@10.2.2)): dependencies: - eslint: 9.39.2 + eslint: 9.39.2(supports-color@10.2.2) - eslint-plugin-react-hooks@7.1.1(eslint@9.39.2): + eslint-plugin-react-hooks@7.1.1(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2): dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/parser': 7.29.7 - eslint: 9.39.2 + eslint: 9.39.2(supports-color@10.2.2) hermes-parser: 0.25.1 zod: 4.4.3 zod-validation-error: 4.0.2(zod@4.4.3) transitivePeerDependencies: - supports-color - eslint-plugin-react@7.37.5(eslint@9.39.2): + eslint-plugin-react-jsx@5.19.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2): + dependencies: + '@eslint-react/ast': 5.19.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) + '@eslint-react/core': 5.19.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) + '@eslint-react/eslint': 5.19.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) + '@eslint-react/jsx': 5.19.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) + '@eslint-react/shared': 5.19.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) + '@typescript-eslint/types': 8.70.0 + '@typescript-eslint/utils': 8.70.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) + eslint: 9.39.2(supports-color@10.2.2) + typescript: '@typescript/typescript6@6.0.2' + transitivePeerDependencies: + - supports-color + + eslint-plugin-react-naming-convention@5.19.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2): + dependencies: + '@eslint-react/ast': 5.19.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) + '@eslint-react/core': 5.19.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) + '@eslint-react/eslint': 5.19.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) + '@eslint-react/shared': 5.19.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) + '@eslint-react/var': 5.19.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) + '@typescript-eslint/types': 8.70.0 + '@typescript-eslint/utils': 8.70.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) + eslint: 9.39.2(supports-color@10.2.2) + ts-pattern: 5.9.0 + typescript: '@typescript/typescript6@6.0.2' + transitivePeerDependencies: + - supports-color + + eslint-plugin-react-rsc@5.19.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2): + dependencies: + '@eslint-react/ast': 5.19.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) + '@eslint-react/core': 5.19.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) + '@eslint-react/eslint': 5.19.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) + '@eslint-react/shared': 5.19.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) + '@eslint-react/var': 5.19.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) + '@typescript-eslint/types': 8.70.0 + '@typescript-eslint/utils': 8.70.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) + eslint: 9.39.2(supports-color@10.2.2) + typescript: '@typescript/typescript6@6.0.2' + transitivePeerDependencies: + - supports-color + + eslint-plugin-react-web-api@5.19.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2): + dependencies: + '@eslint-react/ast': 5.19.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) + '@eslint-react/core': 5.19.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) + '@eslint-react/eslint': 5.19.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) + '@eslint-react/shared': 5.19.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) + '@eslint-react/var': 5.19.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) + '@typescript-eslint/types': 8.70.0 + '@typescript-eslint/utils': 8.70.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) + birecord: 0.1.2 + eslint: 9.39.2(supports-color@10.2.2) + ts-pattern: 5.9.0 + typescript: '@typescript/typescript6@6.0.2' + transitivePeerDependencies: + - supports-color + + eslint-plugin-react-x@5.19.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2): + dependencies: + '@eslint-react/ast': 5.19.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) + '@eslint-react/core': 5.19.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) + '@eslint-react/eslint': 5.19.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) + '@eslint-react/jsx': 5.19.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) + '@eslint-react/shared': 5.19.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) + '@eslint-react/var': 5.19.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) + '@typescript-eslint/scope-manager': 8.70.0 + '@typescript-eslint/type-utils': 8.70.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) + '@typescript-eslint/types': 8.70.0 + '@typescript-eslint/typescript-estree': 8.70.0(@typescript/typescript6@6.0.2)(supports-color@10.2.2) + '@typescript-eslint/utils': 8.70.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) + compare-versions: 6.1.1 + eslint: 9.39.2(supports-color@10.2.2) + string-ts: 2.3.1 + ts-api-utils: 2.5.0(@typescript/typescript6@6.0.2) + ts-pattern: 5.9.0 + typescript: '@typescript/typescript6@6.0.2' + transitivePeerDependencies: + - supports-color + + eslint-plugin-react@7.37.5(eslint@9.39.2(supports-color@10.2.2)): dependencies: array-includes: 3.1.9 array.prototype.findlast: 1.2.5 @@ -27558,7 +28492,7 @@ snapshots: array.prototype.tosorted: 1.1.4 doctrine: 2.1.0 es-iterator-helpers: 1.3.2 - eslint: 9.39.2 + eslint: 9.39.2(supports-color@10.2.2) estraverse: 5.3.0 hasown: 2.0.3 jsx-ast-utils: 3.3.5 @@ -27572,24 +28506,24 @@ snapshots: string.prototype.matchall: 4.0.12 string.prototype.repeat: 1.0.0 - eslint-plugin-regexp@2.6.0(eslint@9.39.2): + eslint-plugin-regexp@2.6.0(eslint@9.39.2(supports-color@10.2.2)): dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2) + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2(supports-color@10.2.2)) '@eslint-community/regexpp': 4.12.2 comment-parser: 1.4.7 - eslint: 9.39.2 + eslint: 9.39.2(supports-color@10.2.2) jsdoc-type-pratt-parser: 4.8.0 refa: 0.12.1 regexp-ast-analysis: 0.7.1 scslre: 0.3.0 - eslint-plugin-vitest@0.5.4(@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@8.59.2(@typescript/typescript6@6.0.2)(eslint@9.39.2))(@typescript/typescript6@6.0.2)(eslint@9.39.2))(@typescript/typescript6@6.0.2)(eslint@9.39.2)(vitest@4.1.7(@opentelemetry/api@1.9.1)(@types/node@20.16.7)(jsdom@27.3.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(vite@6.4.3(@types/node@20.16.7)(terser@5.48.0)(yaml@2.9.0))): + eslint-plugin-vitest@0.5.4(@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2))(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2))(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2)(vitest@5.0.0): dependencies: - '@typescript-eslint/utils': 7.18.0(@typescript/typescript6@6.0.2)(eslint@9.39.2) - eslint: 9.39.2 + '@typescript-eslint/utils': 7.18.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) + eslint: 9.39.2(supports-color@10.2.2) optionalDependencies: - '@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@8.59.2(@typescript/typescript6@6.0.2)(eslint@9.39.2))(@typescript/typescript6@6.0.2)(eslint@9.39.2) - vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@20.16.7)(jsdom@27.3.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(vite@6.4.3(@types/node@20.16.7)(terser@5.48.0)(yaml@2.9.0)) + '@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.62.0(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2))(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) + vitest: 5.0.0(@opentelemetry/api@1.9.1)(@types/node@24.13.4)(@vitest/coverage-v8@5.0.0)(jsdom@27.3.0(bufferutil@4.1.0)(supports-color@10.2.2)(utf-8-validate@6.0.6))(vite@6.4.3(@types/node@24.13.4)(terser@5.48.0)(yaml@2.9.0)) transitivePeerDependencies: - supports-color - typescript @@ -27610,14 +28544,14 @@ snapshots: eslint-visitor-keys@5.0.1: {} - eslint@9.39.2: + eslint@9.39.2(supports-color@10.2.2): dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2) + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2(supports-color@10.2.2)) '@eslint-community/regexpp': 4.12.2 - '@eslint/config-array': 0.21.2 + '@eslint/config-array': 0.21.2(supports-color@10.2.2) '@eslint/config-helpers': 0.4.2 '@eslint/core': 0.17.0 - '@eslint/eslintrc': 3.3.5 + '@eslint/eslintrc': 3.3.5(supports-color@10.2.2) '@eslint/js': 9.39.2 '@eslint/plugin-kit': 0.4.1 '@humanfs/node': 0.16.8 @@ -27627,7 +28561,7 @@ snapshots: ajv: 6.15.0 chalk: 4.1.2 cross-spawn: 7.0.6 - debug: 4.4.3 + debug: 4.4.3(supports-color@10.2.2) escape-string-regexp: 4.0.0 eslint-scope: 8.4.0 eslint-visitor-keys: 4.2.1 @@ -27679,11 +28613,11 @@ snapshots: etag@1.8.1: {} - eth-block-tracker@7.1.0: + eth-block-tracker@7.1.0(supports-color@10.2.2): dependencies: - '@metamask/eth-json-rpc-provider': 1.0.1 + '@metamask/eth-json-rpc-provider': 1.0.1(supports-color@10.2.2) '@metamask/safe-event-emitter': 3.1.2 - '@metamask/utils': 5.0.2 + '@metamask/utils': 5.0.2(supports-color@10.2.2) json-rpc-random-id: 1.0.1 pify: 3.0.0 transitivePeerDependencies: @@ -27736,9 +28670,24 @@ snapshots: events@3.3.0: {} + execa@9.6.1: + dependencies: + '@sindresorhus/merge-streams': 4.0.0 + cross-spawn: 7.0.6 + figures: 6.1.0 + get-stream: 9.0.1 + human-signals: 8.0.1 + is-plain-obj: 4.1.0 + is-stream: 4.0.1 + npm-run-path: 6.0.0 + pretty-ms: 9.3.0 + signal-exit: 4.1.0 + strip-final-newline: 4.0.0 + yoctocolors: 2.2.0 + expand-template@2.0.3: {} - expect-type@1.3.0: {} + expect-type@1.4.0: {} exponential-backoff@3.1.3: {} @@ -27840,35 +28789,24 @@ snapshots: fflate@0.8.3: {} + figures@6.1.0: + dependencies: + is-unicode-supported: 2.1.0 + file-entry-cache@8.0.0: dependencies: flat-cache: 4.0.1 - file-type@20.5.0: + file-type@20.5.0(supports-color@10.2.2): dependencies: - '@tokenizer/inflate': 0.2.7 + '@tokenizer/inflate': 0.2.7(supports-color@10.2.2) strtok3: 10.3.5 token-types: 6.1.2 uint8array-extras: 1.5.0 transitivePeerDependencies: - - supports-color - - file-uri-to-path@1.0.0: {} - - filing-cabinet@4.2.0: - dependencies: - app-module-path: 2.2.0 - commander: 10.0.1 - enhanced-resolve: 5.22.0 - is-relative-path: 1.0.2 - module-definition: 5.0.1 - module-lookup-amd: 8.0.5 - resolve: 1.22.12 - resolve-dependency-path: 3.0.2 - sass-lookup: 5.0.1 - stylus-lookup: 5.0.1 - tsconfig-paths: 4.2.0 - typescript: 5.9.3 + - supports-color + + file-uri-to-path@1.0.0: {} fill-range@7.1.1: dependencies: @@ -27876,9 +28814,9 @@ snapshots: filter-obj@1.1.0: {} - finalhandler@1.1.2: + finalhandler@1.1.2(supports-color@10.2.2): dependencies: - debug: 2.6.9 + debug: 2.6.9(supports-color@10.2.2) encodeurl: 1.0.2 escape-html: 1.0.3 on-finished: 2.3.0 @@ -27917,9 +28855,9 @@ snapshots: focus-visible@5.2.1: {} - follow-redirects@1.16.0(debug@4.4.3): + follow-redirects@1.16.0(debug@4.4.3(supports-color@10.2.2)): optionalDependencies: - debug: 4.4.3 + debug: 4.4.3(supports-color@10.2.2) fontfaceobserver@2.1.0: {} @@ -28005,23 +28943,23 @@ snapshots: call-bound: 1.0.4 define-properties: 1.2.1 functions-have-names: 1.2.3 - hasown: 2.0.3 + hasown: 2.0.4 is-callable: 1.2.7 functions-have-names@1.2.3: {} - gaxios@7.1.3: + gaxios@7.1.3(supports-color@10.2.2): dependencies: extend: 3.0.2 - https-proxy-agent: 7.0.6 + https-proxy-agent: 7.0.6(supports-color@10.2.2) node-fetch: 3.3.2 rimraf: 5.0.10 transitivePeerDependencies: - supports-color - gcp-metadata@8.1.3: + gcp-metadata@8.1.3(supports-color@10.2.2): dependencies: - gaxios: 7.1.3 + gaxios: 7.1.3(supports-color@10.2.2) google-logging-utils: 1.1.3 json-bigint: 1.0.0 transitivePeerDependencies: @@ -28033,11 +28971,6 @@ snapshots: gensync@1.0.0-beta.2: {} - get-amd-module-type@5.0.1: - dependencies: - ast-module-types: 5.0.0 - node-source-walk: 6.0.2 - get-caller-file@2.0.5: {} get-east-asian-width@1.6.0: {} @@ -28059,8 +28992,6 @@ snapshots: get-nonce@1.0.1: {} - get-own-enumerable-property-symbols@3.0.2: {} - get-port@7.2.0: {} get-proto@1.0.1: @@ -28112,15 +29043,6 @@ snapshots: package-json-from-dist: 1.0.1 path-scurry: 1.11.1 - glob@7.2.3: - dependencies: - fs.realpath: 1.0.0 - inflight: 1.0.6 - inherits: 2.0.4 - minimatch: 3.1.5 - once: 1.4.0 - path-is-absolute: 1.0.1 - glob@8.1.0: dependencies: fs.realpath: 1.0.0 @@ -28168,10 +29090,6 @@ snapshots: globrex@0.1.2: {} - gonzales-pe@4.3.0: - dependencies: - minimist: 1.2.8 - google-logging-utils@1.1.3: {} gopd@1.2.0: {} @@ -28227,11 +29145,11 @@ snapshots: graphemer@1.4.0: {} - graphiql@4.1.2(@codemirror/language@6.0.0)(@emotion/is-prop-valid@1.4.0)(@types/node@20.16.7)(@types/react-dom@19.1.6(@types/react@19.1.7))(@types/react@19.1.7)(graphql-ws@5.16.2(graphql@16.14.0))(graphql@16.14.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(use-sync-external-store@1.4.0(react@19.1.4)): + graphiql@4.1.2(@codemirror/language@6.0.0)(@emotion/is-prop-valid@1.4.0)(@types/node@24.13.4)(@types/react-dom@19.1.6(@types/react@19.1.7))(@types/react@19.1.7)(graphql-ws@5.16.2(graphql@16.14.0))(graphql@16.14.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(use-sync-external-store@1.4.0(react@19.1.4)): dependencies: - '@graphiql/plugin-doc-explorer': 0.2.2(@codemirror/language@6.0.0)(@emotion/is-prop-valid@1.4.0)(@types/node@20.16.7)(@types/react-dom@19.1.6(@types/react@19.1.7))(@types/react@19.1.7)(graphql-ws@5.16.2(graphql@16.14.0))(graphql@16.14.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(use-sync-external-store@1.4.0(react@19.1.4)) - '@graphiql/plugin-history': 0.2.2(@codemirror/language@6.0.0)(@emotion/is-prop-valid@1.4.0)(@types/node@20.16.7)(@types/react-dom@19.1.6(@types/react@19.1.7))(@types/react@19.1.7)(graphql-ws@5.16.2(graphql@16.14.0))(graphql@16.14.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(use-sync-external-store@1.4.0(react@19.1.4)) - '@graphiql/react': 0.34.1(@codemirror/language@6.0.0)(@emotion/is-prop-valid@1.4.0)(@types/node@20.16.7)(@types/react-dom@19.1.6(@types/react@19.1.7))(@types/react@19.1.7)(graphql-ws@5.16.2(graphql@16.14.0))(graphql@16.14.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(use-sync-external-store@1.4.0(react@19.1.4)) + '@graphiql/plugin-doc-explorer': 0.2.2(@codemirror/language@6.0.0)(@emotion/is-prop-valid@1.4.0)(@types/node@24.13.4)(@types/react-dom@19.1.6(@types/react@19.1.7))(@types/react@19.1.7)(graphql-ws@5.16.2(graphql@16.14.0))(graphql@16.14.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(use-sync-external-store@1.4.0(react@19.1.4)) + '@graphiql/plugin-history': 0.2.2(@codemirror/language@6.0.0)(@emotion/is-prop-valid@1.4.0)(@types/node@24.13.4)(@types/react-dom@19.1.6(@types/react@19.1.7))(@types/react@19.1.7)(graphql-ws@5.16.2(graphql@16.14.0))(graphql@16.14.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(use-sync-external-store@1.4.0(react@19.1.4)) + '@graphiql/react': 0.34.1(@codemirror/language@6.0.0)(@emotion/is-prop-valid@1.4.0)(@types/node@24.13.4)(@types/react-dom@19.1.6(@types/react@19.1.7))(@types/react@19.1.7)(graphql-ws@5.16.2(graphql@16.14.0))(graphql@16.14.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(use-sync-external-store@1.4.0(react@19.1.4)) graphql: 16.14.0 react: 19.1.4 react-compiler-runtime: 19.1.0-rc.1(react@19.1.4) @@ -28336,7 +29254,7 @@ snapshots: he@1.2.0: {} - helia@5.5.1(bufferutil@4.1.0)(react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(utf-8-validate@6.0.6): + helia@5.5.1(bufferutil@4.1.0)(react-native@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6))(supports-color@10.2.2)(utf-8-validate@6.0.6): dependencies: '@chainsafe/libp2p-noise': 16.1.5 '@chainsafe/libp2p-yamux': 7.0.4 @@ -28345,7 +29263,7 @@ snapshots: '@helia/interface': 5.4.0 '@helia/routers': 3.1.3 '@helia/utils': 1.4.0 - '@ipshipyard/libp2p-auto-tls': 1.0.0 + '@ipshipyard/libp2p-auto-tls': 1.0.0(supports-color@10.2.2) '@libp2p/autonat': 2.0.38 '@libp2p/bootstrap': 11.0.47 '@libp2p/circuit-relay-v2': 3.2.24 @@ -28361,7 +29279,7 @@ snapshots: '@libp2p/tcp': 10.1.19 '@libp2p/tls': 2.2.7 '@libp2p/upnp-nat': 3.1.22 - '@libp2p/webrtc': 5.2.24(react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)) + '@libp2p/webrtc': 5.2.24(react-native@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6))(supports-color@10.2.2) '@libp2p/websockets': 9.2.19(bufferutil@4.1.0)(utf-8-validate@6.0.6) '@multiformats/dns': 1.0.13 blockstore-core: 5.0.4 @@ -28457,10 +29375,10 @@ snapshots: statuses: 2.0.2 toidentifier: 1.0.1 - http-proxy-agent@7.0.2: + http-proxy-agent@7.0.2(supports-color@10.2.2): dependencies: agent-base: 7.1.4 - debug: 4.4.3 + debug: 4.4.3(supports-color@10.2.2) transitivePeerDependencies: - supports-color @@ -28474,13 +29392,15 @@ snapshots: quick-lru: 5.1.1 resolve-alpn: 1.2.1 - https-proxy-agent@7.0.6: + https-proxy-agent@7.0.6(supports-color@10.2.2): dependencies: agent-base: 7.1.4 - debug: 4.4.3 + debug: 4.4.3(supports-color@10.2.2) transitivePeerDependencies: - supports-color + human-signals@8.0.1: {} + humanize-duration@3.33.2: {} humanize-ms@1.2.1: @@ -28497,6 +29417,10 @@ snapshots: dependencies: safer-buffer: 2.1.2 + iconv-lite@0.7.3: + dependencies: + safer-buffer: 2.1.2 + icss-utils@5.1.0(postcss@8.5.15): dependencies: postcss: 8.5.15 @@ -28608,7 +29532,7 @@ snapshots: p-queue: 8.1.1 progress-events: 1.1.0 - ipfs-unixfs-importer@15.4.0: + ipfs-unixfs-importer@15.4.0(supports-color@10.2.2): dependencies: '@ipld/dag-pb': 4.1.7 '@multiformats/murmur3': 2.2.5 @@ -28622,7 +29546,7 @@ snapshots: it-parallel-batch: 3.0.11 multiformats: 13.4.2 progress-events: 1.1.0 - rabin-wasm: 0.1.5 + rabin-wasm: 0.1.5(supports-color@10.2.2) uint8arraylist: 2.4.9 uint8arrays: 5.1.1 transitivePeerDependencies: @@ -28692,7 +29616,7 @@ snapshots: is-bun-module@2.0.0: dependencies: - semver: 7.8.1 + semver: 7.8.5 is-callable@1.2.7: {} @@ -28702,7 +29626,7 @@ snapshots: is-core-module@2.16.2: dependencies: - hasown: 2.0.3 + hasown: 2.0.4 is-data-view@1.0.2: dependencies: @@ -28769,8 +29693,6 @@ snapshots: is-number@7.0.0: {} - is-obj@1.0.1: {} - is-path-inside@4.0.0: {} is-plain-obj@2.1.0: {} @@ -28792,14 +29714,10 @@ snapshots: call-bound: 1.0.4 gopd: 1.2.0 has-tostringtag: 1.0.2 - hasown: 2.0.3 - - is-regexp@1.0.0: {} + hasown: 2.0.4 is-regexp@3.1.0: {} - is-relative-path@1.0.2: {} - is-retry-allowed@2.2.0: {} is-safe-filename@0.1.1: {} @@ -28829,9 +29747,7 @@ snapshots: dependencies: which-typed-array: 1.1.21 - is-url-superb@4.0.0: {} - - is-url@1.2.4: {} + is-unicode-supported@2.1.0: {} is-weakmap@2.0.2: {} @@ -29064,7 +29980,7 @@ snapshots: jest-util@29.7.0: dependencies: '@jest/types': 29.6.3 - '@types/node': 22.12.0 + '@types/node': 24.13.4 chalk: 4.1.2 ci-info: 3.9.0 graceful-fs: 4.2.11 @@ -29081,13 +29997,13 @@ snapshots: jest-worker@27.5.1: dependencies: - '@types/node': 22.12.0 + '@types/node': 24.13.4 merge-stream: 2.0.0 supports-color: 8.1.1 jest-worker@29.7.0: dependencies: - '@types/node': 22.12.0 + '@types/node': 24.13.4 jest-util: 29.7.0 merge-stream: 2.0.0 supports-color: 8.1.1 @@ -29102,8 +30018,12 @@ snapshots: js-file-download@0.4.12: {} + js-md4@0.3.2: {} + js-sha3@0.8.0: {} + js-tokens@10.0.0: {} + js-tokens@4.0.0: {} js-yaml@4.1.1: @@ -29114,7 +30034,7 @@ snapshots: jsdoc-type-pratt-parser@4.8.0: {} - jsdom@27.3.0(bufferutil@4.1.0)(utf-8-validate@6.0.6): + jsdom@27.3.0(bufferutil@4.1.0)(supports-color@10.2.2)(utf-8-validate@6.0.6): dependencies: '@acemir/cssom': 0.9.31 '@asamuzakjp/dom-selector': 6.8.1 @@ -29122,8 +30042,8 @@ snapshots: data-urls: 6.0.1 decimal.js: 10.6.0 html-encoding-sniffer: 4.0.0 - http-proxy-agent: 7.0.2 - https-proxy-agent: 7.0.6 + http-proxy-agent: 7.0.2(supports-color@10.2.2) + https-proxy-agent: 7.0.6(supports-color@10.2.2) is-potential-custom-element-name: 1.0.1 parse5: 8.0.1 saxes: 6.0.0 @@ -29151,6 +30071,8 @@ snapshots: json-parse-even-better-errors@2.3.1: {} + json-rpc-2.0@1.7.1: {} + json-rpc-engine@6.1.0: dependencies: '@metamask/safe-event-emitter': 2.0.0 @@ -29259,10 +30181,10 @@ snapshots: rc: 1.2.8 stream-json: 1.9.1 - license-report@6.8.1: + license-report@6.8.1(supports-color@10.2.2): dependencies: '@kessler/tableify': 1.0.2 - debug: 4.4.3 + debug: 4.4.3(supports-color@10.2.2) eol: 0.10.0 find-up-simple: 1.0.1 got: 14.6.6 @@ -29275,9 +30197,9 @@ snapshots: transitivePeerDependencies: - supports-color - lighthouse-logger@1.4.2: + lighthouse-logger@1.4.2(supports-color@10.2.2): dependencies: - debug: 2.6.9 + debug: 2.6.9(supports-color@10.2.2) marky: 1.3.0 transitivePeerDependencies: - supports-color @@ -29344,6 +30266,8 @@ snapshots: lodash.debounce@4.0.8: {} + lodash.groupby@4.6.0: {} + lodash.merge@4.6.2: {} lodash.throttle@4.1.1: {} @@ -29401,6 +30325,16 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 + magic-string@1.3.1: + dependencies: + '@jridgewell/sourcemap-codec': 1.6.0 + + magicast@0.5.4: + dependencies: + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 + source-map-js: 1.2.1 + main-event@1.0.4: {} make-error@1.3.6: {} @@ -29456,15 +30390,15 @@ snapshots: treeify: 1.1.0 web3-utils: 1.10.4 - meros@1.3.2(@types/node@20.16.7): + meros@1.3.2(@types/node@24.13.4): optionalDependencies: - '@types/node': 20.16.7 + '@types/node': 24.13.4 mersenne-twister@1.1.0: {} - metro-babel-transformer@0.84.4: + metro-babel-transformer@0.84.4(supports-color@10.2.2): dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) flow-enums-runtime: 0.0.6 hermes-parser: 0.35.0 metro-cache-key: 0.84.4 @@ -29476,22 +30410,22 @@ snapshots: dependencies: flow-enums-runtime: 0.0.6 - metro-cache@0.84.4: + metro-cache@0.84.4(supports-color@10.2.2): dependencies: exponential-backoff: 3.1.3 flow-enums-runtime: 0.0.6 - https-proxy-agent: 7.0.6 + https-proxy-agent: 7.0.6(supports-color@10.2.2) metro-core: 0.84.4 transitivePeerDependencies: - supports-color - metro-config@0.84.4(bufferutil@4.1.0)(utf-8-validate@6.0.6): + metro-config@0.84.4(bufferutil@4.1.0)(supports-color@10.2.2)(utf-8-validate@6.0.6): dependencies: - connect: 3.7.0 + connect: 3.7.0(supports-color@10.2.2) flow-enums-runtime: 0.0.6 jest-validate: 29.7.0 - metro: 0.84.4(bufferutil@4.1.0)(utf-8-validate@6.0.6) - metro-cache: 0.84.4 + metro: 0.84.4(bufferutil@4.1.0)(supports-color@10.2.2)(utf-8-validate@6.0.6) + metro-cache: 0.84.4(supports-color@10.2.2) metro-core: 0.84.4 metro-runtime: 0.84.4 yaml: 2.9.0 @@ -29506,9 +30440,9 @@ snapshots: lodash.throttle: 4.1.1 metro-resolver: 0.84.4 - metro-file-map@0.84.4: + metro-file-map@0.84.4(supports-color@10.2.2): dependencies: - debug: 4.4.3 + debug: 4.4.3(supports-color@10.2.2) fb-watchman: 2.0.2 flow-enums-runtime: 0.0.6 graceful-fs: 4.2.11 @@ -29534,9 +30468,9 @@ snapshots: '@babel/runtime': 7.29.7 flow-enums-runtime: 0.0.6 - metro-source-map@0.84.4: + metro-source-map@0.84.4(supports-color@10.2.2): dependencies: - '@babel/traverse': 7.29.7 + '@babel/traverse': 7.29.7(supports-color@10.2.2) '@babel/types': 7.29.7 flow-enums-runtime: 0.0.6 invariant: 2.2.4 @@ -29552,57 +30486,55 @@ snapshots: dependencies: flow-enums-runtime: 0.0.6 invariant: 2.2.4 - metro-source-map: 0.84.4 + metro-source-map: 0.84.4(supports-color@10.2.2) nullthrows: 1.1.1 source-map: 0.5.7 vlq: 1.0.1 - transitivePeerDependencies: - - supports-color - metro-transform-plugins@0.84.4: + metro-transform-plugins@0.84.4(supports-color@10.2.2): dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/generator': 7.29.7 '@babel/template': 7.29.7 - '@babel/traverse': 7.29.7 + '@babel/traverse': 7.29.7(supports-color@10.2.2) flow-enums-runtime: 0.0.6 nullthrows: 1.1.1 transitivePeerDependencies: - supports-color - metro-transform-worker@0.84.4(bufferutil@4.1.0)(utf-8-validate@6.0.6): + metro-transform-worker@0.84.4(bufferutil@4.1.0)(supports-color@10.2.2)(utf-8-validate@6.0.6): dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/generator': 7.29.7 '@babel/parser': 7.29.7 '@babel/types': 7.29.7 flow-enums-runtime: 0.0.6 - metro: 0.84.4(bufferutil@4.1.0)(utf-8-validate@6.0.6) - metro-babel-transformer: 0.84.4 - metro-cache: 0.84.4 + metro: 0.84.4(bufferutil@4.1.0)(supports-color@10.2.2)(utf-8-validate@6.0.6) + metro-babel-transformer: 0.84.4(supports-color@10.2.2) + metro-cache: 0.84.4(supports-color@10.2.2) metro-cache-key: 0.84.4 metro-minify-terser: 0.84.4 - metro-source-map: 0.84.4 - metro-transform-plugins: 0.84.4 + metro-source-map: 0.84.4(supports-color@10.2.2) + metro-transform-plugins: 0.84.4(supports-color@10.2.2) nullthrows: 1.1.1 transitivePeerDependencies: - bufferutil - supports-color - utf-8-validate - metro@0.84.4(bufferutil@4.1.0)(utf-8-validate@6.0.6): + metro@0.84.4(bufferutil@4.1.0)(supports-color@10.2.2)(utf-8-validate@6.0.6): dependencies: '@babel/code-frame': 7.29.7 - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/generator': 7.29.7 '@babel/parser': 7.29.7 '@babel/template': 7.29.7 - '@babel/traverse': 7.29.7 + '@babel/traverse': 7.29.7(supports-color@10.2.2) '@babel/types': 7.29.7 accepts: 2.0.0 ci-info: 2.0.0 - connect: 3.7.0 - debug: 4.4.3 + connect: 3.7.0(supports-color@10.2.2) + debug: 4.4.3(supports-color@10.2.2) error-stack-parser: 2.1.4 flow-enums-runtime: 0.0.6 graceful-fs: 4.2.11 @@ -29612,18 +30544,18 @@ snapshots: jest-worker: 29.7.0 jsc-safe-url: 0.2.4 lodash.throttle: 4.1.1 - metro-babel-transformer: 0.84.4 - metro-cache: 0.84.4 + metro-babel-transformer: 0.84.4(supports-color@10.2.2) + metro-cache: 0.84.4(supports-color@10.2.2) metro-cache-key: 0.84.4 - metro-config: 0.84.4(bufferutil@4.1.0)(utf-8-validate@6.0.6) + metro-config: 0.84.4(bufferutil@4.1.0)(supports-color@10.2.2)(utf-8-validate@6.0.6) metro-core: 0.84.4 - metro-file-map: 0.84.4 + metro-file-map: 0.84.4(supports-color@10.2.2) metro-resolver: 0.84.4 metro-runtime: 0.84.4 - metro-source-map: 0.84.4 + metro-source-map: 0.84.4(supports-color@10.2.2) metro-symbolicate: 0.84.4 - metro-transform-plugins: 0.84.4 - metro-transform-worker: 0.84.4(bufferutil@4.1.0)(utf-8-validate@6.0.6) + metro-transform-plugins: 0.84.4(supports-color@10.2.2) + metro-transform-worker: 0.84.4(bufferutil@4.1.0)(supports-color@10.2.2)(utf-8-validate@6.0.6) mime-types: 3.0.2 nullthrows: 1.1.1 serialize-error: 2.1.0 @@ -29673,11 +30605,11 @@ snapshots: minimatch@10.2.3: dependencies: - brace-expansion: 1.1.12 + brace-expansion: 5.0.9 minimatch@10.2.5: dependencies: - brace-expansion: 1.1.12 + brace-expansion: 5.0.9 minimatch@3.1.5: dependencies: @@ -29718,20 +30650,8 @@ snapshots: mockdate@3.0.5: {} - module-definition@5.0.1: - dependencies: - ast-module-types: 5.0.0 - node-source-walk: 6.0.2 - module-details-from-path@1.0.4: {} - module-lookup-amd@8.0.5: - dependencies: - commander: 10.0.1 - glob: 7.2.3 - requirejs: 2.3.8 - requirejs-config-file: 4.0.0 - monaco-editor@0.52.2: {} mortice@3.3.1: @@ -29769,6 +30689,20 @@ snapshots: multiformats@9.9.0: {} + mutation-server-protocol@0.4.1: + dependencies: + zod: 4.4.3 + + mutation-testing-elements@3.8.4: {} + + mutation-testing-metrics@3.8.4: + dependencies: + mutation-testing-report-schema: 3.8.4 + + mutation-testing-report-schema@3.8.4: {} + + mute-stream@3.0.0: {} + nanoclone@0.2.1: {} nanoid@3.3.12: {} @@ -29793,76 +30727,77 @@ snapshots: netmask@2.1.1: {} - next-sitemap@4.2.3(next@16.3.1(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.57.0)(@types/node@20.16.7)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)): + next-sitemap@4.2.3(next@16.3.5(@babel/core@8.0.1)(@opentelemetry/api@1.9.1)(@playwright/test@1.57.0)(@types/node@24.13.4)(babel-plugin-macros@3.1.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)): dependencies: '@corex/deepmerge': 4.0.43 '@next/env': 13.5.11 fast-glob: 3.3.3 minimist: 1.2.8 - next: 16.3.1(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.57.0)(@types/node@20.16.7)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + next: 16.3.5(@babel/core@8.0.1)(@opentelemetry/api@1.9.1)(@playwright/test@1.57.0)(@types/node@24.13.4)(babel-plugin-macros@3.1.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) next-themes@0.4.4(react-dom@19.1.4(react@19.1.4))(react@19.1.4): dependencies: react: 19.1.4 react-dom: 19.1.4(react@19.1.4) - next@16.2.6(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.57.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4): + next@16.3.5(@babel/core@7.29.7(supports-color@10.2.2))(@opentelemetry/api@1.9.1)(@playwright/test@1.57.0)(@types/node@24.13.4)(babel-plugin-macros@3.1.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4): dependencies: - '@next/env': 16.2.6 - '@swc/helpers': 0.5.15 + '@next/env': 16.3.5 + '@swc/helpers': 0.5.23 baseline-browser-mapping: 2.10.32 caniuse-lite: 1.0.30001793 - postcss: 8.4.31 + postcss: 8.5.23 react: 19.1.4 react-dom: 19.1.4(react@19.1.4) - styled-jsx: 5.1.6(@babel/core@7.29.7)(react@19.1.4) + styled-jsx: 5.1.6(@babel/core@7.29.7(supports-color@10.2.2))(babel-plugin-macros@3.1.0)(react@19.1.4) optionalDependencies: - '@next/swc-darwin-arm64': 16.2.6 - '@next/swc-darwin-x64': 16.2.6 - '@next/swc-linux-arm64-gnu': 16.2.6 - '@next/swc-linux-arm64-musl': 16.2.6 - '@next/swc-linux-x64-gnu': 16.2.6 - '@next/swc-linux-x64-musl': 16.2.6 - '@next/swc-win32-arm64-msvc': 16.2.6 - '@next/swc-win32-x64-msvc': 16.2.6 + '@next/swc-darwin-arm64': 16.3.5 + '@next/swc-darwin-x64': 16.3.5 + '@next/swc-linux-arm64-gnu': 16.3.5 + '@next/swc-linux-arm64-musl': 16.3.5 + '@next/swc-linux-x64-gnu': 16.3.5 + '@next/swc-linux-x64-musl': 16.3.5 + '@next/swc-win32-arm64-msvc': 16.3.5 + '@next/swc-win32-x64-msvc': 16.3.5 '@opentelemetry/api': 1.9.1 '@playwright/test': 1.57.0 - sharp: 0.34.5 + sharp: 0.35.4(@types/node@24.13.4) transitivePeerDependencies: - '@babel/core' + - '@types/node' - babel-plugin-macros - next@16.3.1(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.57.0)(@types/node@20.16.7)(react-dom@19.1.4(react@19.1.4))(react@19.1.4): + next@16.3.5(@babel/core@8.0.1)(@opentelemetry/api@1.9.1)(@playwright/test@1.57.0)(@types/node@24.13.4)(babel-plugin-macros@3.1.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4): dependencies: - '@next/env': 16.3.1 + '@next/env': 16.3.5 '@swc/helpers': 0.5.23 baseline-browser-mapping: 2.10.32 caniuse-lite: 1.0.30001793 postcss: 8.5.23 react: 19.1.4 react-dom: 19.1.4(react@19.1.4) - styled-jsx: 5.1.6(@babel/core@7.29.7)(react@19.1.4) + styled-jsx: 5.1.6(@babel/core@8.0.1)(babel-plugin-macros@3.1.0)(react@19.1.4) optionalDependencies: - '@next/swc-darwin-arm64': 16.3.1 - '@next/swc-darwin-x64': 16.3.1 - '@next/swc-linux-arm64-gnu': 16.3.1 - '@next/swc-linux-arm64-musl': 16.3.1 - '@next/swc-linux-x64-gnu': 16.3.1 - '@next/swc-linux-x64-musl': 16.3.1 - '@next/swc-win32-arm64-msvc': 16.3.1 - '@next/swc-win32-x64-msvc': 16.3.1 + '@next/swc-darwin-arm64': 16.3.5 + '@next/swc-darwin-x64': 16.3.5 + '@next/swc-linux-arm64-gnu': 16.3.5 + '@next/swc-linux-arm64-musl': 16.3.5 + '@next/swc-linux-x64-gnu': 16.3.5 + '@next/swc-linux-x64-musl': 16.3.5 + '@next/swc-win32-arm64-msvc': 16.3.5 + '@next/swc-win32-x64-msvc': 16.3.5 '@opentelemetry/api': 1.9.1 '@playwright/test': 1.57.0 - sharp: 0.35.3(@types/node@20.16.7) + sharp: 0.35.4(@types/node@24.13.4) transitivePeerDependencies: - '@babel/core' - '@types/node' - babel-plugin-macros - nextjs-routes@2.2.5(next@16.3.1(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.57.0)(@types/node@20.16.7)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)): + nextjs-routes@2.2.5(next@16.3.5(@babel/core@7.29.7(supports-color@10.2.2))(@opentelemetry/api@1.9.1)(@playwright/test@1.57.0)(@types/node@24.13.4)(babel-plugin-macros@3.1.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)): dependencies: chokidar: 4.0.3 - next: 16.3.1(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.57.0)(@types/node@20.16.7)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) + next: 16.3.5(@babel/core@7.29.7(supports-color@10.2.2))(@opentelemetry/api@1.9.1)(@playwright/test@1.57.0)(@types/node@24.13.4)(babel-plugin-macros@3.1.0)(react-dom@19.1.4(react@19.1.4))(react@19.1.4) no-case@3.0.4: dependencies: @@ -29871,7 +30806,7 @@ snapshots: node-abi@3.92.0: dependencies: - semver: 7.8.4 + semver: 7.8.5 node-abort-controller@3.1.1: {} @@ -29916,16 +30851,17 @@ snapshots: node-releases@2.0.46: {} - node-source-walk@6.0.2: - dependencies: - '@babel/parser': 7.29.7 - normalize-path@3.0.0: {} normalize-url@6.1.0: {} normalize-url@8.1.1: {} + npm-run-path@6.0.0: + dependencies: + path-key: 4.0.0 + unicorn-magic: 0.3.0 + nth-check@2.1.1: dependencies: boolbase: 1.0.0 @@ -29996,6 +30932,8 @@ snapshots: obug@2.1.1: {} + obug@2.2.1: {} + ofetch@1.5.1: dependencies: destr: 2.0.5 @@ -30075,35 +31013,50 @@ snapshots: transitivePeerDependencies: - zod - ox@0.6.7(@typescript/typescript6@6.0.2)(zod@3.25.76): + ox@0.14.25(@typescript/typescript6@6.0.2)(zod@4.4.3): + dependencies: + '@adraffy/ens-normalize': 1.11.1 + '@noble/ciphers': 1.3.0 + '@noble/curves': 1.9.1 + '@noble/hashes': 1.8.0 + '@scure/bip32': 1.7.0 + '@scure/bip39': 1.6.0 + abitype: 1.2.3(@typescript/typescript6@6.0.2)(zod@4.4.3) + eventemitter3: 5.0.1 + optionalDependencies: + typescript: '@typescript/typescript6@6.0.2' + transitivePeerDependencies: + - zod + + ox@0.6.7(@typescript/typescript6@6.0.2)(zod@4.4.3): dependencies: '@adraffy/ens-normalize': 1.11.1 '@noble/curves': 1.9.7 '@noble/hashes': 1.8.0 '@scure/bip32': 1.7.0 '@scure/bip39': 1.6.0 - abitype: 1.2.3(@typescript/typescript6@6.0.2)(zod@3.25.76) + abitype: 1.2.3(@typescript/typescript6@6.0.2)(zod@4.4.3) eventemitter3: 5.0.1 optionalDependencies: typescript: '@typescript/typescript6@6.0.2' transitivePeerDependencies: - zod - ox@0.6.9(@typescript/typescript6@6.0.2)(zod@3.25.76): + ox@0.6.9(@typescript/typescript6@6.0.2)(zod@4.4.3): dependencies: '@adraffy/ens-normalize': 1.11.1 '@noble/curves': 1.9.7 '@noble/hashes': 1.8.0 '@scure/bip32': 1.7.0 '@scure/bip39': 1.6.0 - abitype: 1.2.3(@typescript/typescript6@6.0.2)(zod@3.25.76) + abitype: 1.2.3(@typescript/typescript6@6.0.2)(zod@4.4.3) eventemitter3: 5.0.1 optionalDependencies: typescript: '@typescript/typescript6@6.0.2' transitivePeerDependencies: - zod - ox@0.7.1(@typescript/typescript6@6.0.2)(zod@3.25.76): + ox@0.7.1(@typescript/typescript6@6.0.2)(zod@4.4.3): dependencies: '@adraffy/ens-normalize': 1.11.1 '@noble/ciphers': 1.3.0 @@ -30111,7 +31064,7 @@ snapshots: '@noble/hashes': 1.8.0 '@scure/bip32': 1.7.0 '@scure/bip39': 1.6.0 - abitype: 1.2.3(@typescript/typescript6@6.0.2)(zod@3.25.76) + abitype: 1.2.3(@typescript/typescript6@6.0.2)(zod@4.4.3) eventemitter3: 5.0.1 optionalDependencies: typescript: '@typescript/typescript6@6.0.2' @@ -30133,7 +31086,7 @@ snapshots: transitivePeerDependencies: - zod - ox@0.9.3(@typescript/typescript6@6.0.2)(zod@3.25.76): + ox@0.9.3(@typescript/typescript6@6.0.2)(zod@4.4.3): dependencies: '@adraffy/ens-normalize': 1.11.1 '@noble/ciphers': 1.3.0 @@ -30141,7 +31094,7 @@ snapshots: '@noble/hashes': 1.8.0 '@scure/bip32': 1.7.0 '@scure/bip39': 1.6.0 - abitype: 1.2.3(@typescript/typescript6@6.0.2)(zod@3.25.76) + abitype: 1.2.3(@typescript/typescript6@6.0.2)(zod@4.4.3) eventemitter3: 5.0.1 optionalDependencies: typescript: '@typescript/typescript6@6.0.2' @@ -30163,7 +31116,7 @@ snapshots: transitivePeerDependencies: - zod - ox@0.9.6(@typescript/typescript6@6.0.2)(zod@3.25.76): + ox@0.9.6(@typescript/typescript6@6.0.2)(zod@4.4.3): dependencies: '@adraffy/ens-normalize': 1.11.1 '@noble/ciphers': 1.3.0 @@ -30171,7 +31124,7 @@ snapshots: '@noble/hashes': 1.8.0 '@scure/bip32': 1.7.0 '@scure/bip39': 1.6.0 - abitype: 1.2.3(@typescript/typescript6@6.0.2)(zod@3.25.76) + abitype: 1.2.3(@typescript/typescript6@6.0.2)(zod@4.4.3) eventemitter3: 5.0.1 optionalDependencies: typescript: '@typescript/typescript6@6.0.2' @@ -30278,6 +31231,8 @@ snapshots: json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 + parse-ms@4.0.0: {} + parse5@8.0.1: dependencies: entities: 8.0.0 @@ -30288,10 +31243,10 @@ snapshots: path-exists@4.0.0: {} - path-is-absolute@1.0.1: {} - path-key@3.1.1: {} + path-key@4.0.0: {} + path-parse@1.0.7: {} path-scurry@1.11.1: @@ -30327,6 +31282,8 @@ snapshots: picomatch@4.0.4: {} + picomatch@4.0.7: {} + pify@3.0.0: {} pify@5.0.0: {} @@ -30445,22 +31402,22 @@ snapshots: pony-cause@2.1.11: {} - porto@0.2.35(71352da693451397413658bdd9d79753): + porto@0.2.35(8095bfa4b9e61ff0a46bb10857184fb4): dependencies: - '@wagmi/core': 2.22.1(@tanstack/query-core@5.55.4)(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(react@19.1.4)(use-sync-external-store@1.4.0(react@19.1.4))(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76)) + '@wagmi/core': 2.22.1(@tanstack/query-core@5.55.4)(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(react@19.1.4)(use-sync-external-store@1.4.0(react@19.1.4))(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3)) hono: 4.12.25 idb-keyval: 6.2.4 mipd: 0.0.7(@typescript/typescript6@6.0.2) ox: 0.9.17(@typescript/typescript6@6.0.2)(zod@4.4.3) - viem: 2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) + viem: 2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) zod: 4.4.3 zustand: 5.0.13(@types/react@19.1.7)(react@19.1.4)(use-sync-external-store@1.4.0(react@19.1.4)) optionalDependencies: '@tanstack/react-query': 5.55.4(react@19.1.4) react: 19.1.4 - react-native: 0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6) + react-native: 0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6) typescript: '@typescript/typescript6@6.0.2' - wagmi: 2.19.5(@tanstack/query-core@5.55.4)(@tanstack/react-query@5.55.4(react@19.1.4))(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6)(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76))(zod@3.25.76) + wagmi: 2.19.5(@tanstack/query-core@5.55.4)(@tanstack/react-query@5.55.4(react@19.1.4))(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(debug@4.4.3(supports-color@10.2.2))(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6))(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6)(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3))(zod@4.4.3) transitivePeerDependencies: - '@types/react' - immer @@ -30498,19 +31455,6 @@ snapshots: postcss-value-parser@4.2.0: {} - postcss-values-parser@6.0.2(postcss@8.5.15): - dependencies: - color-name: 1.1.4 - is-url-superb: 4.0.0 - postcss: 8.5.15 - quote-unquote: 1.0.0 - - postcss@8.4.31: - dependencies: - nanoid: 3.3.12 - picocolors: 1.1.1 - source-map-js: 1.2.1 - postcss@8.5.15: dependencies: nanoid: 3.3.12 @@ -30533,26 +31477,30 @@ snapshots: dependencies: xtend: 4.0.2 - posthog-js@1.376.2: + posthog-js@1.434.15(@types/react@19.1.7)(react@19.1.4): dependencies: - '@opentelemetry/api': 1.9.1 - '@opentelemetry/api-logs': 0.208.0 - '@opentelemetry/exporter-logs-otlp-http': 0.208.0(@opentelemetry/api@1.9.1) - '@opentelemetry/resources': 2.7.1(@opentelemetry/api@1.9.1) - '@opentelemetry/sdk-logs': 0.208.0(@opentelemetry/api@1.9.1) - '@posthog/core': 1.29.11 - '@posthog/types': 1.376.2 + '@posthog/browser-common': 0.9.0 + '@posthog/core': 1.55.2 + '@posthog/types': 1.412.4 core-js: 3.49.0 - dompurify: 3.4.9 + dompurify: 3.4.16 fflate: 0.4.8 - preact: 10.29.2 + preact: 10.29.8 query-selector-shadow-dom: 1.0.1 - web-vitals: 5.2.0 + web-vitals: 6.2.2 + web-vitals-soft-navs: web-vitals@6.2.1 + optionalDependencies: + '@types/react': 19.1.7 + react: 19.1.4 + transitivePeerDependencies: + - preact-render-to-string preact@10.24.2: {} preact@10.29.2: {} + preact@10.29.8: {} + prebuild-install@7.1.3: dependencies: detect-libc: 2.1.2 @@ -30568,23 +31516,6 @@ snapshots: tar-fs: 2.1.4 tunnel-agent: 0.6.0 - precinct@11.0.5: - dependencies: - '@dependents/detective-less': 4.1.0 - commander: 10.0.1 - detective-amd: 5.0.2 - detective-cjs: 5.0.1 - detective-es6: 4.0.1 - detective-postcss: 6.1.3 - detective-sass: 5.0.3 - detective-scss: 4.0.3 - detective-stylus: 4.0.0 - detective-typescript: 11.2.0 - module-definition: 5.0.1 - node-source-walk: 6.0.2 - transitivePeerDependencies: - - supports-color - prelude-ls@1.2.1: {} prettier@3.8.3: {} @@ -30601,6 +31532,10 @@ snapshots: ansi-styles: 5.2.0 react-is: 18.3.1 + pretty-ms@9.3.0: + dependencies: + parse-ms: 4.0.0 + prismjs@1.30.0: {} process-nextick-args@2.0.1: {} @@ -30617,6 +31552,8 @@ snapshots: progress-events@1.1.0: {} + progress@2.0.3: {} + prom-client@15.1.1: dependencies: '@opentelemetry/api': 1.9.1 @@ -30648,7 +31585,7 @@ snapshots: '@protobufjs/path': 1.1.2 '@protobufjs/pool': 1.1.0 '@protobufjs/utf8': 1.1.1 - '@types/node': 22.12.0 + '@types/node': 25.9.2 long: 5.3.2 protons-runtime@5.6.0: @@ -30716,6 +31653,10 @@ snapshots: pngjs: 5.0.0 yargs: 15.4.1 + qs@6.15.1: + dependencies: + side-channel: 1.1.0 + quansync@0.2.11: {} query-selector-shadow-dom@1.0.1: {} @@ -30741,13 +31682,11 @@ snapshots: quick-lru@5.1.1: {} - quote-unquote@1.0.0: {} - - rabin-wasm@0.1.5: + rabin-wasm@0.1.5(supports-color@10.2.2): dependencies: '@assemblyscript/loader': 0.9.4 bl: 5.1.0 - debug: 4.4.3 + debug: 4.4.3(supports-color@10.2.2) minimist: 1.2.8 node-fetch: 2.7.0 readable-stream: 3.6.2 @@ -30873,7 +31812,7 @@ snapshots: dependencies: react: 19.1.4 - react-i18next@13.5.0(i18next@23.4.6)(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4): + react-i18next@13.5.0(i18next@23.4.6)(react-dom@19.1.4(react@19.1.4))(react-native@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6))(react@19.1.4): dependencies: '@babel/runtime': 7.29.7 html-parse-stringify: 3.0.1 @@ -30881,7 +31820,7 @@ snapshots: react: 19.1.4 optionalDependencies: react-dom: 19.1.4(react@19.1.4) - react-native: 0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6) + react-native: 0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6) react-identicons@1.2.5(react-dom@19.1.4(react@19.1.4))(react@19.1.4): dependencies: @@ -30925,24 +31864,24 @@ snapshots: react: 19.1.4 react-dom: 19.1.4(react@19.1.4) - react-native-webrtc@124.0.7(react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6)): + react-native-webrtc@124.0.7(react-native@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6))(supports-color@10.2.2): dependencies: base64-js: 1.5.1 - debug: 4.3.4 + debug: 4.3.4(supports-color@10.2.2) event-target-shim: 6.0.2 - react-native: 0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6) + react-native: 0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6) transitivePeerDependencies: - supports-color - react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6): + react-native@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6): dependencies: '@react-native/assets-registry': 0.85.3 - '@react-native/codegen': 0.85.3(@babel/core@7.29.7) - '@react-native/community-cli-plugin': 0.85.3(bufferutil@4.1.0)(utf-8-validate@6.0.6) + '@react-native/codegen': 0.85.3(@babel/core@7.29.7(supports-color@10.2.2)) + '@react-native/community-cli-plugin': 0.85.3(bufferutil@4.1.0)(supports-color@10.2.2)(utf-8-validate@6.0.6) '@react-native/gradle-plugin': 0.85.3 '@react-native/js-polyfills': 0.85.3 '@react-native/normalize-colors': 0.85.3 - '@react-native/virtualized-lists': 0.85.3(@types/react@19.1.7)(react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4) + '@react-native/virtualized-lists': 0.85.3(@types/react@19.1.7)(react-native@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6))(react@19.1.4) abort-controller: 3.0.0 anser: 1.4.10 ansi-regex: 5.0.1 @@ -30954,7 +31893,7 @@ snapshots: invariant: 2.2.4 memoize-one: 5.2.1 metro-runtime: 0.84.4 - metro-source-map: 0.84.4 + metro-source-map: 0.84.4(supports-color@10.2.2) nullthrows: 1.1.1 pretty-format: 29.7.0 promise: 8.3.0 @@ -31190,30 +32129,21 @@ snapshots: require-from-string@2.0.2: {} - require-in-the-middle@8.0.1: + require-in-the-middle@8.0.1(supports-color@10.2.2): dependencies: - debug: 4.4.3 + debug: 4.4.3(supports-color@10.2.2) module-details-from-path: 1.0.4 transitivePeerDependencies: - supports-color require-main-filename@2.0.0: {} - requirejs-config-file@4.0.0: - dependencies: - esprima: 4.0.1 - stringify-object: 3.3.0 - - requirejs@2.3.8: {} - requires-port@1.0.0: {} reselect@5.2.0: {} resolve-alpn@1.2.1: {} - resolve-dependency-path@3.0.2: {} - resolve-from@4.0.0: {} resolve-from@5.0.0: {} @@ -31344,6 +32274,10 @@ snapshots: rw@1.3.3: {} + rxjs@7.8.2: + dependencies: + tslib: 2.8.1 + safe-array-concat@1.1.4: dependencies: call-bind: 1.0.9 @@ -31375,10 +32309,6 @@ snapshots: dependencies: truncate-utf8-bytes: 1.0.2 - sass-lookup@5.0.1: - dependencies: - commander: 10.0.1 - sax@1.6.0: {} saxes@6.0.0: @@ -31424,13 +32354,11 @@ snapshots: semver@7.8.1: {} - semver@7.8.4: {} - semver@7.8.5: {} - send@0.19.2: + send@0.19.2(supports-color@10.2.2): dependencies: - debug: 2.6.9 + debug: 2.6.9(supports-color@10.2.2) depd: 2.0.0 destroy: 1.2.0 encodeurl: 2.0.0 @@ -31452,12 +32380,12 @@ snapshots: dependencies: type-fest: 0.20.2 - serve-static@1.16.3: + serve-static@1.16.3(supports-color@10.2.2): dependencies: encodeurl: 2.0.0 escape-html: 1.0.3 parseurl: 1.3.3 - send: 0.19.2 + send: 0.19.2(supports-color@10.2.2) transitivePeerDependencies: - supports-color @@ -31504,7 +32432,7 @@ snapshots: dependencies: color: 4.2.3 detect-libc: 2.1.2 - semver: 7.8.1 + semver: 7.8.5 optionalDependencies: '@img/sharp-darwin-arm64': 0.33.5 '@img/sharp-darwin-x64': 0.33.5 @@ -31526,70 +32454,38 @@ snapshots: '@img/sharp-win32-ia32': 0.33.5 '@img/sharp-win32-x64': 0.33.5 - sharp@0.34.5: - dependencies: - '@img/colour': 1.1.0 - detect-libc: 2.1.2 - semver: 7.8.1 - optionalDependencies: - '@img/sharp-darwin-arm64': 0.34.5 - '@img/sharp-darwin-x64': 0.34.5 - '@img/sharp-libvips-darwin-arm64': 1.2.4 - '@img/sharp-libvips-darwin-x64': 1.2.4 - '@img/sharp-libvips-linux-arm': 1.2.4 - '@img/sharp-libvips-linux-arm64': 1.2.4 - '@img/sharp-libvips-linux-ppc64': 1.2.4 - '@img/sharp-libvips-linux-riscv64': 1.2.4 - '@img/sharp-libvips-linux-s390x': 1.2.4 - '@img/sharp-libvips-linux-x64': 1.2.4 - '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 - '@img/sharp-libvips-linuxmusl-x64': 1.2.4 - '@img/sharp-linux-arm': 0.34.5 - '@img/sharp-linux-arm64': 0.34.5 - '@img/sharp-linux-ppc64': 0.34.5 - '@img/sharp-linux-riscv64': 0.34.5 - '@img/sharp-linux-s390x': 0.34.5 - '@img/sharp-linux-x64': 0.34.5 - '@img/sharp-linuxmusl-arm64': 0.34.5 - '@img/sharp-linuxmusl-x64': 0.34.5 - '@img/sharp-wasm32': 0.34.5 - '@img/sharp-win32-arm64': 0.34.5 - '@img/sharp-win32-ia32': 0.34.5 - '@img/sharp-win32-x64': 0.34.5 - optional: true - - sharp@0.35.3(@types/node@20.16.7): + sharp@0.35.4(@types/node@24.13.4): dependencies: '@img/colour': 1.1.0 detect-libc: 2.1.2 semver: 7.8.5 optionalDependencies: - '@img/sharp-darwin-arm64': 0.35.3 - '@img/sharp-darwin-x64': 0.35.3 - '@img/sharp-freebsd-wasm32': 0.35.3 - '@img/sharp-libvips-darwin-arm64': 1.3.2 - '@img/sharp-libvips-darwin-x64': 1.3.2 - '@img/sharp-libvips-linux-arm': 1.3.2 - '@img/sharp-libvips-linux-arm64': 1.3.2 - '@img/sharp-libvips-linux-ppc64': 1.3.2 - '@img/sharp-libvips-linux-riscv64': 1.3.2 - '@img/sharp-libvips-linux-s390x': 1.3.2 - '@img/sharp-libvips-linux-x64': 1.3.2 - '@img/sharp-libvips-linuxmusl-arm64': 1.3.2 - '@img/sharp-libvips-linuxmusl-x64': 1.3.2 - '@img/sharp-linux-arm': 0.35.3 - '@img/sharp-linux-arm64': 0.35.3 - '@img/sharp-linux-ppc64': 0.35.3 - '@img/sharp-linux-riscv64': 0.35.3 - '@img/sharp-linux-s390x': 0.35.3 - '@img/sharp-linux-x64': 0.35.3 - '@img/sharp-linuxmusl-arm64': 0.35.3 - '@img/sharp-linuxmusl-x64': 0.35.3 - '@img/sharp-webcontainers-wasm32': 0.35.3 - '@img/sharp-win32-arm64': 0.35.3 - '@img/sharp-win32-ia32': 0.35.3 - '@img/sharp-win32-x64': 0.35.3 - '@types/node': 20.16.7 + '@img/sharp-darwin-arm64': 0.35.4 + '@img/sharp-darwin-x64': 0.35.4 + '@img/sharp-freebsd-wasm32': 0.35.4 + '@img/sharp-libvips-darwin-arm64': 1.3.3 + '@img/sharp-libvips-darwin-x64': 1.3.3 + '@img/sharp-libvips-linux-arm': 1.3.3 + '@img/sharp-libvips-linux-arm64': 1.3.3 + '@img/sharp-libvips-linux-ppc64': 1.3.3 + '@img/sharp-libvips-linux-riscv64': 1.3.3 + '@img/sharp-libvips-linux-s390x': 1.3.3 + '@img/sharp-libvips-linux-x64': 1.3.3 + '@img/sharp-libvips-linuxmusl-arm64': 1.3.3 + '@img/sharp-libvips-linuxmusl-x64': 1.3.3 + '@img/sharp-linux-arm': 0.35.4 + '@img/sharp-linux-arm64': 0.35.4 + '@img/sharp-linux-ppc64': 0.35.4 + '@img/sharp-linux-riscv64': 0.35.4 + '@img/sharp-linux-s390x': 0.35.4 + '@img/sharp-linux-x64': 0.35.4 + '@img/sharp-linuxmusl-arm64': 0.35.4 + '@img/sharp-linuxmusl-x64': 0.35.4 + '@img/sharp-webcontainers-wasm32': 0.35.4 + '@img/sharp-win32-arm64': 0.35.4 + '@img/sharp-win32-ia32': 0.35.4 + '@img/sharp-win32-x64': 0.35.4 + '@types/node': 24.13.4 optional: true shebang-command@2.0.0: @@ -31677,21 +32573,21 @@ snapshots: dot-case: 3.0.4 tslib: 2.8.1 - socket.io-client@4.8.3(bufferutil@4.1.0)(utf-8-validate@6.0.6): + socket.io-client@4.8.3(bufferutil@4.1.0)(supports-color@10.2.2)(utf-8-validate@6.0.6): dependencies: '@socket.io/component-emitter': 3.1.2 - debug: 4.4.3 - engine.io-client: 6.6.5(bufferutil@4.1.0)(utf-8-validate@6.0.6) - socket.io-parser: 4.2.6 + debug: 4.4.3(supports-color@10.2.2) + engine.io-client: 6.6.5(bufferutil@4.1.0)(supports-color@10.2.2)(utf-8-validate@6.0.6) + socket.io-parser: 4.2.6(supports-color@10.2.2) transitivePeerDependencies: - bufferutil - supports-color - utf-8-validate - socket.io-parser@4.2.6: + socket.io-parser@4.2.6(supports-color@10.2.2): dependencies: '@socket.io/component-emitter': 3.1.2 - debug: 4.4.3 + debug: 4.4.3(supports-color@10.2.2) transitivePeerDependencies: - supports-color @@ -31718,6 +32614,8 @@ snapshots: source-map@0.6.1: {} + source-map@0.7.6: {} + space-separated-tokens@2.0.2: {} sparse-array@1.3.2: {} @@ -31746,7 +32644,7 @@ snapshots: statuses@2.0.2: {} - std-env@4.1.0: {} + std-env@4.2.0: {} stop-iteration-iterator@1.1.0: dependencies: @@ -31773,6 +32671,8 @@ snapshots: string-template@0.2.1: {} + string-ts@2.3.1: {} + string-width@4.2.3: dependencies: emoji-regex: 8.0.0 @@ -31854,12 +32754,6 @@ snapshots: dependencies: safe-buffer: 5.2.1 - stringify-object@3.3.0: - dependencies: - get-own-enumerable-property-symbols: 3.0.2 - is-obj: 1.0.1 - is-regexp: 1.0.0 - strip-ansi@6.0.1: dependencies: ansi-regex: 5.0.1 @@ -31870,6 +32764,8 @@ snapshots: strip-bom@3.0.0: {} + strip-final-newline@4.0.0: {} + strip-hex-prefix@1.0.0: dependencies: is-hex-prefixed: 1.0.0 @@ -31882,24 +32778,29 @@ snapshots: dependencies: '@tokenizer/token': 0.3.0 - style-loader@3.3.4(webpack@5.107.2(esbuild@0.25.12)(postcss@8.5.15)): + style-loader@3.3.4(webpack@5.107.2(csso@5.0.5)(esbuild@0.25.12)(postcss@8.5.15)(uglify-js@3.19.3)): dependencies: - webpack: 5.107.2(esbuild@0.25.12)(postcss@8.5.15) + webpack: 5.107.2(csso@5.0.5)(esbuild@0.25.12)(postcss@8.5.15)(uglify-js@3.19.3) style-mod@4.1.3: {} - styled-jsx@5.1.6(@babel/core@7.29.7)(react@19.1.4): + styled-jsx@5.1.6(@babel/core@7.29.7(supports-color@10.2.2))(babel-plugin-macros@3.1.0)(react@19.1.4): dependencies: client-only: 0.0.1 react: 19.1.4 optionalDependencies: - '@babel/core': 7.29.7 - - stylis@4.2.0: {} + '@babel/core': 7.29.7(supports-color@10.2.2) + babel-plugin-macros: 3.1.0 - stylus-lookup@5.0.1: + styled-jsx@5.1.6(@babel/core@8.0.1)(babel-plugin-macros@3.1.0)(react@19.1.4): dependencies: - commander: 10.0.1 + client-only: 0.0.1 + react: 19.1.4 + optionalDependencies: + '@babel/core': 8.0.1 + babel-plugin-macros: 3.1.0 + + stylis@4.2.0: {} super-regex@0.2.0: dependencies: @@ -31956,7 +32857,7 @@ snapshots: picocolors: 1.1.1 sax: 1.6.0 - swagger-client@3.37.4: + swagger-client@3.37.4(debug@4.4.3(supports-color@10.2.2)): dependencies: '@babel/runtime-corejs3': 7.29.7 '@scarf/scarf': 1.4.0 @@ -31965,7 +32866,7 @@ snapshots: '@swagger-api/apidom-json-pointer': 1.11.1 '@swagger-api/apidom-ns-openapi-3-1': 1.11.1 '@swagger-api/apidom-ns-openapi-3-2': 1.11.1 - '@swagger-api/apidom-reference': 1.11.1 + '@swagger-api/apidom-reference': 1.11.1(debug@4.4.3(supports-color@10.2.2)) '@swaggerexpert/cookie': 2.0.2 deepmerge: 4.3.1 fast-json-patch: 3.1.1 @@ -31979,7 +32880,7 @@ snapshots: transitivePeerDependencies: - debug - swagger-ui-react@5.32.6(@types/react@19.1.7)(react-dom@19.1.4(react@19.1.4))(react@19.1.4): + swagger-ui-react@5.32.6(@types/react@19.1.7)(debug@4.4.3(supports-color@10.2.2))(react-dom@19.1.4(react@19.1.4))(react@19.1.4): dependencies: '@babel/runtime-corejs3': 7.29.7 '@scarf/scarf': 1.4.0 @@ -32012,7 +32913,7 @@ snapshots: reselect: 5.2.0 serialize-error: 8.1.0 sha.js: 2.4.12 - swagger-client: 3.37.4 + swagger-client: 3.37.4(debug@4.4.3(supports-color@10.2.2)) url-parse: 1.5.10 xml: 1.0.1 xml-but-prettier: 1.0.1 @@ -32056,16 +32957,18 @@ snapshots: dependencies: bintrees: 1.0.2 - terser-webpack-plugin@5.6.1(esbuild@0.25.12)(postcss@8.5.15)(webpack@5.107.2(esbuild@0.25.12)(postcss@8.5.15)): + terser-webpack-plugin@5.6.1(csso@5.0.5)(esbuild@0.25.12)(postcss@8.5.15)(uglify-js@3.19.3)(webpack@5.107.2(csso@5.0.5)(esbuild@0.25.12)(postcss@8.5.15)(uglify-js@3.19.3)): dependencies: '@jridgewell/trace-mapping': 0.3.31 jest-worker: 27.5.1 schema-utils: 4.3.3 terser: 5.48.0 - webpack: 5.107.2(esbuild@0.25.12)(postcss@8.5.15) + webpack: 5.107.2(csso@5.0.5)(esbuild@0.25.12)(postcss@8.5.15)(uglify-js@3.19.3) optionalDependencies: + csso: 5.0.5 esbuild: 0.25.12 postcss: 8.5.15 + uglify-js: 3.19.3 terser@5.48.0: dependencies: @@ -32116,10 +33019,12 @@ snapshots: tiny-warning@1.0.3: {} - tinybench@2.9.0: {} + tinybench@6.1.4: {} tinyexec@1.2.2: {} + tinyexec@1.3.0: {} + tinyglobby@0.2.16: dependencies: fdir: 6.5.0(picomatch@4.0.4) @@ -32130,7 +33035,7 @@ snapshots: fdir: 6.5.0(picomatch@4.0.4) picomatch: 4.0.4 - tinyrainbow@3.1.0: {} + tinyrainbow@3.1.1: {} tldts-core@6.1.86: {} @@ -32190,6 +33095,8 @@ snapshots: dependencies: punycode: 2.3.1 + tree-kill@1.2.2: {} + tree-sitter-json@0.24.8(tree-sitter@0.21.1): dependencies: node-addon-api: 8.8.0 @@ -32226,14 +33133,14 @@ snapshots: ts-mixer@6.0.4: {} - ts-node@10.9.2(@types/node@20.16.7)(@typescript/typescript6@6.0.2): + ts-node@10.9.2(@types/node@24.13.4)(@typescript/typescript6@6.0.2): dependencies: '@cspotcode/source-map-support': 0.8.1 '@tsconfig/node10': 1.0.12 '@tsconfig/node12': 1.0.11 '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 - '@types/node': 20.16.7 + '@types/node': 24.13.4 acorn: 8.16.0 acorn-walk: 8.3.5 arg: 4.1.3 @@ -32244,6 +33151,8 @@ snapshots: v8-compile-cache-lib: 3.0.1 yn: 3.1.1 + ts-pattern@5.9.0: {} + ts-toolbelt@9.6.0: {} tsconfck@3.1.6(@typescript/typescript6@6.0.2): @@ -32257,12 +33166,6 @@ snapshots: minimist: 1.2.8 strip-bom: 3.0.0 - tsconfig-paths@4.2.0: - dependencies: - json5: 2.2.3 - minimist: 1.2.8 - strip-bom: 3.0.0 - tslib@1.14.1: {} tslib@2.4.1: {} @@ -32274,11 +33177,6 @@ snapshots: tslib: 1.14.1 typescript: '@typescript/typescript6@6.0.2' - tsutils@3.21.0(typescript@5.9.3): - dependencies: - tslib: 1.14.1 - typescript: 5.9.3 - tsyringe@4.10.0: dependencies: tslib: 1.14.1 @@ -32287,6 +33185,8 @@ snapshots: dependencies: safe-buffer: 5.2.1 + tunnel@0.0.6: {} + type-check@0.4.0: dependencies: prelude-ls: 1.2.1 @@ -32332,17 +33232,27 @@ snapshots: possible-typed-array-names: 1.1.0 reflect.getprototypeof: 1.0.10 + typed-inject@5.0.0: {} + + typed-rest-client@2.3.1: + dependencies: + des.js: 1.1.0 + js-md4: 0.3.2 + qs: 6.15.1 + tunnel: 0.0.6 + underscore: 1.13.8 + types-ramda@0.30.1: dependencies: ts-toolbelt: 9.6.0 - typescript-eslint@8.59.2(@typescript/typescript6@6.0.2)(eslint@9.39.2): + typescript-eslint@8.59.2(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2): dependencies: - '@typescript-eslint/eslint-plugin': 8.59.2(@typescript-eslint/parser@8.59.2(@typescript/typescript6@6.0.2)(eslint@9.39.2))(@typescript/typescript6@6.0.2)(eslint@9.39.2) - '@typescript-eslint/parser': 8.59.2(@typescript/typescript6@6.0.2)(eslint@9.39.2) - '@typescript-eslint/typescript-estree': 8.59.2(@typescript/typescript6@6.0.2) - '@typescript-eslint/utils': 8.59.2(@typescript/typescript6@6.0.2)(eslint@9.39.2) - eslint: 9.39.2 + '@typescript-eslint/eslint-plugin': 8.59.2(@typescript-eslint/parser@8.59.2(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2))(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) + '@typescript-eslint/parser': 8.59.2(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) + '@typescript-eslint/typescript-estree': 8.59.2(@typescript/typescript6@6.0.2)(supports-color@10.2.2) + '@typescript-eslint/utils': 8.59.2(@typescript/typescript6@6.0.2)(eslint@9.39.2(supports-color@10.2.2))(supports-color@10.2.2) + eslint: 9.39.2(supports-color@10.2.2) typescript: '@typescript/typescript6@6.0.2' transitivePeerDependencies: - supports-color @@ -32430,12 +33340,11 @@ snapshots: uncrypto@0.1.3: {} - undici-types@6.19.8: {} + underscore@1.13.8: {} - undici-types@6.20.0: {} + undici-types@7.18.2: {} - undici-types@7.24.6: - optional: true + undici-types@7.24.6: {} undici-types@7.26.0: {} @@ -32454,6 +33363,8 @@ snapshots: unicode-property-aliases-ecmascript@2.2.0: {} + unicorn-magic@0.3.0: {} + unicorn-magic@0.4.0: {} universalify@2.0.1: {} @@ -32641,15 +33552,15 @@ snapshots: varint@6.0.0: {} - viem@2.23.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76): + viem@2.23.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3): dependencies: '@noble/curves': 1.8.1 '@noble/hashes': 1.8.0 '@scure/bip32': 1.6.2 '@scure/bip39': 1.5.4 - abitype: 1.0.8(@typescript/typescript6@6.0.2)(zod@3.25.76) + abitype: 1.0.8(@typescript/typescript6@6.0.2)(zod@4.4.3) isows: 1.0.6(ws@8.21.0(bufferutil@4.1.0)(utf-8-validate@6.0.6)) - ox: 0.6.7(@typescript/typescript6@6.0.2)(zod@3.25.76) + ox: 0.6.7(@typescript/typescript6@6.0.2)(zod@4.4.3) ws: 8.21.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) optionalDependencies: typescript: '@typescript/typescript6@6.0.2' @@ -32658,15 +33569,15 @@ snapshots: - utf-8-validate - zod - viem@2.31.0(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76): + viem@2.31.0(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3): dependencies: '@noble/curves': 1.9.1 '@noble/hashes': 1.8.0 '@scure/bip32': 1.7.0 '@scure/bip39': 1.6.0 - abitype: 1.0.8(@typescript/typescript6@6.0.2)(zod@3.25.76) + abitype: 1.0.8(@typescript/typescript6@6.0.2)(zod@4.4.3) isows: 1.0.7(ws@8.21.0(bufferutil@4.1.0)(utf-8-validate@6.0.6)) - ox: 0.7.1(@typescript/typescript6@6.0.2)(zod@3.25.76) + ox: 0.7.1(@typescript/typescript6@6.0.2)(zod@4.4.3) ws: 8.21.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) optionalDependencies: typescript: '@typescript/typescript6@6.0.2' @@ -32709,15 +33620,15 @@ snapshots: - utf-8-validate - zod - viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76): + viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3): dependencies: '@noble/curves': 1.9.1 '@noble/hashes': 1.8.0 '@scure/bip32': 1.7.0 '@scure/bip39': 1.6.0 - abitype: 1.1.0(@typescript/typescript6@6.0.2)(zod@3.25.76) + abitype: 1.1.0(@typescript/typescript6@6.0.2)(zod@4.4.3) isows: 1.0.7(ws@8.21.0(bufferutil@4.1.0)(utf-8-validate@6.0.6)) - ox: 0.9.6(@typescript/typescript6@6.0.2)(zod@3.25.76) + ox: 0.9.6(@typescript/typescript6@6.0.2)(zod@4.4.3) ws: 8.21.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) optionalDependencies: typescript: '@typescript/typescript6@6.0.2' @@ -32743,132 +33654,116 @@ snapshots: - utf-8-validate - zod + viem@2.51.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3): + dependencies: + '@noble/curves': 1.9.1 + '@noble/hashes': 1.8.0 + '@scure/bip32': 1.7.0 + '@scure/bip39': 1.6.0 + abitype: 1.2.3(@typescript/typescript6@6.0.2)(zod@4.4.3) + isows: 1.0.7(ws@8.21.0(bufferutil@4.1.0)(utf-8-validate@6.0.6)) + ox: 0.14.25(@typescript/typescript6@6.0.2)(zod@4.4.3) + ws: 8.21.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) + optionalDependencies: + typescript: '@typescript/typescript6@6.0.2' + transitivePeerDependencies: + - bufferutil + - utf-8-validate + - zod + visit-values@2.0.0: {} - vite-plugin-dts@4.5.4(@types/node@20.16.7)(@typescript/typescript6@6.0.2)(rollup@4.60.4)(vite@6.4.3(@types/node@20.16.7)(terser@5.48.0)(yaml@2.9.0)): + vite-plugin-dts@4.5.4(@types/node@24.13.4)(@typescript/typescript6@6.0.2)(rollup@4.60.4)(supports-color@10.2.2)(vite@6.4.3(@types/node@24.13.4)(terser@5.48.0)(yaml@2.9.0)): dependencies: - '@microsoft/api-extractor': 7.58.7(@types/node@20.16.7) + '@microsoft/api-extractor': 7.58.7(@types/node@24.13.4) '@rollup/pluginutils': 5.3.0(rollup@4.60.4) '@volar/typescript': 2.4.28 '@vue/language-core': 2.2.0(@typescript/typescript6@6.0.2) compare-versions: 6.1.1 - debug: 4.4.3 + debug: 4.4.3(supports-color@10.2.2) kolorist: 1.8.0 local-pkg: 1.2.1 magic-string: 0.30.21 typescript: '@typescript/typescript6@6.0.2' optionalDependencies: - vite: 6.4.3(@types/node@20.16.7)(terser@5.48.0)(yaml@2.9.0) + vite: 6.4.3(@types/node@24.13.4)(terser@5.48.0)(yaml@2.9.0) transitivePeerDependencies: - '@types/node' - rollup - supports-color - vite-plugin-svgr@4.5.0(@typescript/typescript6@6.0.2)(rollup@4.60.4)(vite@6.4.3(@types/node@20.16.7)(terser@5.48.0)(yaml@2.9.0)): + vite-plugin-svgr@4.5.0(@typescript/typescript6@6.0.2)(rollup@4.60.4)(supports-color@10.2.2)(vite@6.4.3(@types/node@24.13.4)(terser@5.48.0)(yaml@2.9.0)): dependencies: '@rollup/pluginutils': 5.3.0(rollup@4.60.4) - '@svgr/core': 8.1.0(@typescript/typescript6@6.0.2) - '@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0(@typescript/typescript6@6.0.2)) - vite: 6.4.3(@types/node@20.16.7)(terser@5.48.0)(yaml@2.9.0) + '@svgr/core': 8.1.0(@typescript/typescript6@6.0.2)(supports-color@10.2.2) + '@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0(@typescript/typescript6@6.0.2)(supports-color@10.2.2))(supports-color@10.2.2) + vite: 6.4.3(@types/node@24.13.4)(terser@5.48.0)(yaml@2.9.0) transitivePeerDependencies: - rollup - supports-color - typescript - vite-plugin-svgr@5.2.0(@typescript/typescript6@6.0.2)(rollup@4.60.4)(vite@6.4.3(@types/node@20.16.7)(terser@5.48.0)(yaml@2.9.0)): + vite-plugin-svgr@5.2.0(@typescript/typescript6@6.0.2)(rollup@4.60.4)(supports-color@10.2.2)(vite@6.4.3(@types/node@24.13.4)(terser@5.48.0)(yaml@2.9.0)): dependencies: '@rollup/pluginutils': 5.3.0(rollup@4.60.4) - '@svgr/core': 8.1.0(@typescript/typescript6@6.0.2) - '@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0(@typescript/typescript6@6.0.2)) - vite: 6.4.3(@types/node@20.16.7)(terser@5.48.0)(yaml@2.9.0) + '@svgr/core': 8.1.0(@typescript/typescript6@6.0.2)(supports-color@10.2.2) + '@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0(@typescript/typescript6@6.0.2)(supports-color@10.2.2))(supports-color@10.2.2) + vite: 6.4.3(@types/node@24.13.4)(terser@5.48.0)(yaml@2.9.0) transitivePeerDependencies: - rollup - supports-color - typescript - vite-tsconfig-paths@5.1.4(@typescript/typescript6@6.0.2)(vite@6.4.3(@types/node@20.16.7)(terser@5.48.0)(yaml@2.9.0)): + vite-tsconfig-paths@5.1.4(@typescript/typescript6@6.0.2)(supports-color@10.2.2)(vite@6.4.3(@types/node@24.13.4)(terser@5.48.0)(yaml@2.9.0)): dependencies: - debug: 4.4.3 + debug: 4.4.3(supports-color@10.2.2) globrex: 0.1.2 tsconfck: 3.1.6(@typescript/typescript6@6.0.2) optionalDependencies: - vite: 6.4.3(@types/node@20.16.7)(terser@5.48.0)(yaml@2.9.0) + vite: 6.4.3(@types/node@24.13.4)(terser@5.48.0)(yaml@2.9.0) transitivePeerDependencies: - supports-color - typescript - vite@6.4.3(@types/node@20.16.7)(terser@5.48.0)(yaml@2.9.0): - dependencies: - esbuild: 0.25.12 - fdir: 6.5.0(picomatch@4.0.4) - picomatch: 4.0.4 - postcss: 8.5.15 - rollup: 4.60.4 - tinyglobby: 0.2.16 - optionalDependencies: - '@types/node': 20.16.7 - fsevents: 2.3.3 - terser: 5.48.0 - yaml: 2.9.0 - - vite@6.4.3(@types/node@22.12.0)(terser@5.48.0)(yaml@2.9.0): - dependencies: - esbuild: 0.25.12 - fdir: 6.5.0(picomatch@4.0.4) - picomatch: 4.0.4 - postcss: 8.5.15 - rollup: 4.60.4 - tinyglobby: 0.2.16 - optionalDependencies: - '@types/node': 22.12.0 - fsevents: 2.3.3 - terser: 5.48.0 - yaml: 2.9.0 - - vite@6.4.3(@types/node@25.9.2)(terser@5.48.0)(yaml@2.9.0): + vite@6.4.3(@types/node@24.13.4)(terser@5.48.0)(yaml@2.9.0): dependencies: esbuild: 0.25.12 fdir: 6.5.0(picomatch@4.0.4) picomatch: 4.0.4 - postcss: 8.5.15 + postcss: 8.5.23 rollup: 4.60.4 - tinyglobby: 0.2.16 + tinyglobby: 0.2.17 optionalDependencies: - '@types/node': 25.9.2 + '@types/node': 24.13.4 fsevents: 2.3.3 terser: 5.48.0 yaml: 2.9.0 - vitest-fetch-mock@0.4.5(vitest@4.1.7(@opentelemetry/api@1.9.1)(@types/node@20.16.7)(jsdom@27.3.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(vite@6.4.3(@types/node@20.16.7)(terser@5.48.0)(yaml@2.9.0))): + vitest-fetch-mock@0.4.5(vitest@5.0.0): dependencies: - vitest: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@20.16.7)(jsdom@27.3.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(vite@6.4.3(@types/node@20.16.7)(terser@5.48.0)(yaml@2.9.0)) + vitest: 5.0.0(@opentelemetry/api@1.9.1)(@types/node@24.13.4)(@vitest/coverage-v8@5.0.0)(jsdom@27.3.0(bufferutil@4.1.0)(supports-color@10.2.2)(utf-8-validate@6.0.6))(vite@6.4.3(@types/node@24.13.4)(terser@5.48.0)(yaml@2.9.0)) - vitest@4.1.7(@opentelemetry/api@1.9.1)(@types/node@20.16.7)(jsdom@27.3.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))(vite@6.4.3(@types/node@20.16.7)(terser@5.48.0)(yaml@2.9.0)): + vitest@5.0.0(@opentelemetry/api@1.9.1)(@types/node@24.13.4)(@vitest/coverage-v8@5.0.0)(jsdom@27.3.0(bufferutil@4.1.0)(supports-color@10.2.2)(utf-8-validate@6.0.6))(vite@6.4.3(@types/node@24.13.4)(terser@5.48.0)(yaml@2.9.0)): dependencies: - '@vitest/expect': 4.1.7 - '@vitest/mocker': 4.1.7(vite@6.4.3(@types/node@20.16.7)(terser@5.48.0)(yaml@2.9.0)) - '@vitest/pretty-format': 4.1.7 - '@vitest/runner': 4.1.7 - '@vitest/snapshot': 4.1.7 - '@vitest/spy': 4.1.7 - '@vitest/utils': 4.1.7 - es-module-lexer: 2.1.0 - expect-type: 1.3.0 - magic-string: 0.30.21 - obug: 2.1.1 - pathe: 2.0.3 - picomatch: 4.0.4 - std-env: 4.1.0 - tinybench: 2.9.0 - tinyexec: 1.2.2 - tinyglobby: 0.2.16 - tinyrainbow: 3.1.0 - vite: 6.4.3(@types/node@20.16.7)(terser@5.48.0)(yaml@2.9.0) + '@types/chai': 5.2.3 + '@vitest/mocker': 5.0.0(vite@6.4.3(@types/node@24.13.4)(terser@5.48.0)(yaml@2.9.0)) + chai: 6.2.2 + es-module-lexer: 2.3.2 + expect-type: 1.4.0 + magic-string: 1.3.1 + obug: 2.2.1 + picomatch: 4.0.7 + std-env: 4.2.0 + tinybench: 6.1.4 + tinyexec: 1.3.0 + tinyglobby: 0.2.17 + vite: 6.4.3(@types/node@24.13.4)(terser@5.48.0)(yaml@2.9.0) why-is-node-running: 2.3.0 optionalDependencies: '@opentelemetry/api': 1.9.1 - '@types/node': 20.16.7 - jsdom: 27.3.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) + '@types/node': 24.13.4 + '@vitest/coverage-v8': 5.0.0(vitest@5.0.0) + jsdom: 27.3.0(bufferutil@4.1.0)(supports-color@10.2.2)(utf-8-validate@6.0.6) transitivePeerDependencies: - msw @@ -32888,14 +33783,14 @@ snapshots: dependencies: xml-name-validator: 5.0.0 - wagmi@2.19.5(@tanstack/query-core@5.55.4)(@tanstack/react-query@5.55.4(react@19.1.4))(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.85.3(@babel/core@7.29.7)(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(utf-8-validate@6.0.6))(react@19.1.4)(utf-8-validate@6.0.6)(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76))(zod@3.25.76): + wagmi@2.19.5(@tanstack/query-core@5.55.4)(@tanstack/react-query@5.55.4(react@19.1.4))(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(debug@4.4.3(supports-color@10.2.2))(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.85.3(@babel/core@7.29.7(supports-color@10.2.2))(@types/react@19.1.7)(bufferutil@4.1.0)(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6))(react@19.1.4)(supports-color@10.2.2)(utf-8-validate@6.0.6)(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3))(zod@4.4.3): dependencies: '@tanstack/react-query': 5.55.4(react@19.1.4) - '@wagmi/connectors': 6.2.0(09e58c13ebbf9932f0a56e13a5b340ff) - '@wagmi/core': 2.22.1(@tanstack/query-core@5.55.4)(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(react@19.1.4)(use-sync-external-store@1.4.0(react@19.1.4))(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76)) + '@wagmi/connectors': 6.2.0(df3f0d3afef4414b7f4264d6db7d6a30) + '@wagmi/core': 2.22.1(@tanstack/query-core@5.55.4)(@types/react@19.1.7)(@typescript/typescript6@6.0.2)(react@19.1.4)(use-sync-external-store@1.4.0(react@19.1.4))(viem@2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3)) react: 19.1.4 use-sync-external-store: 1.4.0(react@19.1.4) - viem: 2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@3.25.76) + viem: 2.41.2(@typescript/typescript6@6.0.2)(bufferutil@4.1.0)(utf-8-validate@6.0.6)(zod@4.4.3) optionalDependencies: typescript: '@typescript/typescript6@6.0.2' transitivePeerDependencies: @@ -32952,12 +33847,16 @@ snapshots: ms: 4.0.0-nightly.202508271359 supports-color: 10.2.2 + weapon-regex@2.0.4: {} + web-streams-polyfill@3.3.3: {} web-tree-sitter@0.24.5: optional: true - web-vitals@5.2.0: {} + web-vitals@6.2.1: {} + + web-vitals@6.2.2: {} web3-utils@1.10.4: dependencies: @@ -33013,7 +33912,7 @@ snapshots: webpack-sources@3.5.0: {} - webpack@5.107.2(esbuild@0.25.12)(postcss@8.5.15): + webpack@5.107.2(csso@5.0.5)(esbuild@0.25.12)(postcss@8.5.15)(uglify-js@3.19.3): dependencies: '@types/estree': 1.0.9 '@types/json-schema': 7.0.15 @@ -33025,7 +33924,7 @@ snapshots: browserslist: 4.28.2 chrome-trace-event: 1.0.4 enhanced-resolve: 5.23.0 - es-module-lexer: 2.1.0 + es-module-lexer: 2.3.2 eslint-scope: 5.1.1 events: 3.3.0 glob-to-regexp: 0.4.1 @@ -33035,7 +33934,7 @@ snapshots: neo-async: 2.6.2 schema-utils: 4.3.3 tapable: 2.3.3 - terser-webpack-plugin: 5.6.1(esbuild@0.25.12)(postcss@8.5.15)(webpack@5.107.2(esbuild@0.25.12)(postcss@8.5.15)) + terser-webpack-plugin: 5.6.1(csso@5.0.5)(esbuild@0.25.12)(postcss@8.5.15)(uglify-js@3.19.3)(webpack@5.107.2(csso@5.0.5)(esbuild@0.25.12)(postcss@8.5.15)(uglify-js@3.19.3)) watchpack: 2.5.1 webpack-sources: 3.5.0 transitivePeerDependencies: @@ -33240,6 +34139,8 @@ snapshots: yocto-queue@0.1.0: {} + yoctocolors@2.2.0: {} + yup@0.32.11: dependencies: '@babel/runtime': 7.29.7 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 8306071f981..219661795eb 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -11,10 +11,17 @@ packages: - 'deploy/tools/multichain-config-generator' - 'deploy/tools/essential-dapps-chains-config-generator' - 'deploy/tools/llms-txt-generator' - - 'deploy/tools/affected-tests' verifyDepsBeforeRun: warn +# The widget-light EVM handler is patched for these wagmi v2 / viem versions. +# Resolve wallet clients per request; explicit chainId supports syncConnectedChain: false. +peerDependencyRules: + allowedVersions: + '@lifi/widget-light@4.3.2>wagmi': '2.19.5' + '@lifi/widget-light@4.3.2>@wagmi/core': '2.22.1' + '@lifi/widget-light@4.3.2>viem': '2.41.2' + overrides: '@wagmi/connectors': "6.2.0" ws: "8.21.0" @@ -25,6 +32,7 @@ overrides: js-yaml: "4.1.1" "@noble/hashes": "1.8.0" "vite-plugin-dts>brace-expansion": "2.0.2" + "minimatch@10>brace-expansion": "5.0.9" protobufjs: "7.5.5" # pnpm 11+: allowlisted dependency install scripts (required for native addons). @@ -64,3 +72,7 @@ minimumReleaseAgeExclude: - '@blockscout/tac-operation-lifecycle-types' - '@blockscout/visualizer-types' - '@blockscout/zetachain-cctx-types' + +patchedDependencies: + '@lifi/widget-light@4.3.2': patches/@lifi__widget-light@4.3.2.patch + '@stryker-mutator/vitest-runner@10.0.0': patches/@stryker-mutator__vitest-runner@10.0.0.patch diff --git a/public/icons/name.d.ts b/public/icons/name.d.ts index e86e0f18072..67f88f41e95 100644 --- a/public/icons/name.d.ts +++ b/public/icons/name.d.ts @@ -89,7 +89,6 @@ | "lightning" | "link_external" | "link" - | "list_view" | "lock" | "merits_colored" | "merits_with_dot" diff --git a/src/api/config.ts b/src/api/config.ts index 17cd051a6d7..43978a10368 100644 --- a/src/api/config.ts +++ b/src/api/config.ts @@ -2,7 +2,8 @@ import { pickBy } from 'es-toolkit'; -import type { ApiName } from 'src/api/types'; +import type { Apis } from 'src/api/types'; +// eslint-disable-next-line boundaries/element-types -- the list of stats resources with a refetch interval is owned by the chain-stats feature import { STATS_API_RESOURCES_REFETCH_INTERVAL } from 'src/features/chain-stats/types/config'; import type { StatsApiResourceNameRefetchInterval } from 'src/features/chain-stats/types/config'; @@ -10,23 +11,6 @@ import { getEnvValue, parseEnvJson } from 'src/config/utils/envs'; import { stripTrailingSlash } from 'src/toolkit/utils/url'; -import type { ResourceName } from './resources'; - -export interface ApiPropsBase { - endpoint: string; - basePath?: string; - socketEndpoint?: string; - instanceId?: string; - refetchInterval?: Partial>; -} - -export interface ApiPropsFull extends ApiPropsBase { - host: string; - protocol: string; - port?: string; - socketEndpoint: string; -} - const coreApi = (() => { const apiHost = getEnvValue('NEXT_PUBLIC_API_HOST'); if (!apiHost) { @@ -243,10 +227,6 @@ const zetachainApi = (() => { } })(); -export type Apis = { - core: ApiPropsFull | undefined; -} & Partial, ApiPropsBase>>; - const apis: Apis = Object.freeze({ core: coreApi, admin: adminApi, diff --git a/src/api/hooks/useApiQuery.ts b/src/api/hooks/useApiQuery.ts index 8cd6710c029..ad5052abe80 100644 --- a/src/api/hooks/useApiQuery.ts +++ b/src/api/hooks/useApiQuery.ts @@ -5,6 +5,7 @@ import { useQuery } from '@tanstack/react-query'; import type { ExternalChainExtended } from 'src/shared/external-chains/types'; +// eslint-disable-next-line boundaries/element-types -- there is no better way to get chain value from multichain context in this global hook import { useMultichainContext } from 'src/features/multichain/context'; import type { ResourceError, ResourceName, ResourcePathParams, ResourcePayload } from '../resources'; diff --git a/src/api/resources/services/core/token.ts b/src/api/resources/services/core/token.ts index 86e152b4fb2..5c78da14199 100644 --- a/src/api/resources/services/core/token.ts +++ b/src/api/resources/services/core/token.ts @@ -26,6 +26,12 @@ export const CORE_API_TOKEN_RESOURCES = { filterFields: [], paginated: true, }, + token_ui_multiplier_changes: { + path: '/api/v2/tokens/:hash/ui-multiplier-changes', + pathParams: [ 'hash' as const ], + filterFields: [], + paginated: true, + }, token_transfers: { path: '/api/v2/tokens/:hash/transfers', pathParams: [ 'hash' as const ], @@ -96,6 +102,7 @@ R extends 'core:token' ? paths['/api/v2/tokens/{address_hash_param}']['get'] : R extends 'core:token_counters' ? paths['/api/v2/tokens/{address_hash_param}/counters']['get'] : R extends 'core:token_transfers' ? paths['/api/v2/tokens/{address_hash_param}/transfers']['get'] : R extends 'core:token_holders' ? paths['/api/v2/tokens/{address_hash_param}/holders']['get'] : +R extends 'core:token_ui_multiplier_changes' ? paths['/api/v2/tokens/{address_hash_param}/ui-multiplier-changes']['get'] : R extends 'core:token_instance' ? paths['/api/v2/tokens/{address_hash_param}/instances/{token_id_param}']['get'] : R extends 'core:token_instance_transfers_count' ? paths['/api/v2/tokens/{address_hash_param}/instances/{token_id_param}/transfers-count']['get'] : R extends 'core:token_instance_transfers' ? paths['/api/v2/tokens/{address_hash_param}/instances/{token_id_param}/transfers']['get'] : diff --git a/src/api/resources/services/interchain-indexer.ts b/src/api/resources/services/interchain-indexer.ts index 701550ee838..3e93c2e7215 100644 --- a/src/api/resources/services/interchain-indexer.ts +++ b/src/api/resources/services/interchain-indexer.ts @@ -11,10 +11,12 @@ import type { export const INTERCHAIN_INDEXER_API_RESOURCES = { chains: { path: '/api/v1/interchain/chains', + scopeFilters: [ 'bridge_ids', 'include_unindexed_chains' ], }, messages: { path: '/api/v1/interchain/messages', filterFields: [ 'q' as const ], + scopeFilters: [ 'home_chain_id', 'bridge_ids', 'include_unindexed_chains' ], paginated: true, }, message: { @@ -25,50 +27,54 @@ export const INTERCHAIN_INDEXER_API_RESOURCES = { path: '/api/v1/interchain/messages\\:byTx/:hash', pathParams: [ 'hash' as const ], filterFields: [ 'q' as const ], + scopeFilters: [ 'home_chain_id', 'bridge_ids', 'include_unindexed_chains' ], paginated: true, }, address_messages: { path: '/api/v1/interchain/messages\\:byAddress/:hash', pathParams: [ 'hash' as const ], + scopeFilters: [ 'home_chain_id', 'bridge_ids', 'include_unindexed_chains' ], paginated: true, }, transfers: { path: '/api/v1/interchain/transfers', filterFields: [ 'q' as const ], + scopeFilters: [ 'home_chain_id', 'bridge_ids', 'include_unindexed_chains' ], paginated: true, }, tx_transfers: { path: '/api/v1/interchain/transfers\\:byTx/:hash', pathParams: [ 'hash' as const ], filterFields: [ 'q' as const ], + scopeFilters: [ 'home_chain_id', 'bridge_ids', 'include_unindexed_chains' ], paginated: true, }, address_transfers: { path: '/api/v1/interchain/transfers\\:byAddress/:hash', pathParams: [ 'hash' as const ], + scopeFilters: [ 'home_chain_id', 'bridge_ids', 'include_unindexed_chains' ], paginated: true, }, - stats_daily: { - path: '/api/v1/stats/daily', - }, - stats_common: { - path: '/api/v1/stats/common', - }, stats_chains: { path: '/api/v1/stats/chains', + scopeFilters: [ 'bridge_ids', 'include_unindexed_chains' ], paginated: true, }, stats_chain_messages_sent: { path: '/api/v1/stats/chain/:chainId/messages-paths/sent', pathParams: [ 'chainId' as const ], + scopeFilters: [ 'bridge_ids', 'include_unindexed_chains' ], }, stats_chain_messages_received: { path: '/api/v1/stats/chain/:chainId/messages-paths/received', pathParams: [ 'chainId' as const ], + scopeFilters: [ 'bridge_ids', 'include_unindexed_chains' ], }, bridged_tokens: { path: '/api/v1/stats/chain/:chainId/bridged-tokens', pathParams: [ 'chainId' as const ], + filterFields: [ 'q' as const ], + scopeFilters: [ 'bridge_ids', 'include_unindexed_chains' ], paginated: true, }, } satisfies Record; @@ -85,8 +91,6 @@ R extends 'interchainIndexer:address_messages' ? interchainIndexer.GetMessagesRe R extends 'interchainIndexer:transfers' ? interchainIndexer.GetTransfersResponse : R extends 'interchainIndexer:tx_transfers' ? interchainIndexer.GetTransfersResponse : R extends 'interchainIndexer:address_transfers' ? interchainIndexer.GetTransfersResponse : -R extends 'interchainIndexer:stats_daily' ? interchainIndexer.GetDailyStatisticsResponse : -R extends 'interchainIndexer:stats_common' ? interchainIndexer.GetCommonStatisticsResponse : R extends 'interchainIndexer:stats_chains' ? interchainIndexer.GetChainsStatsResponse : R extends 'interchainIndexer:bridged_tokens' ? interchainIndexer.GetBridgedTokensResponse : R extends 'interchainIndexer:stats_chain_messages_sent' ? interchainIndexer.GetMessagePathsResponse : diff --git a/src/api/resources/services/multichain-stats.ts b/src/api/resources/services/multichain-stats.ts index 350af7c6949..7f30bd7e87b 100644 --- a/src/api/resources/services/multichain-stats.ts +++ b/src/api/resources/services/multichain-stats.ts @@ -4,6 +4,9 @@ import type { ApiResource } from '../types'; import type * as stats from '@blockscout/stats-types'; export const MULTICHAIN_STATS_API_RESOURCES = { + counters: { + path: '/api/v1/counters', + }, pages_main: { path: '/api/v1/pages/multichain/main', }, @@ -13,6 +16,7 @@ export type MultichainStatsApiResourceName = `multichainStats:${ keyof typeof MU /* eslint-disable @stylistic/indent */ export type MultichainStatsApiResourcePayload = +R extends 'multichainStats:counters' ? stats.Counters : R extends 'multichainStats:pages_main' ? stats.MainPageMultichainStats : never; /* eslint-enable @stylistic/indent */ diff --git a/src/api/resources/types.ts b/src/api/resources/types.ts index 20152b85556..b528292affd 100644 --- a/src/api/resources/types.ts +++ b/src/api/resources/types.ts @@ -4,6 +4,7 @@ export interface ApiResource { path: string; pathParams?: Array; filterFields?: Array; + scopeFilters?: Array; paginated?: boolean; headers?: RequestInit['headers']; } diff --git a/src/api/socket/SocketNewItemsNotice.tsx b/src/api/socket/SocketNewItemsNotice.tsx index 9c69e2d5106..f8bbd18116a 100644 --- a/src/api/socket/SocketNewItemsNotice.tsx +++ b/src/api/socket/SocketNewItemsNotice.tsx @@ -113,12 +113,3 @@ export const Desktop = ({ ...props }: Props) => { ); }; - -export const Mobile = ({ ...props }: Props) => { - return ( - - ); -}; diff --git a/src/api/socket/useSocketChannel.ts b/src/api/socket/useSocketChannel.ts index 5d161ffb65e..ae60012ff9a 100644 --- a/src/api/socket/useSocketChannel.ts +++ b/src/api/socket/useSocketChannel.ts @@ -40,13 +40,6 @@ export default function useSocketChannel({ topic, params, isDisabled, onJoin, on return cleanUpRefs; }, [ onSocketClose, onSocketError, socket, isDisabled ]); - useEffect(() => { - if (isDisabled && channel) { - channel.leave(); - setChannel(undefined); - } - }, [ channel, isDisabled ]); - useEffect(() => { if (!socket || isDisabled || !topic || !channelRegistry) { return; diff --git a/src/api/types.ts b/src/api/types.ts index 3a747e9669c..3aa5d883b70 100644 --- a/src/api/types.ts +++ b/src/api/types.ts @@ -1,6 +1,27 @@ // SPDX-License-Identifier: LicenseRef-Blockscout +import type { ResourceName } from './resources'; + export type ApiName = 'core' | 'admin' | 'bens' | 'contractInfo' | 'clusters' | 'external' | 'interchainIndexer' | 'metadata' | 'multichainAggregator' | 'multichainStats' | 'rewards' | 'stats' | 'tac' | 'userOps' | 'visualize' | 'zetachain'; + +export interface ApiPropsBase { + endpoint: string; + basePath?: string; + socketEndpoint?: string; + instanceId?: string; + refetchInterval?: Partial>; +} + +export interface ApiPropsFull extends ApiPropsBase { + host: string; + protocol: string; + port?: string; + socketEndpoint: string; +} + +export type Apis = { + core: ApiPropsFull | undefined; +} & Partial, ApiPropsBase>>; diff --git a/src/api/utils/build-url.ts b/src/api/utils/build-url.ts index d2b87255255..efc8311541b 100644 --- a/src/api/utils/build-url.ts +++ b/src/api/utils/build-url.ts @@ -9,6 +9,7 @@ import config from 'src/config'; import type { ResourceName, ResourcePathParams } from '../resources'; import getResourceParams from './get-resource-params'; import isNeedProxy from './is-need-proxy'; +import { resolveScopeFilters } from './scope-filters'; export default function buildUrl( resourceFullName: R, @@ -17,13 +18,14 @@ export default function buildUrl( noProxy?: boolean, chain?: ExternalChainExtended, ): string { - const { api, resource } = getResourceParams(resourceFullName, chain); + const { api, apiName, resource } = getResourceParams(resourceFullName, chain); const baseUrl = !noProxy && isNeedProxy() ? config.app.baseUrl : api.endpoint; const basePath = api.basePath ?? ''; const path = !noProxy && isNeedProxy() ? '/node-api/proxy' + basePath + resource.path : basePath + resource.path; const url = new URL(compile(path)(pathParams), baseUrl); + const params = { ...queryParams, ...resolveScopeFilters(apiName, resource.scopeFilters, chain) }; - queryParams && Object.entries(queryParams).forEach(([ key, value ]) => { + Object.entries(params).forEach(([ key, value ]) => { // there are some pagination params that can be null or false for the next page value !== undefined && value !== '' && url.searchParams.append(key, String(value)); }); diff --git a/src/api/utils/get-resource-params.ts b/src/api/utils/get-resource-params.ts index bfa8f4cd89e..c53a92f96bc 100644 --- a/src/api/utils/get-resource-params.ts +++ b/src/api/utils/get-resource-params.ts @@ -1,16 +1,15 @@ // SPDX-License-Identifier: LicenseRef-Blockscout -import type { ApiResource } from '../resources/types'; import type { ApiName } from '../types'; import type { ExternalChainExtended } from 'src/shared/external-chains/types'; import config from 'src/config'; import type { ResourceName } from '../resources'; -import { RESOURCES } from '../resources'; +import { getResource } from './get-resource'; export default function getResourceParams(resourceFullName: ResourceName, chain?: ExternalChainExtended) { - const [ apiName, resourceName ] = resourceFullName.split(':') as [ ApiName, string ]; + const [ apiName ] = resourceFullName.split(':') as [ ApiName, string ]; const apiConfig = (() => { if (chain?.app_config?.apis) { @@ -27,6 +26,6 @@ export default function getResourceParams(resourceFullName: ResourceName, chain? return { api: apiConfig, apiName, - resource: RESOURCES[apiName][resourceName as keyof typeof RESOURCES[ApiName]] as ApiResource, + resource: getResource(resourceFullName), }; } diff --git a/src/api/utils/get-resource.spec.ts b/src/api/utils/get-resource.spec.ts new file mode 100644 index 00000000000..4dc1d8e5ff2 --- /dev/null +++ b/src/api/utils/get-resource.spec.ts @@ -0,0 +1,18 @@ +import { describe, it, expect } from 'vitest'; + +import { getResource } from './get-resource'; + +describe('getResource', () => { + it('returns the registry definition of a resource', () => { + expect(getResource('core:address_txs')).toEqual({ + path: '/api/v2/addresses/:hash/transactions', + pathParams: [ 'hash' ], + filterFields: [ 'filter' ], + paginated: true, + }); + }); + + it('does not depend on the API being configured', () => { + expect(getResource('interchainIndexer:bridged_tokens').path).toEqual(expect.any(String)); + }); +}); diff --git a/src/api/utils/get-resource.ts b/src/api/utils/get-resource.ts new file mode 100644 index 00000000000..e0354f78d86 --- /dev/null +++ b/src/api/utils/get-resource.ts @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: LicenseRef-Blockscout + +import type { ApiResource } from '../resources/types'; +import type { ApiName } from '../types'; + +import type { ResourceName } from '../resources'; +import { RESOURCES } from '../resources'; + +export function getResource(resourceFullName: ResourceName): ApiResource { + const [ apiName, resourceName ] = resourceFullName.split(':') as [ ApiName, keyof typeof RESOURCES[ApiName] ]; + return RESOURCES[apiName][resourceName]; +} diff --git a/src/api/utils/scope-filters.spec.ts b/src/api/utils/scope-filters.spec.ts new file mode 100644 index 00000000000..79334dbdd08 --- /dev/null +++ b/src/api/utils/scope-filters.spec.ts @@ -0,0 +1,235 @@ +import type { ApiResource } from '../resources/types'; +import type { ExternalChainExtended } from 'src/shared/external-chains/types'; + +// eslint-disable-next-line boundaries/element-types -- the spec needs a real multichain fixture; the api → features ban targets runtime code +import { chainA } from 'src/features/multichain/mocks/chains'; + +import { ENVS_MAP } from 'src/config/test-utils/env-presets'; + +import { describe, expect, test, vi } from 'vitest'; +import withEnvs from 'vitest/utils/mockEnvs'; + +import type { ResourceName } from '../resources'; +import { RESOURCES } from '../resources'; +import { SCOPE_FILTER_RESOLVERS } from './scope-filters'; + +const INTERCHAIN_HOST = 'http://localhost:3014'; +// NEXT_PUBLIC_NETWORK_ID in vitest/.env.vitest +const DEPLOYMENT_CHAIN_ID = '1'; +const BRIDGE_IDS = '1,2'; +const BRIDGE_IDS_ENCODED = encodeURIComponent(BRIDGE_IDS); +const PATH_PARAM_STUB = 'stub'; +const NO_SCOPE_FILTERS: Array = []; + +const CROSS_CHAIN_ENVS = ENVS_MAP.crossChainTxs; +const MULTICHAIN_ENVS = [ ...ENVS_MAP.crossChainTxs, ...ENVS_MAP.multichain ]; + +// a cluster chain always carries `app_config.apis`, and `getResourceParams` prefers the host declared +// there over the deployment's own — so the fixture has to name the indexer it points at +const CHAIN_IN_SCOPE = { + ...chainA, + app_config: { + ...chainA.app_config, + apis: { + ...chainA.app_config.apis, + interchainIndexer: { endpoint: INTERCHAIN_HOST }, + }, + }, +}; + +type BuildUrl = ( + resource: ResourceName, + pathParams?: Record, + queryParams?: Record, + noProxy?: boolean, + chain?: ExternalChainExtended, +) => string; + +// the resource name and its path params are only known at runtime here, so buildUrl's per-resource +// generics cannot be satisfied — the registry walk below is the whole point of the test +const importBuildUrl = async() => (await import('./build-url')).default as unknown as BuildUrl; + +function buildUrlWithEnvs( + envs: Array<[ string, string ]>, + resource: ResourceName, + { pathParams, queryParams, chain }: { + pathParams?: Record; + queryParams?: Record; + chain?: ExternalChainExtended; + } = {}, +) { + return withEnvs(envs, async() => { + const buildUrl = await importBuildUrl(); + return buildUrl(resource, pathParams, queryParams, undefined, chain); + }); +} + +describe('scope filter derivation', () => { + test('multichain off — the deployment chain is the focal chain', async() => { + const url = await buildUrlWithEnvs(CROSS_CHAIN_ENVS, 'interchainIndexer:messages'); + + expect(url).toBe(`${ INTERCHAIN_HOST }/api/v1/interchain/messages?home_chain_id=1&include_unindexed_chains=false`); + }); + + test('multichain with a chain in scope — that chain is the focal chain, and no bridge_ids narrows it', async() => { + const url = await buildUrlWithEnvs(MULTICHAIN_ENVS, 'interchainIndexer:messages', { chain: CHAIN_IN_SCOPE }); + + expect(url).toBe(`${ INTERCHAIN_HOST }/api/v1/interchain/messages?home_chain_id=${ CHAIN_IN_SCOPE.id }&include_unindexed_chains=false`); + }); + + test('multichain aggregated — the deployment bridges scope the request', async() => { + const url = await buildUrlWithEnvs(MULTICHAIN_ENVS, 'interchainIndexer:messages'); + + expect(url).toBe( + `${ INTERCHAIN_HOST }/api/v1/interchain/messages?bridge_ids=${ BRIDGE_IDS_ENCODED }&include_unindexed_chains=false`, + ); + }); + + test('include_unindexed_chains comes from its own env', async() => { + const url = await buildUrlWithEnvs( + [ ...CROSS_CHAIN_ENVS, [ 'NEXT_PUBLIC_CROSS_CHAIN_TXS_INCLUDE_UNINDEXED_CHAINS', 'true' ] ], + 'interchainIndexer:messages', + ); + + expect(url).toBe(`${ INTERCHAIN_HOST }/api/v1/interchain/messages?home_chain_id=1&include_unindexed_chains=true`); + }); + + test('message details declares no scope filter and carries none', async() => { + const url = await buildUrlWithEnvs(CROSS_CHAIN_ENVS, 'interchainIndexer:message', { pathParams: { id: '0x123' } }); + + expect(url).toBe(`${ INTERCHAIN_HOST }/api/v1/interchain/messages/0x123`); + }); + + test('the caller query params keep their place in front of the profile', async() => { + const url = await buildUrlWithEnvs(CROSS_CHAIN_ENVS, 'interchainIndexer:messages', { + queryParams: { q: 'duck', page_token: 'token' }, + }); + + expect(url).toBe( + `${ INTERCHAIN_HOST }/api/v1/interchain/messages?q=duck&page_token=token&home_chain_id=1&include_unindexed_chains=false`, + ); + }); + + test('a resource declaring bridge_ids only never sends home_chain_id', async() => { + const url = await buildUrlWithEnvs(CROSS_CHAIN_ENVS, 'interchainIndexer:chains'); + + expect(url).toBe( + `${ INTERCHAIN_HOST }/api/v1/interchain/chains?bridge_ids=${ BRIDGE_IDS_ENCODED }&include_unindexed_chains=false`, + ); + }); +}); + +const INTERCHAIN_RESOURCES = Object.entries(RESOURCES.interchainIndexer) as Array<[ string, ApiResource ]>; + +// message details deliberately takes no profile, so a link to it resolves even for a row the lists hide +const UNSCOPED_BY_DESIGN = [ 'message' ]; + +const KNOWN_SCOPE_FILTERS = Object.keys(SCOPE_FILTER_RESOLVERS.interchainIndexer ?? {}); + +function pathParamsStub(resource: ApiResource) { + return Object.fromEntries((resource.pathParams ?? []).map((param) => [ param, PATH_PARAM_STUB ])); +} + +function expectedFocalChainProfile(scopeFilters: Array = NO_SCOPE_FILTERS) { + const chainPredicate = (() => { + if (scopeFilters.includes('home_chain_id')) { + return { home_chain_id: DEPLOYMENT_CHAIN_ID }; + } + return scopeFilters.includes('bridge_ids') ? { bridge_ids: BRIDGE_IDS } : {}; + })(); + + return { + ...chainPredicate, + ...(scopeFilters.includes('include_unindexed_chains') ? { include_unindexed_chains: 'false' } : {}), + }; +} + +function expectedAggregatedProfile(scopeFilters: Array = NO_SCOPE_FILTERS) { + return { + ...(scopeFilters.includes('bridge_ids') ? { bridge_ids: BRIDGE_IDS } : {}), + ...(scopeFilters.includes('include_unindexed_chains') ? { include_unindexed_chains: 'false' } : {}), + }; +} + +describe('every registered resource answers how it is scoped', () => { + test.each(INTERCHAIN_RESOURCES)('interchainIndexer:%s declares a scope', (name, resource) => { + const declared = resource.scopeFilters ?? NO_SCOPE_FILTERS; + + expect(declared.length > 0).toBe(!UNSCOPED_BY_DESIGN.includes(name)); + }); + + test.each(INTERCHAIN_RESOURCES)('interchainIndexer:%s declares only resolvable filters', (name, resource) => { + expect(KNOWN_SCOPE_FILTERS).toEqual(expect.arrayContaining(resource.scopeFilters ?? NO_SCOPE_FILTERS)); + }); +}); + +describe('every declared scope filter reaches the URL of every registered resource', () => { + test.each(INTERCHAIN_RESOURCES)('interchainIndexer:%s on a focal-chain deployment', async(name, resource) => { + const url = await buildUrlWithEnvs(CROSS_CHAIN_ENVS, `interchainIndexer:${ name }` as ResourceName, { + pathParams: pathParamsStub(resource), + }); + + expect(Object.fromEntries(new URL(url).searchParams)).toEqual(expectedFocalChainProfile(resource.scopeFilters)); + }); + + test.each(INTERCHAIN_RESOURCES)('interchainIndexer:%s on an aggregated multichain view', async(name, resource) => { + const url = await buildUrlWithEnvs(MULTICHAIN_ENVS, `interchainIndexer:${ name }` as ResourceName, { + pathParams: pathParamsStub(resource), + }); + + expect(Object.fromEntries(new URL(url).searchParams)).toEqual(expectedAggregatedProfile(resource.scopeFilters)); + }); +}); + +describe('a declared scope filter with no resolver for its own service', () => { + const FIXTURE_RESOURCE_NAME = 'scope_filter_fixture'; + const MISSPELLED_FILTER = 'bridge_id'; + + const FIXTURE_RESOURCES = { + interchainIndexer: { + path: '/api/v1/interchain/fixture', + scopeFilters: [ 'home_chain_id', MISSPELLED_FILTER ], + }, + core: { + path: '/api/v2/fixture', + scopeFilters: [ 'home_chain_id', 'bridge_ids', 'include_unindexed_chains' ], + }, + } satisfies Record; + + type FixtureApiName = keyof typeof FIXTURE_RESOURCES; + + function buildUrlForFixtureResource(envs: Array<[ string, string ]>, apiName: FixtureApiName) { + return withEnvs(envs, async() => { + vi.doMock('../resources', async() => { + const actual = await vi.importActual<{ RESOURCES: typeof RESOURCES }>('../resources'); + + return { + ...actual, + RESOURCES: { + ...actual.RESOURCES, + [apiName]: { ...actual.RESOURCES[apiName], [FIXTURE_RESOURCE_NAME]: FIXTURE_RESOURCES[apiName] }, + }, + }; + }); + + try { + const buildUrl = await importBuildUrl(); + return buildUrl(`${ apiName }:${ FIXTURE_RESOURCE_NAME }` as ResourceName); + } finally { + vi.doUnmock('../resources'); + } + }); + } + + test('is left out of the query string instead of failing the request', async() => { + const url = await buildUrlForFixtureResource(CROSS_CHAIN_ENVS, 'interchainIndexer'); + + expect(url).toBe(`${ INTERCHAIN_HOST }/api/v1/interchain/fixture?home_chain_id=${ DEPLOYMENT_CHAIN_ID }`); + }); + + test('does not inherit the interchain resolver of the same name', async() => { + const url = await buildUrlForFixtureResource(CROSS_CHAIN_ENVS, 'core'); + + expect(Object.fromEntries(new URL(url).searchParams)).toEqual({}); + }); +}); diff --git a/src/api/utils/scope-filters.ts b/src/api/utils/scope-filters.ts new file mode 100644 index 00000000000..1bdd14de999 --- /dev/null +++ b/src/api/utils/scope-filters.ts @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: LicenseRef-Blockscout + +import type { ApiName } from '../types'; +import type { ExternalChainExtended } from 'src/shared/external-chains/types'; + +import config from 'src/config'; + +const crossChainTxsFeature = config.features.crossChainTxs; + +interface ScopeFilterContext { + readonly declaredFilters: ReadonlyArray; + readonly chain?: ExternalChainExtended; +} + +type ScopeFilterResolver = (context: ScopeFilterContext) => string | undefined; + +const resolveFocalChainId: ScopeFilterResolver = ({ chain }) => { + return config.features.multichain.isEnabled ? chain?.id : config.chain.id; +}; + +const resolveBridgeIds: ScopeFilterResolver = (context) => { + if (crossChainTxsFeature.isEnabled) { + const hasFocalChain = context.declaredFilters.includes('home_chain_id') && Boolean(resolveFocalChainId(context)); + + if (hasFocalChain || crossChainTxsFeature.bridgeIds.length === 0) { + return; + } + + return crossChainTxsFeature.bridgeIds.join(','); + } +}; + +const resolveIncludeUnindexedChains: ScopeFilterResolver = () => { + if (crossChainTxsFeature.isEnabled) { + return String(crossChainTxsFeature.includeUnindexedChains); + } +}; + +export const SCOPE_FILTER_RESOLVERS: Partial>> = { + interchainIndexer: { + home_chain_id: resolveFocalChainId, + bridge_ids: resolveBridgeIds, + include_unindexed_chains: resolveIncludeUnindexedChains, + }, +}; + +const NO_SCOPE_FILTERS: ReadonlyArray = []; +const NO_RESOLVERS: Record = {}; + +export function resolveScopeFilters( + apiName: ApiName, + scopeFilters: ReadonlyArray | undefined, + chain?: ExternalChainExtended, +): Record { + const resolvers = SCOPE_FILTER_RESOLVERS[apiName] ?? NO_RESOLVERS; + const declaredFilters = scopeFilters ?? NO_SCOPE_FILTERS; + const context: ScopeFilterContext = { declaredFilters, chain }; + + const entries = declaredFilters + .map((name) => [ name, resolvers[name]?.(context) ] as const) + .filter((entry): entry is [ string, string ] => entry[1] !== undefined); + + return Object.fromEntries(entries); +} diff --git a/src/config/CONTEXT.md b/src/config/CONTEXT.md new file mode 100644 index 00000000000..5b170c62d66 --- /dev/null +++ b/src/config/CONTEXT.md @@ -0,0 +1,39 @@ +# App config — context + +`src/config/index.ts` assembles the app config from the co-located config modules: `src/api/config.ts`, +`src/features/**/config.ts`, `src/slices/*/config.ts`, `src/shell/*/config.ts`, `src/services/*/config.ts`. +Each module turns raw env values into typed, validated data. The aggregator is the only place the app reads +that data from. + +## The convention + +Three rules. ESLint enforces all of them (`eslint.config.mjs`, the "App config convention" blocks), so a +violation fails before review. + +1. **A config module has no export besides its default.** Everything the feature owns lives inside the + config object, widening its `Feature`. `Feature<>` hides the payload when `isEnabled` is + `false`. A value exported beside the object stays readable while the feature is off, which is the state + the type exists to rule out. Types and constant lists go to a sibling role file — `types/config.ts`, + `types.ts`, `consts.ts` — never the config module. +2. **Only `src/config` is imported.** App code reads `config.
.` (the sections: + `.agents/rules/env-vars.md`). It never imports the module itself. +3. **Only config modules read envs.** `src/config/utils/envs.ts` is importable from a `config.ts` or + `src/config/**`, nowhere else, and the raw `window.__envs` map is read by `getEnvValue` only. A value + read elsewhere is untyped, unvalidated and invisible to the aggregator. It can also differ between the + browser and Node. A util that called `getEnvValue` directly read one value in the browser and another + under Node, and broke an unrelated test suite in a way that showed up only as a visual diff. + +## Exemptions the rules allow + +- **Config-to-config imports.** A config module may import another config module directly, for example a + feature gated on whether another one is enabled. The aggregator cannot be used from inside a module it + aggregates. +- **A `config.spec.ts`** imports the module under test directly and dynamically, under `withEnvs`, because + the config is a frozen module-level singleton. The exemption is by file name, not by neighbourhood. +- **Type-only imports** of a config module carry no runtime payload and pass. +- **Companions.** `/types/config.ts` and `/mocks/config.ts` are not config modules and + export freely. +- **Outside `src/`.** `playwright/`, `vitest/`, `deploy/` and `tools/` are not checked. + +The Node env-validator constraint on these files (no React, browser APIs, or code from other slices and +features) lives in `.agents/rules/architecture.md`. diff --git a/src/config/services.ts b/src/config/services.ts index 563efe8f512..105b01a4edb 100644 --- a/src/config/services.ts +++ b/src/config/services.ts @@ -3,6 +3,7 @@ export { default as googleAnalytics } from 'src/services/google-analytics/config'; export { default as growthBook } from 'src/services/growthbook/config'; export { default as mixpanel } from 'src/services/mixpanel/config'; +export { default as posthog } from 'src/services/posthog/config'; export { default as reCaptcha } from 'src/services/re-captcha/config'; export { default as rollbar } from 'src/services/rollbar/config'; export { default as usercentrics } from 'src/services/usercentrics/config'; diff --git a/src/config/test-utils/env-presets.ts b/src/config/test-utils/env-presets.ts index e7e726707f9..d4b2639e05d 100644 --- a/src/config/test-utils/env-presets.ts +++ b/src/config/test-utils/env-presets.ts @@ -132,6 +132,7 @@ export const ENVS_MAP: Record> = { crossChainTxs: [ [ 'NEXT_PUBLIC_INTERCHAIN_INDEXER_API_HOST', 'http://localhost:3014' ], [ 'NEXT_PUBLIC_CROSS_CHAIN_TXS_ENABLED', 'true' ], + [ 'NEXT_PUBLIC_CROSS_CHAIN_TXS_BRIDGE_IDS', '[1,2]' ], ], proApi: [ [ 'NEXT_PUBLIC_PRO_API_SUPPORTED', 'true' ], @@ -139,11 +140,16 @@ export const ENVS_MAP: Record> = { flashblocks: [ [ 'NEXT_PUBLIC_FLASHBLOCKS_SOCKET_URL', 'wss://localhost:3120/ws' ], ], +<<<<<<< HEAD tokenActionButton: [ [ 'NEXT_PUBLIC_TOKEN_ACTION_BUTTON_CONFIG', '{"text":"Buy on Duck Portal","url":"https://portal.duck.io/swap?chainId=1&token={hash}","colors":{"_default":{"bg":["rgb(179, 167, 0)","rgb(134, 86, 239)"],"text":["white"]}}}', ], +======= + additionalTokenTypes: [ + [ 'NEXT_PUBLIC_NETWORK_ADDITIONAL_TOKEN_TYPES', '[{"id":"ERC-7984","name":"ERC-7984"},{"id":"ERC-8056","name":"ERC-8056"}]' ], +>>>>>>> v2.12.0 ], verifiedAddresses: [ [ 'NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED', 'true' ], diff --git a/src/features/account/components/auth-modal/AuthModal.tsx b/src/features/account/components/auth-modal/AuthModal.tsx index d414acaa396..867960ebe03 100644 --- a/src/features/account/components/auth-modal/AuthModal.tsx +++ b/src/features/account/components/auth-modal/AuthModal.tsx @@ -12,7 +12,7 @@ import useGetCsrfToken from 'src/features/account/hooks/useGetCsrfToken'; import { useRewardsContext } from 'src/features/rewards/context'; import config from 'src/config'; -import * as mixpanel from 'src/services/mixpanel'; +import * as analytics from 'src/shared/analytics'; import { DialogBody, DialogContent, DialogHeader, DialogRoot } from 'src/toolkit/chakra/dialog'; @@ -28,18 +28,18 @@ const feature = config.features.account; interface Props { initialScreen: Screen; onClose: (isSuccess?: boolean, rewardsApiToken?: string) => void; - mixpanelConfig?: { + analyticsConfig?: { wallet_connect?: { - source: mixpanel.EventPayload['Source']; + source: analytics.EventPayload['Source']; }; account_link_info: { - source: mixpanel.EventPayload['Source']; + source: analytics.EventPayload['Source']; }; }; closeOnError?: boolean; } -const AuthModal = ({ initialScreen, onClose, mixpanelConfig, closeOnError }: Props) => { +const AuthModal = ({ initialScreen, onClose, analyticsConfig, closeOnError }: Props) => { const [ steps, setSteps ] = React.useState>([ initialScreen ]); const [ isSuccess, setIsSuccess ] = React.useState(false); const [ rewardsApiToken, setRewardsApiToken ] = React.useState(undefined); @@ -51,18 +51,18 @@ const AuthModal = ({ initialScreen, onClose, mixpanelConfig, closeOnError }: Pro React.useEffect(() => { if ('isAuth' in initialScreen && initialScreen.isAuth) { - mixpanel.logEvent(mixpanel.EventTypes.ACCOUNT_LINK_INFO, { + analytics.logEvent(analytics.EventTypes.ACCOUNT_LINK_INFO, { Status: 'Started', Type: initialScreen.type === 'connect_wallet' ? 'Wallet' : 'Email', - Source: mixpanelConfig?.account_link_info.source ?? 'Profile dropdown', + Source: analyticsConfig?.account_link_info.source ?? 'Profile dropdown', }); } else { - mixpanel.logEvent(mixpanel.EventTypes.LOGIN, { + analytics.logEvent(analytics.EventTypes.LOGIN, { Action: 'Started', - Source: mixpanel.getPageType(router.pathname), + Source: analytics.getPageType(router.pathname), }); } - }, [ initialScreen, mixpanelConfig, router.pathname ]); + }, [ initialScreen, analyticsConfig, router.pathname ]); const onNextStep = React.useCallback((screen: Screen) => { setSteps((prev) => [ ...prev, screen ]); @@ -80,13 +80,13 @@ const AuthModal = ({ initialScreen, onClose, mixpanelConfig, closeOnError }: Pro setIsSuccess(true); if ('isAuth' in initialScreen && initialScreen.isAuth) { - mixpanel.logEvent(mixpanel.EventTypes.ACCOUNT_LINK_INFO, { + analytics.logEvent(analytics.EventTypes.ACCOUNT_LINK_INFO, { Status: 'Finished', Type: screen.type === 'success_wallet' ? 'Wallet' : 'Email', - Source: mixpanelConfig?.account_link_info.source ?? 'Profile dropdown', + Source: analyticsConfig?.account_link_info.source ?? 'Profile dropdown', }); } else { - mixpanel.logEvent(mixpanel.EventTypes.LOGIN, { + analytics.logEvent(analytics.EventTypes.LOGIN, { Action: 'Success', Source: screen.type === 'success_wallet' ? 'Wallet' : 'Email', }); @@ -101,7 +101,7 @@ const AuthModal = ({ initialScreen, onClose, mixpanelConfig, closeOnError }: Pro } onNextStep(screen); - }, [ initialScreen, mixpanelConfig?.account_link_info.source, onNextStep, csrfQuery, queryClient, onRewardsLoginSuccess ]); + }, [ initialScreen, analyticsConfig?.account_link_info.source, onNextStep, csrfQuery, queryClient, onRewardsLoginSuccess ]); const onModalClose = React.useCallback(() => { onClose(isSuccess, rewardsApiToken); @@ -140,7 +140,7 @@ const AuthModal = ({ initialScreen, onClose, mixpanelConfig, closeOnError }: Pro onError={ onReset } isAuth={ currentStep.isAuth } loginToRewards={ currentStep.loginToRewards } - source={ mixpanelConfig?.wallet_connect?.source } + source={ analyticsConfig?.wallet_connect?.source } /> ); case 'email': @@ -148,7 +148,7 @@ const AuthModal = ({ initialScreen, onClose, mixpanelConfig, closeOnError }: Pro ); case 'otp_code': diff --git a/src/features/account/components/auth-modal/guard/AuthGuard.tsx b/src/features/account/components/auth-modal/guard/AuthGuard.ts similarity index 100% rename from src/features/account/components/auth-modal/guard/AuthGuard.tsx rename to src/features/account/components/auth-modal/guard/AuthGuard.ts diff --git a/src/features/account/components/auth-modal/guard/AuthGuardDynamic.tsx b/src/features/account/components/auth-modal/guard/AuthGuardDynamic.ts similarity index 100% rename from src/features/account/components/auth-modal/guard/AuthGuardDynamic.tsx rename to src/features/account/components/auth-modal/guard/AuthGuardDynamic.ts diff --git a/src/features/account/components/auth-modal/screens/AuthModalScreenConnectWallet.tsx b/src/features/account/components/auth-modal/screens/AuthModalScreenConnectWallet.tsx index c0a2f010ee6..cc12731281a 100644 --- a/src/features/account/components/auth-modal/screens/AuthModalScreenConnectWallet.tsx +++ b/src/features/account/components/auth-modal/screens/AuthModalScreenConnectWallet.tsx @@ -8,15 +8,15 @@ import type { UserInfo } from 'src/features/account/types/api'; import useSignInWithWallet from 'src/features/account/hooks/useSignInWithWallet'; -import type * as mixpanel from 'src/services/mixpanel'; import ReCaptcha from 'src/services/re-captcha/ReCaptcha'; import useReCaptcha from 'src/services/re-captcha/useReCaptcha'; +import type * as analytics from 'src/shared/analytics'; interface Props { onSuccess: (screen: ScreenSuccess) => void; onError: (isAuth?: boolean) => void; isAuth?: boolean; - source?: mixpanel.EventPayload['Source']; + source?: analytics.EventPayload['Source']; loginToRewards?: boolean; } diff --git a/src/features/account/components/auth-modal/screens/AuthModalScreenEmail.tsx b/src/features/account/components/auth-modal/screens/AuthModalScreenEmail.tsx index 24b5f1f30f4..2d7285f092a 100644 --- a/src/features/account/components/auth-modal/screens/AuthModalScreenEmail.tsx +++ b/src/features/account/components/auth-modal/screens/AuthModalScreenEmail.tsx @@ -9,9 +9,9 @@ import type { EmailFormFields, Screen } from '../types'; import useApiFetch from 'src/api/hooks/useApiFetch'; -import * as mixpanel from 'src/services/mixpanel'; import ReCaptcha from 'src/services/re-captcha/ReCaptcha'; import useReCaptcha from 'src/services/re-captcha/useReCaptcha'; +import * as analytics from 'src/shared/analytics'; import getErrorMessage from 'src/shared/errors/get-error-message'; import getErrorObjPayload from 'src/shared/errors/get-error-obj-payload'; @@ -22,14 +22,14 @@ import { FormFieldEmail } from 'src/toolkit/components/forms/fields/FormFieldEma interface Props { onSubmit: (screen: Screen) => void; isAuth?: boolean; - mixpanelConfig?: { + analyticsConfig?: { account_link_info: { - source: mixpanel.EventPayload['Source']; + source: analytics.EventPayload['Source']; }; }; } -const AuthModalScreenEmail = ({ onSubmit, isAuth, mixpanelConfig }: Props) => { +const AuthModalScreenEmail = ({ onSubmit, isAuth, analyticsConfig }: Props) => { const apiFetch = useApiFetch(); const recaptcha = useReCaptcha(); @@ -58,13 +58,13 @@ const AuthModalScreenEmail = ({ onSubmit, isAuth, mixpanelConfig }: Props) => { await recaptcha.fetchProtectedResource(sendCodeFetchFactory(formData.email)); if (isAuth) { - mixpanelConfig?.account_link_info.source !== 'Profile' && mixpanel.logEvent(mixpanel.EventTypes.ACCOUNT_LINK_INFO, { - Source: mixpanelConfig?.account_link_info.source ?? 'Profile dropdown', + analyticsConfig?.account_link_info.source !== 'Profile' && analytics.logEvent(analytics.EventTypes.ACCOUNT_LINK_INFO, { + Source: analyticsConfig?.account_link_info.source ?? 'Profile dropdown', Status: 'OTP sent', Type: 'Email', }); } else { - mixpanel.logEvent(mixpanel.EventTypes.LOGIN, { + analytics.logEvent(analytics.EventTypes.LOGIN, { Action: 'OTP sent', Source: 'Email', }); @@ -76,7 +76,7 @@ const AuthModalScreenEmail = ({ onSubmit, isAuth, mixpanelConfig }: Props) => { description: getErrorObjPayload<{ message: string }>(error)?.message || getErrorMessage(error) || 'Something went wrong', }); } - }, [ recaptcha, sendCodeFetchFactory, isAuth, onSubmit, mixpanelConfig?.account_link_info.source ]); + }, [ recaptcha, sendCodeFetchFactory, isAuth, onSubmit, analyticsConfig?.account_link_info.source ]); return ( diff --git a/src/features/account/components/auth-modal/screens/AuthModalScreenSelectMethod.tsx b/src/features/account/components/auth-modal/screens/AuthModalScreenSelectMethod.tsx index 1f841a437a3..a1d04e5ce2c 100644 --- a/src/features/account/components/auth-modal/screens/AuthModalScreenSelectMethod.tsx +++ b/src/features/account/components/auth-modal/screens/AuthModalScreenSelectMethod.tsx @@ -5,7 +5,7 @@ import React from 'react'; import type { Screen } from '../types'; -import * as mixpanel from 'src/services/mixpanel'; +import * as analytics from 'src/shared/analytics'; import { Button } from 'src/toolkit/chakra/button'; @@ -16,7 +16,7 @@ interface Props { const AuthModalScreenSelectMethod = ({ onSelectMethod }: Props) => { const handleEmailClick = React.useCallback(() => { - mixpanel.logEvent(mixpanel.EventTypes.LOGIN, { + analytics.logEvent(analytics.EventTypes.LOGIN, { Action: 'Email', Source: 'Options selector', }); @@ -24,7 +24,7 @@ const AuthModalScreenSelectMethod = ({ onSelectMethod }: Props) => { }, [ onSelectMethod ]); const handleConnectWalletClick = React.useCallback(() => { - mixpanel.logEvent(mixpanel.EventTypes.LOGIN, { + analytics.logEvent(analytics.EventTypes.LOGIN, { Action: 'Wallet', Source: 'Options selector', }); diff --git a/src/features/account/components/auth-modal/screens/AuthModalScreenSuccessWallet.tsx b/src/features/account/components/auth-modal/screens/AuthModalScreenSuccessWallet.tsx index e9cdae22541..d958324a3a1 100644 --- a/src/features/account/components/auth-modal/screens/AuthModalScreenSuccessWallet.tsx +++ b/src/features/account/components/auth-modal/screens/AuthModalScreenSuccessWallet.tsx @@ -7,10 +7,10 @@ import type { Screen } from '../types'; import type { UserInfo } from 'src/features/account/types/api'; import config from 'src/config'; -import shortenString from 'src/shared/texts/shorten-string'; import { Button } from 'src/toolkit/chakra/button'; import { apos } from 'src/toolkit/utils/htmlEntities'; +import { shortenString } from 'src/toolkit/utils/texts'; interface Props { address: string; diff --git a/src/features/account/components/user-profile/auth0/UserProfileButton.tsx b/src/features/account/components/user-profile/auth0/UserProfileButton.tsx index 5929cf7fc41..bc086e53946 100644 --- a/src/features/account/components/user-profile/auth0/UserProfileButton.tsx +++ b/src/features/account/components/user-profile/auth0/UserProfileButton.tsx @@ -12,11 +12,11 @@ import useWeb3AccountWithDomain from 'src/features/connect-wallet/hooks/useAccou import { useMarketplaceContext } from 'src/features/marketplace/context'; import useIsMobile from 'src/shared/hooks/useIsMobile'; -import shortenString from 'src/shared/texts/shorten-string'; import SpriteIcon from 'src/sprite/SpriteIcon'; import { Button } from 'src/toolkit/chakra/button'; import { Tooltip } from 'src/toolkit/chakra/tooltip'; +import { shortenString } from 'src/toolkit/utils/texts'; import UserIdenticon from '../UserIdenticon'; diff --git a/src/features/account/components/user-profile/auth0/UserProfileContent.tsx b/src/features/account/components/user-profile/auth0/UserProfileContent.tsx index f642e2e6e8f..61a50688023 100644 --- a/src/features/account/components/user-profile/auth0/UserProfileContent.tsx +++ b/src/features/account/components/user-profile/auth0/UserProfileContent.tsx @@ -12,12 +12,12 @@ import { useMarketplaceContext } from 'src/features/marketplace/context'; import config from 'src/config'; import { getFeaturePayload } from 'src/config/utils/features'; -import shortenString from 'src/shared/texts/shorten-string'; import { Button } from 'src/toolkit/chakra/button'; import { Link } from 'src/toolkit/chakra/link'; import { Hint } from 'src/toolkit/components/Hint/Hint'; -import { TruncatedText } from 'src/toolkit/components/truncation/TruncatedText'; +import { Truncate } from 'src/toolkit/components/truncation/Truncate'; +import { shortenString } from 'src/toolkit/utils/texts'; import UserProfileContentWallet from '../common/UserProfileContentWallet'; import UserWalletAutoConnectAlert from '../UserWalletAutoConnectAlert'; @@ -111,7 +111,7 @@ const UserProfileContent = ({ data, onClose, onLogin, onAddEmail, onAddAddress } Email { data?.email ? - : Add email } + : Add email } diff --git a/src/features/account/components/user-profile/auth0/UserProfileDesktop.tsx b/src/features/account/components/user-profile/auth0/UserProfileDesktop.tsx index 80f3651d5ab..57243d6bd6d 100644 --- a/src/features/account/components/user-profile/auth0/UserProfileDesktop.tsx +++ b/src/features/account/components/user-profile/auth0/UserProfileDesktop.tsx @@ -11,7 +11,7 @@ import useProfileQuery from 'src/features/account/hooks/useProfileQuery'; import useAccount from 'src/features/connect-wallet/hooks/useAccount'; import config from 'src/config'; -import * as mixpanel from 'src/services/mixpanel'; +import * as analytics from 'src/shared/analytics'; import { PopoverBody, PopoverContent, PopoverRoot, PopoverTrigger } from 'src/toolkit/chakra/popover'; import { useDisclosure } from 'src/toolkit/hooks/useDisclosure'; @@ -40,7 +40,7 @@ const UserProfileDesktop = ({ buttonSize, buttonVariant = 'header' }: Props) => const handleProfileButtonClick = React.useCallback(() => { if (profileQuery.data || web3Address) { - mixpanel.logEvent(mixpanel.EventTypes.ACCOUNT_ACCESS, { Action: 'Dropdown open' }); + analytics.logEvent(analytics.EventTypes.ACCOUNT_ACCESS, { Action: 'Dropdown open' }); profileMenu.onOpen(); return; } diff --git a/src/features/account/components/user-profile/auth0/UserProfileMobile.tsx b/src/features/account/components/user-profile/auth0/UserProfileMobile.tsx index 164d0af9941..33c8e8b0a61 100644 --- a/src/features/account/components/user-profile/auth0/UserProfileMobile.tsx +++ b/src/features/account/components/user-profile/auth0/UserProfileMobile.tsx @@ -10,7 +10,7 @@ import useProfileQuery from 'src/features/account/hooks/useProfileQuery'; import useAccount from 'src/features/connect-wallet/hooks/useAccount'; import config from 'src/config'; -import * as mixpanel from 'src/services/mixpanel'; +import * as analytics from 'src/shared/analytics'; import { DrawerBody, DrawerContent, DrawerRoot, DrawerTrigger } from 'src/toolkit/chakra/drawer'; import { useDisclosure } from 'src/toolkit/hooks/useDisclosure'; @@ -34,7 +34,7 @@ const UserProfileMobile = () => { const handleProfileButtonClick = React.useCallback(() => { if (profileQuery.data || web3Address) { - mixpanel.logEvent(mixpanel.EventTypes.ACCOUNT_ACCESS, { Action: 'Dropdown open' }); + analytics.logEvent(analytics.EventTypes.ACCOUNT_ACCESS, { Action: 'Dropdown open' }); profileMenu.onOpen(); return; } diff --git a/src/features/account/components/user-profile/dynamic/UserProfileButton.tsx b/src/features/account/components/user-profile/dynamic/UserProfileButton.tsx index ae8065707e4..4e3f96dca2d 100644 --- a/src/features/account/components/user-profile/dynamic/UserProfileButton.tsx +++ b/src/features/account/components/user-profile/dynamic/UserProfileButton.tsx @@ -7,10 +7,10 @@ import { getUserHandle } from 'src/features/account/utils/user-handle'; import useAccountWithDomain from 'src/features/connect-wallet/hooks/useAccountWithDomain'; import { useMarketplaceContext } from 'src/features/marketplace/context'; -import shortenString from 'src/shared/texts/shorten-string'; import SpriteIcon from 'src/sprite/SpriteIcon'; import { Button, type ButtonProps } from 'src/toolkit/chakra/button'; +import { shortenString } from 'src/toolkit/utils/texts'; import UserIdenticon from '../UserIdenticon'; diff --git a/src/features/account/components/user-profile/wallet/UserWalletButton.tsx b/src/features/account/components/user-profile/wallet/UserWalletButton.tsx index a8d89cb25c4..40ddc3c8616 100644 --- a/src/features/account/components/user-profile/wallet/UserWalletButton.tsx +++ b/src/features/account/components/user-profile/wallet/UserWalletButton.tsx @@ -5,10 +5,10 @@ import { Box, HStack } from '@chakra-ui/react'; import React from 'react'; import useIsMobile from 'src/shared/hooks/useIsMobile'; -import shortenString from 'src/shared/texts/shorten-string'; import { Button } from 'src/toolkit/chakra/button'; import { Tooltip } from 'src/toolkit/chakra/tooltip'; +import { shortenString } from 'src/toolkit/utils/texts'; import UserIdenticon from '../UserIdenticon'; diff --git a/src/features/account/hooks/useGetCsrfToken.tsx b/src/features/account/hooks/useGetCsrfToken.ts similarity index 100% rename from src/features/account/hooks/useGetCsrfToken.tsx rename to src/features/account/hooks/useGetCsrfToken.ts diff --git a/src/features/account/hooks/useLinkEmailDynamic.ts b/src/features/account/hooks/useLinkEmailDynamic.ts index 81765301ecb..bea5288f6d9 100644 --- a/src/features/account/hooks/useLinkEmailDynamic.ts +++ b/src/features/account/hooks/useLinkEmailDynamic.ts @@ -8,14 +8,14 @@ import type { UserInfo } from 'src/features/account/types/api'; import { getResourceKey } from 'src/api/hooks/useApiQuery'; -import * as mixpanel from 'src/services/mixpanel'; +import * as analytics from 'src/shared/analytics'; export default function useLinkEmailDynamic(): () => void { const { updateUserWithModal } = useUserUpdateRequest(); const queryClient = useQueryClient(); return React.useCallback(() => { - mixpanel.logEvent(mixpanel.EventTypes.ACCOUNT_LINK_INFO, { + analytics.logEvent(analytics.EventTypes.ACCOUNT_LINK_INFO, { Status: 'Started', Type: 'Email', Source: 'Profile dropdown', @@ -24,7 +24,7 @@ export default function useLinkEmailDynamic(): () => void { queryClient.setQueryData(getResourceKey('core:user_info'), (prevData: UserInfo | undefined) => { return { ...prevData, email: fields.email }; }); - mixpanel.logEvent(mixpanel.EventTypes.ACCOUNT_LINK_INFO, { + analytics.logEvent(analytics.EventTypes.ACCOUNT_LINK_INFO, { Status: 'Finished', Type: 'Email', Source: 'Profile dropdown', diff --git a/src/features/account/hooks/useLogout.ts b/src/features/account/hooks/useLogout.ts index 86c0ca4e178..2640fa997ad 100644 --- a/src/features/account/hooks/useLogout.ts +++ b/src/features/account/hooks/useLogout.ts @@ -12,7 +12,7 @@ import useWalletReown from 'src/features/connect-wallet/hooks/wallet/useWalletRe import { useRewardsContext } from 'src/features/rewards/context'; import config from 'src/config'; -import * as mixpanel from 'src/services/mixpanel'; +import * as analytics from 'src/shared/analytics'; import * as cookies from 'src/shared/storage/cookies'; import { toaster } from 'src/toolkit/chakra/toaster'; @@ -44,8 +44,8 @@ export default function useLogout() { rewardsLogout(); } - mixpanel.logEvent(mixpanel.EventTypes.ACCOUNT_ACCESS, { Action: 'Logged out' }, { send_immediately: true }); - mixpanel.reset(); + analytics.logEvent(analytics.EventTypes.ACCOUNT_ACCESS, { Action: 'Logged out' }, { sendImmediately: true }); + analytics.reset(); if ( PROTECTED_ROUTES.includes(router.pathname) || diff --git a/src/features/account/hooks/useSignInWithWallet.ts b/src/features/account/hooks/useSignInWithWallet.ts index 5300a3d80da..26587488215 100644 --- a/src/features/account/hooks/useSignInWithWallet.ts +++ b/src/features/account/hooks/useSignInWithWallet.ts @@ -12,7 +12,7 @@ import useWalletReown from 'src/features/connect-wallet/hooks/wallet/useWalletRe import { getWeb3Runtime } from 'src/features/connect-wallet/utils/runtime'; import config from 'src/config'; -import type * as mixpanel from 'src/services/mixpanel'; +import type * as analytics from 'src/shared/analytics'; import getErrorMessage from 'src/shared/errors/get-error-message'; import getErrorObj from 'src/shared/errors/get-error-obj'; import getErrorObjPayload from 'src/shared/errors/get-error-obj-payload'; @@ -44,7 +44,7 @@ function composeMessage(address: string, nonceBlockscout: string, nonceRewards: interface Props { onSuccess?: ({ address, profile, rewardsToken }: { address: string; profile: UserInfo; rewardsToken?: string }) => void; onError?: () => void; - source?: mixpanel.EventPayload['Source']; + source?: analytics.EventPayload['Source']; isAuth?: boolean; loginToRewards?: boolean; fetchProtectedResource: (fetcher: (token?: string) => Promise, token?: string) => Promise; diff --git a/src/features/account/pages/address/AddressFavoriteButton.tsx b/src/features/account/pages/address/AddressFavoriteButton.tsx index 20b650329b8..efe21036ed6 100644 --- a/src/features/account/pages/address/AddressFavoriteButton.tsx +++ b/src/features/account/pages/address/AddressFavoriteButton.tsx @@ -13,7 +13,7 @@ import WatchlistAddModal from 'src/features/account/pages/watchlist/AddressModal import DeleteAddressModal from 'src/features/account/pages/watchlist/DeleteAddressModal'; import config from 'src/config'; -import * as mixpanel from 'src/services/mixpanel'; +import * as analytics from 'src/shared/analytics'; import usePreventFocusAfterModalClosing from 'src/shared/hooks/usePreventFocusAfterModalClosing'; import SpriteIcon from 'src/sprite/SpriteIcon'; @@ -37,7 +37,7 @@ const AddressFavoriteButton = ({ className, hash, watchListId }: Props) => { const handleAddToFavorite = React.useCallback(() => { watchListId ? deleteModalProps.onOpen() : addModalProps.onOpen(); - !watchListId && mixpanel.logEvent(mixpanel.EventTypes.PAGE_WIDGET, { Type: 'Add to watchlist' }); + !watchListId && analytics.logEvent(analytics.EventTypes.PAGE_WIDGET, { Type: 'Add to watchlist' }); }, [ watchListId, deleteModalProps, addModalProps ]); const handleAddOrDeleteSuccess = React.useCallback(async() => { @@ -88,7 +88,7 @@ const AddressFavoriteButton = ({ className, hash, watchListId }: Props) => { hasEmail={ Boolean(profileQuery.data?.email) } showEmailAlert /> - { formData.id && ( + { formData.id !== undefined && ( void; - onDeleteClick: (item: ApiKey) => void; -} - -const ApiKeyListItem = ({ item, isLoading, onEditClick, onDeleteClick }: Props) => { - - const onItemEditClick = useCallback(() => { - return onEditClick(item); - }, [ item, onEditClick ]); - - const onItemDeleteClick = useCallback(() => { - return onDeleteClick(item); - }, [ item, onDeleteClick ]); - - return ( - - - - - ); -}; - -export default ApiKeyListItem; diff --git a/src/features/account/pages/api-keys/ApiKeys.tsx b/src/features/account/pages/api-keys/ApiKeys.tsx index bb732be36bc..093604a81ad 100644 --- a/src/features/account/pages/api-keys/ApiKeys.tsx +++ b/src/features/account/pages/api-keys/ApiKeys.tsx @@ -1,6 +1,6 @@ // SPDX-License-Identifier: LicenseRef-Blockscout -import { Box, Text } from '@chakra-ui/react'; +import { Text } from '@chakra-ui/react'; import React, { useCallback, useState } from 'react'; import type { ApiKey } from 'src/features/account/types/api'; @@ -21,11 +21,11 @@ import { BoxHtml } from 'src/toolkit/chakra/box'; import { Button } from 'src/toolkit/chakra/button'; import { Link } from 'src/toolkit/chakra/link'; import { Skeleton } from 'src/toolkit/chakra/skeleton'; +import { TableContainerScrollable } from 'src/toolkit/chakra/table'; import { useDisclosure } from 'src/toolkit/hooks/useDisclosure'; import { space } from 'src/toolkit/utils/htmlEntities'; import ApiKeyModal from './ApiKeyModal/ApiKeyModal'; -import ApiKeyListItem from './ApiKeyTable/ApiKeyListItem'; import ApiKeyTable from './ApiKeyTable/ApiKeyTable'; import DeleteApiKeyModal from './DeleteApiKeyModal'; @@ -85,28 +85,15 @@ const ApiKeysPage: React.FC = () => { } const list = ( - <> - - { data?.map((item, index) => ( - - )) } - - - - - + + + ); const canAdd = !isPlaceholderData ? (data?.length || 0) < DATA_LIMIT : true; diff --git a/src/features/account/pages/custom-abi/CustomAbi.tsx b/src/features/account/pages/custom-abi/CustomAbi.tsx index 7b19ab3763f..a8120226dce 100644 --- a/src/features/account/pages/custom-abi/CustomAbi.tsx +++ b/src/features/account/pages/custom-abi/CustomAbi.tsx @@ -1,6 +1,5 @@ // SPDX-License-Identifier: LicenseRef-Blockscout -import { Box } from '@chakra-ui/react'; import React, { useCallback, useState } from 'react'; import type { CustomAbi } from 'src/features/account/types/api'; @@ -17,10 +16,10 @@ import ApiFetchAlert from 'src/shared/alerts/ApiFetchAlert'; import { Button } from 'src/toolkit/chakra/button'; import { Skeleton } from 'src/toolkit/chakra/skeleton'; +import { TableContainerScrollable } from 'src/toolkit/chakra/table'; import { useDisclosure } from 'src/toolkit/hooks/useDisclosure'; import CustomAbiModal from './CustomAbiModal/CustomAbiModal'; -import CustomAbiListItem from './CustomAbiTable/CustomAbiListItem'; import CustomAbiTable from './CustomAbiTable/CustomAbiTable'; import DeleteCustomAbiModal from './DeleteCustomAbiModal'; @@ -70,27 +69,14 @@ const CustomAbiPage: React.FC = () => { } const list = ( - <> - - { data?.map((item, index) => ( - - )) } - - - - - + + + ); return ( diff --git a/src/features/account/pages/custom-abi/CustomAbiTable/CustomAbiListItem.tsx b/src/features/account/pages/custom-abi/CustomAbiTable/CustomAbiListItem.tsx deleted file mode 100644 index 1edaf0c03f5..00000000000 --- a/src/features/account/pages/custom-abi/CustomAbiTable/CustomAbiListItem.tsx +++ /dev/null @@ -1,50 +0,0 @@ -// SPDX-License-Identifier: LicenseRef-Blockscout - -import { Box } from '@chakra-ui/react'; -import React, { useCallback } from 'react'; - -import type { CustomAbi } from 'src/features/account/types/api'; - -import AddressEntity from 'src/slices/address/components/entity/AddressEntity'; - -import TableItemActionButtons from 'src/features/account/components/TableItemActionButtons'; - -import ListItemMobile from 'src/shared/lists/ListItemMobile'; - -import { Skeleton } from 'src/toolkit/chakra/skeleton'; - -interface Props { - item: CustomAbi; - isLoading?: boolean; - onEditClick: (item: CustomAbi) => void; - onDeleteClick: (item: CustomAbi) => void; -} - -const CustomAbiListItem = ({ item, isLoading, onEditClick, onDeleteClick }: Props) => { - - const onItemEditClick = useCallback(() => { - return onEditClick(item); - }, [ item, onEditClick ]); - - const onItemDeleteClick = useCallback(() => { - return onDeleteClick(item); - }, [ item, onDeleteClick ]); - - return ( - - - - - { item.name } - - - - - ); -}; - -export default React.memo(CustomAbiListItem); diff --git a/src/features/account/pages/home/LatestWatchlistTxs.tsx b/src/features/account/pages/home/LatestWatchlistTxs.tsx index 945b5fd3c3c..4f8c9e31bc4 100644 --- a/src/features/account/pages/home/LatestWatchlistTxs.tsx +++ b/src/features/account/pages/home/LatestWatchlistTxs.tsx @@ -7,20 +7,17 @@ import React from 'react'; import useApiQuery from 'src/api/hooks/useApiQuery'; import LatestTxsFallback from 'src/slices/home/pages/index/txs/LatestTxsFallback'; -import LatestTxsItem from 'src/slices/home/pages/index/txs/LatestTxsItem'; -import LatestTxsItemMobile from 'src/slices/home/pages/index/txs/LatestTxsItemMobile'; +import LatestTxsItem, { LATEST_TXS_TABLE_MIN_WIDTH } from 'src/slices/home/pages/index/txs/LatestTxsItem'; import { TX } from 'src/slices/tx/stubs/tx'; import useRedirectForInvalidAuthToken from 'src/features/account/hooks/useRedirectForInvalidAuthToken'; -import useIsMobile from 'src/shared/hooks/useIsMobile'; - import { Link } from 'src/toolkit/chakra/link'; +import { TableBody, TableContainerScrollable, TableRoot } from 'src/toolkit/chakra/table'; const LatestWatchlistTxs = () => { useRedirectForInvalidAuthToken(); - const isMobile = useIsMobile(); - const txsCount = isMobile ? 2 : 5; + const txsCount = 5; const { data, isPlaceholderData, isError } = useApiQuery('core:homepage_txs_watchlist', { queryOptions: { placeholderData: Array(txsCount).fill(TX), @@ -39,23 +36,20 @@ const LatestWatchlistTxs = () => { const txsUrl = route({ pathname: '/txs', query: { tab: 'watchlist' } }); return ( <> - - { data.slice(0, txsCount).map(((tx, index) => ( - - ))) } - - - { data.slice(0, txsCount).map(((tx, index) => ( - - ))) } + + + + + { data.slice(0, txsCount).map(((tx, index) => ( + + ))) } + + + View all watch list transactions diff --git a/src/features/account/pages/login/Login.tsx b/src/features/account/pages/login/Login.tsx index 923b5885efa..26817b0090c 100644 --- a/src/features/account/pages/login/Login.tsx +++ b/src/features/account/pages/login/Login.tsx @@ -8,8 +8,8 @@ import PageTitle from 'src/shell/page/title/PageTitle'; import config from 'src/config'; import useFeatureValue from 'src/services/growthbook/useFeatureValue'; -import { track } from 'src/services/mixpanel/queue'; import { useRollbar } from 'src/services/rollbar'; +import * as analytics from 'src/shared/analytics'; import useGradualIncrement from 'src/shared/numbers/useGradualIncrement'; import * as cookies from 'src/shared/storage/cookies'; @@ -36,8 +36,8 @@ const Login = () => { rollbar?.error('Test error', { payload: 'foo' }); }, [ rollbar ]); - const checkMixpanel = React.useCallback(() => { - track('Test event', { my_prop: 'foo bar' }); + const checkAnalytics = React.useCallback(() => { + analytics.logEvent(analytics.EventTypes.BUTTON_CLICK, { Content: 'Check analytics', Source: 'Login page' }); }, []); const handleTokenChange = React.useCallback((event: ChangeEvent) => { @@ -85,7 +85,7 @@ const Login = () => { ) } - + { num } diff --git a/src/features/account/pages/private-tags/AddressModal/AddressModal.tsx b/src/features/account/pages/private-tags/AddressModal/AddressModal.tsx index 17cfb919c22..81ef14d5eea 100644 --- a/src/features/account/pages/private-tags/AddressModal/AddressModal.tsx +++ b/src/features/account/pages/private-tags/AddressModal/AddressModal.tsx @@ -4,7 +4,7 @@ import React, { useCallback, useState } from 'react'; import type { AddressTag } from 'src/features/account/types/api'; -import * as mixpanel from 'src/services/mixpanel'; +import * as analytics from 'src/shared/analytics'; import FormModal from 'src/shared/forms/FormModal'; import AddressForm from './AddressForm'; @@ -24,16 +24,16 @@ const AddressModal: React.FC = ({ open, onOpenChange, onSuccess, data, pa const [ isAlertVisible, setAlertVisible ] = useState(false); React.useEffect(() => { - open && !data?.id && mixpanel.logEvent( - mixpanel.EventTypes.PRIVATE_TAG, + open && !data?.id && analytics.logEvent( + analytics.EventTypes.PRIVATE_TAG, { Action: 'Form opened', 'Page type': pageType, 'Tag type': 'Address' }, ); }, [ data?.id, open, pageType ]); const handleSuccess = React.useCallback(() => { if (!data?.id) { - mixpanel.logEvent( - mixpanel.EventTypes.PRIVATE_TAG, + analytics.logEvent( + analytics.EventTypes.PRIVATE_TAG, { Action: 'Submit', 'Page type': pageType, 'Tag type': 'Address' }, ); } diff --git a/src/features/account/pages/private-tags/AddressTagTable/AddressTagListItem.tsx b/src/features/account/pages/private-tags/AddressTagTable/AddressTagListItem.tsx deleted file mode 100644 index a9bfa91d010..00000000000 --- a/src/features/account/pages/private-tags/AddressTagTable/AddressTagListItem.tsx +++ /dev/null @@ -1,56 +0,0 @@ -// SPDX-License-Identifier: LicenseRef-Blockscout - -import { Flex, HStack, Text } from '@chakra-ui/react'; -import React, { useCallback } from 'react'; - -import type { AddressTag } from 'src/features/account/types/api'; - -import AddressEntity from 'src/slices/address/components/entity/AddressEntity'; - -import TableItemActionButtons from 'src/features/account/components/TableItemActionButtons'; - -import ListItemMobile from 'src/shared/lists/ListItemMobile'; - -import { Skeleton } from 'src/toolkit/chakra/skeleton'; -import { Tag } from 'src/toolkit/chakra/tag'; - -interface Props { - item: AddressTag; - onEditClick: (data: AddressTag) => void; - onDeleteClick: (data: AddressTag) => void; - isLoading?: boolean; -} - -const AddressTagListItem = ({ item, onEditClick, onDeleteClick, isLoading }: Props) => { - const onItemEditClick = useCallback(() => { - return onEditClick(item); - }, [ item, onEditClick ]); - - const onItemDeleteClick = useCallback(() => { - return onDeleteClick(item); - }, [ item, onDeleteClick ]); - - return ( - - - - - Private tag - - - { item.name } - - - - - - - ); -}; - -export default React.memo(AddressTagListItem); diff --git a/src/features/account/pages/private-tags/PrivateAddressTags.tsx b/src/features/account/pages/private-tags/PrivateAddressTags.tsx index 3bde7b17c42..a5e1ad2e842 100644 --- a/src/features/account/pages/private-tags/PrivateAddressTags.tsx +++ b/src/features/account/pages/private-tags/PrivateAddressTags.tsx @@ -1,6 +1,5 @@ // SPDX-License-Identifier: LicenseRef-Blockscout -import { Box } from '@chakra-ui/react'; import React, { useCallback, useState } from 'react'; import type { AddressTag } from 'src/features/account/types/api'; @@ -10,22 +9,22 @@ import ActionBar, { ACTION_BAR_HEIGHT_DESKTOP } from 'src/shell/page/action-bar/ import AccountPageDescription from 'src/features/account/components/AccountPageDescription'; import { PRIVATE_TAG_ADDRESS } from 'src/features/account/stubs'; -import * as mixpanel from 'src/services/mixpanel'; +import * as analytics from 'src/shared/analytics'; import DataList from 'src/shared/lists/DataList'; import Pagination from 'src/shared/pagination/Pagination'; -import useQueryWithPages from 'src/shared/pagination/useQueryWithPages'; +import useApiPaginatedQuery from 'src/shared/pagination/useApiPaginatedQuery'; import { Button } from 'src/toolkit/chakra/button'; import { Skeleton } from 'src/toolkit/chakra/skeleton'; +import { TableContainerScrollable } from 'src/toolkit/chakra/table'; import { useDisclosure } from 'src/toolkit/hooks/useDisclosure'; import AddressModal from './AddressModal/AddressModal'; -import AddressTagListItem from './AddressTagTable/AddressTagListItem'; import AddressTagTable from './AddressTagTable/AddressTagTable'; import DeletePrivateTagModal from './DeletePrivateTagModal'; const PrivateAddressTags = () => { - const { data: addressTagsData, isError, isPlaceholderData, refetch, pagination } = useQueryWithPages({ + const { data: addressTagsData, isError, isInitialLoading, isTransitioning, refetch, pagination } = useApiPaginatedQuery({ resourceName: 'core:private_tags_address', options: { refetchOnMount: false, @@ -80,29 +79,19 @@ const PrivateAddressTags = () => { itemsNum={ addressTagsData?.items.length } emptyText="" actionBar={ actionBar } + isTransitioning={ isTransitioning } > - - { addressTagsData?.items.map((item: AddressTag, index: number) => ( - - )) } - - + - + - +
diff --git a/src/features/account/pages/profile/__screenshots__/MyProfile.pw.tsx_default_without-address-1.png b/src/features/account/pages/profile/__screenshots__/MyProfile.pw.tsx_default_without-address-1.png index 34fca2b4215..26ddcc2f776 100644 Binary files a/src/features/account/pages/profile/__screenshots__/MyProfile.pw.tsx_default_without-address-1.png and b/src/features/account/pages/profile/__screenshots__/MyProfile.pw.tsx_default_without-address-1.png differ diff --git a/src/features/account/pages/tx-index-watchlist/TxsWatchlist.tsx b/src/features/account/pages/tx-index-watchlist/TxsWatchlist.tsx index 09848a21750..29ce2b35346 100644 --- a/src/features/account/pages/tx-index-watchlist/TxsWatchlist.tsx +++ b/src/features/account/pages/tx-index-watchlist/TxsWatchlist.tsx @@ -6,10 +6,10 @@ import TxsWithFrontendSorting from 'src/slices/tx/pages/index/list/TxsWithFronte import useRedirectForInvalidAuthToken from 'src/features/account/hooks/useRedirectForInvalidAuthToken'; -import type { QueryWithPagesResult } from 'src/shared/pagination/useQueryWithPages'; +import type { ApiPaginatedQueryResult } from 'src/shared/pagination/useApiPaginatedQuery'; type Props = { - query: QueryWithPagesResult<'core:txs_watchlist'>; + query: ApiPaginatedQueryResult<'core:txs_watchlist'>; top?: number; }; diff --git a/src/features/account/pages/verified-addresses/VerifiedAddresses.pw.tsx b/src/features/account/pages/verified-addresses/VerifiedAddresses.pw.tsx index ce2e53da077..4432857b034 100644 --- a/src/features/account/pages/verified-addresses/VerifiedAddresses.pw.tsx +++ b/src/features/account/pages/verified-addresses/VerifiedAddresses.pw.tsx @@ -17,7 +17,7 @@ test.beforeEach(async({ mockAssetResponse }) => { await mockAssetResponse(mocks.TOKEN_INFO_APPLICATION_BASE.iconUrl, './playwright/mocks/image_s.jpg'); }); -test('base view +@mobile', async({ render, mockApiResponse }) => { +test('base view', async({ render, mockApiResponse }) => { await mockApiResponse('contractInfo:verified_addresses', mocks.VERIFIED_ADDRESS_RESPONSE.DEFAULT, { pathParams: { instanceId: '1' } }); await mockApiResponse('admin:token_info_applications', mocks.TOKEN_INFO_APPLICATIONS_RESPONSE.DEFAULT, { pathParams: { instanceId: '1', id: undefined } }); await mockApiResponse('core:user_info', profileMock.base); diff --git a/src/features/account/pages/verified-addresses/VerifiedAddresses.tsx b/src/features/account/pages/verified-addresses/VerifiedAddresses.tsx index fdb54762cec..9548136088c 100644 --- a/src/features/account/pages/verified-addresses/VerifiedAddresses.tsx +++ b/src/features/account/pages/verified-addresses/VerifiedAddresses.tsx @@ -1,6 +1,6 @@ // SPDX-License-Identifier: LicenseRef-Blockscout -import { List, chakra, Box } from '@chakra-ui/react'; +import { List, chakra } from '@chakra-ui/react'; import { useQueryClient } from '@tanstack/react-query'; import { useRouter } from 'next/router'; import React from 'react'; @@ -17,19 +17,19 @@ import useProfileQuery from 'src/features/account/hooks/useProfileQuery'; import useRedirectForInvalidAuthToken from 'src/features/account/hooks/useRedirectForInvalidAuthToken'; import AddressVerificationModal from 'src/features/account/pages/verified-addresses/address-verification/AddressVerificationModal'; import VerifiedAddressesEmailAlert from 'src/features/account/pages/verified-addresses/index/VerifiedAddressesEmailAlert'; -import VerifiedAddressesListItem from 'src/features/account/pages/verified-addresses/index/VerifiedAddressesListItem'; import VerifiedAddressesTable from 'src/features/account/pages/verified-addresses/index/VerifiedAddressesTable'; import TokenInfoForm from 'src/features/account/pages/verified-addresses/token-info/TokenInfoForm'; import { TOKEN_INFO_APPLICATION, VERIFIED_ADDRESS } from 'src/features/account/stubs'; import config from 'src/config'; -import * as mixpanel from 'src/services/mixpanel'; +import * as analytics from 'src/shared/analytics'; import DataList from 'src/shared/lists/DataList'; import getQueryParamString from 'src/shared/router/get-query-param-string'; import { collator } from 'src/shared/texts/collator'; import { Button } from 'src/toolkit/chakra/button'; import { Link } from 'src/toolkit/chakra/link'; +import { TableContainerScrollable } from 'src/toolkit/chakra/table'; import { BackToButton } from 'src/toolkit/components/buttons/BackToButton'; import { useDisclosure } from 'src/toolkit/hooks/useDisclosure'; @@ -161,32 +161,15 @@ const VerifiedAddresses = () => { if (addressesQuery.data?.verifiedAddresses) { return ( - <> - - { addressesQuery.data.verifiedAddresses.map((item, index) => ( - tokenAddress.toLowerCase() === item.contractAddress.toLowerCase()) - } - onAdd={ handleItemAdd } - onEdit={ handleItemEdit } - isLoading={ isLoading } - /> - )) } - - - - - + + + ); } @@ -229,7 +212,7 @@ const VerifiedAddresses = () => { { addButton } ({ address: '', signingMessage: '', contractCreator: '' }); React.useEffect(() => { - open && mixpanel.logEvent( - mixpanel.EventTypes.VERIFY_ADDRESS, + open && analytics.logEvent( + analytics.EventTypes.VERIFY_ADDRESS, { Action: 'Form opened', 'Page type': pageType }, ); }, [ open, pageType ]); @@ -40,8 +40,8 @@ const AddressVerificationModal = ({ defaultAddress, open, onOpenChange, onSubmit const handleGoToSecondStep = React.useCallback((firstStepResult: typeof data) => { setData(firstStepResult); setStepIndex((prev) => prev + 1); - mixpanel.logEvent( - mixpanel.EventTypes.VERIFY_ADDRESS, + analytics.logEvent( + analytics.EventTypes.VERIFY_ADDRESS, { Action: 'Address entered', 'Page type': pageType }, ); }, [ pageType ]); @@ -50,8 +50,8 @@ const AddressVerificationModal = ({ defaultAddress, open, onOpenChange, onSubmit onSubmit(address); setStepIndex((prev) => prev + 1); setData((prev) => ({ ...prev, isToken: Boolean(address.metadata?.tokenName) })); - mixpanel.logEvent( - mixpanel.EventTypes.VERIFY_ADDRESS, + analytics.logEvent( + analytics.EventTypes.VERIFY_ADDRESS, { Action: 'Sign ownership', 'Page type': pageType, 'Sign method': signMethod }, ); }, [ onSubmit, pageType ]); diff --git a/src/features/account/pages/verified-addresses/address-verification/steps/AddressVerificationStepSignature.tsx b/src/features/account/pages/verified-addresses/address-verification/steps/AddressVerificationStepSignature.tsx index 31530fe8802..599942d12de 100644 --- a/src/features/account/pages/verified-addresses/address-verification/steps/AddressVerificationStepSignature.tsx +++ b/src/features/account/pages/verified-addresses/address-verification/steps/AddressVerificationStepSignature.tsx @@ -21,7 +21,6 @@ import { getWeb3Runtime } from 'src/features/connect-wallet/utils/runtime'; import config from 'src/config'; import CopyToClipboard from 'src/shared/texts/CopyToClipboard'; -import shortenString from 'src/shared/texts/shorten-string'; import { Alert } from 'src/toolkit/chakra/alert'; import { Button } from 'src/toolkit/chakra/button'; @@ -29,6 +28,7 @@ import { Link } from 'src/toolkit/chakra/link'; import { Radio, RadioGroup } from 'src/toolkit/chakra/radio'; import { FormFieldText } from 'src/toolkit/components/forms/fields/FormFieldText'; import { SIGNATURE_REGEXP } from 'src/toolkit/components/forms/validators/signature'; +import { shortenString } from 'src/toolkit/utils/texts'; import AdminSupportText from '../../AdminSupportText'; diff --git a/src/features/account/pages/verified-addresses/index/VerifiedAddressesListItem.tsx b/src/features/account/pages/verified-addresses/index/VerifiedAddressesListItem.tsx deleted file mode 100644 index 1947ca6ef1b..00000000000 --- a/src/features/account/pages/verified-addresses/index/VerifiedAddressesListItem.tsx +++ /dev/null @@ -1,135 +0,0 @@ -// SPDX-License-Identifier: LicenseRef-Blockscout - -import React from 'react'; - -import type * as adminRs from '@blockscout/admin-rs-types'; -import type * as contractsInfo from '@blockscout/contracts-info-types'; - -import AddressEntity from 'src/slices/address/components/entity/AddressEntity'; -import TokenEntity from 'src/slices/token/components/entity/TokenEntity'; - -import dayjs from 'src/shared/date-and-time/dayjs'; -import ListItemMobileGrid from 'src/shared/lists/ListItemMobileGrid'; -import SpriteIcon from 'src/sprite/SpriteIcon'; - -import { IconButton } from 'src/toolkit/chakra/icon-button'; -import { Link } from 'src/toolkit/chakra/link'; -import { Skeleton } from 'src/toolkit/chakra/skeleton'; -import { Tooltip } from 'src/toolkit/chakra/tooltip'; - -import VerifiedAddressesStatus from './VerifiedAddressesStatus'; - -interface Props { - item: contractsInfo.VerifiedAddress; - application: adminRs.TokenInfoSubmission | undefined; - onAdd: (address: string) => void; - onEdit: (address: string) => void; - isLoading: boolean; -} - -const VerifiedAddressesListItem = ({ item, application, onAdd, onEdit, isLoading }: Props) => { - const handleAddClick = React.useCallback(() => { - if (isLoading) { - return; - } - onAdd(item.contractAddress); - }, [ isLoading, item.contractAddress, onAdd ]); - - const handleEditClick = React.useCallback(() => { - if (isLoading) { - return; - } - onEdit(item.contractAddress); - }, [ isLoading, item.contractAddress, onEdit ]); - - const tokenInfo = (() => { - if (isLoading) { - return ; - } - - if (!item.metadata?.tokenName) { - return Not a token; - } - - if (!application) { - return Add details; - } - - const token = { - type: 'ERC-20' as const, - icon_url: application.iconUrl, - address_hash: application.tokenAddress, - name: item.metadata.tokenName, - symbol: '', - reputation: null, - }; - - return ( - <> - - - - - - - - ); - })(); - - return ( - - Address - - - - - { item.metadata?.tokenName && ( - <> - Token Info - - { tokenInfo } - - - ) } - - { item.metadata?.tokenName && application && ( - <> - Status - - - - - - - ) } - - { item.metadata?.tokenName && application && ( - <> - Date - - - { dayjs(application.updatedAt).format('MMM DD, YYYY') } - - - - ) } - - ); -}; - -export default React.memo(VerifiedAddressesListItem); diff --git a/src/features/account/pages/verified-addresses/index/VerifiedAddressesTable.tsx b/src/features/account/pages/verified-addresses/index/VerifiedAddressesTable.tsx index 2cceae1fe51..faa3540d96b 100644 --- a/src/features/account/pages/verified-addresses/index/VerifiedAddressesTable.tsx +++ b/src/features/account/pages/verified-addresses/index/VerifiedAddressesTable.tsx @@ -19,7 +19,7 @@ interface Props { const VerifiedAddressesTable = ({ data, applications, onItemEdit, onItemAdd, isLoading }: Props) => { return ( - + Address diff --git a/src/features/account/pages/verified-addresses/token-info/TokenInfoForm.tsx b/src/features/account/pages/verified-addresses/token-info/TokenInfoForm.tsx index 3ddd4e6f892..f53ac3c36d2 100644 --- a/src/features/account/pages/verified-addresses/token-info/TokenInfoForm.tsx +++ b/src/features/account/pages/verified-addresses/token-info/TokenInfoForm.tsx @@ -14,8 +14,8 @@ import type { ResourceError } from 'src/api/resources'; import config from 'src/config'; import { getFeaturePayload } from 'src/config/utils/features'; -import * as mixpanel from 'src/services/mixpanel'; import ApiFetchAlert from 'src/shared/alerts/ApiFetchAlert'; +import * as analytics from 'src/shared/analytics'; import { Button } from 'src/toolkit/chakra/button'; import { toaster } from 'src/toolkit/chakra/toaster'; @@ -62,7 +62,7 @@ const TokenInfoForm = ({ address, tokenName, application, onSubmit }: Props) => React.useEffect(() => { if (!application?.id && !openEventSent.current) { - mixpanel.logEvent(mixpanel.EventTypes.VERIFY_TOKEN, { Action: 'Form opened' }); + analytics.logEvent(analytics.EventTypes.VERIFY_TOKEN, { Action: 'Form opened' }); openEventSent.current = true; } }, [ application?.id ]); @@ -87,7 +87,7 @@ const TokenInfoForm = ({ address, tokenName, application, onSubmit }: Props) => onSubmit(result); if (!application?.id) { - mixpanel.logEvent(mixpanel.EventTypes.VERIFY_TOKEN, { Action: 'Submit' }); + analytics.logEvent(analytics.EventTypes.VERIFY_TOKEN, { Action: 'Submit' }); } } else { diff --git a/src/features/account/pages/watchlist/Watchlist.tsx b/src/features/account/pages/watchlist/Watchlist.tsx index 0833078a577..a4dd05ca3d6 100644 --- a/src/features/account/pages/watchlist/Watchlist.tsx +++ b/src/features/account/pages/watchlist/Watchlist.tsx @@ -1,6 +1,5 @@ // SPDX-License-Identifier: LicenseRef-Blockscout -import { Box } from '@chakra-ui/react'; import { useQueryClient } from '@tanstack/react-query'; import React, { useCallback, useState } from 'react'; @@ -19,21 +18,21 @@ import { WATCH_LIST_ITEM_WITH_TOKEN_INFO } from 'src/features/account/stubs'; import DataList from 'src/shared/lists/DataList'; import Pagination from 'src/shared/pagination/Pagination'; -import useQueryWithPages from 'src/shared/pagination/useQueryWithPages'; +import useApiPaginatedQuery from 'src/shared/pagination/useApiPaginatedQuery'; import { Button } from 'src/toolkit/chakra/button'; import { Skeleton } from 'src/toolkit/chakra/skeleton'; +import { TableContainerScrollable } from 'src/toolkit/chakra/table'; import { useDisclosure } from 'src/toolkit/hooks/useDisclosure'; import AddressModal from './AddressModal/AddressModal'; import DeleteAddressModal from './DeleteAddressModal'; import WatchlistEmailAlert from './WatchlistEmailAlert'; -import WatchListItem from './WatchlistTable/WatchListItem'; import WatchlistTable from './WatchlistTable/WatchlistTable'; const WatchList: React.FC = () => { - const { data, isPlaceholderData, isError, pagination } = useQueryWithPages({ + const { data, isInitialLoading, isTransitioning, isError, pagination } = useApiPaginatedQuery({ resourceName: 'core:watchlist', options: { placeholderData: { items: Array(5).fill(WATCH_LIST_ITEM_WITH_TOKEN_INFO), next_page_params: null }, @@ -103,31 +102,20 @@ const WatchList: React.FC = () => { itemsNum={ data?.items.length } emptyText="" actionBar={ actionBar } + isTransitioning={ isTransitioning } > - - { data?.items.map((item, index) => ( - - )) } - - + - + - + - ) } - - ); -} diff --git a/src/features/marketplace/pages/essential-dapp/revoke/components/ApprovalsTable.tsx b/src/features/marketplace/pages/essential-dapp/revoke/components/ApprovalsTable.tsx index 284849ef5fb..029e63b4925 100644 --- a/src/features/marketplace/pages/essential-dapp/revoke/components/ApprovalsTable.tsx +++ b/src/features/marketplace/pages/essential-dapp/revoke/components/ApprovalsTable.tsx @@ -34,7 +34,7 @@ export default function ApprovalsTable({ const { cutRef, renderedItemsNum } = useLazyRenderedList({ list: approvals, isEnabled: !isLoading, resetKey }); return ( - + Token diff --git a/src/features/marketplace/pages/essential-dapp/revoke/components/Content.pw.tsx b/src/features/marketplace/pages/essential-dapp/revoke/components/Content.pw.tsx index de8d32bc6bd..f7ee7416988 100644 --- a/src/features/marketplace/pages/essential-dapp/revoke/components/Content.pw.tsx +++ b/src/features/marketplace/pages/essential-dapp/revoke/components/Content.pw.tsx @@ -23,7 +23,7 @@ const approvalsQueryDataWithPagination = { total: APPROVALS_PAGE_SIZE + 1, }; -test('base view +@dark-mode +@mobile', async({ render, mockAssetResponse }: TestFnArgs) => { +test('base view with responsive summary +@dark-mode +@mobile', async({ render, mockAssetResponse }: TestFnArgs) => { const props = { selectedChain: chainDataMock.chainA, searchAddress: addressHash, diff --git a/src/features/marketplace/pages/essential-dapp/revoke/components/__screenshots__/Content.pw.tsx_dark-color-mode_base-view-dark-mode-mobile-1.png b/src/features/marketplace/pages/essential-dapp/revoke/components/__screenshots__/Content.pw.tsx_dark-color-mode_base-view-with-responsive-summary-dark-mode-mobile-1.png similarity index 100% rename from src/features/marketplace/pages/essential-dapp/revoke/components/__screenshots__/Content.pw.tsx_dark-color-mode_base-view-dark-mode-mobile-1.png rename to src/features/marketplace/pages/essential-dapp/revoke/components/__screenshots__/Content.pw.tsx_dark-color-mode_base-view-with-responsive-summary-dark-mode-mobile-1.png diff --git a/src/features/marketplace/pages/essential-dapp/revoke/components/__screenshots__/Content.pw.tsx_default_base-view-dark-mode-mobile-1.png b/src/features/marketplace/pages/essential-dapp/revoke/components/__screenshots__/Content.pw.tsx_default_base-view-with-responsive-summary-dark-mode-mobile-1.png similarity index 100% rename from src/features/marketplace/pages/essential-dapp/revoke/components/__screenshots__/Content.pw.tsx_default_base-view-dark-mode-mobile-1.png rename to src/features/marketplace/pages/essential-dapp/revoke/components/__screenshots__/Content.pw.tsx_default_base-view-with-responsive-summary-dark-mode-mobile-1.png diff --git a/src/features/marketplace/pages/essential-dapp/revoke/components/__screenshots__/Content.pw.tsx_default_with-pagination-1.png b/src/features/marketplace/pages/essential-dapp/revoke/components/__screenshots__/Content.pw.tsx_default_with-pagination-1.png index b5427412400..ab00df5ce81 100644 Binary files a/src/features/marketplace/pages/essential-dapp/revoke/components/__screenshots__/Content.pw.tsx_default_with-pagination-1.png and b/src/features/marketplace/pages/essential-dapp/revoke/components/__screenshots__/Content.pw.tsx_default_with-pagination-1.png differ diff --git a/src/features/marketplace/pages/essential-dapp/revoke/components/__screenshots__/Content.pw.tsx_mobile_base-view-dark-mode-mobile-1.png b/src/features/marketplace/pages/essential-dapp/revoke/components/__screenshots__/Content.pw.tsx_mobile_base-view-dark-mode-mobile-1.png deleted file mode 100644 index 1f23510a2ed..00000000000 Binary files a/src/features/marketplace/pages/essential-dapp/revoke/components/__screenshots__/Content.pw.tsx_mobile_base-view-dark-mode-mobile-1.png and /dev/null differ diff --git a/src/features/marketplace/pages/essential-dapp/revoke/components/__screenshots__/Content.pw.tsx_mobile_base-view-with-responsive-summary-dark-mode-mobile-1.png b/src/features/marketplace/pages/essential-dapp/revoke/components/__screenshots__/Content.pw.tsx_mobile_base-view-with-responsive-summary-dark-mode-mobile-1.png new file mode 100644 index 00000000000..a5139c55382 Binary files /dev/null and b/src/features/marketplace/pages/essential-dapp/revoke/components/__screenshots__/Content.pw.tsx_mobile_base-view-with-responsive-summary-dark-mode-mobile-1.png differ diff --git a/src/features/marketplace/pages/essential-dapp/revoke/hooks/useApprovalsQuery.tsx b/src/features/marketplace/pages/essential-dapp/revoke/hooks/useApprovalsQuery.ts similarity index 93% rename from src/features/marketplace/pages/essential-dapp/revoke/hooks/useApprovalsQuery.tsx rename to src/features/marketplace/pages/essential-dapp/revoke/hooks/useApprovalsQuery.ts index 4478f932d76..0b355fa3488 100644 --- a/src/features/marketplace/pages/essential-dapp/revoke/hooks/useApprovalsQuery.tsx +++ b/src/features/marketplace/pages/essential-dapp/revoke/hooks/useApprovalsQuery.ts @@ -3,7 +3,7 @@ import ERC20Artifact from '@openzeppelin/contracts/build/contracts/ERC20.json'; import NftArtifact from '@openzeppelin/contracts/build/contracts/ERC721.json'; import { useQuery } from '@tanstack/react-query'; -import { useCallback, useEffect, useMemo, useState } from 'react'; +import { useCallback, useMemo, useState } from 'react'; import { formatUnits, getAbiItem, getAddress, isAddress } from 'viem'; import type { GetLogsParameters, PublicClient } from 'viem'; import { usePublicClient } from 'wagmi'; @@ -169,12 +169,21 @@ function buildAllowance(record: BaseAllowanceType, tokenData: schemas['Token'] | }; } +const EMPTY_KEYS: Array = []; + +interface HiddenApprovalKeysScope { + readonly scopeKey: string; + readonly keys: Array; +} + export default function useApprovalsQuery(chain: EssentialDappsChainConfig | undefined, userAddress: string, page: number) { const apiFetch = useApiFetch(); const getBlockTimestamp = useGetBlockTimestamp(); const searchErc20Allowances = useSearchErc20Allowances(); const searchNftAllowances = useSearchNftAllowances(); - const [ hiddenApprovalKeys, setHiddenApprovalKeys ] = useState>([]); + const scopeKey = `${ chain?.id }:${ userAddress }`; + const [ hiddenApprovalKeysScope, setHiddenApprovalKeysScope ] = useState({ scopeKey, keys: EMPTY_KEYS }); + const hiddenApprovalKeys = hiddenApprovalKeysScope.scopeKey === scopeKey ? hiddenApprovalKeysScope.keys : EMPTY_KEYS; const chainId = chain?.id ? Number(chain.id) : undefined; const publicClient = usePublicClient({ chainId }) as PublicClient | undefined; @@ -186,15 +195,14 @@ export default function useApprovalsQuery(chain: EssentialDappsChainConfig | und const isQueryEnabled = Boolean(userAddress) && isAddress(userAddress) && Boolean(publicClient && blockscoutClient); const hiddenApprovalKeysKey = hiddenApprovalKeys.join(':'); - useEffect(() => { - setHiddenApprovalKeys([]); - }, [ chain?.id, userAddress ]); - const hideApproval = useCallback((approval: AllowanceType) => { const key = getApprovalHiddenKey(approval); - setHiddenApprovalKeys((prev) => prev.includes(key) ? prev : [ ...prev, key ]); - }, []); + setHiddenApprovalKeysScope((prev) => { + const keys = prev.scopeKey === scopeKey ? prev.keys : EMPTY_KEYS; + return keys.includes(key) ? prev : { scopeKey, keys: [ ...keys, key ] }; + }); + }, [ scopeKey ]); const searchBaseAllowances = useCallback(async(signal?: AbortSignal): Promise> => { throwIfAborted(signal); diff --git a/src/features/marketplace/pages/essential-dapp/revoke/hooks/useCoinBalanceQuery.tsx b/src/features/marketplace/pages/essential-dapp/revoke/hooks/useCoinBalanceQuery.ts similarity index 100% rename from src/features/marketplace/pages/essential-dapp/revoke/hooks/useCoinBalanceQuery.tsx rename to src/features/marketplace/pages/essential-dapp/revoke/hooks/useCoinBalanceQuery.ts diff --git a/src/features/marketplace/pages/essential-dapp/revoke/hooks/useGetBlockTimestamp.tsx b/src/features/marketplace/pages/essential-dapp/revoke/hooks/useGetBlockTimestamp.ts similarity index 100% rename from src/features/marketplace/pages/essential-dapp/revoke/hooks/useGetBlockTimestamp.tsx rename to src/features/marketplace/pages/essential-dapp/revoke/hooks/useGetBlockTimestamp.ts diff --git a/src/features/marketplace/pages/essential-dapp/revoke/hooks/useRevoke.spec.ts b/src/features/marketplace/pages/essential-dapp/revoke/hooks/useRevoke.spec.ts new file mode 100644 index 00000000000..f8fb43755c2 --- /dev/null +++ b/src/features/marketplace/pages/essential-dapp/revoke/hooks/useRevoke.spec.ts @@ -0,0 +1,144 @@ +// SPDX-License-Identifier: LicenseRef-Blockscout +// @vitest-environment jsdom + +import type * as viemActions from 'viem/actions'; +import type * as wagmi from 'wagmi'; + +import type { AllowanceType } from '../types'; +import type { EssentialDappsChainConfig } from 'src/features/marketplace/types/client'; + +import { toaster } from 'src/toolkit/chakra/toaster'; + +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; +import { renderHook, wrapper } from 'vitest/lib'; + +import useRevoke from './useRevoke'; + +const USER_ADDRESS = '0x1111111111111111111111111111111111111111'; +const TOKEN_ADDRESS = '0x2222222222222222222222222222222222222222'; +const SPENDER_ADDRESS = '0x3333333333333333333333333333333333333333'; +const TX_HASH = `0x${ 'ab'.repeat(32) }`; +const CHAIN: EssentialDappsChainConfig = { id: '10', name: 'OP Mainnet' }; +const ERC20_APPROVAL: AllowanceType = { + type: 'ERC-20', + address: TOKEN_ADDRESS, + spender: SPENDER_ADDRESS, + transactionId: null, + tokenReputation: null, + timestamp: 0, +}; + +const walletState = vi.hoisted(() => ({ + address: undefined as string | undefined, + publicClient: undefined as object | undefined, + switchChainAsync: vi.fn(), + writeContractAsync: vi.fn(), + waitForTransactionReceipt: vi.fn(), +})); + +vi.mock('wagmi', async(importOriginal) => ({ + ...await importOriginal(), + useAccount: () => ({ address: walletState.address }), + useSwitchChain: () => ({ switchChainAsync: walletState.switchChainAsync }), + useWriteContract: () => ({ writeContractAsync: walletState.writeContractAsync }), + usePublicClient: () => walletState.publicClient, +})); + +vi.mock('viem/actions', async(importOriginal) => ({ + ...await importOriginal(), + waitForTransactionReceipt: walletState.waitForTransactionReceipt, +})); + +function renderRevoke(chain: EssentialDappsChainConfig = CHAIN) { + return renderHook(() => useRevoke(chain), { wrapper }).result.current; +} + +describe('useRevoke', () => { + beforeEach(() => { + walletState.address = USER_ADDRESS; + walletState.publicClient = {}; + walletState.switchChainAsync.mockResolvedValue(undefined); + walletState.writeContractAsync.mockResolvedValue(TX_HASH); + walletState.waitForTransactionReceipt.mockResolvedValue({ status: 'success' }); + vi.spyOn(toaster, 'success'); + vi.spyOn(toaster, 'error'); + }); + + afterEach(() => { + vi.restoreAllMocks(); + vi.resetAllMocks(); + }); + + it('should revoke an ERC-20 approval on the approval chain and report success', async() => { + const revoke = renderRevoke(); + + await expect(revoke(ERC20_APPROVAL)).resolves.toBe(true); + + expect(walletState.switchChainAsync).toHaveBeenCalledWith({ chainId: 10 }); + expect(walletState.writeContractAsync).toHaveBeenCalledWith(expect.objectContaining({ + account: USER_ADDRESS, + address: TOKEN_ADDRESS, + functionName: 'approve', + args: [ SPENDER_ADDRESS, 0 ], + chainId: 10, + })); + expect(toaster.success).toHaveBeenCalledWith(expect.objectContaining({ title: 'Success' })); + }); + + it('should do nothing when no wallet is connected', async() => { + walletState.address = undefined; + const revoke = renderRevoke(); + + await expect(revoke(ERC20_APPROVAL)).resolves.toBeUndefined(); + + expect(walletState.writeContractAsync).not.toHaveBeenCalled(); + }); + + it.each([ + { + condition: 'the chain has no id', + setup: () => {}, + chain: { ...CHAIN, id: '' }, + message: 'Chain not found', + }, + { + condition: 'no public client is available', + setup: () => { + walletState.publicClient = undefined; + }, + chain: CHAIN, + message: 'Public client not found', + }, + { + condition: 'the transaction is reverted', + setup: () => { + walletState.waitForTransactionReceipt.mockResolvedValue({ status: 'reverted' }); + }, + chain: CHAIN, + message: 'Failed to revoke approval.', + }, + { + condition: 'the wallet rejects with a short message', + setup: () => { + walletState.writeContractAsync.mockRejectedValue({ shortMessage: 'User rejected the request.' }); + }, + chain: CHAIN, + message: 'User rejected the request.', + }, + { + condition: 'the error carries no message', + setup: () => { + walletState.switchChainAsync.mockRejectedValue({}); + }, + chain: CHAIN, + message: 'Something went wrong. Try again later.', + }, + ])('should report failure when $condition', async({ setup, chain, message }) => { + setup(); + const revoke = renderRevoke(chain); + + await expect(revoke(ERC20_APPROVAL)).resolves.toBe(false); + + expect(toaster.error).toHaveBeenCalledWith(expect.objectContaining({ description: message })); + }); +}); diff --git a/src/features/marketplace/pages/essential-dapp/revoke/hooks/useRevoke.tsx b/src/features/marketplace/pages/essential-dapp/revoke/hooks/useRevoke.tsx index 6b84df33f73..5fc06206adf 100644 --- a/src/features/marketplace/pages/essential-dapp/revoke/hooks/useRevoke.tsx +++ b/src/features/marketplace/pages/essential-dapp/revoke/hooks/useRevoke.tsx @@ -13,7 +13,7 @@ import TxEntity from 'src/slices/tx/components/entity/TxEntity'; import useRewardsActivity from 'src/features/rewards/hooks/useRewardsActivity'; -import * as mixpanel from 'src/services/mixpanel'; +import * as analytics from 'src/shared/analytics'; import { toaster } from 'src/toolkit/chakra/toaster'; @@ -44,7 +44,7 @@ export default function useRevoke(chain?: EssentialDappsChainConfig) { chainId: Number(chain?.id), }); - mixpanel.logEvent(mixpanel.EventTypes.WALLET_ACTION, { + analytics.logEvent(analytics.EventTypes.WALLET_ACTION, { Action: 'Send Transaction', Address: userAddress, AppId: 'revoke', diff --git a/src/features/marketplace/pages/essential-dapp/revoke/hooks/useSearchErc20Allowances.tsx b/src/features/marketplace/pages/essential-dapp/revoke/hooks/useSearchErc20Allowances.ts similarity index 100% rename from src/features/marketplace/pages/essential-dapp/revoke/hooks/useSearchErc20Allowances.tsx rename to src/features/marketplace/pages/essential-dapp/revoke/hooks/useSearchErc20Allowances.ts diff --git a/src/features/marketplace/pages/essential-dapp/revoke/hooks/useSearchNftAllowances.tsx b/src/features/marketplace/pages/essential-dapp/revoke/hooks/useSearchNftAllowances.ts similarity index 100% rename from src/features/marketplace/pages/essential-dapp/revoke/hooks/useSearchNftAllowances.tsx rename to src/features/marketplace/pages/essential-dapp/revoke/hooks/useSearchNftAllowances.ts diff --git a/src/features/marketplace/pages/essential-dapp/swap/Swap.tsx b/src/features/marketplace/pages/essential-dapp/swap/Swap.tsx index 8d91fc9a37b..7d7d6fb2002 100644 --- a/src/features/marketplace/pages/essential-dapp/swap/Swap.tsx +++ b/src/features/marketplace/pages/essential-dapp/swap/Swap.tsx @@ -1,9 +1,11 @@ // SPDX-License-Identifier: LicenseRef-Blockscout -import { Flex, useToken } from '@chakra-ui/react'; +import { Center, Flex, useToken } from '@chakra-ui/react'; import React, { useMemo } from 'react'; import AdBanner from 'src/features/ads/banner/components/AdBanner'; +import Web3Boundary from 'src/features/connect-wallet/components/Web3Boundary'; +import useWeb3Wallet from 'src/features/connect-wallet/hooks/useWallet'; import essentialDappsChainsConfig from 'src/features/marketplace/chains-config/essential-dapps'; import config from 'src/config'; @@ -11,54 +13,67 @@ import { getFeaturePayload } from 'src/config/utils/features'; import useIsMobile from 'src/shared/hooks/useIsMobile'; import { useColorMode } from 'src/toolkit/chakra/color-mode'; +import { ContentLoader } from 'src/toolkit/components/loaders/ContentLoader'; import { BODY_TYPEFACE } from 'src/toolkit/theme/foundations/typography'; -import MarketplaceAppIframe from '../../../components/MarketplaceAppIframe'; +import { SWAP_WIDGET_MIN_HEIGHT } from './consts'; +import SwapWidgetIframe from './SwapWidgetIframe'; +import { useSwapWallet } from './useSwapWallet'; +import { getSwapWidgetConfig } from './widget-config'; const feature = getFeaturePayload(config.features.marketplace); const dappConfig = feature?.essentialDapps?.swap; -const defaultChainId = Number( - dappConfig?.chains.includes(config.chain.id as string) ? - config.chain.id : - dappConfig?.chains[0], -); +const widgetConfig = dappConfig ? getSwapWidgetConfig(dappConfig, config.chain.id, essentialDappsChainsConfig()?.chains ?? []) : undefined; -function getExplorerUrls() { - return Object.fromEntries(dappConfig?.chains.map((chainId) => { - const chainConfig = essentialDappsChainsConfig()?.chains.find( - (chain) => chain.id === chainId, - ); - return [ Number(chainId), [ chainConfig?.explorer_url ] ]; - }) || []); -} - -export default function Swap() { - const isMobile = useIsMobile(); +const SwapWidget = () => { const { colorMode } = useColorMode(); const [ mainColor ] = useToken('colors', 'blue.600'); const [ borderColor ] = useToken('colors', colorMode === 'light' ? 'blackAlpha.100' : 'whiteAlpha.100'); + const [ borderRadius, borderRadiusSecondary ] = useToken('radii', [ 'md', 'base' ]); + const wallet = useWeb3Wallet({ source: 'Essential dapps' }); + const handler = useSwapWallet(); + const handlers = useMemo(() => [ handler ], [ handler ]); + const themedConfig = useMemo(() => widgetConfig && ({ + ...widgetConfig, + // The iframe's CSS color-scheme follows our theme, including changes during an active swap. + appearance: 'system' as const, + // widget-light omits these serializable MUI fields from its types; the hosted widget forwards them unchanged. + theme: { + typography: { fontFamily: BODY_TYPEFACE }, + colorSchemes: { + light: { palette: { primary: { main: mainColor }, secondary: { main: mainColor } } }, + dark: { palette: { primary: { main: mainColor }, secondary: { main: mainColor } } }, + }, + shape: { + borderRadius: Number.parseInt(borderRadius, 10), + borderRadiusSecondary: Number.parseInt(borderRadiusSecondary, 10), + }, + container: { height: 'fit-content', border: `1px solid ${ borderColor }`, borderRadius }, + }, + }), [ mainColor, borderColor, borderRadius, borderRadiusSecondary ]); + + if (!themedConfig) { + return null; + } + + return ( + + ); +}; - const message = useMemo(() => ({ - type: 'config', - integrator: dappConfig?.integrator, - fee: Number(dappConfig?.fee), - chains: dappConfig?.chains.map((chainId) => Number(chainId)), - explorerUrls: getExplorerUrls(), - mainColor, - borderColor, - fontFamily: BODY_TYPEFACE, - defaultChainId, - }), [ mainColor, borderColor ]); +export default function Swap() { + const isMobile = useIsMobile(); return ( - + }> + + { (feature?.essentialDappsAdEnabled && !isMobile) && ( , origin = WIDGET_ORIGIN, source: Window | null = iframe.contentWindow): void { + act(() => { + window.dispatchEvent(new MessageEvent('message', { origin, source, data: { source: 'widget-light', ...data } })); + }); +} + +afterEach(() => { + cleanup(); + vi.useRealTimers(); +}); + +describe('Swap iframe loading', () => { + it('keeps the unstyled iframe hidden until its configured page renders', () => { + const { container } = render(); + const iframe = container.querySelector('iframe')!; + expect(window.getComputedStyle(iframe).visibility).toBe('hidden'); + expect(iframe.parentElement?.getAttribute('aria-busy')).toBe('true'); + + fireEvent.load(iframe); + sendMessage(iframe, { type: 'READY' }); + expect(window.getComputedStyle(iframe).visibility).toBe('hidden'); + + const ready = { type: 'WIDGET_EVENT', event: 'pageEntered', data: '/' }; + sendMessage(iframe, ready, 'https://untrusted.test'); + sendMessage(iframe, ready, WIDGET_ORIGIN, window); + expect(window.getComputedStyle(iframe).visibility).toBe('hidden'); + + sendMessage(iframe, ready); + expect(window.getComputedStyle(iframe).visibility).toBe('visible'); + expect(iframe.parentElement?.getAttribute('aria-busy')).toBe('false'); + }); + + it('keeps an active iframe when its config changes and waits again after leaving the page', () => { + const { container, rerender, unmount } = render(); + const iframe = container.querySelector('iframe')!; + sendMessage(iframe, { type: 'READY' }); + sendMessage(iframe, { type: 'WIDGET_EVENT', event: 'pageEntered', data: '/' }); + + rerender(); + expect(container.querySelector('iframe')).toBe(iframe); + expect(window.getComputedStyle(iframe).visibility).toBe('visible'); + unmount(); + + const nextRender = render(); + const nextIframe = nextRender.container.querySelector('iframe')!; + expect(window.getComputedStyle(nextIframe).visibility).toBe('hidden'); + sendMessage(nextIframe, { type: 'READY' }); + sendMessage(nextIframe, { type: 'WIDGET_EVENT', event: 'pageEntered', data: '/' }); + expect(window.getComputedStyle(nextIframe).visibility).toBe('visible'); + }); + + it('offers a fresh attempt when the widget never becomes ready', () => { + vi.useFakeTimers(); + const { container } = render(); + const iframe = container.querySelector('iframe')!; + const widget = iframe.parentElement!; + sendMessage(iframe, { type: 'READY' }); + act(() => vi.advanceTimersByTime(30 * SECOND)); + + expect(screen.getByRole('alert').textContent).toContain('Unable to load the swap widget'); + expect(container.querySelector('iframe')).toBeNull(); + expect(widget.getAttribute('aria-busy')).toBe('false'); + fireEvent.click(screen.getByRole('button', { name: 'Try again' })); + + const nextIframe = container.querySelector('iframe')!; + expect(nextIframe).not.toBe(iframe); + expect(window.getComputedStyle(nextIframe).visibility).toBe('hidden'); + expect(screen.queryByRole('alert')).toBeNull(); + sendMessage(iframe, { type: 'WIDGET_EVENT', event: 'pageEntered', data: '/' }); + expect(window.getComputedStyle(nextIframe).visibility).toBe('hidden'); + sendMessage(nextIframe, { type: 'READY' }); + sendMessage(nextIframe, { type: 'WIDGET_EVENT', event: 'pageEntered', data: '/' }); + act(() => vi.advanceTimersByTime(30 * SECOND)); + expect(window.getComputedStyle(nextIframe).visibility).toBe('visible'); + expect(screen.queryByRole('alert')).toBeNull(); + }); +}); diff --git a/src/features/marketplace/pages/essential-dapp/swap/SwapWidgetIframe.tsx b/src/features/marketplace/pages/essential-dapp/swap/SwapWidgetIframe.tsx new file mode 100644 index 00000000000..48adde1d089 --- /dev/null +++ b/src/features/marketplace/pages/essential-dapp/swap/SwapWidgetIframe.tsx @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: LicenseRef-Blockscout + +import { Box, Center, Text, chakra } from '@chakra-ui/react'; +import { LiFiWidgetLight, useWidgetLightEvents, WidgetLightEvent, type WidgetLightIframeProps } from '@lifi/widget-light'; +import React, { useCallback, useEffect, useState } from 'react'; + +import { Button } from 'src/toolkit/chakra/button'; +import { useColorMode } from 'src/toolkit/chakra/color-mode'; +import { ContentLoader } from 'src/toolkit/components/loaders/ContentLoader'; + +import { SWAP_WIDGET_LOAD_TIMEOUT, SWAP_WIDGET_MIN_HEIGHT } from './consts'; + +const StyledLiFiWidget = chakra(LiFiWidgetLight); + +type Props = Pick; + +export default function SwapWidgetIframe(props: Props) { + const { colorMode } = useColorMode(); + const [ status, setStatus ] = useState<'loading' | 'ready' | 'error'>('loading'); + const events = useWidgetLightEvents(); + + useEffect(() => { + if (status !== 'loading') return; + const timeout = window.setTimeout(() => setStatus('error'), SWAP_WIDGET_LOAD_TIMEOUT); + // READY only starts configuration; PageEntered means the configured widget has rendered. + const handleReady = () => { + window.clearTimeout(timeout); + setStatus('ready'); + }; + events.on(WidgetLightEvent.PageEntered, handleReady); + return () => { + window.clearTimeout(timeout); + events.off(WidgetLightEvent.PageEntered, handleReady); + }; + }, [ events, status ]); + + const handleRetry = useCallback(() => setStatus('loading'), []); + + return ( + + { status !== 'error' && ( + + ) } + { status === 'loading' &&
} + { status === 'error' && ( +
+ Unable to load the swap widget. Please try again. + +
+ ) } +
+ ); +} diff --git a/src/features/marketplace/pages/essential-dapp/swap/consts.ts b/src/features/marketplace/pages/essential-dapp/swap/consts.ts new file mode 100644 index 00000000000..a52783acc38 --- /dev/null +++ b/src/features/marketplace/pages/essential-dapp/swap/consts.ts @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: LicenseRef-Blockscout + +import { SECOND } from 'src/toolkit/utils/consts'; + +export const SWAP_WIDGET_MIN_HEIGHT = '500px'; +export const SWAP_WIDGET_LOAD_TIMEOUT = 30 * SECOND; diff --git a/src/features/marketplace/pages/essential-dapp/swap/useSwapWallet.spec.tsx b/src/features/marketplace/pages/essential-dapp/swap/useSwapWallet.spec.tsx new file mode 100644 index 00000000000..d619dcc8ab6 --- /dev/null +++ b/src/features/marketplace/pages/essential-dapp/swap/useSwapWallet.spec.tsx @@ -0,0 +1,473 @@ +// SPDX-License-Identifier: LicenseRef-Blockscout +// @vitest-environment jsdom + +import { LiFiWidgetLight, type IframeEcosystemHandler } from '@lifi/widget-light'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import mixpanel from 'mixpanel-browser'; +import React, { useMemo } from 'react'; +import { http, UserRejectedRequestError, type EIP1193Parameters, type EIP1193Provider, type WalletRpcSchema } from 'viem'; +import { createConfig, mock, WagmiProvider, type Config } from 'wagmi'; +import { connect, disconnect } from 'wagmi/actions'; +import { mainnet, optimism } from 'wagmi/chains'; + +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; +import { act, cleanup, render, renderHook, waitFor } from 'vitest/lib'; +import withEnvs from 'vitest/utils/mockEnvs'; + +vi.mock('mixpanel-browser', () => ({ 'default': { init: vi.fn(), track: vi.fn() } })); + +const ACCOUNT = '0x1111111111111111111111111111111111111111'; +const OTHER_ACCOUNT = '0x2222222222222222222222222222222222222222'; +const TX_HASH = `0x${ 'ab'.repeat(32) }`; +const SIGNATURE = `0x${ 'cd'.repeat(65) }`; +const ETHEREUM_RPC = 'https://ethereum-rpc.test'; +const OPTIMISM_RPC = 'https://optimism-rpc.test'; +const WIDGET_ORIGIN = 'https://widget.li.fi'; +const WIDGET_CONFIG = { integrator: 'blockscout' }; +const BATCH_ID = 'wallet-batch-id'; +const APPROVAL_HASH = `0x${ 'ef'.repeat(32) }`; +const REWARDS_ORIGIN = 'https://rewards.test'; +const ACTIVITY_TOKEN = 'batch-activity-token'; +const ENV_OVERRIDES: Array<[ string, string ]> = [ + [ 'NEXT_PUBLIC_REWARDS_SERVICE_API_HOST', REWARDS_ORIGIN ], + [ 'NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID', 'test-project' ], + [ 'NEXT_PUBLIC_RE_CAPTCHA_APP_SITE_KEY', 'test-captcha' ], + [ 'NEXT_PUBLIC_MIXPANEL_PROJECT_TOKEN', 'test-mixpanel' ], +]; +const BATCH_PARAMS = [ { + chainId: '0x1', from: ACCOUNT, version: '2.0.0', + calls: [ { to: ACCOUNT, data: '0x1234' }, { to: OTHER_ACCOUNT, data: '0x5678' } ], +} ]; + +interface BatchTestState { + batchResult: unknown; + status: unknown; + reject: boolean; +} + +async function renderTrackedSwap(): Promise<{ + readonly result: { readonly current: { readonly wallet: IframeEcosystemHandler } }; + readonly state: BatchTestState; + readonly activityRequests: Array<{ path: string; body: unknown }>; +}> { + const { useSwapWallet } = await import('./useSwapWallet'); + const { RewardsContextProvider, useRewardsContext } = await import('src/features/rewards/context'); + const { getResourceKey } = await import('src/api/hooks/useApiQuery'); + const { init } = await import('src/shared/analytics/queue'); + const { getEnabledProviders } = await import('src/shared/analytics/providers'); + await init(getEnabledProviders().map((provider) => ({ provider, options: { debug: false } })), () => {}); + queryClient.setQueryData(getResourceKey('core:user_info'), { address_hash: ACCOUNT }); + queryClient.setQueryData(getResourceKey('rewards:user_check_activity_pass', { queryParams: { address: ACCOUNT } }), { is_valid: true }); + + const state: BatchTestState = { batchResult: { id: BATCH_ID }, status: { status: 100, receipts: [] }, reject: false }; + const connector = walletConfig.connectors[0]; + const getProvider = connector.getProvider.bind(connector); + vi.spyOn(connector, 'getProvider').mockImplementation(async(options) => { + const provider = await getProvider(options) as EIP1193Provider; + const request = async(args: EIP1193Parameters): Promise => { + if (args.method === 'wallet_sendCalls') { + if (state.reject) { + throw new UserRejectedRequestError(new Error('User rejected the batch.')); + } + return state.batchResult; + } + if (args.method === 'wallet_getCallsStatus') { + return state.status; + } + return provider.request(args); + }; + return { ...provider, request: request as typeof provider.request }; + }); + + const activityRequests: Array<{ path: string; body: unknown }> = []; + fetchMock.mockResponse(async(request) => { + if (new URL(request.url).origin !== REWARDS_ORIGIN) { + const { id, method } = JSON.parse(await request.text()) as { readonly id: number; readonly method: string }; + let result: unknown = [ ACCOUNT ]; + if (method === 'eth_sendTransaction') result = TX_HASH; + if (method === 'eth_sign' || method === 'eth_signTypedData_v4') result = SIGNATURE; + return JSON.stringify({ jsonrpc: '2.0', id, result }); + } + const path = new URL(request.url).pathname; + if (path.includes('/track/transaction')) { + activityRequests.push({ path, body: JSON.parse(await request.text()) }); + return { body: JSON.stringify({ token: ACTIVITY_TOKEN }), headers: { 'content-type': 'application/json' } }; + } + return { body: JSON.stringify({ is_valid: true }), headers: { 'content-type': 'application/json' } }; + }); + const TrackedWalletProvider = ({ children }: React.PropsWithChildren) => ( + { children } + ); + const { result } = renderHook(() => ({ wallet: useSwapWallet(), rewards: useRewardsContext() }), { wrapper: TrackedWalletProvider }); + act(() => result.current.rewards.onLoginSuccess('test-rewards-token')); + await waitFor(() => expect(result.current.rewards.isAuth).toBe(true)); + return { result, state, activityRequests }; +} + +function createWalletConfig(): Config { + return createConfig({ + chains: [ + { ...mainnet, rpcUrls: { 'default': { http: [ ETHEREUM_RPC ] } } }, + { ...optimism, rpcUrls: { 'default': { http: [ OPTIMISM_RPC ] } } }, + ], + connectors: [ mock({ accounts: [ ACCOUNT ], features: { reconnect: true } }) ], + transports: { [mainnet.id]: http(ETHEREUM_RPC), [optimism.id]: http(OPTIMISM_RPC) }, + syncConnectedChain: false, + storage: null, + }); +} + +let walletConfig: ReturnType; +let queryClient: QueryClient; +let rejectTransaction = false; +const sentTransactions: Array = []; +const walletRpcUrls: Array = []; + +const WalletProvider = ({ children }: React.PropsWithChildren) => ( + + { children } + +); + +beforeEach(async() => { + walletConfig = createWalletConfig(); + queryClient = new QueryClient({ defaultOptions: { queries: { retry: false }, mutations: { retry: false } } }); + rejectTransaction = false; + sentTransactions.length = 0; + walletRpcUrls.length = 0; + fetchMock.resetMocks(); + fetchMock.mockResponse(async(request) => { + const { id, method, params } = JSON.parse(await request.text()) as { + readonly id: number; + readonly method: string; + readonly params: Array; + }; + let result: unknown; + switch (method) { + case 'eth_accounts': result = [ ACCOUNT ]; break; + case 'eth_getBalance': result = new URL(request.url).origin === OPTIMISM_RPC ? '0x20' : '0x10'; break; + case 'eth_sendTransaction': + walletRpcUrls.push(request.url); + if (rejectTransaction) { + return JSON.stringify({ jsonrpc: '2.0', id, error: { code: 4001, message: 'User rejected the request.' } }); + } + sentTransactions.push(params[0]); + result = TX_HASH; + break; + case 'eth_sign': + case 'eth_signTypedData_v4': + walletRpcUrls.push(request.url); + result = SIGNATURE; + break; + default: throw new Error(`Unexpected RPC: ${ method }`); + } + return JSON.stringify({ jsonrpc: '2.0', id, result }); + }); + await connect(walletConfig, { connector: walletConfig.connectors[0] }); +}); + +afterEach(() => { + cleanup(); + queryClient.clear(); + document.cookie = 'rewards_api_token=; Max-Age=0; path=/'; + vi.clearAllMocks(); + vi.restoreAllMocks(); +}); + +describe('Swap wallet with wagmi v2', () => { + it('exposes the existing connection and publishes account changes and disconnects', async() => { + const { useSwapWallet } = await import('./useSwapWallet'); + const { result } = renderHook(useSwapWallet, { wrapper: WalletProvider }); + expect(result.current.getInitState()).toMatchObject({ chainType: 'EVM', state: { accounts: [ ACCOUNT ], chainId: mainnet.id } }); + const emit = vi.fn(); + const unsubscribe = result.current.subscribe(emit); + + act(() => walletConfig.connectors[0].onAccountsChanged([ OTHER_ACCOUNT ])); + await waitFor(() => expect(emit).toHaveBeenCalledWith('accountsChanged', [ OTHER_ACCOUNT ])); + await act(async() => { + await disconnect(walletConfig); + }); + expect(await result.current.handleRequest('accounts', 'eth_accounts')).toEqual([]); + expect(emit).toHaveBeenCalledWith('accountsChanged', []); + unsubscribe(); + }); + + it('switches chains and reads from the connected chain even when the explorer stays on Ethereum', async() => { + const { useSwapWallet } = await import('./useSwapWallet'); + const { result } = renderHook(useSwapWallet, { wrapper: WalletProvider }); + await act(async() => { + await result.current.handleRequest('switch', 'wallet_switchEthereumChain', [ { chainId: '0xa' } ]); + }); + expect(walletConfig.state.chainId).toBe(mainnet.id); + await waitFor(() => expect(result.current.getInitState()).toMatchObject({ state: { chainId: optimism.id } })); + expect(await result.current.handleRequest('balance', 'eth_getBalance', [ ACCOUNT, 'latest' ])).toBe('0x20'); + await waitFor(() => expect(queryClient.isFetching()).toBe(0)); + expect(await result.current.handleRequest('send', 'eth_sendTransaction', [ { to: OTHER_ACCOUNT, value: '0x0' } ])).toBe(TX_HASH); + }); + + it('can read the account and switch away from a wallet chain outside the configuration', async() => { + const { useSwapWallet } = await import('./useSwapWallet'); + const { result } = renderHook(useSwapWallet, { wrapper: WalletProvider }); + act(() => walletConfig.connectors[0].onChainChanged('0x89')); + + expect(await result.current.handleRequest('chain', 'eth_chainId')).toBe('0x89'); + expect(await result.current.handleRequest('accounts', 'eth_accounts')).toEqual([ ACCOUNT ]); + await act(async() => { + expect(await result.current.handleRequest('switch', 'wallet_switchEthereumChain', [ { chainId: '0xa' } ])).toBeNull(); + }); + expect(await result.current.handleRequest('chain', 'eth_chainId')).toBe('0xa'); + expect(await result.current.handleRequest('balance', 'eth_getBalance', [ ACCOUNT, 'latest' ])).toBe('0x20'); + }); + + it.each([ + { method: 'eth_sendTransaction', params: [ { to: OTHER_ACCOUNT, value: '0x0' } ], expected: TX_HASH }, + { method: 'personal_sign', params: [ '0x1234', ACCOUNT ], expected: SIGNATURE }, + ])('uses the new chain immediately after switching for $method', async({ method, params, expected }) => { + const { useSwapWallet } = await import('./useSwapWallet'); + const { result } = renderHook(useSwapWallet, { wrapper: WalletProvider }); + await waitFor(() => expect(queryClient.isFetching()).toBe(0)); + const emit = vi.fn(); + result.current.subscribe(emit); + await act(async() => { + await result.current.handleRequest('switch', 'wallet_switchEthereumChain', [ { chainId: '0xa' } ]); + }); + await waitFor(() => expect(emit).toHaveBeenCalledWith('chainChanged', '0xa')); + + expect(await result.current.handleRequest('after-switch', method, params)).toEqual(expected); + expect(walletRpcUrls).toEqual([ `${ OPTIMISM_RPC }/` ]); + }); + + it('preserves transaction fields and returns wallet signatures', async() => { + const { useSwapWallet } = await import('./useSwapWallet'); + const { result } = renderHook(useSwapWallet, { wrapper: WalletProvider }); + await waitFor(() => expect(queryClient.isFetching()).toBe(0)); + expect(await result.current.handleRequest('send', 'eth_sendTransaction', [ { + to: OTHER_ACCOUNT, value: '0x123456789abcdef', gas: '0x5208', data: '0x1234', + } ])).toBe(TX_HASH); + expect(sentTransactions).toEqual([ expect.objectContaining({ + from: ACCOUNT, to: OTHER_ACCOUNT, value: '0x123456789abcdef', gas: '0x5208', data: '0x1234', + }) ]); + expect(await result.current.handleRequest('sign', 'personal_sign', [ '0x1234', ACCOUNT ])).toBe(SIGNATURE); + expect(await result.current.handleRequest('typed', 'eth_signTypedData_v4', [ ACCOUNT, JSON.stringify({ + domain: { chainId: mainnet.id }, types: { Message: [ { name: 'value', type: 'string' } ] }, primaryType: 'Message', message: { value: 'test' }, + }) ])).toBe(SIGNATURE); + }); + + it('propagates a rejected transaction without reporting a successful send', async() => { + const { useSwapWallet } = await import('./useSwapWallet'); + const { result } = renderHook(useSwapWallet, { wrapper: WalletProvider }); + await waitFor(() => expect(queryClient.isFetching()).toBe(0)); + rejectTransaction = true; + await expect(result.current.handleRequest('send', 'eth_sendTransaction', [ { to: OTHER_ACCOUNT, value: '0x0' } ])) + .rejects.toThrow('User rejected the request'); + expect(sentTransactions).toEqual([]); + }); + + it('bridges iframe requests and ignores messages from a different origin', async() => { + const { useSwapWallet } = await import('./useSwapWallet'); + const Widget = () => { + const handler = useSwapWallet(); + const handlers = useMemo(() => [ handler ], [ handler ]); + return ; + }; + const { container } = render(); + const iframe = container.querySelector('iframe'); + expect(iframe?.src).toBe(`${ WIDGET_ORIGIN }/`); + const frameWindow = iframe!.contentWindow!; + const postMessage = vi.spyOn(frameWindow, 'postMessage'); + const dispatch = (origin: string) => window.dispatchEvent(new MessageEvent('message', { + origin, source: frameWindow, data: { source: 'widget-light', type: 'READY' }, + })); + act(() => { + dispatch('https://untrusted.test'); + }); + expect(postMessage).not.toHaveBeenCalled(); + act(() => { + dispatch(WIDGET_ORIGIN); + }); + expect(postMessage).toHaveBeenCalledWith(expect.objectContaining({ + type: 'INIT', ecosystems: [ expect.objectContaining({ chainType: 'EVM', state: expect.objectContaining({ accounts: [ ACCOUNT ] }) }) ], + }), WIDGET_ORIGIN); + + act(() => { + window.dispatchEvent(new MessageEvent('message', { + origin: WIDGET_ORIGIN, source: frameWindow, + data: { source: 'widget-light', type: 'RPC_REQUEST', chainType: 'EVM', id: 'accounts', method: 'eth_accounts' }, + })); + }); + await waitFor(() => expect(postMessage).toHaveBeenCalledWith(expect.objectContaining({ + type: 'RPC_RESPONSE', id: 'accounts', result: [ ACCOUNT ], + }), WIDGET_ORIGIN)); + + act(() => walletConfig.connectors[0].onAccountsChanged([ OTHER_ACCOUNT ])); + await waitFor(() => expect(postMessage).toHaveBeenCalledWith(expect.objectContaining({ + type: 'EVENT', event: 'accountsChanged', data: [ OTHER_ACCOUNT ], + }), WIDGET_ORIGIN)); + }); +}); + +describe('Swap batch activity', () => { + it.each([ + { name: 'atomic', batchResult: { id: BATCH_ID }, status: 200, hashes: [ TX_HASH ] }, + { name: 'multiple receipts', batchResult: { id: BATCH_ID }, status: 200, hashes: [ APPROVAL_HASH, TX_HASH ] }, + { name: 'legacy wallet response', batchResult: BATCH_ID, status: 'CONFIRMED', hashes: [ TX_HASH ] }, + ])('confirms the swap receipt once for $name', async({ batchResult, status, hashes }) => { + await withEnvs(ENV_OVERRIDES, async() => { + const { result, state, activityRequests } = await renderTrackedSwap(); + state.batchResult = batchResult; + expect(await result.current.wallet.handleRequest('send-batch', 'wallet_sendCalls', BATCH_PARAMS)).toEqual(batchResult); + expect(activityRequests).toEqual([ { + path: '/api/v1/user/activity/track/transaction', + body: { from_address: ACCOUNT, to_address: OTHER_ACCOUNT, chain_id: '1' }, + } ]); + + await result.current.wallet.handleRequest('pending', 'wallet_getCallsStatus', [ BATCH_ID ]); + expect(activityRequests).toHaveLength(1); + await act(async() => { + await result.current.wallet.handleRequest('switch', 'wallet_switchEthereumChain', [ { chainId: '0xa' } ]); + }); + state.status = { status, receipts: hashes.map((transactionHash) => ({ status: '0x1', transactionHash })) }; + const responses = await Promise.all([ + result.current.wallet.handleRequest('poll-1', 'wallet_getCallsStatus', [ BATCH_ID ]), + result.current.wallet.handleRequest('poll-2', 'wallet_getCallsStatus', [ BATCH_ID ]), + ]); + expect(responses).toEqual([ state.status, state.status ]); + await result.current.wallet.handleRequest('poll-3', 'wallet_getCallsStatus', [ BATCH_ID ]); + expect(activityRequests).toHaveLength(2); + expect(activityRequests[1]).toEqual({ + path: '/api/v1/activity/track/transaction/confirm', body: { tx_hash: TX_HASH, token: ACTIVITY_TOKEN }, + }); + expect(mixpanel.track).toHaveBeenCalledTimes(1); + expect(mixpanel.track).toHaveBeenCalledWith(expect.any(String), expect.objectContaining({ + Action: 'Send Transaction', Address: ACCOUNT, AppId: 'swap', Source: 'Essential dapps', ChainId: '1', + }), undefined); + }); + }); + + it('does not report a batch that the wallet rejects', async() => { + await withEnvs(ENV_OVERRIDES, async() => { + const { result, state, activityRequests } = await renderTrackedSwap(); + state.reject = true; + await expect(result.current.wallet.handleRequest('reject-batch', 'wallet_sendCalls', BATCH_PARAMS)).rejects.toThrow('User rejected'); + state.status = { status: 200, receipts: [ { status: '0x1', transactionHash: TX_HASH } ] }; + await result.current.wallet.handleRequest('poll', 'wallet_getCallsStatus', [ BATCH_ID ]); + expect(activityRequests).toHaveLength(1); + expect(mixpanel.track).not.toHaveBeenCalled(); + }); + }); + + it.each([ + { status: 300, receipts: [ { status: '0x1', transactionHash: TX_HASH } ] }, + { status: 400, receipts: [] }, + { status: 500, receipts: [ { status: '0x0', transactionHash: TX_HASH } ] }, + { status: 200, receipts: [ { status: '0x0', transactionHash: TX_HASH } ] }, + { status: 200, receipts: [ { status: '0x1', transactionHash: BATCH_ID } ] }, + { status: 200, receipts: [] }, + ])('does not confirm failed or missing transaction receipts: %j', async(response) => { + await withEnvs(ENV_OVERRIDES, async() => { + const { result, state, activityRequests } = await renderTrackedSwap(); + await result.current.wallet.handleRequest('send-batch', 'wallet_sendCalls', BATCH_PARAMS); + state.status = response; + await result.current.wallet.handleRequest('poll', 'wallet_getCallsStatus', [ BATCH_ID ]); + expect(activityRequests).toHaveLength(1); + expect(mixpanel.track).toHaveBeenCalledTimes(1); + }); + }); + + it.each([ [ {} ], [ { calls: [] } ] ])('forwards a batch without swap calls without tracking it: %j', async(params) => { + await withEnvs(ENV_OVERRIDES, async() => { + const { result, activityRequests } = await renderTrackedSwap(); + expect(await result.current.wallet.handleRequest('send-batch', 'wallet_sendCalls', params)).toEqual({ id: BATCH_ID }); + expect(activityRequests).toEqual([]); + expect(mixpanel.track).not.toHaveBeenCalled(); + }); + }); + + it('does not retain or log an invalid batch ID', async() => { + await withEnvs(ENV_OVERRIDES, async() => { + const { result, state, activityRequests } = await renderTrackedSwap(); + state.batchResult = 42; + expect(await result.current.wallet.handleRequest('send-batch', 'wallet_sendCalls', BATCH_PARAMS)).toBe(42); + expect(activityRequests).toHaveLength(1); + expect(mixpanel.track).not.toHaveBeenCalled(); + state.status = { status: 200, receipts: [ { status: '0x1', transactionHash: TX_HASH } ] }; + await result.current.wallet.handleRequest('poll', 'wallet_getCallsStatus', [ BATCH_ID ]); + expect(activityRequests).toHaveLength(1); + }); + }); + + it('ignores a status response without a valid batch ID', async() => { + await withEnvs(ENV_OVERRIDES, async() => { + const { result, state, activityRequests } = await renderTrackedSwap(); + await result.current.wallet.handleRequest('send-batch', 'wallet_sendCalls', BATCH_PARAMS); + state.status = { status: 200, receipts: [ { status: '0x1', transactionHash: TX_HASH } ] }; + expect(await result.current.wallet.handleRequest('poll', 'wallet_getCallsStatus', { '0': BATCH_ID })).toEqual(state.status); + expect(activityRequests).toHaveLength(1); + }); + }); + + it.each([ 100, 199, 'PENDING' ])('does not confirm an unfinished batch with status %s', async(status) => { + await withEnvs(ENV_OVERRIDES, async() => { + const { result, state, activityRequests } = await renderTrackedSwap(); + await result.current.wallet.handleRequest('send-batch', 'wallet_sendCalls', BATCH_PARAMS); + state.status = { status, receipts: [ { status: '0x1', transactionHash: TX_HASH } ] }; + await result.current.wallet.handleRequest('poll', 'wallet_getCallsStatus', [ BATCH_ID ]); + expect(activityRequests).toHaveLength(1); + state.status = { status: 200, receipts: [ { status: '0x1', transactionHash: TX_HASH } ] }; + await result.current.wallet.handleRequest('confirmed', 'wallet_getCallsStatus', [ BATCH_ID ]); + expect(activityRequests).toHaveLength(2); + }); + }); + + it('forgets a failed batch even if a later poll reports success', async() => { + await withEnvs(ENV_OVERRIDES, async() => { + const { result, state, activityRequests } = await renderTrackedSwap(); + await result.current.wallet.handleRequest('send-batch', 'wallet_sendCalls', BATCH_PARAMS); + state.status = { status: 300, receipts: [ { status: '0x1', transactionHash: TX_HASH } ] }; + await result.current.wallet.handleRequest('failed', 'wallet_getCallsStatus', [ BATCH_ID ]); + state.status = { status: 200, receipts: [ { status: '0x1', transactionHash: TX_HASH } ] }; + await result.current.wallet.handleRequest('later', 'wallet_getCallsStatus', [ BATCH_ID ]); + expect(activityRequests).toHaveLength(1); + }); + }); + + it('requires every receipt to succeed before confirming the last swap hash', async() => { + await withEnvs(ENV_OVERRIDES, async() => { + const { result, state, activityRequests } = await renderTrackedSwap(); + await result.current.wallet.handleRequest('send-batch', 'wallet_sendCalls', BATCH_PARAMS); + state.status = { status: 200, receipts: [ + { status: '0x0', transactionHash: APPROVAL_HASH }, + { status: '0x1', transactionHash: TX_HASH }, + ] }; + await result.current.wallet.handleRequest('poll', 'wallet_getCallsStatus', [ BATCH_ID ]); + expect(activityRequests).toHaveLength(1); + }); + }); + + it('tracks a single transaction with its recipient and confirms its returned hash', async() => { + await withEnvs(ENV_OVERRIDES, async() => { + const { result, activityRequests } = await renderTrackedSwap(); + expect(await result.current.wallet.handleRequest('send', 'eth_sendTransaction', [ { to: OTHER_ACCOUNT, value: '0x1' } ])).toBe(TX_HASH); + expect(activityRequests).toEqual([ + { path: '/api/v1/user/activity/track/transaction', body: { from_address: ACCOUNT, to_address: OTHER_ACCOUNT, chain_id: '1' } }, + { path: '/api/v1/activity/track/transaction/confirm', body: { tx_hash: TX_HASH, token: ACTIVITY_TOKEN } }, + ]); + expect(mixpanel.track).toHaveBeenCalledWith(expect.any(String), expect.objectContaining({ Action: 'Send Transaction' }), undefined); + }); + }); + + it.each([ + { method: 'personal_sign', params: [ '0x1234', ACCOUNT ], action: 'Sign Message' }, + { method: 'eth_sign', params: [ ACCOUNT, '0x1234' ], action: 'Sign Message' }, + { method: 'eth_signTypedData_v4', params: [ ACCOUNT, JSON.stringify({ + domain: { chainId: mainnet.id }, types: { Message: [ { name: 'value', type: 'string' } ] }, + primaryType: 'Message', message: { value: 'test' }, + }) ], action: 'Sign Typed Data' }, + ])('logs $action for $method', async({ method, params, action }) => { + await withEnvs(ENV_OVERRIDES, async() => { + const { result } = await renderTrackedSwap(); + await result.current.wallet.handleRequest('sign', method, params); + expect(mixpanel.track).toHaveBeenCalledWith(expect.any(String), expect.objectContaining({ Action: action }), undefined); + }); + }); +}); diff --git a/src/features/marketplace/pages/essential-dapp/swap/useSwapWallet.ts b/src/features/marketplace/pages/essential-dapp/swap/useSwapWallet.ts new file mode 100644 index 00000000000..f399b5e4cec --- /dev/null +++ b/src/features/marketplace/pages/essential-dapp/swap/useSwapWallet.ts @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: LicenseRef-Blockscout + +import type { IframeEcosystemHandler } from '@lifi/widget-light'; +import { useEthereumIframeHandler } from '@lifi/widget-light/ethereum'; +import { useCallback, useMemo, useRef } from 'react'; +import * as v from 'valibot'; +import { isHash } from 'viem'; + +import { useMarketplaceWalletActivity } from '../../../hooks/useMarketplaceWalletActivity'; + +const BATCH_SUCCESS_STATUS = 200; +const BATCH_FAILURE_STATUS = 300; +type ConfirmTransaction = (hash: string) => Promise; +const transactionRequestSchema = v.tuple([ v.object({ to: v.optional(v.string()) }) ]); +const batchRequestSchema = v.tuple([ v.object({ calls: v.array(v.object({ to: v.optional(v.string()) })) }) ]); +const batchStatusRequestSchema = v.tuple([ v.string() ]); +const batchIdSchema = v.union([ v.string(), v.object({ id: v.string() }) ]); +const batchStatusSchema = v.object({ + status: v.union([ v.number(), v.literal('CONFIRMED'), v.literal('PENDING') ]), + receipts: v.optional(v.array(v.object({ status: v.string(), transactionHash: v.string() }))), +}); + +export function useSwapWallet(): IframeEcosystemHandler { + const handler = useEthereumIframeHandler(); + const { prepareTransaction, trackTransaction, logEvent } = useMarketplaceWalletActivity('swap', true); + const pendingBatches = useRef(new Map()); + + const sendCalls = useCallback(async(id, method, params) => { + const request = v.safeParse(batchRequestSchema, params); + if (!request.success || !request.output[0].calls.length) { + return handler.handleRequest(id, method, params); + } + // LI.FI appends the swap call after approvals and uses the last receipt for its transaction hash. + const confirm = await prepareTransaction(request.output[0].calls.at(-1)?.to ?? ''); + const result = await handler.handleRequest(id, method, params); + const parsedId = v.safeParse(batchIdSchema, result); + if (parsedId.success) { + const batchId = typeof parsedId.output === 'string' ? parsedId.output : parsedId.output.id; + pendingBatches.current.set(batchId, confirm); + logEvent('Send Transaction'); + } + return result; + }, [ handler, prepareTransaction, logEvent ]); + + const confirmCalls = useCallback(async(params: unknown, result: unknown) => { + const request = v.safeParse(batchStatusRequestSchema, params); + if (!request.success) { + return; + } + const [ batchId ] = request.output; + const confirm = pendingBatches.current.get(batchId); + const response = v.safeParse(batchStatusSchema, result); + if (!confirm || !response.success) { + return; + } + const { status, receipts } = response.output; + // The parsed status is numeric or one of two words; Number maps the words to NaN. + const numericStatus = Number(status); + if (numericStatus >= BATCH_FAILURE_STATUS) { + pendingBatches.current.delete(batchId); + return; + } + const isConfirmed = status === 'CONFIRMED' || numericStatus >= BATCH_SUCCESS_STATUS; + const hash = receipts?.at(-1)?.transactionHash; + if (!isConfirmed || !hash || !isHash(hash) || !receipts?.every((receipt) => receipt.status === '0x1')) { + return; + } + pendingBatches.current.delete(batchId); + await confirm(hash); + }, []); + + const handleRequest = useCallback(async(id, method, params) => { + if (method === 'wallet_sendCalls') { + return sendCalls(id, method, params); + } + if (method === 'eth_sendTransaction') { + const request = v.safeParse(transactionRequestSchema, params); + const to = request.success ? request.output[0].to ?? '' : ''; + return trackTransaction(to, () => handler.handleRequest(id, method, params)); + } + + const result = await handler.handleRequest(id, method, params); + if (method === 'wallet_getCallsStatus') { + await confirmCalls(params, result); + } else if (method === 'personal_sign' || method === 'eth_sign') { + logEvent('Sign Message'); + } else if (method === 'eth_signTypedData_v4') { + logEvent('Sign Typed Data'); + } + return result; + }, [ handler, trackTransaction, logEvent, sendCalls, confirmCalls ]); + + return useMemo(() => ({ ...handler, handleRequest }), [ handler, handleRequest ]); +} diff --git a/src/features/marketplace/pages/essential-dapp/swap/widget-config.spec.ts b/src/features/marketplace/pages/essential-dapp/swap/widget-config.spec.ts new file mode 100644 index 00000000000..5153754c9b7 --- /dev/null +++ b/src/features/marketplace/pages/essential-dapp/swap/widget-config.spec.ts @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: LicenseRef-Blockscout + +import { describe, expect, it } from 'vitest'; + +import { getSwapWidgetConfig } from './widget-config'; + +const SWAP_CONFIG = { chains: [ '1', '10' ], fee: '0.004', integrator: 'blockscout' }; + +describe('getSwapWidgetConfig', () => { + it('keeps the integrator fee and limits both sides of a swap to configured chains', () => { + expect(getSwapWidgetConfig(SWAP_CONFIG, '10', [])).toMatchObject({ + integrator: 'blockscout', + feeConfig: { fee: 0.004 }, + chains: { allow: [ 1, 10 ] }, + fromChain: 10, + fromToken: '0x0000000000000000000000000000000000000000', + hiddenUI: { appearance: true, language: true, gasRefuelMessage: true }, + }); + }); + + it.each([ '137', undefined ])('defaults to the first allowed chain when the explorer chain is %s', (chainId) => { + expect(getSwapWidgetConfig(SWAP_CONFIG, chainId, [])).toMatchObject({ fromChain: 1 }); + }); + + it('only sends available explorer URLs for allowed chains', () => { + const result = getSwapWidgetConfig(SWAP_CONFIG, '1', [ + { id: '1', name: 'Ethereum', explorer_url: 'https://eth.blockscout.com' }, + { id: '10', name: 'Optimism' }, + { id: '137', name: 'Polygon', explorer_url: 'https://polygon.blockscout.com' }, + ]); + expect(result.explorerUrls).toEqual({ '1': [ 'https://eth.blockscout.com' ] }); + expect(JSON.parse(JSON.stringify(result))).toEqual(result); + }); +}); diff --git a/src/features/marketplace/pages/essential-dapp/swap/widget-config.ts b/src/features/marketplace/pages/essential-dapp/swap/widget-config.ts new file mode 100644 index 00000000000..1617f3267a3 --- /dev/null +++ b/src/features/marketplace/pages/essential-dapp/swap/widget-config.ts @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: LicenseRef-Blockscout + +import type { WidgetLightConfig } from '@lifi/widget-light'; +import { zeroAddress } from 'viem'; + +import type { EssentialDappsChainConfig, EssentialDappsConfig } from 'src/features/marketplace/types/client'; + +export function getSwapWidgetConfig( + swap: NonNullable, + chainId: string | undefined, + chainConfigs: Array, +): WidgetLightConfig { + const chains = swap.chains.map(Number); + const explorerUrls = Object.fromEntries(chainConfigs.flatMap((chain) => + swap.chains.includes(chain.id) && chain.explorer_url ? [ [ Number(chain.id), [ chain.explorer_url ] ] ] : [], + )); + + return { + integrator: swap.integrator, + feeConfig: { fee: Number(swap.fee) }, + chains: { allow: chains }, + fromChain: chains.includes(Number(chainId)) ? Number(chainId) : chains[0], + fromToken: zeroAddress, + explorerUrls, + variant: 'compact', + mode: 'default', + hiddenUI: { appearance: true, language: true, gasRefuelMessage: true }, + }; +} diff --git a/src/features/marketplace/pages/index/MarketplaceAppCard.tsx b/src/features/marketplace/pages/index/MarketplaceAppCard.tsx index 563792f0723..19c635dc079 100644 --- a/src/features/marketplace/pages/index/MarketplaceAppCard.tsx +++ b/src/features/marketplace/pages/index/MarketplaceAppCard.tsx @@ -6,7 +6,7 @@ import React, { useCallback } from 'react'; import type { MarketplaceDapp } from '@blockscout/admin-rs-types'; -import * as mixpanel from 'src/services/mixpanel'; +import * as analytics from 'src/shared/analytics'; import { route } from 'src/shared/router/routes'; import CopyToClipboard from 'src/shared/texts/CopyToClipboard'; @@ -54,7 +54,7 @@ const MarketplaceAppCard = ({ const categoriesLabel = categories.join(', '); const handleInfoClick = useCallback(() => { - mixpanel.logEvent(mixpanel.EventTypes.PAGE_WIDGET, { Type: 'More button', Info: id, Source: 'Discovery view' }); + analytics.logEvent(analytics.EventTypes.PAGE_WIDGET, { Type: 'More button', Info: id, Source: 'Discovery view' }); }, [ id ]); const handleFavoriteClick = useCallback(() => { diff --git a/src/features/marketplace/pages/index/__screenshots__/Marketplace.pw.tsx_dark-color-mode_base-view-dark-mode-1.png b/src/features/marketplace/pages/index/__screenshots__/Marketplace.pw.tsx_dark-color-mode_base-view-dark-mode-1.png index 9f811a13741..192a2c6ddf4 100644 Binary files a/src/features/marketplace/pages/index/__screenshots__/Marketplace.pw.tsx_dark-color-mode_base-view-dark-mode-1.png and b/src/features/marketplace/pages/index/__screenshots__/Marketplace.pw.tsx_dark-color-mode_base-view-dark-mode-1.png differ diff --git a/src/features/marketplace/pages/index/__screenshots__/Marketplace.pw.tsx_dark-color-mode_with-banner-dark-mode-1.png b/src/features/marketplace/pages/index/__screenshots__/Marketplace.pw.tsx_dark-color-mode_with-banner-dark-mode-1.png index 7af998dd443..49b90bd5448 100644 Binary files a/src/features/marketplace/pages/index/__screenshots__/Marketplace.pw.tsx_dark-color-mode_with-banner-dark-mode-1.png and b/src/features/marketplace/pages/index/__screenshots__/Marketplace.pw.tsx_dark-color-mode_with-banner-dark-mode-1.png differ diff --git a/src/features/marketplace/pages/index/__screenshots__/Marketplace.pw.tsx_dark-color-mode_with-essential-dapps-dark-mode-1.png b/src/features/marketplace/pages/index/__screenshots__/Marketplace.pw.tsx_dark-color-mode_with-essential-dapps-dark-mode-1.png index f469283ee87..d3db8018302 100644 Binary files a/src/features/marketplace/pages/index/__screenshots__/Marketplace.pw.tsx_dark-color-mode_with-essential-dapps-dark-mode-1.png and b/src/features/marketplace/pages/index/__screenshots__/Marketplace.pw.tsx_dark-color-mode_with-essential-dapps-dark-mode-1.png differ diff --git a/src/features/marketplace/pages/index/__screenshots__/Marketplace.pw.tsx_dark-color-mode_with-featured-app-dark-mode-1.png b/src/features/marketplace/pages/index/__screenshots__/Marketplace.pw.tsx_dark-color-mode_with-featured-app-dark-mode-1.png index af591d13868..cf436522cfe 100644 Binary files a/src/features/marketplace/pages/index/__screenshots__/Marketplace.pw.tsx_dark-color-mode_with-featured-app-dark-mode-1.png and b/src/features/marketplace/pages/index/__screenshots__/Marketplace.pw.tsx_dark-color-mode_with-featured-app-dark-mode-1.png differ diff --git a/src/features/marketplace/pages/index/__screenshots__/Marketplace.pw.tsx_default_base-view-dark-mode-1.png b/src/features/marketplace/pages/index/__screenshots__/Marketplace.pw.tsx_default_base-view-dark-mode-1.png index ffc20ed5b80..ffaff2575c1 100644 Binary files a/src/features/marketplace/pages/index/__screenshots__/Marketplace.pw.tsx_default_base-view-dark-mode-1.png and b/src/features/marketplace/pages/index/__screenshots__/Marketplace.pw.tsx_default_base-view-dark-mode-1.png differ diff --git a/src/features/marketplace/pages/index/__screenshots__/Marketplace.pw.tsx_default_mobile-base-view-1.png b/src/features/marketplace/pages/index/__screenshots__/Marketplace.pw.tsx_default_mobile-base-view-1.png index 90f7d9b7199..2f916b83e65 100644 Binary files a/src/features/marketplace/pages/index/__screenshots__/Marketplace.pw.tsx_default_mobile-base-view-1.png and b/src/features/marketplace/pages/index/__screenshots__/Marketplace.pw.tsx_default_mobile-base-view-1.png differ diff --git a/src/features/marketplace/pages/index/__screenshots__/Marketplace.pw.tsx_default_mobile-with-banner-1.png b/src/features/marketplace/pages/index/__screenshots__/Marketplace.pw.tsx_default_mobile-with-banner-1.png index bed853bc58b..94f4ae62ca2 100644 Binary files a/src/features/marketplace/pages/index/__screenshots__/Marketplace.pw.tsx_default_mobile-with-banner-1.png and b/src/features/marketplace/pages/index/__screenshots__/Marketplace.pw.tsx_default_mobile-with-banner-1.png differ diff --git a/src/features/marketplace/pages/index/__screenshots__/Marketplace.pw.tsx_default_mobile-with-essential-dapps-1.png b/src/features/marketplace/pages/index/__screenshots__/Marketplace.pw.tsx_default_mobile-with-essential-dapps-1.png index e194014e7df..ca309df771f 100644 Binary files a/src/features/marketplace/pages/index/__screenshots__/Marketplace.pw.tsx_default_mobile-with-essential-dapps-1.png and b/src/features/marketplace/pages/index/__screenshots__/Marketplace.pw.tsx_default_mobile-with-essential-dapps-1.png differ diff --git a/src/features/marketplace/pages/index/__screenshots__/Marketplace.pw.tsx_default_mobile-with-featured-app-1.png b/src/features/marketplace/pages/index/__screenshots__/Marketplace.pw.tsx_default_mobile-with-featured-app-1.png index 787ac079134..76bfb9bdd16 100644 Binary files a/src/features/marketplace/pages/index/__screenshots__/Marketplace.pw.tsx_default_mobile-with-featured-app-1.png and b/src/features/marketplace/pages/index/__screenshots__/Marketplace.pw.tsx_default_mobile-with-featured-app-1.png differ diff --git a/src/features/marketplace/pages/index/__screenshots__/Marketplace.pw.tsx_default_with-banner-dark-mode-1.png b/src/features/marketplace/pages/index/__screenshots__/Marketplace.pw.tsx_default_with-banner-dark-mode-1.png index 295d68b2cdf..3f1ea7e0e36 100644 Binary files a/src/features/marketplace/pages/index/__screenshots__/Marketplace.pw.tsx_default_with-banner-dark-mode-1.png and b/src/features/marketplace/pages/index/__screenshots__/Marketplace.pw.tsx_default_with-banner-dark-mode-1.png differ diff --git a/src/features/marketplace/pages/index/__screenshots__/Marketplace.pw.tsx_default_with-essential-dapps-dark-mode-1.png b/src/features/marketplace/pages/index/__screenshots__/Marketplace.pw.tsx_default_with-essential-dapps-dark-mode-1.png index 4832f9bc6e3..60ff1145824 100644 Binary files a/src/features/marketplace/pages/index/__screenshots__/Marketplace.pw.tsx_default_with-essential-dapps-dark-mode-1.png and b/src/features/marketplace/pages/index/__screenshots__/Marketplace.pw.tsx_default_with-essential-dapps-dark-mode-1.png differ diff --git a/src/features/marketplace/pages/index/__screenshots__/Marketplace.pw.tsx_default_with-featured-app-dark-mode-1.png b/src/features/marketplace/pages/index/__screenshots__/Marketplace.pw.tsx_default_with-featured-app-dark-mode-1.png index 78e42b59794..c75f4e56ae2 100644 Binary files a/src/features/marketplace/pages/index/__screenshots__/Marketplace.pw.tsx_default_with-featured-app-dark-mode-1.png and b/src/features/marketplace/pages/index/__screenshots__/Marketplace.pw.tsx_default_with-featured-app-dark-mode-1.png differ diff --git a/src/features/marketplace/pages/index/banner/FeaturedApp.tsx b/src/features/marketplace/pages/index/banner/FeaturedApp.tsx index 29f9fcd188c..e4a5939e02f 100644 --- a/src/features/marketplace/pages/index/banner/FeaturedApp.tsx +++ b/src/features/marketplace/pages/index/banner/FeaturedApp.tsx @@ -7,7 +7,7 @@ import React, { useCallback } from 'react'; import type { MarketplaceDapp } from '@blockscout/admin-rs-types'; -import * as mixpanel from 'src/services/mixpanel'; +import * as analytics from 'src/shared/analytics'; import useIsMobile from 'src/shared/hooks/useIsMobile'; import { useColorModeValue } from 'src/toolkit/chakra/color-mode'; @@ -41,7 +41,7 @@ const FeaturedApp = ({ const categoriesLabel = categories.join(', '); const handleInfoClick = useCallback(() => { - mixpanel.logEvent(mixpanel.EventTypes.PAGE_WIDGET, { Type: 'More button', Info: id, Source: 'Banner' }); + analytics.logEvent(analytics.EventTypes.PAGE_WIDGET, { Type: 'More button', Info: id, Source: 'Banner' }); }, [ id ]); const handleFavoriteClick = useCallback(() => { diff --git a/src/features/marketplace/pages/index/banner/IframeBanner.tsx b/src/features/marketplace/pages/index/banner/IframeBanner.tsx index 0541ddc227e..1a8c13687b6 100644 --- a/src/features/marketplace/pages/index/banner/IframeBanner.tsx +++ b/src/features/marketplace/pages/index/banner/IframeBanner.tsx @@ -3,7 +3,7 @@ import { chakra } from '@chakra-ui/react'; import React, { useCallback, useState } from 'react'; -import * as mixpanel from 'src/services/mixpanel'; +import * as analytics from 'src/shared/analytics'; import { Link } from 'src/toolkit/chakra/link'; import { Skeleton } from 'src/toolkit/chakra/skeleton'; @@ -16,7 +16,7 @@ const IframeBanner = ({ contentUrl, linkUrl }: { contentUrl: string; linkUrl: st }, []); const handleClick = useCallback(() => { - mixpanel.logEvent(mixpanel.EventTypes.PROMO_BANNER, { Source: 'Marketplace', Link: linkUrl }); + analytics.logEvent(analytics.EventTypes.PROMO_BANNER, { Source: 'Marketplace', Link: linkUrl }); }, [ linkUrl ]); return ( diff --git a/src/features/marketplace/types/client.ts b/src/features/marketplace/types/client.ts index 5583efe11bb..c731e00bb13 100644 --- a/src/features/marketplace/types/client.ts +++ b/src/features/marketplace/types/client.ts @@ -20,7 +20,6 @@ export interface EssentialDappsChainConfig extends ExternalChain { export type EssentialDappsConfig = { swap?: { - url: string; chains: Array; fee: string; integrator: string; diff --git a/src/features/metasuites/hooks/useNotifyOnNavigation.tsx b/src/features/metasuites/hooks/useNotifyOnNavigation.ts similarity index 100% rename from src/features/metasuites/hooks/useNotifyOnNavigation.tsx rename to src/features/metasuites/hooks/useNotifyOnNavigation.ts diff --git a/src/features/multichain-button/config.ts b/src/features/multichain-button/config.ts index 5c9cb3a8163..4ff1f3642f3 100644 --- a/src/features/multichain-button/config.ts +++ b/src/features/multichain-button/config.ts @@ -1,6 +1,6 @@ // SPDX-License-Identifier: LicenseRef-Blockscout -import type { MultichainProviderConfig, MultichainProviderConfigParsed } from 'src/features/multichain-button/types/client'; +import type { MultichainProviderConfig } from 'src/features/multichain-button/types/client'; import marketplace from 'src/features/marketplace/config'; @@ -11,20 +11,18 @@ const value = parseEnvJson>(getEnvValue('NEXT_PU const title = 'Multichain balance'; -const config: Feature<{ providers: Array }> = (() => { +const config: Feature<{ providers: Array }> = (() => { if (value) { return Object.freeze({ title, isEnabled: true, - providers: value - .map((provider) => ({ - name: provider.name, - logoUrl: provider.logo, - urlTemplate: provider.url_template, - dappId: marketplace.isEnabled ? provider.dapp_id : undefined, - promo: provider.promo, - })) - .sort((_, b) => (b.promo ? 1 : -1)), + providers: value.map((provider) => ({ + name: provider.name, + logo: provider.logo, + url_template: provider.url_template, + dapp_id: marketplace.isEnabled ? provider.dapp_id : undefined, + view: provider.view, + })), }); } diff --git a/src/features/multichain-button/pages/address/AddressMultichainButton.module.css b/src/features/multichain-button/pages/address/AddressMultichainButton.module.css deleted file mode 100644 index 77b15a1c6cb..00000000000 --- a/src/features/multichain-button/pages/address/AddressMultichainButton.module.css +++ /dev/null @@ -1,119 +0,0 @@ -/* Animation source - https://codepen.io/ARS/pen/vEwEPP */ -.promo { - position: relative; - z-index: 1; - - &::after { - position: absolute; - top: -2px; - left: -2px; - right: -2px; - bottom: -2px; - margin: auto; - z-index: -1; - border-radius: 6px; - - content: ""; - background: linear-gradient(120deg, #e15eff, #fffb22, #e15eff); - background-size: 400% 400%; - clip-path: polygon( - 0% 100%, - 4px 100%, - 4px 4px, - 20px 4px, - 20px 20px, - 4px 20px, - 4px 100%, - 100% 100%, - 100% 0%, - 0% 0% - ); - animation: gradient 3s ease-in-out 0.5s infinite, border 5s ease-in-out; - } -} - -@keyframes gradient { - 0% { - background-position: 14% 0%; - } - 50% { - background-position: 87% 100%; - } - 100% { - background-position: 14% 0%; - } -} - -@keyframes border { - 0% { - clip-path: polygon( - 0% 100%, - 4px 100%, - 4px 100%, - 4px 100%, - 4px 100%, - 4px 100%, - 4px 100%, - 4px 100%, - 4px 100%, - 0% 100% - ); - } - 5% { - clip-path: polygon( - 0% 100%, - 4px 100%, - 4px 4px, - 4px 4px, - 4px 4px, - 4px 4px, - 4px 4px, - 4px 4px, - 4px 0%, - 0% 0% - ); - } - 10% { - clip-path: polygon( - 0% 100%, - 4px 100%, - 4px 4px, - 20px 4px, - 20px 4px, - 20px 4px, - 20px 4px, - 20px 4px, - 100% 0%, - 0% 0% - ); - } - 15% { - clip-path: polygon( - 0% 100%, - 4px 100%, - 4px 4px, - 20px 4px, - 20px 20px, - 20px 20px, - 20px 100%, - 100% 100%, - 100% 0%, - 0% 0% - ); - } - 20%, - 100% { - clip-path: polygon( - 0% 100%, - 4px 100%, - 4px 4px, - 20px 4px, - 20px 20px, - 4px 20px, - 4px 100%, - 100% 100%, - 100% 0%, - 0% 0% - ); - } -} \ No newline at end of file diff --git a/src/features/multichain-button/pages/address/AddressMultichainButton.tsx b/src/features/multichain-button/pages/address/AddressMultichainButton.tsx index 591f35d1535..81b33d8ab85 100644 --- a/src/features/multichain-button/pages/address/AddressMultichainButton.tsx +++ b/src/features/multichain-button/pages/address/AddressMultichainButton.tsx @@ -4,44 +4,39 @@ import { upperFirst } from 'es-toolkit'; import { route } from 'nextjs-routes'; import React from 'react'; -import type { MultichainProviderConfigParsed } from 'src/features/multichain-button/types/client'; - -import TextSeparator from 'src/shared/texts/TextSeparator'; +import type { MultichainProviderConfig } from 'src/features/multichain-button/types/client'; import { Image } from 'src/toolkit/chakra/image'; import { Link } from 'src/toolkit/chakra/link'; import { Tooltip } from 'src/toolkit/chakra/tooltip'; -import styles from './AddressMultichainButton.module.css'; - const TEMPLATE_ADDRESS = '{address}'; -type Props = { - item: MultichainProviderConfigParsed; +interface Props { + item: MultichainProviderConfig; addressHash: string; onClick?: () => void; - isFirst?: boolean; - isLast?: boolean; }; -const AddressMultichainButton = ({ item, addressHash, onClick, isFirst, isLast }: Props) => { +const AddressMultichainButton = ({ item, addressHash, onClick }: Props) => { - const isOnlyOne = isFirst && isLast; + const isFullView = (item.view ?? 'full') === 'full'; const buttonIcon = ( { ); const name = upperFirst(item.name.replaceAll('_', ' ')); - const buttonContent = isOnlyOne ? ( + const buttonContent = isFullView ? ( <> { buttonIcon } { name } @@ -51,29 +46,25 @@ const AddressMultichainButton = ({ item, addressHash, onClick, isFirst, isLast } ); try { - const portfolioUrlString = item.urlTemplate.replace(TEMPLATE_ADDRESS, addressHash); + const portfolioUrlString = item.url_template.replace(TEMPLATE_ADDRESS, addressHash); const portfolioUrl = new URL(portfolioUrlString); portfolioUrl.searchParams.append('utm_source', 'blockscout'); portfolioUrl.searchParams.append('utm_medium', 'address'); - const dappId = item.dappId; + const dappId = item.dapp_id; const isExternal = typeof dappId !== 'string'; return ( - <> - - { buttonContent } - - { item.promo && isFirst && !isLast && } - + + { buttonContent } + ); } catch (error) {} diff --git a/src/features/multichain-button/pages/address/AddressMultichainInfoButton.tsx b/src/features/multichain-button/pages/address/AddressMultichainInfoButton.tsx deleted file mode 100644 index b3270db043e..00000000000 --- a/src/features/multichain-button/pages/address/AddressMultichainInfoButton.tsx +++ /dev/null @@ -1,60 +0,0 @@ -// SPDX-License-Identifier: LicenseRef-Blockscout - -import React from 'react'; - -import type { schemas } from '@blockscout/api-types'; - -import config from 'src/config'; - -import type { LinkProps } from 'src/toolkit/chakra/link'; -import { Link } from 'src/toolkit/chakra/link'; - -const feature = config.features.multichainButton; - -interface Props extends LinkProps { - addressData?: schemas['AddressResponse']; -} - -const AddressMultichainInfoButton = ({ addressData, ...rest }: Props) => { - if (!feature.isEnabled) { - return null; - } - - const promotedProvider = feature.providers.find((provider) => provider.promo); - - if (!promotedProvider) { - return null; - } - - if (!addressData || (addressData.is_contract && addressData.proxy_type !== 'eip7702')) { - return null; - } - - const url = (() => { - try { - const url = new URL(promotedProvider.urlTemplate.replace('{address}', addressData.hash)); - url.searchParams.append('utm_source', 'blockscout'); - url.searchParams.append('utm_medium', 'address'); - return url.toString(); - } catch (error) {} - return null; - })(); - - if (!url) { - return null; - } - - return ( - - Multichain info - - ); -}; - -export default React.memo(AddressMultichainInfoButton); diff --git a/src/features/multichain-button/types/client.ts b/src/features/multichain-button/types/client.ts index 3b64dc045d6..9256f766c5b 100644 --- a/src/features/multichain-button/types/client.ts +++ b/src/features/multichain-button/types/client.ts @@ -1,17 +1,11 @@ // SPDX-License-Identifier: LicenseRef-Blockscout -export type MultichainProviderConfig = { +export type MultichainProviderView = 'full' | 'icon'; + +export interface MultichainProviderConfig { name: string; dapp_id?: string; url_template: string; logo: string; - promo?: boolean; -}; - -export type MultichainProviderConfigParsed = { - name: string; - logoUrl: string; - urlTemplate: string; - dappId?: string; - promo?: boolean; + view?: MultichainProviderView; }; diff --git a/src/features/multichain/components/ListCounterText.tsx b/src/features/multichain/components/ListCounterText.tsx index ff251e58385..dd3bd7b19bd 100644 --- a/src/features/multichain/components/ListCounterText.tsx +++ b/src/features/multichain/components/ListCounterText.tsx @@ -4,7 +4,7 @@ import React from 'react'; import useIsInitialLoading from 'src/shared/hooks/useIsInitialLoading'; -import { TruncatedText } from 'src/toolkit/components/truncation/TruncatedText'; +import { Truncate } from 'src/toolkit/components/truncation/Truncate'; interface Props { value: string | undefined; @@ -23,15 +23,12 @@ const ListCounterText = ({ isLoading, value, type }: Props) => { const text = `A total of ${ valueNum.toLocaleString() } ${ valueNum === 1 ? type : `${ type }s` } found`; return ( - + mb={{ base: 4, lg: 0 }}/> ); }; diff --git a/src/features/multichain/hooks/useChainValue.spec.ts b/src/features/multichain/hooks/useChainValue.spec.ts new file mode 100644 index 00000000000..f0243ec9857 --- /dev/null +++ b/src/features/multichain/hooks/useChainValue.spec.ts @@ -0,0 +1,161 @@ +// @vitest-environment jsdom + +import { chainA, chainB } from 'src/features/multichain/mocks/chains'; + +import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'; +import { renderHook, act, cleanup } from 'vitest/lib'; +import { routerStandIn } from 'vitest/utils/routerStandIn'; + +const { mockScrollToTop } = vi.hoisted(() => ({ + mockScrollToTop: vi.fn(), +})); + +vi.mock('next/router', () => import('vitest/utils/routerStandIn').then((m) => m.nextRouterModule)); +vi.mock('react-scroll', () => ({ animateScroll: { scrollToTop: mockScrollToTop } })); +vi.mock('src/features/multichain/chains-config', () => ({ + 'default': () => ({ chains: [ chainA, chainB ] }), +})); + +import { useChainValue } from './useChainValue'; + +const ENCODED_CURSOR = encodeURIComponent(JSON.stringify({ block_number: 11, index: 12, items_count: 13 })); + +beforeEach(() => { + mockScrollToTop.mockClear(); + routerStandIn.reset({ pathname: '/address/[hash]', query: { hash: '0x1' } }); +}); + +afterEach(cleanup); + +describe('useChainValue', () => { + describe('derives the value from the URL', () => { + it('reads chain_id and resolves its config', () => { + routerStandIn.setQuery({ hash: '0x1', chain_id: chainB.id }); + + const { result } = renderHook(() => useChainValue()); + + expect(result.current.chainValue).toEqual([ chainB.id ]); + expect(result.current.chain).toBe(chainB); + }); + + it('falls back to the first configured chain when the URL names none', () => { + const { result } = renderHook(() => useChainValue()); + + expect(result.current.chainValue).toEqual([ chainA.id ]); + expect(result.current.chain).toBe(chainA); + }); + + it('falls back to the first available chain when chainIds restrict the choice', () => { + const { result } = renderHook(() => useChainValue({ chainIds: [ chainB.id ] })); + + expect(result.current.chainValue).toEqual([ chainB.id ]); + expect(result.current.chain).toBe(chainB); + }); + + it('ignores a chain_id that is not configured', () => { + routerStandIn.setQuery({ hash: '0x1', chain_id: '999' }); + + const { result } = renderHook(() => useChainValue()); + + expect(result.current.chainValue).toEqual([ chainA.id ]); + }); + + it('selects "all" with no chain config when the all option is allowed and the URL names no chain', () => { + const { result } = renderHook(() => useChainValue({ withAllOption: true })); + + expect(result.current.chainValue).toEqual([ 'all' ]); + expect(result.current.chain).toBeUndefined(); + }); + + it('follows an external URL change without a handler call', () => { + const { result } = renderHook(() => useChainValue()); + + act(() => { + routerStandIn.setQuery({ hash: '0x1', chain_id: chainB.id }); + }); + + expect(result.current.chainValue).toEqual([ chainB.id ]); + expect(result.current.chain).toBe(chainB); + }); + }); + + describe('onChainValueChange', () => { + it('pushes chain_id, strips the page params and keeps the rest of the query', async() => { + routerStandIn.setQuery({ hash: '0x1', tab: 'txs', filter: 'from', page: '3', next_page_params: ENCODED_CURSOR }); + const { result } = renderHook(() => useChainValue()); + + await act(async() => { + result.current.onChainValueChange({ value: [ chainB.id ] }); + }); + + expect(routerStandIn.push).toHaveBeenCalledTimes(1); + expect(routerStandIn.push).toHaveBeenCalledWith(expect.anything(), undefined, { shallow: true }); + expect(routerStandIn.query).toEqual({ hash: '0x1', tab: 'txs', filter: 'from', chain_id: chainB.id }); + expect(result.current.chainValue).toEqual([ chainB.id ]); + expect(result.current.chain).toBe(chainB); + }); + + it('scrolls to the top when leaving a deeper page', async() => { + routerStandIn.setQuery({ hash: '0x1', page: '3', next_page_params: ENCODED_CURSOR }); + const { result } = renderHook(() => useChainValue()); + + await act(async() => { + result.current.onChainValueChange({ value: [ chainB.id ] }); + }); + + expect(mockScrollToTop).toHaveBeenCalledTimes(1); + }); + + it('does not scroll when already on the first page', async() => { + const { result } = renderHook(() => useChainValue()); + + await act(async() => { + result.current.onChainValueChange({ value: [ chainB.id ] }); + }); + + expect(mockScrollToTop).not.toHaveBeenCalled(); + }); + + it('scrolls to the given element instead of the window', async() => { + routerStandIn.setQuery({ hash: '0x1', page: '2', next_page_params: ENCODED_CURSOR }); + const scrollIntoView = vi.fn(); + const scrollRef = { current: { scrollIntoView } as unknown as HTMLDivElement }; + const { result } = renderHook(() => useChainValue({ scrollRef })); + + await act(async() => { + result.current.onChainValueChange({ value: [ chainB.id ] }); + }); + + expect(scrollIntoView).toHaveBeenCalledTimes(1); + expect(mockScrollToTop).not.toHaveBeenCalled(); + }); + }); + + describe('referential stability', () => { + it('keeps the result and handler across a re-render with the same URL', () => { + const { result, rerender } = renderHook(() => useChainValue()); + const before = result.current; + + rerender(); + + expect(result.current).toBe(before); + }); + + it('keeps the handler when the chain changes and reads the latest router from it', async() => { + const { result } = renderHook(() => useChainValue()); + const handlerBefore = result.current.onChainValueChange; + + await act(async() => { + routerStandIn.setQuery({ hash: '0x1', tab: 'logs' }); + }); + expect(result.current.onChainValueChange).toBe(handlerBefore); + + await act(async() => { + handlerBefore({ value: [ chainB.id ] }); + }); + + expect(routerStandIn.query).toEqual({ hash: '0x1', tab: 'logs', chain_id: chainB.id }); + expect(result.current.onChainValueChange).toBe(handlerBefore); + }); + }); +}); diff --git a/src/features/multichain/hooks/useChainValue.ts b/src/features/multichain/hooks/useChainValue.ts new file mode 100644 index 00000000000..1e79d5258ba --- /dev/null +++ b/src/features/multichain/hooks/useChainValue.ts @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: LicenseRef-Blockscout + +import { omit } from 'es-toolkit'; +import type { NextRouter } from 'next/router'; +import { useRouter } from 'next/router'; +import React from 'react'; + +import type { ClusterChainConfig } from 'src/features/multichain/types/client'; + +import multichainConfig from 'src/features/multichain/chains-config'; +import getChainValueFromQuery from 'src/features/multichain/utils/get-chain-value-from-query'; + +import { scrollListToTop } from 'src/shared/pagination/usePaginationActions'; +import { getPageFromQuery, PAGE_FIELDS } from 'src/shared/pagination/usePaginationParams'; + +export interface ChainValueParams { + readonly chainIds?: Array; + readonly withAllOption?: boolean; + readonly scrollRef?: React.RefObject; +} + +export interface ChainValue { + readonly chainValue: Array; + readonly chain: ClusterChainConfig | undefined; + readonly onChainValueChange: ({ value }: { value: Array }) => void; +} + +interface LatestInputs { + readonly router: NextRouter; + readonly scrollRef: React.RefObject | undefined; +} + +const NO_CHAIN_VALUE: Array = []; + +export function useChainValue({ chainIds, withAllOption, scrollRef }: ChainValueParams = {}): ChainValue { + const router = useRouter(); + const chainId = getChainValueFromQuery(router.query, chainIds, withAllOption); + + const chainValue = React.useMemo(() => (chainId ? [ chainId ] : NO_CHAIN_VALUE), [ chainId ]); + const chain = React.useMemo(() => multichainConfig()?.chains.find((item) => item.id === chainId), [ chainId ]); + + const latest = React.useRef({ router, scrollRef }); + latest.current = { router, scrollRef }; + + const onChainValueChange = React.useCallback(({ value }: { value: Array }) => { + const { router: { pathname, query, push }, scrollRef } = latest.current; + if (getPageFromQuery(query) !== 1) { + scrollListToTop(scrollRef, false); + } + push({ pathname, query: { ...omit(query, PAGE_FIELDS), chain_id: value[0] } }, undefined, { shallow: true }); + }, []); + + return React.useMemo(() => ({ chainValue, chain, onChainValueChange }), [ chainValue, chain, onChainValueChange ]); +} diff --git a/src/features/multichain/mocks/chains.ts b/src/features/multichain/mocks/chains.ts index de09a805b0b..6f478e001a4 100644 --- a/src/features/multichain/mocks/chains.ts +++ b/src/features/multichain/mocks/chains.ts @@ -86,7 +86,7 @@ export const chainB = { ...chainA.app_config?.slices, token: { ...chainA.app_config?.slices?.token, - additionalTypes: [ { id: 'ERC-7984', name: 'ERC-7984' } ], + additionalTypes: [ { id: 'ERC-7984', name: 'ERC-7984' }, { id: 'ERC-8056', name: 'ERC-8056' } ], }, }, apis: { diff --git a/src/features/multichain/mocks/tokens.ts b/src/features/multichain/mocks/tokens.ts index 76d705ccf8b..8864d97ffac 100644 --- a/src/features/multichain/mocks/tokens.ts +++ b/src/features/multichain/mocks/tokens.ts @@ -25,6 +25,9 @@ export const tokenAA: AddressTokenItem = { }, }, reputation: null, + ui_multiplier: null, + new_ui_multiplier: null, + ui_multiplier_effective_at: null, }, token_instance: null, token_id: null, @@ -55,6 +58,9 @@ export const tokenAB: AddressTokenItem = { }, }, reputation: null, + ui_multiplier: null, + new_ui_multiplier: null, + ui_multiplier_effective_at: null, }, token_id: null, value: '32115625288281011210', @@ -136,3 +142,31 @@ export const tokenDA: AddressTokenItem = { exchange_rate: null, }, }; + +export const tokenBC: AddressTokenItem = { + ...tokenAA, + token: { + ...tokenAA.token, + address_hash: '0x7336D3B609bAc510842d7F7Ceb61bcB6910f501D', + name: 'Caterpillar Inc 3.7% 2028', + symbol: '14913UBF6', + decimals: '18', + type: 'ERC-8056', + exchange_rate: '1.02', + ui_multiplier: '1250000000000000000', + new_ui_multiplier: null, + ui_multiplier_effective_at: null, + chain_infos: { + [chainB.id]: { + holders_count: '4', + total_supply: '9692016594070863347712', + is_verified: false, + contract_name: undefined, + }, + }, + }, + value: '4987401008000000000000', + chain_values: { + [chainB.id]: '4987401008000000000000', + }, +}; diff --git a/src/features/multichain/pages/address/MultichainAddress.pw.tsx b/src/features/multichain/pages/address/MultichainAddress.pw.tsx index a7dd869fadb..c941a11e827 100644 --- a/src/features/multichain/pages/address/MultichainAddress.pw.tsx +++ b/src/features/multichain/pages/address/MultichainAddress.pw.tsx @@ -34,7 +34,7 @@ test('base view', async({ mockApiResponse, render, page, mockMultichainConfig, m await mockApiResponse('multichainAggregator:address_tokens', { items: [ tokensMock.tokenAA, tokensMock.tokenAB, tokensMock.tokenBA, tokensMock.tokenCA, tokensMock.tokenDA ], next_page_params: { page_token: '1', page_size: 10 }, - }, { pathParams: { hash: CURRENT_ADDRESS }, queryParams: { type: 'ERC-20,NATIVE,ERC-7984', include_poor_reputation_tokens: false } }); + }, { pathParams: { hash: CURRENT_ADDRESS }, queryParams: { type: 'ERC-20,NATIVE,ERC-7984,ERC-8056', include_poor_reputation_tokens: false } }); await mockAssetResponse(chainDataMock.chainA.logo as string, './playwright/mocks/image_s.jpg'); await mockAssetResponse(chainDataMock.chainB.logo as string, './playwright/mocks/image_md.jpg'); diff --git a/src/features/multichain/pages/address/MultichainAddressInternalTxs.tsx b/src/features/multichain/pages/address/MultichainAddressInternalTxs.tsx index 46518570f7a..0776379265e 100644 --- a/src/features/multichain/pages/address/MultichainAddressInternalTxs.tsx +++ b/src/features/multichain/pages/address/MultichainAddressInternalTxs.tsx @@ -1,6 +1,5 @@ // SPDX-License-Identifier: LicenseRef-Blockscout -import { Box } from '@chakra-ui/react'; import React from 'react'; import type * as multichain from '@blockscout/multichain-aggregator-types'; @@ -9,17 +8,18 @@ import ActionBar from 'src/shell/page/action-bar/ActionBar'; import useAddressInternalTxsQuery from 'src/slices/address/pages/details/internal-txs/useAddressInternalTxsQuery'; import AddressTxsFilter from 'src/slices/address/pages/details/txs/AddressTxsFilter'; -import InternalTxsList from 'src/slices/internal-tx/components/InternalTxsList'; import InternalTxsTable from 'src/slices/internal-tx/components/InternalTxsTable'; import CsvExport from 'src/features/csv-export/components/CsvExport'; -import multichainConfig from 'src/features/multichain/chains-config'; import ChainSelect from 'src/features/multichain/components/ChainSelect'; import { MultichainProvider } from 'src/features/multichain/context'; +import { useChainValue } from 'src/features/multichain/hooks/useChainValue'; import DataList from 'src/shared/lists/DataList'; import Pagination from 'src/shared/pagination/Pagination'; +import { TableContainerScrollable } from 'src/toolkit/chakra/table'; + import getAvailableChainIds from './get-available-chain-ids'; interface Props { @@ -29,31 +29,23 @@ interface Props { const MultichainAddressInternalTxs = ({ addressData, isLoading }: Props) => { const chainIds = React.useMemo(() => getAvailableChainIds(addressData), [ addressData ]); + const { chainValue, chain: chainData, onChainValueChange } = useChainValue({ chainIds }); const { hash, query, filterValue, onFilterChange } = useAddressInternalTxsQuery({ enabled: !isLoading && chainIds.length > 0, - isMultichain: true, - chainIds, + chain: chainData, }); - const { data, isPlaceholderData, isError, pagination, chainValue, onChainValueChange } = query; - - const chainData = React.useMemo(() => { - const config = multichainConfig(); - return config?.chains.find(({ id }) => id === chainValue?.[0]); - }, [ chainValue ]); + const { data, isInitialLoading, isError, pagination } = query; if (chainIds.length === 0) { return

There are no internal transactions.

; } const content = data?.items ? ( - - - - - - - + + + + ) : null ; @@ -100,6 +92,7 @@ const MultichainAddressInternalTxs = ({ addressData, isLoading }: Props) => { showActionBarIfEmpty showActionBarIfError actionBar={ actionBar } + isTransitioning={ query.isTransitioning } > { content } diff --git a/src/features/multichain/pages/address/MultichainAddressLogs.tsx b/src/features/multichain/pages/address/MultichainAddressLogs.tsx index dce543d5c49..23d8b69dc05 100644 --- a/src/features/multichain/pages/address/MultichainAddressLogs.tsx +++ b/src/features/multichain/pages/address/MultichainAddressLogs.tsx @@ -11,15 +11,15 @@ import LogItem from 'src/slices/log/components/LogItem'; import { LOG } from 'src/slices/log/stubs/log'; import CsvExport from 'src/features/csv-export/components/CsvExport'; -import multichainConfig from 'src/features/multichain/chains-config'; import ChainSelect from 'src/features/multichain/components/ChainSelect'; import { MultichainProvider } from 'src/features/multichain/context'; +import { useChainValue } from 'src/features/multichain/hooks/useChainValue'; import useIsMobile from 'src/shared/hooks/useIsMobile'; import DataList from 'src/shared/lists/DataList'; import useLazyRenderedList from 'src/shared/lists/useLazyRenderedList'; import Pagination from 'src/shared/pagination/Pagination'; -import useQueryWithPages from 'src/shared/pagination/useQueryWithPages'; +import useApiPaginatedQuery from 'src/shared/pagination/useApiPaginatedQuery'; import { generateListStub } from 'src/shared/pagination/utils'; import getQueryParamString from 'src/shared/router/get-query-param-string'; @@ -36,10 +36,11 @@ interface Props { const MultichainAddressLogs = ({ addressData, isLoading }: Props) => { const router = useRouter(); const chainIds = React.useMemo(() => getAvailableChainIds(addressData), [ addressData ]); + const { chainValue, chain: chainData, onChainValueChange } = useChainValue({ chainIds }); const isMobile = useIsMobile(); const hash = getQueryParamString(router.query.hash); - const { data, isPlaceholderData, isError, pagination, chainValue, onChainValueChange, queryHash } = useQueryWithPages({ + const { data, isInitialLoading, isTransitioning, isError, pagination, queryHash } = useApiPaginatedQuery({ resourceName: 'core:address_logs', pathParams: { hash }, options: { @@ -51,22 +52,16 @@ const MultichainAddressLogs = ({ addressData, isLoading }: Props) => { } }), enabled: !isLoading, }, - isMultichain: true, - chainIds, + chain: chainData, }); const { cutRef, renderedItemsNum } = useLazyRenderedList({ list: data?.items, - isEnabled: !isPlaceholderData, + isEnabled: !isInitialLoading, minItemsNum: INITIAL_RENDERED_ITEMS_NUM, resetKey: queryHash, }); - const chainData = React.useMemo(() => { - const config = multichainConfig(); - return config?.chains.find(({ id }) => id === chainValue?.[0]); - }, [ chainValue ]); - const actionBar = ( { key={ index } data={ item } type="address" - isLoading={ isPlaceholderData } + isLoading={ isInitialLoading } chainData={ chainData } /> )); @@ -115,8 +110,9 @@ const MultichainAddressLogs = ({ addressData, isLoading }: Props) => { showActionBarIfEmpty showActionBarIfError actionBar={ actionBar } + isTransitioning={ isTransitioning } > - + { content } diff --git a/src/features/multichain/pages/address/MultichainAddressPortfolio.pw.tsx b/src/features/multichain/pages/address/MultichainAddressPortfolio.pw.tsx index 280afb1b2a7..99181d8b6fd 100644 --- a/src/features/multichain/pages/address/MultichainAddressPortfolio.pw.tsx +++ b/src/features/multichain/pages/address/MultichainAddressPortfolio.pw.tsx @@ -32,7 +32,7 @@ test.describe('nfts', () => { await mockApiResponse( 'core:address_collections', { ...tokensMock.collections, items: [ tokensMock.collections.items[2] ] }, - { pathParams: { hash: CURRENT_ADDRESS }, queryParams: { type: [] }, chainConfig: chainDataMock.chainA }, + { pathParams: { hash: CURRENT_ADDRESS }, chainConfig: chainDataMock.chainA }, ); await mockAssetResponse(chainDataMock.chainA.logo as string, './playwright/mocks/image_s.jpg'); diff --git a/src/features/multichain/pages/address/MultichainAddressPortfolio.tsx b/src/features/multichain/pages/address/MultichainAddressPortfolio.tsx index 6b2be749f70..94c2fbce4ed 100644 --- a/src/features/multichain/pages/address/MultichainAddressPortfolio.tsx +++ b/src/features/multichain/pages/address/MultichainAddressPortfolio.tsx @@ -15,6 +15,7 @@ import useAddressNftQuery from 'src/slices/token/pages/address/useAddressNftQuer import ChainSelect from 'src/features/multichain/components/ChainSelect'; import { MultichainProvider } from 'src/features/multichain/context'; +import { useChainValue } from 'src/features/multichain/hooks/useChainValue'; import useIsMobile from 'src/shared/hooks/useIsMobile'; import Pagination from 'src/shared/pagination/Pagination'; @@ -54,21 +55,15 @@ const MultichainAddressPortfolio = ({ addressData, isLoading }: Props) => { const tab = getQueryParamString(router.query.tab) as typeof ADDRESS_OP_PORTFOLIO_TAB_IDS[number] | 'portfolio' | undefined; const hash = getQueryParamString(router.query.hash); const chainIds = React.useMemo(() => getAvailableChainIds(addressData), [ addressData ]); + const { chainValue, chain, onChainValueChange } = useChainValue({ chainIds, scrollRef }); const { nftsQuery, collectionsQuery, displayType: nftDisplayType, tokenTypes: nftTokenTypes, onDisplayTypeChange, onTokenTypesChange } = useAddressNftQuery({ scrollRef, enabled: !isLoading && tab === 'portfolio_nfts' && chainIds.length > 0, addressHash: hash, - isMultichain: true, - chainIds, + chain, }); - const handleChainChange = React.useCallback((chainId: string | null) => { - const chainValue = chainId ? [ chainId ] : [ chainIds[0] ]; - nftsQuery.onChainValueChange({ value: chainValue }); - collectionsQuery.onChainValueChange({ value: chainValue }); - }, [ nftsQuery, collectionsQuery, chainIds ]); - const hasActiveFilters = (() => { if (tab === 'portfolio_nfts') { return Boolean(nftTokenTypes?.length); @@ -83,15 +78,15 @@ const MultichainAddressPortfolio = ({ addressData, isLoading }: Props) => { const chainSelect = ( ); const hasData = - (!nftsQuery.isPlaceholderData && nftsQuery.data?.items.length) || - (!collectionsQuery.isPlaceholderData && collectionsQuery.data?.items.length); + Boolean(!nftsQuery.isPlaceholderData && nftsQuery.data?.items.length) || + Boolean(!collectionsQuery.isPlaceholderData && collectionsQuery.data?.items.length); return ( <> @@ -113,17 +108,17 @@ const MultichainAddressPortfolio = ({ addressData, isLoading }: Props) => { { id: 'portfolio_tokens', title: 'Tokens', - component: , + component: , }, { id: 'portfolio_nfts', - title: 'NFT', + title: 'NFTs', component: nftDisplayType === 'list' ? ( - + ) : ( - + { await mockApiResponse( 'core:address_token_transfers', { items: [ tokenTransferMock.erc20 ], next_page_params: DEFAULT_PAGINATION }, - { pathParams: { hash: CURRENT_ADDRESS }, queryParams: { type: [] }, chainConfig: chainDataMock.chainA }, + { pathParams: { hash: CURRENT_ADDRESS }, chainConfig: chainDataMock.chainA }, ); await mockApiResponse( 'core:address_counters', diff --git a/src/features/multichain/pages/address/MultichainAddressTokenTransfers.tsx b/src/features/multichain/pages/address/MultichainAddressTokenTransfers.tsx index f9a225ab5aa..3c3179e94a4 100644 --- a/src/features/multichain/pages/address/MultichainAddressTokenTransfers.tsx +++ b/src/features/multichain/pages/address/MultichainAddressTokenTransfers.tsx @@ -26,11 +26,12 @@ import multichainConfig from 'src/features/multichain/chains-config'; import ChainSelect from 'src/features/multichain/components/ChainSelect'; import ListCounterText from 'src/features/multichain/components/ListCounterText'; import { MultichainProvider } from 'src/features/multichain/context'; +import { useChainValue } from 'src/features/multichain/hooks/useChainValue'; import config from 'src/config'; import useIsMobile from 'src/shared/hooks/useIsMobile'; import Pagination from 'src/shared/pagination/Pagination'; -import useQueryWithPages from 'src/shared/pagination/useQueryWithPages'; +import useApiPaginatedQuery from 'src/shared/pagination/useApiPaginatedQuery'; import { generateListStub } from 'src/shared/pagination/utils'; import getQueryParamString from 'src/shared/router/get-query-param-string'; @@ -68,8 +69,10 @@ const MultichainAddressTokenTransfers = ({ addressData, isLoading }: Props) => { const isLocalTab = tab === 'token_transfers_local' || tab === 'token_transfers'; const chainIds = React.useMemo(() => getAvailableChainIds(addressData), [ addressData ]); + const { chainValue, chain: chainData, onChainValueChange } = useChainValue({ chainIds }); + const chainId = chainData?.id; - const transfersQueryCrossChain = useQueryWithPages({ + const transfersQueryCrossChain = useApiPaginatedQuery({ resourceName: 'interchainIndexer:address_transfers', pathParams: { hash }, options: { @@ -81,22 +84,18 @@ const MultichainAddressTokenTransfers = ({ addressData, isLoading }: Props) => { const transfersQueryLocal = useAddressTokenTransfersQuery({ currentAddress: hash, enabled: isLocalTab && !isLoading && chainIds.length > 0, - isMultichain: true, - chainIds, + chain: chainData, }); - const chainId = transfersQueryLocal.query.chainValue?.[0]; - const chainData = multichainConfig()?.chains.find(chain => chain.id === chainId); - const countersQueryLocal = useAddressCountersQuery({ hash, - isLoading: transfersQueryLocal.query.isPlaceholderData, + isLoading: transfersQueryLocal.query.isInitialLoading, isEnabled: isLocalTab && !isLoading && chainIds.length > 0, chain: chainData, }); const handleChainValueChange = React.useCallback(({ value }: { value: Array }) => { - transfersQueryLocal.query.onChainValueChange({ value }); + onChainValueChange({ value }); const chainConfig = multichainConfig()?.chains.find(chain => chain.id === value[0]); const tokenTypes = getTokenFilterValue(router.query.type, chainConfig?.app_config); if (tokenTypes) { @@ -105,7 +104,7 @@ const MultichainAddressTokenTransfers = ({ addressData, isLoading }: Props) => { transfersQueryLocal.onTypeFilterChange(chainsTokenTypes); } } - }, [ transfersQueryLocal, router.query.type ]); + }, [ onChainValueChange, transfersQueryLocal, router.query.type ]); const countersText = (() => { if (isLocalTab) { @@ -113,7 +112,7 @@ const MultichainAddressTokenTransfers = ({ addressData, isLoading }: Props) => { ); @@ -131,7 +130,7 @@ const MultichainAddressTokenTransfers = ({ addressData, isLoading }: Props) => { const chainSelect = ( @@ -151,7 +150,7 @@ const MultichainAddressTokenTransfers = ({ addressData, isLoading }: Props) => { withAddressFilter onAddressFilterChange={ transfersQueryLocal.onAddressFilterChange } defaultAddressFilter={ transfersQueryLocal.filters.filter } - isLoading={ transfersQueryLocal.query.isPlaceholderData } + isLoading={ transfersQueryLocal.query.isInitialLoading } chainConfig={ chainData?.app_config } /> { chainSelect } @@ -167,7 +166,7 @@ const MultichainAddressTokenTransfers = ({ addressData, isLoading }: Props) => { loadingInitial={ transfersQueryLocal.query.pagination.isLoading } /> { ) } { id: [ 'token_transfers_local', 'token_transfers' ], title: 'Local', component: chainIds.length > 0 ? ( - + { isMobile && countersText } { test('base view', async() => { await expect(component).toHaveScreenshot(); }); - - test.describe('mobile', () => { - test.use({ viewport: pwConfig.viewport.mobile }); - - test('base view', async() => { - await expect(component).toHaveScreenshot(); - }); - }); }); diff --git a/src/features/multichain/pages/address/MultichainAddressTxs.tsx b/src/features/multichain/pages/address/MultichainAddressTxs.tsx index 2a913070df7..1852fa7ec2a 100644 --- a/src/features/multichain/pages/address/MultichainAddressTxs.tsx +++ b/src/features/multichain/pages/address/MultichainAddressTxs.tsx @@ -20,15 +20,15 @@ import TxsWithApiSorting from 'src/slices/tx/pages/index/list/TxsWithApiSorting' import TransactionsCrossChainContent from 'src/features/cross-chain-txs/components/txs/TransactionsCrossChainContent'; import { INTERCHAIN_MESSAGE } from 'src/features/cross-chain-txs/stubs/messages'; import CsvExport from 'src/features/csv-export/components/CsvExport'; -import multichainConfig from 'src/features/multichain/chains-config'; import ChainSelect from 'src/features/multichain/components/ChainSelect'; import ListCounterText from 'src/features/multichain/components/ListCounterText'; import { MultichainProvider } from 'src/features/multichain/context'; +import { useChainValue } from 'src/features/multichain/hooks/useChainValue'; import config from 'src/config'; import useIsMobile from 'src/shared/hooks/useIsMobile'; import Pagination from 'src/shared/pagination/Pagination'; -import useQueryWithPages from 'src/shared/pagination/useQueryWithPages'; +import useApiPaginatedQuery from 'src/shared/pagination/useApiPaginatedQuery'; import { generateListStub } from 'src/shared/pagination/utils'; import getQueryParamString from 'src/shared/router/get-query-param-string'; @@ -66,8 +66,10 @@ const MultichainAddressTxs = ({ addressData, isLoading }: Props) => { const isLocalTab = tab === 'txs_local' || tab === 'txs'; const chainIds = React.useMemo(() => getAvailableChainIds(addressData), [ addressData ]); + const { chainValue, chain: chainData, onChainValueChange } = useChainValue({ chainIds }); + const chainId = chainData?.id; - const txsQueryCrossChain = useQueryWithPages({ + const txsQueryCrossChain = useApiPaginatedQuery({ resourceName: 'interchainIndexer:address_messages', pathParams: { hash }, options: { @@ -79,16 +81,12 @@ const MultichainAddressTxs = ({ addressData, isLoading }: Props) => { const txsQueryLocal = useAddressTxsQuery({ addressHash: hash, enabled: !isLoading && isLocalTab && chainIds.length > 0, - isMultichain: true, - chainIds, + chain: chainData, }); - const chainId = txsQueryLocal.query.chainValue?.[0]; - const chainData = multichainConfig()?.chains.find(chain => chain.id === chainId); - const countersQueryLocal = useAddressCountersQuery({ hash, - isLoading: txsQueryLocal.query.isPlaceholderData || isLoading, + isLoading: txsQueryLocal.query.isInitialLoading || isLoading, isEnabled: !isLoading && isLocalTab && chainIds.length > 0, chain: chainData, }); @@ -108,7 +106,7 @@ const MultichainAddressTxs = ({ addressData, isLoading }: Props) => { ); @@ -120,8 +118,8 @@ const MultichainAddressTxs = ({ addressData, isLoading }: Props) => { const chainSelect = ( ); @@ -182,7 +180,8 @@ const MultichainAddressTxs = ({ addressData, isLoading }: Props) => { { socketType="address_txs" top={ ACTION_BAR_HEIGHT_DESKTOP } sorting={ txsQueryLocal.sort } - setSort={ txsQueryLocal.setSort } - showTableView /> diff --git a/src/features/multichain/pages/address/__screenshots__/MultichainAddress.pw.tsx_default_base-view-1.png b/src/features/multichain/pages/address/__screenshots__/MultichainAddress.pw.tsx_default_base-view-1.png index 36514b0134b..d470183bb55 100644 Binary files a/src/features/multichain/pages/address/__screenshots__/MultichainAddress.pw.tsx_default_base-view-1.png and b/src/features/multichain/pages/address/__screenshots__/MultichainAddress.pw.tsx_default_base-view-1.png differ diff --git a/src/features/multichain/pages/address/__screenshots__/MultichainAddressPortfolio.pw.tsx_default_nfts-base-view-1.png b/src/features/multichain/pages/address/__screenshots__/MultichainAddressPortfolio.pw.tsx_default_nfts-base-view-1.png index b4b889f47e4..156dcf99dcf 100644 Binary files a/src/features/multichain/pages/address/__screenshots__/MultichainAddressPortfolio.pw.tsx_default_nfts-base-view-1.png and b/src/features/multichain/pages/address/__screenshots__/MultichainAddressPortfolio.pw.tsx_default_nfts-base-view-1.png differ diff --git a/src/features/multichain/pages/address/__screenshots__/MultichainAddressPortfolio.pw.tsx_default_nfts-mobile-base-view-1.png b/src/features/multichain/pages/address/__screenshots__/MultichainAddressPortfolio.pw.tsx_default_nfts-mobile-base-view-1.png index 81ddaed8249..99009a941b7 100644 Binary files a/src/features/multichain/pages/address/__screenshots__/MultichainAddressPortfolio.pw.tsx_default_nfts-mobile-base-view-1.png and b/src/features/multichain/pages/address/__screenshots__/MultichainAddressPortfolio.pw.tsx_default_nfts-mobile-base-view-1.png differ diff --git a/src/features/multichain/pages/address/__screenshots__/MultichainAddressTokenTransfers.pw.tsx_default_local-transfers-base-view-1.png b/src/features/multichain/pages/address/__screenshots__/MultichainAddressTokenTransfers.pw.tsx_default_local-transfers-base-view-1.png index 26c80cdca6c..6e7c0c21a78 100644 Binary files a/src/features/multichain/pages/address/__screenshots__/MultichainAddressTokenTransfers.pw.tsx_default_local-transfers-base-view-1.png and b/src/features/multichain/pages/address/__screenshots__/MultichainAddressTokenTransfers.pw.tsx_default_local-transfers-base-view-1.png differ diff --git a/src/features/multichain/pages/address/__screenshots__/MultichainAddressTokenTransfers.pw.tsx_default_local-transfers-mobile-base-view-1.png b/src/features/multichain/pages/address/__screenshots__/MultichainAddressTokenTransfers.pw.tsx_default_local-transfers-mobile-base-view-1.png index 41d29b50bf0..da931330114 100644 Binary files a/src/features/multichain/pages/address/__screenshots__/MultichainAddressTokenTransfers.pw.tsx_default_local-transfers-mobile-base-view-1.png and b/src/features/multichain/pages/address/__screenshots__/MultichainAddressTokenTransfers.pw.tsx_default_local-transfers-mobile-base-view-1.png differ diff --git a/src/features/multichain/pages/address/__screenshots__/MultichainAddressTxs.pw.tsx_default_local-txs-base-view-1.png b/src/features/multichain/pages/address/__screenshots__/MultichainAddressTxs.pw.tsx_default_local-txs-base-view-1.png index cc3bf84a4d7..f74baacfe3e 100644 Binary files a/src/features/multichain/pages/address/__screenshots__/MultichainAddressTxs.pw.tsx_default_local-txs-base-view-1.png and b/src/features/multichain/pages/address/__screenshots__/MultichainAddressTxs.pw.tsx_default_local-txs-base-view-1.png differ diff --git a/src/features/multichain/pages/address/__screenshots__/MultichainAddressTxs.pw.tsx_default_local-txs-mobile-base-view-1.png b/src/features/multichain/pages/address/__screenshots__/MultichainAddressTxs.pw.tsx_default_local-txs-mobile-base-view-1.png deleted file mode 100644 index 75c5a24359c..00000000000 Binary files a/src/features/multichain/pages/address/__screenshots__/MultichainAddressTxs.pw.tsx_default_local-txs-mobile-base-view-1.png and /dev/null differ diff --git a/src/features/multichain/pages/address/portfolio/MultichainAddressPortfolioCard.tsx b/src/features/multichain/pages/address/portfolio/MultichainAddressPortfolioCard.tsx index f5510fecec8..3cf50f4be2f 100644 --- a/src/features/multichain/pages/address/portfolio/MultichainAddressPortfolioCard.tsx +++ b/src/features/multichain/pages/address/portfolio/MultichainAddressPortfolioCard.tsx @@ -12,7 +12,7 @@ import SimpleValue from 'src/shared/values/entity/SimpleValue'; import { DEFAULT_ACCURACY_USD } from 'src/shared/values/entity/utils'; import { Skeleton } from 'src/toolkit/chakra/skeleton'; -import { TruncatedText } from 'src/toolkit/components/truncation/TruncatedText'; +import { Truncate } from 'src/toolkit/components/truncation/Truncate'; import { formatPercentage } from './utils'; @@ -63,7 +63,7 @@ const MultichainAddressPortfolioCard = ({ chain, value, share, isLoading, isSele > - + { share !== undefined && share > 0 && ( diff --git a/src/features/multichain/pages/address/portfolio/MultichainAddressPortfolioNetWorth.pw.tsx b/src/features/multichain/pages/address/portfolio/MultichainAddressPortfolioNetWorth.pw.tsx new file mode 100644 index 00000000000..410ad8e0d17 --- /dev/null +++ b/src/features/multichain/pages/address/portfolio/MultichainAddressPortfolioNetWorth.pw.tsx @@ -0,0 +1,61 @@ +import React from 'react'; + +import type { MultichainProviderConfig } from 'src/features/multichain-button/types/client'; + +import { test, expect } from 'playwright/lib'; +import * as pwConfig from 'playwright/utils/config'; + +import MultichainAddressPortfolioNetWorth from './MultichainAddressPortfolioNetWorth'; + +const ICON_URL = 'https://localhost:3000/my-icon.png'; +const ICON_URL_2 = 'https://localhost:3000/my-icon-2.png'; +const ADDRESS_HASH = '0x1234567890123456789012345678901234567890'; +const TOP_TOKENS = [ { symbol: 'USDT', share: 0.5 }, { symbol: 'DuckDuckToken', share: 0.3 }, { symbol: 'Others', share: 0.2 } ]; + +test.beforeEach(async({ mockEnvs, mockAssetResponse }) => { + await mockEnvs([ + [ 'NEXT_PUBLIC_MULTICHAIN_BALANCE_PROVIDER_CONFIG', JSON.stringify([ + { name: 'Duck portfolio', dapp_id: 'duck', url_template: 'https://duck.url/{address}', logo: ICON_URL }, + { name: 'duck3', dapp_id: 'duck', url_template: 'https://duck.url/{address}', logo: ICON_URL, view: 'icon' }, + { name: 'Goose tracker', url_template: 'https://duck.url/{address}', logo: ICON_URL_2, view: 'full' }, + { name: 'goose', url_template: 'https://duck.url/{address}', logo: ICON_URL_2, view: 'icon' }, + ] satisfies Array) ], + ]); + + await mockAssetResponse(ICON_URL, './playwright/mocks/duck.png'); + await mockAssetResponse(ICON_URL_2, './playwright/mocks/goose.png'); +}); + +test('base view +@mobile', async({ render, page }) => { + const component = await render( + , + ); + + await expect(component).toHaveScreenshot({ + mask: [ page.locator(pwConfig.adsBannerSelector) ], + maskColor: pwConfig.maskColor, + }); +}); + +test.describe('small desktop', () => { + test.use({ viewport: { width: 1024, height: 768 } }); + test('base view', async({ render, page }) => { + const component = await render( + , + ); + await expect(component).toHaveScreenshot({ + mask: [ page.locator(pwConfig.adsBannerSelector) ], + maskColor: pwConfig.maskColor, + }); + }); +}); diff --git a/src/features/multichain/pages/address/portfolio/MultichainAddressPortfolioNetWorth.tsx b/src/features/multichain/pages/address/portfolio/MultichainAddressPortfolioNetWorth.tsx index e089cc38992..f98061ee0c1 100644 --- a/src/features/multichain/pages/address/portfolio/MultichainAddressPortfolioNetWorth.tsx +++ b/src/features/multichain/pages/address/portfolio/MultichainAddressPortfolioNetWorth.tsx @@ -8,7 +8,7 @@ import AdBanner from 'src/features/ads/banner/components/AdBanner'; import AddressMultichainButton from 'src/features/multichain-button/pages/address/AddressMultichainButton'; import config from 'src/config'; -import * as mixpanel from 'src/services/mixpanel'; +import * as analytics from 'src/shared/analytics'; import useIsMobile from 'src/shared/hooks/useIsMobile'; import SimpleValue from 'src/shared/values/entity/SimpleValue'; import { DEFAULT_ACCURACY_USD } from 'src/shared/values/entity/utils'; @@ -43,7 +43,7 @@ const MultichainAddressPortfolioNetWorth = ({ addressHash, netWorth, isLoading, const isMobile = useIsMobile(); const handleMultichainClick = React.useCallback(() => { - mixpanel.logEvent(mixpanel.EventTypes.BUTTON_CLICK, { Content: 'Multichain', Source: 'address' }); + analytics.logEvent(analytics.EventTypes.BUTTON_CLICK, { Content: 'Multichain', Source: 'address' }); }, []); const topTokensContent = (() => { @@ -88,31 +88,31 @@ const MultichainAddressPortfolioNetWorth = ({ addressHash, netWorth, isLoading, })(); return ( - + - - - Total net worth - (without NFT) - - + + + + + Total net worth + (without NFT) + + { multichainBalanceFeature.isEnabled && ( <> - - + + { multichainBalanceFeature.providers.map((item) => ( ) } - +
, @@ -50,9 +43,31 @@ test('many chains +@mobile +@dark-mode', async({ render, mockApiResponse, page } mask: [ page.locator(pwConfig.adsBannerSelector) ], maskColor: pwConfig.maskColor, }); +}); + +test('many chains, selected chain +@mobile +@dark-mode', async({ render, mockApiResponse, page }) => { + await mockApiResponse('multichainAggregator:address_portfolio', + portfolioMock.base, + { pathParams: { hash: CURRENT_ADDRESS }, queryParams: { include_poor_reputation_tokens: false }, + }); + await mockApiResponse('multichainAggregator:address_tokens', { + items: [ tokensMock.tokenAA, tokensMock.tokenAB, tokensMock.tokenBA, tokensMock.tokenBB, tokensMock.tokenBC, tokensMock.tokenCA, tokensMock.tokenDA ], + next_page_params: { page_token: '1', page_size: 10 }, + }, { pathParams: { hash: CURRENT_ADDRESS }, queryParams: { type: 'ERC-20,NATIVE,ERC-7984,ERC-8056', include_poor_reputation_tokens: false } }); + const selectedChainTokensUrl = await mockApiResponse('multichainAggregator:address_tokens', { + items: [ ], + next_page_params: undefined, + }, { + pathParams: { hash: CURRENT_ADDRESS }, + queryParams: { chain_id: chainDataMock.chainE.id, type: 'ERC-20,NATIVE,ERC-7984,ERC-8056', include_poor_reputation_tokens: false }, + }); - await component.getByText('show more').click(); - await component.getByLabel('White goose portfolio selector').click(); + const component = await render( + , + { hooksConfig: { router: { query: { ...hooksConfig.router.query, chain_id: chainDataMock.chainE.id } } } }, + ); + await page.waitForResponse(selectedChainTokensUrl); + await expect(component.getByText('show less')).toBeVisible(); await expect(component).toHaveScreenshot({ mask: [ page.locator(pwConfig.adsBannerSelector) ], maskColor: pwConfig.maskColor, diff --git a/src/features/multichain/pages/address/portfolio/MultichainAddressPortfolioTokens.tsx b/src/features/multichain/pages/address/portfolio/MultichainAddressPortfolioTokens.tsx index 46ea4d677a4..c6ff9f7059f 100644 --- a/src/features/multichain/pages/address/portfolio/MultichainAddressPortfolioTokens.tsx +++ b/src/features/multichain/pages/address/portfolio/MultichainAddressPortfolioTokens.tsx @@ -17,10 +17,10 @@ import ActionBar, { ACTION_BAR_HEIGHT_DESKTOP } from 'src/shell/page/action-bar/ import multichainConfig from 'src/features/multichain/chains-config'; import { ADDRESS_PORTFOLIO, TOKEN } from 'src/features/multichain/stubs'; -import useDebounce from 'src/shared/hooks/useDebounce'; import DataList from 'src/shared/lists/DataList'; import Pagination from 'src/shared/pagination/Pagination'; -import useQueryWithPages from 'src/shared/pagination/useQueryWithPages'; +import useApiPaginatedQuery from 'src/shared/pagination/useApiPaginatedQuery'; +import { useDebouncedFilterChange } from 'src/shared/pagination/useDebouncedFilterChange'; import { generateListStub } from 'src/shared/pagination/utils'; import getQueryParamString from 'src/shared/router/get-query-param-string'; import * as cookies from 'src/shared/storage/cookies'; @@ -37,19 +37,14 @@ import { calculateUsdValue } from './utils'; interface Props { addressData: multichain.GetAddressResponse | undefined; isLoading: boolean; - onChainChange?: (chainId: string | null) => void; } -const MultichainAddressPortfolioTokens = ({ addressData, isLoading, onChainChange }: Props) => { +const MultichainAddressPortfolioTokens = ({ addressData, isLoading }: Props) => { const config = multichainConfig(); const router = useRouter(); const hash = getQueryParamString(router.query.hash); const chainIdParam = getQueryParamString(router.query.chain_id); - const q = getQueryParamString(router.query.query); - - const [ searchTerm, setSearchTerm ] = React.useState(q || undefined); - const debouncedSearchTerm = useDebounce(searchTerm || '', 300); const tokenReputationFilter = React.useMemo(() => { return cookies.get(cookies.NAMES.SHOW_POOR_REPUTATION_TOKENS) === 'true' ? true : false; @@ -72,15 +67,10 @@ const MultichainAddressPortfolioTokens = ({ addressData, isLoading, onChainChang }; }, [ addressData?.chain_infos, portfolioQuery.data?.portfolio?.chain_values ]); - const [ selectedChainId, setSelectedChainId ] = React.useState(null); - - React.useEffect(() => { - if (!portfolioQuery.isPlaceholderData && !isLoading) { - const [ chainId ] = chainIdParam ? chainIdParam.split(',').filter((chainId) => Object.keys(portfolioData).includes(chainId)) : []; - setSelectedChainId(chainId ?? null); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [ portfolioQuery.isPlaceholderData, isLoading ]); + const selectedChainId = React.useMemo(() => { + const [ chainId ] = chainIdParam ? chainIdParam.split(',').filter((chainId) => Object.keys(portfolioData).includes(chainId)) : []; + return chainId ?? null; + }, [ chainIdParam, portfolioData ]); const typeFilter = React.useMemo(() => { const additionalTypes = getAdditionalTokenTypes( @@ -91,13 +81,12 @@ const MultichainAddressPortfolioTokens = ({ addressData, isLoading, onChainChang return [ 'ERC-20', 'NATIVE', ...additionalTypes.map(({ id }) => id) ].filter(Boolean).join(','); }, [ config?.chains, portfolioData ]); - const tokensQuery = useQueryWithPages({ + const tokensQuery = useApiPaginatedQuery({ resourceName: 'multichainAggregator:address_tokens', pathParams: { hash }, - filters: { + queryParams: { type: typeFilter, chain_id: selectedChainId ?? undefined, - query: debouncedSearchTerm, include_poor_reputation_tokens: tokenReputationFilter, }, options: { @@ -107,22 +96,17 @@ const MultichainAddressPortfolioTokens = ({ addressData, isLoading, onChainChang noScroll: true, }); + const searchTerm = getQueryParamString(tokensQuery.filters.query); + const { onFilterChange } = tokensQuery; + const handleSelectedChainChange = React.useCallback((chainId: string) => { - setSelectedChainId((prev) => { - const nextValue = chainId === prev ? null : chainId; - tokensQuery.onFilterChange({ - chain_id: nextValue ?? undefined, - query: debouncedSearchTerm, - }); - onChainChange?.(nextValue); - return nextValue; - }); - }, [ tokensQuery, debouncedSearchTerm, onChainChange ]); - - const handleSearchTermChange = React.useCallback((value: string) => { - setSearchTerm(value); - tokensQuery.onFilterChange({ query: value }); - }, [ tokensQuery ]); + const nextValue = chainId === selectedChainId ? undefined : chainId; + onFilterChange({ chain_id: nextValue, query: searchTerm }); + }, [ onFilterChange, selectedChainId, searchTerm ]); + + const handleSearchTermChange = useDebouncedFilterChange((value) => { + onFilterChange({ chain_id: selectedChainId ?? undefined, query: value }); + }); const allTokensQuery = useApiQuery('multichainAggregator:address_tokens', { pathParams: { hash }, @@ -200,7 +184,7 @@ const MultichainAddressPortfolioTokens = ({ addressData, isLoading, onChainChang @@ -241,10 +225,11 @@ const MultichainAddressPortfolioTokens = ({ addressData, isLoading, onChainChang isError={ tokensQuery.isError } itemsNum={ tokensQuery.data?.items?.length } actionBar={ actionBar } - hasActiveFilters={ Boolean(debouncedSearchTerm) || Boolean(selectedChainId) } + hasActiveFilters={ Boolean(searchTerm) || Boolean(selectedChainId) } emptyStateProps={{ term: 'token', }} + isTransitioning={ tokensQuery.isTransitioning } > { tokensContent } diff --git a/src/features/multichain/pages/address/portfolio/MultichainAddressTokensListItem.tsx b/src/features/multichain/pages/address/portfolio/MultichainAddressTokensListItem.tsx deleted file mode 100644 index e004d62483f..00000000000 --- a/src/features/multichain/pages/address/portfolio/MultichainAddressTokensListItem.tsx +++ /dev/null @@ -1,124 +0,0 @@ -// SPDX-License-Identifier: LicenseRef-Blockscout - -import { Grid } from '@chakra-ui/react'; -import { BigNumber } from 'bignumber.js'; -import React from 'react'; - -import type { AddressTokenItem } from 'src/features/multichain/types/client'; -import { getTokenTypeName, isConfidentialTokenType } from 'src/slices/token/utils/token-types'; - -import AddressEntity from 'src/slices/address/components/entity/AddressEntity'; -import TokenEntity from 'src/slices/token/components/entity/TokenEntity'; -import NativeTokenTag from 'src/slices/token/components/NativeTokenTag'; - -import multichainConfig from 'src/features/multichain/chains-config'; - -import ListItemMobile from 'src/shared/lists/ListItemMobile'; -import calculateUsdValue from 'src/shared/values/entity/calculateUsdValue'; -import ConfidentialValue from 'src/shared/values/entity/ConfidentialValue'; -import SimpleValue from 'src/shared/values/entity/SimpleValue'; - -import { Skeleton } from 'src/toolkit/chakra/skeleton'; -import { Tag } from 'src/toolkit/chakra/tag'; - -interface Props { - data: AddressTokenItem; - isLoading: boolean; -} - -const MultichainAddressTokensListItem = ({ data, isLoading }: Props) => { - const chainInfo = React.useMemo(() => { - if (!data.chain_values) { - return; - } - - const chainId = Object.keys(data.chain_values)[0]; - const chain = multichainConfig()?.chains.find((chain) => chain.id === chainId); - return chain; - }, [ data.chain_values ]); - - const isNativeToken = chainInfo?.app_config.slices.address.nativeTokenAddress && - data.token.address_hash.toLowerCase() === chainInfo?.app_config.slices.address.nativeTokenAddress.toLowerCase(); - - const { - valueBn: tokenQuantity, - usdBn: tokenValue, - } = calculateUsdValue({ amount: data.value, exchangeRate: data.token.exchange_rate, decimals: data.token.decimals }); - - return ( - - { isNativeToken ? - : - { getTokenTypeName(data.token.type, chainInfo?.app_config) } } - - { data.token.type !== 'NATIVE' && ( - - ) } - - { data.token.exchange_rate ? ( - <> - - Price - - - - ) : null } - - Quantity - - { isConfidentialTokenType(data.token.type) ? ( - - ) : ( - - ) } - { data.token.exchange_rate && ( - <> - - Value - - { isConfidentialTokenType(data.token.type) ? ( - - ) : ( - - ) } - - ) } - - - ); -}; - -export default React.memo(MultichainAddressTokensListItem); diff --git a/src/features/multichain/pages/address/portfolio/MultichainAddressTokensTableItem.tsx b/src/features/multichain/pages/address/portfolio/MultichainAddressTokensTableItem.tsx index 2eee241f346..4efcbc59b9e 100644 --- a/src/features/multichain/pages/address/portfolio/MultichainAddressTokensTableItem.tsx +++ b/src/features/multichain/pages/address/portfolio/MultichainAddressTokensTableItem.tsx @@ -10,8 +10,8 @@ import { getTokenTypeName, isConfidentialTokenType } from 'src/slices/token/util import AddressEntity from 'src/slices/address/components/entity/AddressEntity'; import TokenEntity from 'src/slices/token/components/entity/TokenEntity'; import NativeTokenTag from 'src/slices/token/components/NativeTokenTag'; - -import multichainConfig from 'src/features/multichain/chains-config'; +import TokenMultiplierTag from 'src/slices/token/components/ui-multiplier/TokenMultiplierTag'; +import { getUiMultiplier } from 'src/slices/token/utils/ui-multiplier'; import calculateUsdValue from 'src/shared/values/entity/calculateUsdValue'; import ConfidentialValue from 'src/shared/values/entity/ConfidentialValue'; @@ -21,6 +21,8 @@ import { DEFAULT_ACCURACY_USD } from 'src/shared/values/entity/utils'; import { TableCell, TableRow } from 'src/toolkit/chakra/table'; import { Tag } from 'src/toolkit/chakra/tag'; +import { getTokenChain } from './utils'; + interface Props { data: AddressTokenItem; isLoading: boolean; @@ -28,20 +30,15 @@ interface Props { const MultichainAddressTokensTableItem = ({ data, isLoading }: Props) => { + const chainInfo = React.useMemo(() => getTokenChain(data), [ data ]); + + const multiplier = getUiMultiplier(data.token, chainInfo?.app_config); + const { valueBn: tokenQuantity, + rawValueBn: tokenRawQuantity, usdBn: tokenValue, - } = calculateUsdValue({ amount: data.value, exchangeRate: data.token.exchange_rate, decimals: data.token.decimals }); - - const chainInfo = React.useMemo(() => { - if (!data.chain_values) { - return; - } - - const chainId = Object.keys(data.chain_values)[0]; - const chain = multichainConfig()?.chains.find((chain) => chain.id === chainId); - return chain; - }, [ data.chain_values ]); + } = calculateUsdValue({ amount: data.value, exchangeRate: data.token.exchange_rate, decimals: data.token.decimals, multiplier }); const isNativeToken = chainInfo?.app_config.slices.address.nativeTokenAddress && data.token.address_hash.toLowerCase() === chainInfo?.app_config.slices.address.nativeTokenAddress.toLowerCase(); @@ -89,6 +86,9 @@ const MultichainAddressTokensTableItem = ({ data, isLoading }: Props) => { ) : ( } loading={ isLoading } color={ isNativeToken ? 'text.secondary' : undefined } /> diff --git a/src/features/multichain/pages/address/portfolio/__screenshots__/MultichainAddressPortfolioNetWorth.pw.tsx_default_base-view-mobile-1.png b/src/features/multichain/pages/address/portfolio/__screenshots__/MultichainAddressPortfolioNetWorth.pw.tsx_default_base-view-mobile-1.png new file mode 100644 index 00000000000..b32bcccb0f8 Binary files /dev/null and b/src/features/multichain/pages/address/portfolio/__screenshots__/MultichainAddressPortfolioNetWorth.pw.tsx_default_base-view-mobile-1.png differ diff --git a/src/features/multichain/pages/address/portfolio/__screenshots__/MultichainAddressPortfolioNetWorth.pw.tsx_default_small-desktop-base-view-1.png b/src/features/multichain/pages/address/portfolio/__screenshots__/MultichainAddressPortfolioNetWorth.pw.tsx_default_small-desktop-base-view-1.png new file mode 100644 index 00000000000..ad9f0d7811a Binary files /dev/null and b/src/features/multichain/pages/address/portfolio/__screenshots__/MultichainAddressPortfolioNetWorth.pw.tsx_default_small-desktop-base-view-1.png differ diff --git a/src/features/multichain/pages/address/portfolio/__screenshots__/MultichainAddressPortfolioNetWorth.pw.tsx_mobile_base-view-mobile-1.png b/src/features/multichain/pages/address/portfolio/__screenshots__/MultichainAddressPortfolioNetWorth.pw.tsx_mobile_base-view-mobile-1.png new file mode 100644 index 00000000000..2821a92608b Binary files /dev/null and b/src/features/multichain/pages/address/portfolio/__screenshots__/MultichainAddressPortfolioNetWorth.pw.tsx_mobile_base-view-mobile-1.png differ diff --git a/src/features/multichain/pages/address/portfolio/__screenshots__/MultichainAddressPortfolioTokens.pw.tsx_dark-color-mode_many-chains-mobile-dark-mode-1.png b/src/features/multichain/pages/address/portfolio/__screenshots__/MultichainAddressPortfolioTokens.pw.tsx_dark-color-mode_many-chains-mobile-dark-mode-1.png index 94e1bee2cf9..406e21221d2 100644 Binary files a/src/features/multichain/pages/address/portfolio/__screenshots__/MultichainAddressPortfolioTokens.pw.tsx_dark-color-mode_many-chains-mobile-dark-mode-1.png and b/src/features/multichain/pages/address/portfolio/__screenshots__/MultichainAddressPortfolioTokens.pw.tsx_dark-color-mode_many-chains-mobile-dark-mode-1.png differ diff --git a/src/features/multichain/pages/address/portfolio/__screenshots__/MultichainAddressPortfolioTokens.pw.tsx_dark-color-mode_many-chains-mobile-dark-mode-2.png b/src/features/multichain/pages/address/portfolio/__screenshots__/MultichainAddressPortfolioTokens.pw.tsx_dark-color-mode_many-chains-mobile-dark-mode-2.png deleted file mode 100644 index 7c6af7d3b8a..00000000000 Binary files a/src/features/multichain/pages/address/portfolio/__screenshots__/MultichainAddressPortfolioTokens.pw.tsx_dark-color-mode_many-chains-mobile-dark-mode-2.png and /dev/null differ diff --git a/src/features/multichain/pages/address/portfolio/__screenshots__/MultichainAddressPortfolioTokens.pw.tsx_dark-color-mode_many-chains-selected-chain-mobile-dark-mode-1.png b/src/features/multichain/pages/address/portfolio/__screenshots__/MultichainAddressPortfolioTokens.pw.tsx_dark-color-mode_many-chains-selected-chain-mobile-dark-mode-1.png new file mode 100644 index 00000000000..8fa4b4f9436 Binary files /dev/null and b/src/features/multichain/pages/address/portfolio/__screenshots__/MultichainAddressPortfolioTokens.pw.tsx_dark-color-mode_many-chains-selected-chain-mobile-dark-mode-1.png differ diff --git a/src/features/multichain/pages/address/portfolio/__screenshots__/MultichainAddressPortfolioTokens.pw.tsx_default_many-chains-mobile-dark-mode-1.png b/src/features/multichain/pages/address/portfolio/__screenshots__/MultichainAddressPortfolioTokens.pw.tsx_default_many-chains-mobile-dark-mode-1.png index 86e7d979998..812179b5755 100644 Binary files a/src/features/multichain/pages/address/portfolio/__screenshots__/MultichainAddressPortfolioTokens.pw.tsx_default_many-chains-mobile-dark-mode-1.png and b/src/features/multichain/pages/address/portfolio/__screenshots__/MultichainAddressPortfolioTokens.pw.tsx_default_many-chains-mobile-dark-mode-1.png differ diff --git a/src/features/multichain/pages/address/portfolio/__screenshots__/MultichainAddressPortfolioTokens.pw.tsx_default_many-chains-mobile-dark-mode-2.png b/src/features/multichain/pages/address/portfolio/__screenshots__/MultichainAddressPortfolioTokens.pw.tsx_default_many-chains-mobile-dark-mode-2.png deleted file mode 100644 index ba7eb1f5358..00000000000 Binary files a/src/features/multichain/pages/address/portfolio/__screenshots__/MultichainAddressPortfolioTokens.pw.tsx_default_many-chains-mobile-dark-mode-2.png and /dev/null differ diff --git a/src/features/multichain/pages/address/portfolio/__screenshots__/MultichainAddressPortfolioTokens.pw.tsx_default_many-chains-selected-chain-mobile-dark-mode-1.png b/src/features/multichain/pages/address/portfolio/__screenshots__/MultichainAddressPortfolioTokens.pw.tsx_default_many-chains-selected-chain-mobile-dark-mode-1.png new file mode 100644 index 00000000000..90e70177eb3 Binary files /dev/null and b/src/features/multichain/pages/address/portfolio/__screenshots__/MultichainAddressPortfolioTokens.pw.tsx_default_many-chains-selected-chain-mobile-dark-mode-1.png differ diff --git a/src/features/multichain/pages/address/portfolio/__screenshots__/MultichainAddressPortfolioTokens.pw.tsx_default_zero-net-worth-1.png b/src/features/multichain/pages/address/portfolio/__screenshots__/MultichainAddressPortfolioTokens.pw.tsx_default_zero-net-worth-1.png index 47c24d92d7e..367bfb8b6cf 100644 Binary files a/src/features/multichain/pages/address/portfolio/__screenshots__/MultichainAddressPortfolioTokens.pw.tsx_default_zero-net-worth-1.png and b/src/features/multichain/pages/address/portfolio/__screenshots__/MultichainAddressPortfolioTokens.pw.tsx_default_zero-net-worth-1.png differ diff --git a/src/features/multichain/pages/address/portfolio/__screenshots__/MultichainAddressPortfolioTokens.pw.tsx_mobile_many-chains-mobile-dark-mode-1.png b/src/features/multichain/pages/address/portfolio/__screenshots__/MultichainAddressPortfolioTokens.pw.tsx_mobile_many-chains-mobile-dark-mode-1.png index 0a4e942256b..9dc612a9e23 100644 Binary files a/src/features/multichain/pages/address/portfolio/__screenshots__/MultichainAddressPortfolioTokens.pw.tsx_mobile_many-chains-mobile-dark-mode-1.png and b/src/features/multichain/pages/address/portfolio/__screenshots__/MultichainAddressPortfolioTokens.pw.tsx_mobile_many-chains-mobile-dark-mode-1.png differ diff --git a/src/features/multichain/pages/address/portfolio/__screenshots__/MultichainAddressPortfolioTokens.pw.tsx_mobile_many-chains-mobile-dark-mode-2.png b/src/features/multichain/pages/address/portfolio/__screenshots__/MultichainAddressPortfolioTokens.pw.tsx_mobile_many-chains-mobile-dark-mode-2.png deleted file mode 100644 index d78e9aa2614..00000000000 Binary files a/src/features/multichain/pages/address/portfolio/__screenshots__/MultichainAddressPortfolioTokens.pw.tsx_mobile_many-chains-mobile-dark-mode-2.png and /dev/null differ diff --git a/src/features/multichain/pages/address/portfolio/__screenshots__/MultichainAddressPortfolioTokens.pw.tsx_mobile_many-chains-selected-chain-mobile-dark-mode-1.png b/src/features/multichain/pages/address/portfolio/__screenshots__/MultichainAddressPortfolioTokens.pw.tsx_mobile_many-chains-selected-chain-mobile-dark-mode-1.png new file mode 100644 index 00000000000..b080bbc6a95 Binary files /dev/null and b/src/features/multichain/pages/address/portfolio/__screenshots__/MultichainAddressPortfolioTokens.pw.tsx_mobile_many-chains-selected-chain-mobile-dark-mode-1.png differ diff --git a/src/features/multichain/pages/address/portfolio/utils.ts b/src/features/multichain/pages/address/portfolio/utils.ts index 4d7fe6db4cf..dcbbc617bdc 100644 --- a/src/features/multichain/pages/address/portfolio/utils.ts +++ b/src/features/multichain/pages/address/portfolio/utils.ts @@ -2,9 +2,13 @@ import BigNumber from 'bignumber.js'; -import type { AddressTokenItem } from 'src/features/multichain/types/client'; +import type { AddressTokenItem, ClusterChainConfig } from 'src/features/multichain/types/client'; import { isFungibleTokenType } from 'src/slices/token/utils/token-types'; +import { getUiMultiplier } from 'src/slices/token/utils/ui-multiplier'; + +import multichainConfig from 'src/features/multichain/chains-config'; + import { hairsp } from 'src/toolkit/utils/htmlEntities'; export function formatPercentage(percentage: number) { @@ -18,6 +22,16 @@ export function formatPercentage(percentage: number) { return `${ value }%`; } +// An address-portfolio token item belongs to exactly one chain, the single key of `chain_values`. +export const getTokenChain = (data: AddressTokenItem): ClusterChainConfig | undefined => { + if (!data.chain_values) { + return; + } + + const chainId = Object.keys(data.chain_values)[0]; + return multichainConfig()?.chains.find((chain) => chain.id === chainId); +}; + export const calculateUsdValue = (data: AddressTokenItem) => { const isFungibleToken = isFungibleTokenType(data.token?.type); @@ -31,7 +45,9 @@ export const calculateUsdValue = (data: AddressTokenItem) => { } const decimals = Number(data.token?.decimals || '18'); + const multiplier = getUiMultiplier(data.token, getTokenChain(data)?.app_config); + const value = BigNumber(data.value ?? '0').div(BigNumber(10 ** decimals)); return { - usd: BigNumber(data.value ?? '0').div(BigNumber(10 ** decimals)).multipliedBy(BigNumber(exchangeRate)), + usd: (multiplier ? value.times(multiplier) : value).multipliedBy(BigNumber(exchangeRate)), }; }; diff --git a/src/features/multichain/pages/address/useChainSelectErc20.ts b/src/features/multichain/pages/address/useChainSelectErc20.ts deleted file mode 100644 index ce0ca11e669..00000000000 --- a/src/features/multichain/pages/address/useChainSelectErc20.ts +++ /dev/null @@ -1,18 +0,0 @@ -// SPDX-License-Identifier: LicenseRef-Blockscout - -import { useRouter } from 'next/router'; -import React from 'react'; - -import getQueryParamString from 'src/shared/router/get-query-param-string'; - -interface Props { - chainIds: Array; -} -export default function useChainSelectErc20({ chainIds }: Props) { - const router = useRouter(); - const chainId = getQueryParamString(router.query.chain_id); - - return React.useState | undefined>( - chainId && chainIds.includes(chainId) ? [ chainId ] : [ 'all' ], - ); -} diff --git a/src/features/multichain/pages/addresses/MultichainAccounts.tsx b/src/features/multichain/pages/addresses/MultichainAccounts.tsx index fd77fda2975..8c8c59691fe 100644 --- a/src/features/multichain/pages/addresses/MultichainAccounts.tsx +++ b/src/features/multichain/pages/addresses/MultichainAccounts.tsx @@ -1,27 +1,29 @@ // SPDX-License-Identifier: LicenseRef-Blockscout -import { Box } from '@chakra-ui/react'; import { BigNumber } from 'bignumber.js'; import React from 'react'; import ActionBar, { ACTION_BAR_HEIGHT_DESKTOP } from 'src/shell/page/action-bar/ActionBar'; import PageTitle from 'src/shell/page/title/PageTitle'; -import AddressesList from 'src/slices/address/pages/index/AddressesList'; import AddressesTable from 'src/slices/address/pages/index/AddressesTable'; import { TOP_ADDRESS } from 'src/slices/address/stubs/address'; import ChainSelect from 'src/features/multichain/components/ChainSelect'; import { MultichainProvider } from 'src/features/multichain/context'; +import { useChainValue } from 'src/features/multichain/hooks/useChainValue'; import DataList from 'src/shared/lists/DataList'; import getItemIndex from 'src/shared/lists/get-item-index'; import Pagination from 'src/shared/pagination/Pagination'; -import useQueryWithPages from 'src/shared/pagination/useQueryWithPages'; +import useApiPaginatedQuery from 'src/shared/pagination/useApiPaginatedQuery'; import { generateListStub } from 'src/shared/pagination/utils'; +import { TableContainerScrollable } from 'src/toolkit/chakra/table'; + const MultichainAccounts = () => { - const { isError, isPlaceholderData, data, pagination, chainValue, onChainValueChange, queryHash } = useQueryWithPages({ + const { chainValue, chain, onChainValueChange } = useChainValue(); + const { isError, isInitialLoading, isTransitioning, data, pagination, queryHash } = useApiPaginatedQuery({ resourceName: 'core:addresses', options: { placeholderData: generateListStub<'core:addresses'>(TOP_ADDRESS, 50, { @@ -34,7 +36,7 @@ const MultichainAccounts = () => { exchange_rate: '1', }), }, - isMultichain: true, + chain, }); const pageStartIndex = getItemIndex(0, pagination.page); @@ -43,26 +45,17 @@ const MultichainAccounts = () => { }, [ data?.total_supply ]); const content = data?.items ? ( - - + + - - - - + ) : null; @@ -89,6 +82,7 @@ const MultichainAccounts = () => { actionBar={ actionBar } showActionBarIfError showActionBarIfEmpty + isTransitioning={ isTransitioning } > { content } diff --git a/src/features/multichain/pages/blocks/MultichainBlocks.tsx b/src/features/multichain/pages/blocks/MultichainBlocks.tsx index 7a16bef7783..12051a4f8ba 100644 --- a/src/features/multichain/pages/blocks/MultichainBlocks.tsx +++ b/src/features/multichain/pages/blocks/MultichainBlocks.tsx @@ -10,12 +10,12 @@ import PageTitle from 'src/shell/page/title/PageTitle'; import { BLOCK_ITEM } from 'src/slices/block/stubs/list'; -import multichainConfig from 'src/features/multichain/chains-config'; import ChainSelect from 'src/features/multichain/components/ChainSelect'; +import { useChainValue } from 'src/features/multichain/hooks/useChainValue'; import useIsMobile from 'src/shared/hooks/useIsMobile'; import Pagination from 'src/shared/pagination/Pagination'; -import useQueryWithPages from 'src/shared/pagination/useQueryWithPages'; +import useApiPaginatedQuery from 'src/shared/pagination/useApiPaginatedQuery'; import { generateListStub } from 'src/shared/pagination/utils'; import getQueryParamString from 'src/shared/router/get-query-param-string'; import { route } from 'src/shared/router/routes'; @@ -41,10 +41,11 @@ const MultichainBlocks = () => { const router = useRouter(); const tab = getQueryParamString(router.query.tab); const isMobile = useIsMobile(); + const { chainValue, chain, onChainValueChange } = useChainValue(); - const blocksQuery = useQueryWithPages({ + const blocksQuery = useApiPaginatedQuery({ resourceName: 'core:blocks', - filters: { type: 'block' }, + queryParams: { type: 'block' }, options: { enabled: tab === 'blocks' || !tab, placeholderData: generateListStub<'core:blocks'>(BLOCK_ITEM, 50, { next_page_params: { @@ -52,12 +53,12 @@ const MultichainBlocks = () => { items_count: 50, } }), }, - isMultichain: true, + chain, }); - const reorgsQuery = useQueryWithPages({ + const reorgsQuery = useApiPaginatedQuery({ resourceName: 'core:blocks', - filters: { type: 'reorg' }, + queryParams: { type: 'reorg' }, options: { enabled: tab === 'reorgs', placeholderData: generateListStub<'core:blocks'>(BLOCK_ITEM, 50, { next_page_params: { @@ -65,12 +66,12 @@ const MultichainBlocks = () => { items_count: 50, } }), }, - isMultichain: true, + chain, }); - const unclesQuery = useQueryWithPages({ + const unclesQuery = useApiPaginatedQuery({ resourceName: 'core:blocks', - filters: { type: 'uncle' }, + queryParams: { type: 'uncle' }, options: { enabled: tab === 'uncles', placeholderData: generateListStub<'core:blocks'>(BLOCK_ITEM, 50, { next_page_params: { @@ -78,13 +79,13 @@ const MultichainBlocks = () => { items_count: 50, } }), }, - isMultichain: true, + chain, }); const tabs: Array = [ - { id: 'blocks', title: 'All', component: }, - { id: 'reorgs', title: 'Forked', component: }, - { id: 'uncles', title: 'Uncles', component: }, + { id: 'blocks', title: 'All', component: }, + { id: 'reorgs', title: 'Forked', component: }, + { id: 'uncles', title: 'Uncles', component: }, ]; const currentQuery = (() => { @@ -95,18 +96,16 @@ const MultichainBlocks = () => { } })(); - const currentChainInfo = multichainConfig()?.chains.find(chain => chain.id === currentQuery.chainValue?.[0]); - const leftSlot = ( ); const rightSlot = ( - + Block countdown diff --git a/src/features/multichain/pages/contracts/MultichainVerifiedContracts.tsx b/src/features/multichain/pages/contracts/MultichainVerifiedContracts.tsx index 3d3df25f0a8..e6e51fec5f4 100644 --- a/src/features/multichain/pages/contracts/MultichainVerifiedContracts.tsx +++ b/src/features/multichain/pages/contracts/MultichainVerifiedContracts.tsx @@ -10,17 +10,18 @@ import useVerifiedContractsQuery from 'src/slices/contract/hooks/useVerifiedCont import { SORT_OPTIONS } from 'src/slices/contract/pages/index/sort'; import VerifiedContractsCounters from 'src/slices/contract/pages/index/VerifiedContractsCounters'; import VerifiedContractsFilter from 'src/slices/contract/pages/index/VerifiedContractsFilter'; -import VerifiedContractsList from 'src/slices/contract/pages/index/VerifiedContractsList'; import VerifiedContractsTable from 'src/slices/contract/pages/index/VerifiedContractsTable'; import ChainSelect from 'src/features/multichain/components/ChainSelect'; import { MultichainProvider } from 'src/features/multichain/context'; +import { useChainValue } from 'src/features/multichain/hooks/useChainValue'; import useIsMobile from 'src/shared/hooks/useIsMobile'; import DataList from 'src/shared/lists/DataList'; import Pagination from 'src/shared/pagination/Pagination'; import Sort from 'src/shared/sort/Sort'; +import { TableContainerScrollable } from 'src/toolkit/chakra/table'; import { FilterInput } from 'src/toolkit/components/filters/FilterInput'; const sortCollection = createListCollection({ @@ -30,8 +31,9 @@ const sortCollection = createListCollection({ const MultichainVerifiedContracts = () => { const isMobile = useIsMobile(); - const { query, type, searchTerm, sort, onSearchTermChange, onTypeChange, onSortChange } = useVerifiedContractsQuery({ isMultichain: true }); - const { isError, isPlaceholderData, data, pagination, chainValue, onChainValueChange } = query; + const { chainValue, chain, onChainValueChange } = useChainValue(); + const { query, type, searchTerm, sort, onSearchTermChange, onTypeChange, onSortChange } = useVerifiedContractsQuery({ chain }); + const { isError, isInitialLoading, data, pagination } = query; const typeFilter = ( { defaultValue={ [ sort ] } collection={ sortCollection } onValueChange={ onSortChange } - isLoading={ isPlaceholderData } + isLoading={ isInitialLoading } /> ); @@ -81,14 +83,9 @@ const MultichainVerifiedContracts = () => { ); const content = data?.items ? ( - <> - - - - - - - + + + ) : null; return ( @@ -103,7 +100,7 @@ const MultichainVerifiedContracts = () => { mode="default" mb={ 3 } /> - + { term: 'contract', }} actionBar={ actionBar } + isTransitioning={ query.isTransitioning } > { content } diff --git a/src/features/multichain/pages/ecosystems/MultichainEcosystems.pw.tsx b/src/features/multichain/pages/ecosystems/MultichainEcosystems.pw.tsx index 493999239bc..3ad22d4c0b6 100644 --- a/src/features/multichain/pages/ecosystems/MultichainEcosystems.pw.tsx +++ b/src/features/multichain/pages/ecosystems/MultichainEcosystems.pw.tsx @@ -8,7 +8,7 @@ import { test, expect } from 'playwright/lib'; import MultichainEcosystems from './MultichainEcosystems'; -test('base view +@mobile', async({ render, mockTextAd, mockApiResponse, mockEnvs, mockMultichainConfig, mockAssetResponse }) => { +test('base view', async({ render, mockTextAd, mockApiResponse, mockEnvs, mockMultichainConfig, mockAssetResponse }) => { test.slow(); await mockMultichainConfig(); await mockEnvs(ENVS_MAP.multichain); diff --git a/src/features/multichain/pages/ecosystems/MultichainEcosystems.tsx b/src/features/multichain/pages/ecosystems/MultichainEcosystems.tsx index 3a673fc004e..c9f6bc36f8c 100644 --- a/src/features/multichain/pages/ecosystems/MultichainEcosystems.tsx +++ b/src/features/multichain/pages/ecosystems/MultichainEcosystems.tsx @@ -1,6 +1,6 @@ // SPDX-License-Identifier: LicenseRef-Blockscout -import { Box, createListCollection } from '@chakra-ui/react'; +import { createListCollection } from '@chakra-ui/react'; import { useRouter } from 'next/router'; import React from 'react'; @@ -13,12 +13,14 @@ import PageTitle from 'src/shell/page/title/PageTitle'; import { CHAIN_METRICS } from 'src/features/multichain/stubs'; +import useIsMobile from 'src/shared/hooks/useIsMobile'; import DataList from 'src/shared/lists/DataList'; import getSortParamsFromValue from 'src/shared/sort/get-sort-params-from-value'; import getSortValueFromQuery from 'src/shared/sort/get-sort-value-from-query'; import Sort from 'src/shared/sort/Sort'; -import MultichainEcosystemsList from './MultichainEcosystemsList'; +import { TableContainerScrollable } from 'src/toolkit/chakra/table'; + import MultichainEcosystemsTable from './MultichainEcosystemsTable'; import { SORT_OPTIONS } from './utils'; @@ -28,6 +30,7 @@ const sortCollection = createListCollection({ const MultichainEcosystems = () => { const router = useRouter(); + const isMobile = useIsMobile(); const [ sort, setSort ] = React.useState(getSortValueFromQuery(router.query, SORT_OPTIONS) ?? 'default'); @@ -44,33 +47,28 @@ const MultichainEcosystems = () => { }, [ setSort ]); const content = data?.items ? ( - <> - - - - - - - - - - + + + + ) : null; + + const actionBar = isMobile ? ( + + + ) : null; return ( @@ -83,6 +81,7 @@ const MultichainEcosystems = () => { isError={ isError } itemsNum={ data?.items.length } emptyText="There are no chains in the cluster." + actionBar={ actionBar } > { content } diff --git a/src/features/multichain/pages/ecosystems/MultichainEcosystemsList.tsx b/src/features/multichain/pages/ecosystems/MultichainEcosystemsList.tsx deleted file mode 100644 index aadb0fcb2e9..00000000000 --- a/src/features/multichain/pages/ecosystems/MultichainEcosystemsList.tsx +++ /dev/null @@ -1,41 +0,0 @@ -// SPDX-License-Identifier: LicenseRef-Blockscout - -import { Box } from '@chakra-ui/react'; -import React from 'react'; - -import type * as multichain from '@blockscout/multichain-aggregator-types'; - -import multichainConfig from 'src/features/multichain/chains-config'; - -import useLazyRenderedList from 'src/shared/lists/useLazyRenderedList'; - -import MultichainEcosystemsListItem from './MultichainEcosystemsListItem'; - -interface Props { - data: Array; - isLoading?: boolean; - resetKey?: string; -} - -const MultichainEcosystemsList = ({ data, isLoading, resetKey }: Props) => { - const chains = multichainConfig()?.chains; - const { cutRef, renderedItemsNum } = useLazyRenderedList({ list: data, isEnabled: !isLoading, resetKey }); - - return ( - <> - - { data.slice(0, renderedItemsNum).map((item, index) => ( - chain.id === item.chain_id) } - isLoading={ isLoading } - /> - )) } - - - - ); -}; - -export default React.memo(MultichainEcosystemsList); diff --git a/src/features/multichain/pages/ecosystems/MultichainEcosystemsListItem.tsx b/src/features/multichain/pages/ecosystems/MultichainEcosystemsListItem.tsx deleted file mode 100644 index 5b26854f34a..00000000000 --- a/src/features/multichain/pages/ecosystems/MultichainEcosystemsListItem.tsx +++ /dev/null @@ -1,111 +0,0 @@ -// SPDX-License-Identifier: LicenseRef-Blockscout - -import { chakra, Grid, HStack, Stat } from '@chakra-ui/react'; -import React from 'react'; - -import type * as multichain from '@blockscout/multichain-aggregator-types'; -import type { ClusterChainConfig } from 'src/features/multichain/types/client'; - -import ChainSnippetList from 'src/shared/external-chains/ChainSnippetList'; -import ListItemMobile from 'src/shared/lists/ListItemMobile'; - -import { Skeleton } from 'src/toolkit/chakra/skeleton'; - -interface Props { - data: multichain.ChainMetrics; - chainInfo: ClusterChainConfig | undefined; - isLoading?: boolean; -} - -const MultichainEcosystemsListItem = ({ data, chainInfo, isLoading }: Props) => { - const activeAddresses = { - value: data.active_accounts?.current_full_week ? Number(data.active_accounts.current_full_week) : 0, - delta: data.active_accounts?.wow_diff_percent ? Number(data.active_accounts.wow_diff_percent) : 0, - }; - - const newAddresses = { - value: data.new_addresses?.current_full_week ? Number(data.new_addresses.current_full_week) : 0, - delta: data.new_addresses?.wow_diff_percent ? Number(data.new_addresses.wow_diff_percent) : 0, - }; - - const dailyTransactions = { - value: data.daily_transactions?.current_full_week ? Number(data.daily_transactions.current_full_week) : 0, - delta: data.daily_transactions?.wow_diff_percent ? Number(data.daily_transactions.wow_diff_percent) : 0, - }; - - return ( - - { chainInfo && } - - - Active addresses - 7D - - - - { activeAddresses.value.toLocaleString() } - - { activeAddresses.delta ? ( - - 0 }> - - { activeAddresses.delta.toFixed(2) }% - - { activeAddresses.delta > 0 ? : } - - - ) : null } - - - - New addresses - 7D - - - - { newAddresses.value.toLocaleString() } - - { newAddresses.delta ? ( - - 0 }> - - { newAddresses.delta.toFixed(2) }% - - { newAddresses.delta > 0 ? : } - - - ) : null } - - - - Daily txs - 7D - - - - { dailyTransactions.value.toLocaleString() } - - { dailyTransactions.delta ? ( - - 0 }> - - { dailyTransactions.delta.toFixed(2) }% - - { dailyTransactions.delta > 0 ? : } - - - ) : null } - - - - TPS - - - { Number(data.tps ?? 0).toLocaleString(undefined, { maximumFractionDigits: 2 }) } - - - - ); -}; - -export default React.memo(MultichainEcosystemsListItem); diff --git a/src/features/multichain/pages/ecosystems/__screenshots__/MultichainEcosystems.pw.tsx_default_base-view-mobile-1.png b/src/features/multichain/pages/ecosystems/__screenshots__/MultichainEcosystems.pw.tsx_default_base-view-1.png similarity index 100% rename from src/features/multichain/pages/ecosystems/__screenshots__/MultichainEcosystems.pw.tsx_default_base-view-mobile-1.png rename to src/features/multichain/pages/ecosystems/__screenshots__/MultichainEcosystems.pw.tsx_default_base-view-1.png diff --git a/src/features/multichain/pages/ecosystems/__screenshots__/MultichainEcosystems.pw.tsx_mobile_base-view-mobile-1.png b/src/features/multichain/pages/ecosystems/__screenshots__/MultichainEcosystems.pw.tsx_mobile_base-view-mobile-1.png deleted file mode 100644 index 0694f943ed7..00000000000 Binary files a/src/features/multichain/pages/ecosystems/__screenshots__/MultichainEcosystems.pw.tsx_mobile_base-view-mobile-1.png and /dev/null differ diff --git a/src/features/multichain/pages/home/LatestTxsLocal.tsx b/src/features/multichain/pages/home/LatestTxsLocal.tsx index 5a96db22a53..38b901fcb19 100644 --- a/src/features/multichain/pages/home/LatestTxsLocal.tsx +++ b/src/features/multichain/pages/home/LatestTxsLocal.tsx @@ -45,7 +45,7 @@ const LatestTxsLocal = () => { { const statsQuery = useApiQuery('multichainStats:pages_main', { queryOptions: { @@ -22,7 +27,7 @@ const Stats = () => { }, }); - const items: Array = React.useMemo(() => { + const items = React.useMemo(() => { return [ statsQuery.data?.total_multichain_txns && { id: 'total_txs' as const, @@ -45,9 +50,9 @@ const Stats = () => { }, [ statsQuery.data ]); return ( - + { items.length > 0 && ( - + { items.map((item) => ( { icon={ item.icon } isLoading={ statsQuery.isPlaceholderData } hint={ item.hint } - w={{ base: 'calc((100% - 8px) / 2)', lg: '100%' }} /> )) } - + ) } diff --git a/src/features/multichain/pages/home/__screenshots__/MultichainHome.pw.tsx_dark-color-mode_base-view-mobile-dark-mode-1.png b/src/features/multichain/pages/home/__screenshots__/MultichainHome.pw.tsx_dark-color-mode_base-view-mobile-dark-mode-1.png index 5f8b143bf69..ce4440bb3f7 100644 Binary files a/src/features/multichain/pages/home/__screenshots__/MultichainHome.pw.tsx_dark-color-mode_base-view-mobile-dark-mode-1.png and b/src/features/multichain/pages/home/__screenshots__/MultichainHome.pw.tsx_dark-color-mode_base-view-mobile-dark-mode-1.png differ diff --git a/src/features/multichain/pages/home/__screenshots__/MultichainHome.pw.tsx_default_base-view-mobile-dark-mode-1.png b/src/features/multichain/pages/home/__screenshots__/MultichainHome.pw.tsx_default_base-view-mobile-dark-mode-1.png index 480e1c204f7..cae9b4462bf 100644 Binary files a/src/features/multichain/pages/home/__screenshots__/MultichainHome.pw.tsx_default_base-view-mobile-dark-mode-1.png and b/src/features/multichain/pages/home/__screenshots__/MultichainHome.pw.tsx_default_base-view-mobile-dark-mode-1.png differ diff --git a/src/features/multichain/pages/home/__screenshots__/MultichainHome.pw.tsx_mobile_base-view-mobile-dark-mode-1.png b/src/features/multichain/pages/home/__screenshots__/MultichainHome.pw.tsx_mobile_base-view-mobile-dark-mode-1.png index 251b85cb2c0..8e04d646bab 100644 Binary files a/src/features/multichain/pages/home/__screenshots__/MultichainHome.pw.tsx_mobile_base-view-mobile-dark-mode-1.png and b/src/features/multichain/pages/home/__screenshots__/MultichainHome.pw.tsx_mobile_base-view-mobile-dark-mode-1.png differ diff --git a/src/features/multichain/pages/internal-txs/MultichainInternalTxs.tsx b/src/features/multichain/pages/internal-txs/MultichainInternalTxs.tsx index a58cca36c43..80e5f72da8c 100644 --- a/src/features/multichain/pages/internal-txs/MultichainInternalTxs.tsx +++ b/src/features/multichain/pages/internal-txs/MultichainInternalTxs.tsx @@ -6,24 +6,26 @@ import React from 'react'; import ActionBar from 'src/shell/page/action-bar/ActionBar'; import PageTitle from 'src/shell/page/title/PageTitle'; -import InternalTxsList from 'src/slices/internal-tx/components/InternalTxsList'; import InternalTxsTable from 'src/slices/internal-tx/components/InternalTxsTable'; import useInternalTxsQuery from 'src/slices/internal-tx/hooks/useInternalTxsQuery'; import ChainSelect from 'src/features/multichain/components/ChainSelect'; import { MultichainProvider } from 'src/features/multichain/context'; +import { useChainValue } from 'src/features/multichain/hooks/useChainValue'; import useIsMobile from 'src/shared/hooks/useIsMobile'; import DataList from 'src/shared/lists/DataList'; import Pagination from 'src/shared/pagination/Pagination'; +import { TableContainerScrollable } from 'src/toolkit/chakra/table'; import { FilterInput } from 'src/toolkit/components/filters/FilterInput'; const MultichainInternalTxs = () => { const isMobile = useIsMobile(); - const { query, searchTerm, onSearchTermChange } = useInternalTxsQuery({ isMultichain: true }); - const { isError, isPlaceholderData, data, pagination } = query; + const { chainValue, chain, onChainValueChange } = useChainValue(); + const { query, searchTerm, onSearchTermChange } = useInternalTxsQuery({ chain }); + const { isError, isInitialLoading, data, pagination } = query; const filterInput = ( { const chainSelect = ( ); @@ -59,13 +61,10 @@ const MultichainInternalTxs = () => { ); const content = data?.items ? ( - - - - - - - + + + + ) : null; @@ -86,6 +85,7 @@ const MultichainInternalTxs = () => { actionBar={ actionBar } showActionBarIfError showActionBarIfEmpty + isTransitioning={ query.isTransitioning } > { content } diff --git a/src/features/multichain/pages/search-results/SearchResults.tsx b/src/features/multichain/pages/search-results/SearchResults.tsx index e1e95d3f976..120fceca43b 100644 --- a/src/features/multichain/pages/search-results/SearchResults.tsx +++ b/src/features/multichain/pages/search-results/SearchResults.tsx @@ -44,10 +44,10 @@ const SearchResults = () => { persistedParams: [ 'q', 'tab' ], }); const chainId = chainSelect.value?.[0]; - const { searchTerm, debouncedSearchTerm, handleSearchTermChange, handleSubmit, queries, checkRedirectQuery } = useSearchQuery({ + const { searchTerm, appliedSearchTerm, handleSearchTermChange, handleSubmit, queries, checkRedirectQuery } = useSearchQuery({ chainId: chainId === 'all' ? undefined : chainId, }); - const showContent = useSearchRedirect({ checkRedirectQuery, hasSearchTerm: debouncedSearchTerm.trim().length > 0 }); + const showContent = useSearchRedirect({ checkRedirectQuery, hasSearchTerm: appliedSearchTerm.trim().length > 0 }); const isLoading = Object.values(queries).some((query) => query.isPending); @@ -102,7 +102,7 @@ const SearchResults = () => { queries={ queries } queryType={ queryType } isLoading={ isLoading } - searchTerm={ debouncedSearchTerm } + searchTerm={ appliedSearchTerm } beforeContent={ isMobile ? chainSelectElement : undefined } /> ), @@ -118,7 +118,7 @@ const SearchResults = () => { queries={ queries } queryType={ undefined } isLoading={ isLoading } - searchTerm={ debouncedSearchTerm } + searchTerm={ appliedSearchTerm } beforeContent={ isMobile ? chainSelectElement : undefined } /> ), diff --git a/src/features/multichain/pages/search-results/__screenshots__/SearchResults.pw.tsx_default_desktop-base-view-1.png b/src/features/multichain/pages/search-results/__screenshots__/SearchResults.pw.tsx_default_desktop-base-view-1.png index 60ef3594f5e..56ba6089d29 100644 Binary files a/src/features/multichain/pages/search-results/__screenshots__/SearchResults.pw.tsx_default_desktop-base-view-1.png and b/src/features/multichain/pages/search-results/__screenshots__/SearchResults.pw.tsx_default_desktop-base-view-1.png differ diff --git a/src/features/multichain/pages/search-results/__screenshots__/SearchResults.pw.tsx_default_mobile-base-view-1.png b/src/features/multichain/pages/search-results/__screenshots__/SearchResults.pw.tsx_default_mobile-base-view-1.png index 083394dcba7..16e6cad443c 100644 Binary files a/src/features/multichain/pages/search-results/__screenshots__/SearchResults.pw.tsx_default_mobile-base-view-1.png and b/src/features/multichain/pages/search-results/__screenshots__/SearchResults.pw.tsx_default_mobile-base-view-1.png differ diff --git a/src/features/multichain/pages/search-results/items/SearchResultItemToken.tsx b/src/features/multichain/pages/search-results/items/SearchResultItemToken.tsx index 0e4c101b9ba..c9f47e87d77 100644 --- a/src/features/multichain/pages/search-results/items/SearchResultItemToken.tsx +++ b/src/features/multichain/pages/search-results/items/SearchResultItemToken.tsx @@ -6,16 +6,18 @@ import React from 'react'; import type * as multichain from '@blockscout/multichain-aggregator-types'; import type { ClusterChainConfig } from 'src/features/multichain/types/client'; +import { isFungibleTokenType } from 'src/slices/token/utils/token-types'; import TokenEntity from 'src/slices/token/components/entity/TokenEntity'; import * as contract from 'src/features/multichain/utils/contract'; import { route } from 'src/shared/router/routes'; -import HashStringShortenDynamic from 'src/shared/texts/HashStringShortenDynamic'; -import shortenString from 'src/shared/texts/shorten-string'; import SpriteIcon from 'src/sprite/SpriteIcon'; +import { Truncate } from 'src/toolkit/components/truncation/Truncate'; +import { shortenString } from 'src/toolkit/utils/texts'; + import SearchResultListItem from '../SearchResultListItem'; interface Props { @@ -25,6 +27,7 @@ interface Props { } const SearchResultItemToken = ({ data, chain, isMobile }: Props) => { + const isFungibleToken = isFungibleTokenType(data.type, chain.app_config); const isVerified = contract.isVerified({ chain_infos: mapValues(data.chain_infos, (chainInfo) => ({ ...chainInfo, is_contract: true, coin_balance: '0' })) }); @@ -59,7 +62,7 @@ const SearchResultItemToken = ({ data, chain, isMobile }: Props) => { _groupHover={{ color: 'inherit' }} > { isMobile ? shortenString(data.address_hash) : ( - + ) } { isVerified && } @@ -71,8 +74,8 @@ const SearchResultItemToken = ({ data, chain, isMobile }: Props) => { ml="auto" maxW={{ base: '60%', lg: 'unset' }} > - { (data.type as string) === 'ERC-20' && data.exchange_rate && `$${ Number(data.exchange_rate).toLocaleString() }` } - { (data.type as string) !== 'ERC-20' && data.total_supply && `Items ${ Number(data.total_supply).toLocaleString() }` } + { isFungibleToken && data.exchange_rate && `$${ Number(data.exchange_rate).toLocaleString() }` } + { !isFungibleToken && data.total_supply && `Items ${ Number(data.total_supply).toLocaleString() }` } diff --git a/src/features/multichain/pages/search-results/useSearchQuery.tsx b/src/features/multichain/pages/search-results/useSearchQuery.ts similarity index 74% rename from src/features/multichain/pages/search-results/useSearchQuery.tsx rename to src/features/multichain/pages/search-results/useSearchQuery.ts index 126a1ee9068..8dd90ff2931 100644 --- a/src/features/multichain/pages/search-results/useSearchQuery.tsx +++ b/src/features/multichain/pages/search-results/useSearchQuery.ts @@ -6,7 +6,7 @@ import React from 'react'; import useApiInfiniteQuery from 'src/api/hooks/useApiInfiniteQuery'; import useApiQuery from 'src/api/hooks/useApiQuery'; -import useDebounce from 'src/shared/hooks/useDebounce'; +import { useDebouncedFilterChange } from 'src/shared/pagination/useDebouncedFilterChange'; import getQueryParamString from 'src/shared/router/get-query-param-string'; interface Props { @@ -19,9 +19,8 @@ export default function useSearchQuery({ chainId }: Props) { const initialValue = q.current; const checkRedirect = getQueryParamString(router.query.redirect) === 'true'; - const [ searchTerm, setSearchTerm ] = React.useState(initialValue); - - const debouncedSearchTerm = useDebounce(searchTerm, 300); + const appliedSearchTerm = getQueryParamString(router.query.q); + const [ searchTerm, setSearchTerm ] = React.useState(appliedSearchTerm); const checkRedirectQuery = useApiQuery('multichainAggregator:search_check_redirect', { // on search result page we check redirect only once on mount @@ -31,46 +30,50 @@ export default function useSearchQuery({ chainId }: Props) { const addressesQuery = useApiInfiniteQuery({ resourceName: 'multichainAggregator:search_addresses', - queryParams: { q: debouncedSearchTerm, chain_id: chainId }, + queryParams: { q: appliedSearchTerm, chain_id: chainId }, }); const tokensQuery = useApiInfiniteQuery({ resourceName: 'multichainAggregator:search_tokens', - queryParams: { q: debouncedSearchTerm, chain_id: chainId }, + queryParams: { q: appliedSearchTerm, chain_id: chainId }, }); const blockNumbersQuery = useApiInfiniteQuery({ resourceName: 'multichainAggregator:search_block_numbers', - queryParams: { q: debouncedSearchTerm, chain_id: chainId }, + queryParams: { q: appliedSearchTerm, chain_id: chainId }, }); const blocksQuery = useApiInfiniteQuery({ resourceName: 'multichainAggregator:search_blocks', - queryParams: { q: debouncedSearchTerm, chain_id: chainId }, + queryParams: { q: appliedSearchTerm, chain_id: chainId }, }); const nftsQuery = useApiInfiniteQuery({ resourceName: 'multichainAggregator:search_nfts', - queryParams: { q: debouncedSearchTerm, chain_id: chainId }, + queryParams: { q: appliedSearchTerm, chain_id: chainId }, }); const transactionsQuery = useApiInfiniteQuery({ resourceName: 'multichainAggregator:search_transactions', - queryParams: { q: debouncedSearchTerm, chain_id: chainId }, + queryParams: { q: appliedSearchTerm, chain_id: chainId }, }); const domainsQuery = useApiInfiniteQuery({ resourceName: 'multichainAggregator:search_domains', - queryParams: { q: debouncedSearchTerm, chain_id: chainId }, + queryParams: { q: appliedSearchTerm, chain_id: chainId }, }); - const handleSearchTermChange = React.useCallback((value: string) => { - setSearchTerm(value); + const applySearchTerm = useDebouncedFilterChange((value) => { router.push({ pathname: router.pathname, query: { ...router.query, q: value }, - }); - }, [ router ]); + }, undefined, { shallow: true }); + }); + + const handleSearchTermChange = React.useCallback((value: string) => { + setSearchTerm(value); + applySearchTerm(value); + }, [ applySearchTerm ]); const handleSubmit = React.useCallback((event: React.FormEvent) => { event.preventDefault(); @@ -88,14 +91,14 @@ export default function useSearchQuery({ chainId }: Props) { return React.useMemo(() => ({ searchTerm, - debouncedSearchTerm, + appliedSearchTerm, handleSearchTermChange, handleSubmit, queries, checkRedirectQuery, }), [ queries, - debouncedSearchTerm, + appliedSearchTerm, handleSearchTermChange, handleSubmit, searchTerm, diff --git a/src/features/multichain/pages/search-results/useSearchRedirect.tsx b/src/features/multichain/pages/search-results/useSearchRedirect.ts similarity index 100% rename from src/features/multichain/pages/search-results/useSearchRedirect.tsx rename to src/features/multichain/pages/search-results/useSearchRedirect.ts diff --git a/src/features/multichain/pages/token-transfers/MultichainTokenTransfers.tsx b/src/features/multichain/pages/token-transfers/MultichainTokenTransfers.tsx index e2369430330..c900c45b01c 100644 --- a/src/features/multichain/pages/token-transfers/MultichainTokenTransfers.tsx +++ b/src/features/multichain/pages/token-transfers/MultichainTokenTransfers.tsx @@ -16,6 +16,7 @@ import TokenTransfersCrossChain from 'src/features/cross-chain-txs/pages/token-t import multichainConfig from 'src/features/multichain/chains-config'; import ChainSelect from 'src/features/multichain/components/ChainSelect'; import { MultichainProvider } from 'src/features/multichain/context'; +import { useChainValue } from 'src/features/multichain/hooks/useChainValue'; import config from 'src/config'; import PopoverFilter from 'src/shared/filters/PopoverFilter'; @@ -48,12 +49,12 @@ const MultichainTokenTransfers = () => { const isLocalTab = tab === 'local' || !tab; - const queryLocal = useTokenTransfersQuery({ enabled: isLocalTab, isMultichain: true }); - const chainId = queryLocal.query.chainValue?.[0]; - const chainData = multichainConfig()?.chains.find(chain => chain.id === chainId); + const { chainValue, chain, onChainValueChange } = useChainValue(); + const queryLocal = useTokenTransfersQuery({ enabled: isLocalTab, chain }); + const chainId = chain?.id; const handleChainValueChange = React.useCallback(({ value }: { value: Array }) => { - queryLocal.query.onChainValueChange({ value }); + onChainValueChange({ value }); const chainConfig = multichainConfig()?.chains.find(chain => chain.id === value[0]); const tokenTypes = getTokenFilterValue(router.query.type, chainConfig?.app_config); if (tokenTypes) { @@ -62,7 +63,7 @@ const MultichainTokenTransfers = () => { queryLocal.onTokenTypesChange(chainTokenTypes); } } - }, [ queryLocal, router.query.type ]); + }, [ onChainValueChange, queryLocal, router.query.type ]); const tabs: Array = React.useMemo(() => { return [ @@ -93,7 +94,7 @@ const MultichainTokenTransfers = () => { onChange={ queryLocal.onTokenTypesChange } defaultValue={ queryLocal.typeFilter } category="all" - chainConfig={ chainData?.app_config } + chainConfig={ chain?.app_config } /> ); @@ -102,7 +103,7 @@ const MultichainTokenTransfers = () => { <> { !isMobile && filter } diff --git a/src/features/multichain/pages/token-transfers/MultichainTokenTransfersLocal.tsx b/src/features/multichain/pages/token-transfers/MultichainTokenTransfersLocal.tsx index 59d4a9a3f9c..aa508d20aae 100644 --- a/src/features/multichain/pages/token-transfers/MultichainTokenTransfersLocal.tsx +++ b/src/features/multichain/pages/token-transfers/MultichainTokenTransfersLocal.tsx @@ -1,15 +1,12 @@ // SPDX-License-Identifier: LicenseRef-Blockscout -import { Box } from '@chakra-ui/react'; import React from 'react'; import type { TokenType } from 'src/slices/token/types/api'; import ActionBar from 'src/shell/page/action-bar/ActionBar'; -import TokenTransfersListItem from 'src/slices/token-transfer/pages/index/TokenTransfersListItem'; import TokenTransfersTable from 'src/slices/token-transfer/pages/index/TokenTransfersTable'; -import { getTokenTransferKey } from 'src/slices/token-transfer/utils/get-token-transfer-key'; import TokenTypeFilter from 'src/slices/token/components/TokenTypeFilter'; import { useMultichainContext } from 'src/features/multichain/context'; @@ -17,14 +14,15 @@ import { useMultichainContext } from 'src/features/multichain/context'; import PopoverFilter from 'src/shared/filters/PopoverFilter'; import useIsMobile from 'src/shared/hooks/useIsMobile'; import DataList from 'src/shared/lists/DataList'; -import useLazyRenderedList from 'src/shared/lists/useLazyRenderedList'; import Pagination from 'src/shared/pagination/Pagination'; -import type { QueryWithPagesResult } from 'src/shared/pagination/useQueryWithPages'; +import type { ApiPaginatedQueryResult } from 'src/shared/pagination/useApiPaginatedQuery'; + +import { TableContainerScrollable } from 'src/toolkit/chakra/table'; const ACTION_BAR_HEIGHT = 24 * 2 + 40; interface Props { - query: QueryWithPagesResult<'core:token_transfers_all'>; + query: ApiPaginatedQueryResult<'core:token_transfers_all'>; typeFilter: Array; onTokenTypesChange: (value: Array) => void; } @@ -34,11 +32,6 @@ const MultichainTokenTransfersLocal = ({ query, typeFilter, onTokenTypesChange } const isMobile = useIsMobile(); const multichainContext = useMultichainContext(); const chainData = multichainContext?.chain; - const { cutRef, renderedItemsNum } = useLazyRenderedList({ - list: query.data?.items, - isEnabled: !query.isPlaceholderData, - resetKey: query.queryHash, - }); const actionBar = isMobile && ( @@ -60,27 +53,17 @@ const MultichainTokenTransfersLocal = ({ query, typeFilter, onTokenTypesChange } itemsNum={ query.data?.items.length } emptyText="There are no token transfers." actionBar={ actionBar } + isTransitioning={ query.isTransitioning } > - - { query.data?.items.slice(0, renderedItemsNum).map((item, index) => ( - - )) } - - - + - + ); }; diff --git a/src/features/multichain/pages/tokens/MultichainTokens.tsx b/src/features/multichain/pages/tokens/MultichainTokens.tsx index 0ca63a30bf9..23c2ec625a1 100644 --- a/src/features/multichain/pages/tokens/MultichainTokens.tsx +++ b/src/features/multichain/pages/tokens/MultichainTokens.tsx @@ -1,7 +1,6 @@ // SPDX-License-Identifier: LicenseRef-Blockscout import { HStack } from '@chakra-ui/react'; -import { useRouter } from 'next/router'; import React from 'react'; import type { TokenType } from 'src/slices/token/types/api'; @@ -15,13 +14,14 @@ import { getTokenFilterValue } from 'src/slices/token/utils/list-utils'; import multichainConfig from 'src/features/multichain/chains-config'; import ChainSelect from 'src/features/multichain/components/ChainSelect'; +import { useChainValue } from 'src/features/multichain/hooks/useChainValue'; import { TOKEN } from 'src/features/multichain/stubs'; import PopoverFilter from 'src/shared/filters/PopoverFilter'; -import useDebounce from 'src/shared/hooks/useDebounce'; import useIsMobile from 'src/shared/hooks/useIsMobile'; import Pagination from 'src/shared/pagination/Pagination'; -import useQueryWithPages from 'src/shared/pagination/useQueryWithPages'; +import useApiPaginatedQuery from 'src/shared/pagination/useApiPaginatedQuery'; +import { useDebouncedFilterChange } from 'src/shared/pagination/useDebouncedFilterChange'; import { generateListStub } from 'src/shared/pagination/utils'; import getQueryParamString from 'src/shared/router/get-query-param-string'; @@ -32,31 +32,18 @@ const getChainIdFilterValue = (chainIds: Array) => { }; const MultichainTokens = () => { - const router = useRouter(); const isMobile = useIsMobile(); const chainConfigs = React.useMemo(() => { return multichainConfig()?.chains.map((chain) => chain.app_config); }, []); - const q = getQueryParamString(router.query.query); - const chainIdParam = getQueryParamString(router.query.chain_id); + const { chainValue: chainIds } = useChainValue({ withAllOption: true }); + const chainIdFilter = React.useMemo(() => getChainIdFilterValue(chainIds), [ chainIds ]); - const [ chainIds, setChainIds ] = React.useState>(chainIdParam ? [ chainIdParam ] : [ 'all' ]); - const [ searchTerm, setSearchTerm ] = React.useState(q ?? ''); - const [ tokenTypes, setTokenTypes ] = React.useState | undefined>( - getTokenFilterValue(router.query.type, chainConfigs), - ); - - const debouncedSearchTerm = useDebounce(searchTerm, 300); - - const tokensQuery = useQueryWithPages({ + const tokensQuery = useApiPaginatedQuery({ resourceName: 'multichainAggregator:tokens', - filters: { - type: tokenTypes?.join(','), - chain_id: getChainIdFilterValue(chainIds), - query: debouncedSearchTerm, - }, + queryParams: { chain_id: chainIdFilter }, options: { placeholderData: generateListStub<'multichainAggregator:tokens'>(TOKEN['token'], 50, { next_page_params: { @@ -68,32 +55,35 @@ const MultichainTokens = () => { }, }); + const searchTerm = getQueryParamString(tokensQuery.filters.query); + const typeParam = tokensQuery.filters.type; + const tokenTypes = React.useMemo(() => getTokenFilterValue(typeParam, chainConfigs), [ typeParam, chainConfigs ]); + + const { onFilterChange } = tokensQuery; + const handleTokenTypesChange = React.useCallback((value: Array) => { - tokensQuery.onFilterChange({ + onFilterChange({ type: value.join(','), - chain_id: getChainIdFilterValue(chainIds), - query: debouncedSearchTerm, + chain_id: chainIdFilter, + query: searchTerm, }); - setTokenTypes(value); - }, [ tokensQuery, chainIds, debouncedSearchTerm ]); + }, [ onFilterChange, chainIdFilter, searchTerm ]); - const handleSearchTermChange = React.useCallback((value: string) => { - tokensQuery.onFilterChange({ + const handleSearchTermChange = useDebouncedFilterChange((value) => { + onFilterChange({ type: tokenTypes?.join(','), - chain_id: getChainIdFilterValue(chainIds), + chain_id: chainIdFilter, query: value, }); - setSearchTerm(value); - }, [ tokenTypes, tokensQuery, chainIds ]); + }); const handleChainIdsChange = React.useCallback(({ value }: { value: Array }) => { - tokensQuery.onFilterChange({ + onFilterChange({ chain_id: getChainIdFilterValue(value), type: tokenTypes?.join(','), - query: debouncedSearchTerm, + query: searchTerm, }); - setChainIds(value); - }, [ tokensQuery, tokenTypes, debouncedSearchTerm ]); + }, [ onFilterChange, tokenTypes, searchTerm ]); const filter = ( diff --git a/src/features/multichain/pages/user-ops/MultichainUserOps.tsx b/src/features/multichain/pages/user-ops/MultichainUserOps.tsx index 4a4f67eda07..e228049ae22 100644 --- a/src/features/multichain/pages/user-ops/MultichainUserOps.tsx +++ b/src/features/multichain/pages/user-ops/MultichainUserOps.tsx @@ -1,6 +1,5 @@ // SPDX-License-Identifier: LicenseRef-Blockscout -import { Box } from '@chakra-ui/react'; import React from 'react'; import ActionBar, { ACTION_BAR_HEIGHT_DESKTOP } from 'src/shell/page/action-bar/ActionBar'; @@ -9,21 +8,24 @@ import PageTitle from 'src/shell/page/title/PageTitle'; import multichainConfig from 'src/features/multichain/chains-config'; import ChainSelect from 'src/features/multichain/components/ChainSelect'; import { MultichainProvider } from 'src/features/multichain/context'; -import UserOpsList from 'src/features/user-ops/pages/index/UserOpsList'; +import { useChainValue } from 'src/features/multichain/hooks/useChainValue'; import UserOpsTable from 'src/features/user-ops/pages/index/UserOpsTable'; import { USER_OPS_ITEM } from 'src/features/user-ops/stubs'; import DataList from 'src/shared/lists/DataList'; import Pagination from 'src/shared/pagination/Pagination'; -import useQueryWithPages from 'src/shared/pagination/useQueryWithPages'; +import useApiPaginatedQuery from 'src/shared/pagination/useApiPaginatedQuery'; import { generateListStub } from 'src/shared/pagination/utils'; +import { TableContainerScrollable } from 'src/toolkit/chakra/table'; + const MultichainUserOps = () => { const chains = React.useMemo(() => (multichainConfig()?.chains || []).filter(chain => chain.app_config.features.userOps.isEnabled), []); const chainIds = React.useMemo(() => chains.map(chain => chain.id).filter(Boolean), [ chains ]); + const { chainValue, chain, onChainValueChange } = useChainValue({ chainIds }); - const query = useQueryWithPages({ + const query = useApiPaginatedQuery({ resourceName: 'core:user_ops', options: { placeholderData: generateListStub<'core:user_ops'>(USER_OPS_ITEM, 50, { next_page_params: { @@ -31,42 +33,29 @@ const MultichainUserOps = () => { page_size: 50, } }), }, - isMultichain: true, - chainIds, + chain, }); - const chainConfig = chains.find(chain => chain.id === query.chainValue?.[0]); - const content = query.data?.items ? ( - - + + - - - - + ) : null; const actionBar = ( @@ -86,6 +75,7 @@ const MultichainUserOps = () => { actionBar={ actionBar } showActionBarIfError showActionBarIfEmpty + isTransitioning={ query.isTransitioning } > { content } diff --git a/src/features/multichain/stubs.ts b/src/features/multichain/stubs.ts index ff18f4ee7cf..b58d6410c39 100644 --- a/src/features/multichain/stubs.ts +++ b/src/features/multichain/stubs.ts @@ -45,6 +45,9 @@ export const TOKEN: AddressTokenItem = { type: 'ERC-20' as const, exchange_rate: '123.456', reputation: null, + ui_multiplier: null, + new_ui_multiplier: null, + ui_multiplier_effective_at: null, chain_infos: { '11155111': { holders_count: '141268', diff --git a/src/features/multichain/types/client.ts b/src/features/multichain/types/client.ts index 8d973eea4a5..6cce8f6a90d 100644 --- a/src/features/multichain/types/client.ts +++ b/src/features/multichain/types/client.ts @@ -30,6 +30,9 @@ export interface AggregatedTokenInfo extends Pick) => { @@ -45,7 +46,7 @@ const SearchBarSuggestCluster = ({ data, searchTerm, addressFormat }: ItemsProps whiteSpace="nowrap" color="text.secondary" > - + ); diff --git a/src/features/name-services/clusters/pages/details/__screenshots__/Cluster.pw.tsx_default_Cluster-Details-Page-mainnet-cluster-details-mobile-1.png b/src/features/name-services/clusters/pages/details/__screenshots__/Cluster.pw.tsx_default_Cluster-Details-Page-mainnet-cluster-details-mobile-1.png index 6786f75c1b4..0c7b06de5b3 100644 Binary files a/src/features/name-services/clusters/pages/details/__screenshots__/Cluster.pw.tsx_default_Cluster-Details-Page-mainnet-cluster-details-mobile-1.png and b/src/features/name-services/clusters/pages/details/__screenshots__/Cluster.pw.tsx_default_Cluster-Details-Page-mainnet-cluster-details-mobile-1.png differ diff --git a/src/features/name-services/clusters/pages/details/__screenshots__/Cluster.pw.tsx_default_Cluster-Details-Page-testnet-cluster-details-mobile-1.png b/src/features/name-services/clusters/pages/details/__screenshots__/Cluster.pw.tsx_default_Cluster-Details-Page-testnet-cluster-details-mobile-1.png index e7c1d4e4cdf..098a465508c 100644 Binary files a/src/features/name-services/clusters/pages/details/__screenshots__/Cluster.pw.tsx_default_Cluster-Details-Page-testnet-cluster-details-mobile-1.png and b/src/features/name-services/clusters/pages/details/__screenshots__/Cluster.pw.tsx_default_Cluster-Details-Page-testnet-cluster-details-mobile-1.png differ diff --git a/src/features/name-services/clusters/pages/details/__screenshots__/Cluster.pw.tsx_mobile_Cluster-Details-Page-mainnet-cluster-details-mobile-1.png b/src/features/name-services/clusters/pages/details/__screenshots__/Cluster.pw.tsx_mobile_Cluster-Details-Page-mainnet-cluster-details-mobile-1.png index da9979c2ad2..ff93a006fed 100644 Binary files a/src/features/name-services/clusters/pages/details/__screenshots__/Cluster.pw.tsx_mobile_Cluster-Details-Page-mainnet-cluster-details-mobile-1.png and b/src/features/name-services/clusters/pages/details/__screenshots__/Cluster.pw.tsx_mobile_Cluster-Details-Page-mainnet-cluster-details-mobile-1.png differ diff --git a/src/features/name-services/clusters/pages/details/__screenshots__/Cluster.pw.tsx_mobile_Cluster-Details-Page-testnet-cluster-details-mobile-1.png b/src/features/name-services/clusters/pages/details/__screenshots__/Cluster.pw.tsx_mobile_Cluster-Details-Page-testnet-cluster-details-mobile-1.png index 76a022fce7b..f48a89c842b 100644 Binary files a/src/features/name-services/clusters/pages/details/__screenshots__/Cluster.pw.tsx_mobile_Cluster-Details-Page-testnet-cluster-details-mobile-1.png and b/src/features/name-services/clusters/pages/details/__screenshots__/Cluster.pw.tsx_mobile_Cluster-Details-Page-testnet-cluster-details-mobile-1.png differ diff --git a/src/features/name-services/clusters/pages/index/Clusters.tsx b/src/features/name-services/clusters/pages/index/Clusters.tsx index f7b90285ead..29c1ea47c25 100644 --- a/src/features/name-services/clusters/pages/index/Clusters.tsx +++ b/src/features/name-services/clusters/pages/index/Clusters.tsx @@ -1,6 +1,6 @@ // SPDX-License-Identifier: LicenseRef-Blockscout -import { Box, Text } from '@chakra-ui/react'; +import { Text } from '@chakra-ui/react'; import { useRouter } from 'next/router'; import React, { useCallback } from 'react'; @@ -25,11 +25,10 @@ import getQueryParamString from 'src/shared/router/get-query-param-string'; import { useQueryParams } from 'src/shared/router/useQueryParams'; import { Link } from 'src/toolkit/chakra/link'; +import { TableContainerScrollable } from 'src/toolkit/chakra/table'; import ClustersActionBar from './ClustersActionBar'; -import ClustersDirectoryList from './ClustersDirectoryList'; import ClustersDirectoryTable from './ClustersDirectoryTable'; -import ClustersLeaderboardList from './ClustersLeaderboardList'; import ClustersLeaderboardTable from './ClustersLeaderboardTable'; const Clusters = () => { @@ -91,42 +90,24 @@ const Clusters = () => { const resetKey = `${ viewMode }:${ debouncedSearchTerm }:${ page }`; const content = ( - <> - - { showDirectoryView ? ( - - ) : ( - - ) } - - - { showDirectoryView ? ( - - ) : ( - - ) } - - + + { showDirectoryView ? ( + + ) : ( + + ) } + ); const actionBar = ( diff --git a/src/features/name-services/clusters/pages/index/ClustersDirectoryList.tsx b/src/features/name-services/clusters/pages/index/ClustersDirectoryList.tsx deleted file mode 100644 index 824e685ec08..00000000000 --- a/src/features/name-services/clusters/pages/index/ClustersDirectoryList.tsx +++ /dev/null @@ -1,39 +0,0 @@ -// SPDX-License-Identifier: LicenseRef-Blockscout - -import { Box } from '@chakra-ui/react'; -import React from 'react'; - -import type { ClustersDirectoryObject } from 'src/features/name-services/clusters/types/api'; - -import useLazyRenderedList from 'src/shared/lists/useLazyRenderedList'; - -import ClustersDirectoryListItem from './ClustersDirectoryListItem'; - -interface Props { - data: Array; - isLoading?: boolean; - isClusterDetailsLoading?: boolean; - resetKey?: string; -} - -const ClustersDirectoryList = ({ data, isLoading, isClusterDetailsLoading, resetKey }: Props) => { - const { cutRef, renderedItemsNum } = useLazyRenderedList({ list: data, isEnabled: !isLoading, resetKey }); - - return ( - <> - - { data.slice(0, renderedItemsNum).map((item, index) => ( - - )) } - - - - ); -}; - -export default React.memo(ClustersDirectoryList); diff --git a/src/features/name-services/clusters/pages/index/ClustersDirectoryListItem.tsx b/src/features/name-services/clusters/pages/index/ClustersDirectoryListItem.tsx deleted file mode 100644 index dbfdbecef71..00000000000 --- a/src/features/name-services/clusters/pages/index/ClustersDirectoryListItem.tsx +++ /dev/null @@ -1,71 +0,0 @@ -// SPDX-License-Identifier: LicenseRef-Blockscout - -import React from 'react'; - -import type { ClustersDirectoryObject } from 'src/features/name-services/clusters/types/api'; - -import AddressEntity from 'src/slices/address/components/entity/AddressEntity'; -import { isEvmAddress } from 'src/slices/address/utils/is-evm-address'; - -import ClustersEntity from 'src/features/name-services/clusters/components/ClustersEntity'; - -import dayjs from 'src/shared/date-and-time/dayjs'; -import Time from 'src/shared/date-and-time/Time'; -import ListItemMobileGrid from 'src/shared/lists/ListItemMobileGrid'; - -import { Skeleton } from 'src/toolkit/chakra/skeleton'; - -interface Props { - item: ClustersDirectoryObject; - isLoading?: boolean; - isClusterDetailsLoading?: boolean; -} - -const ClustersDirectoryListItem = ({ item, isLoading, isClusterDetailsLoading }: Props) => { - return ( - - - Cluster name - - - - - - - Address - - - { item.owner && ( - - ) } - { !item.owner && — } - - - - Joined - - - - - - - - Active chains - - - - { (item.chainIds?.length || 1) } { (item.chainIds?.length || 1) === 1 ? 'chain' : 'chains' } - - - - ); -}; - -export default React.memo(ClustersDirectoryListItem); diff --git a/src/features/name-services/clusters/pages/index/ClustersDirectoryTable.tsx b/src/features/name-services/clusters/pages/index/ClustersDirectoryTable.tsx index 5a06d3fb76a..79e29b9b486 100644 --- a/src/features/name-services/clusters/pages/index/ClustersDirectoryTable.tsx +++ b/src/features/name-services/clusters/pages/index/ClustersDirectoryTable.tsx @@ -26,7 +26,7 @@ const ClustersDirectoryTable = ({ data, isLoading, top, isClusterDetailsLoading, return ( - + Cluster name diff --git a/src/features/name-services/clusters/pages/index/ClustersLeaderboardList.tsx b/src/features/name-services/clusters/pages/index/ClustersLeaderboardList.tsx deleted file mode 100644 index cc8137b7ebe..00000000000 --- a/src/features/name-services/clusters/pages/index/ClustersLeaderboardList.tsx +++ /dev/null @@ -1,37 +0,0 @@ -// SPDX-License-Identifier: LicenseRef-Blockscout - -import { Box } from '@chakra-ui/react'; -import React from 'react'; - -import type { ClustersLeaderboardObject } from 'src/features/name-services/clusters/types/api'; - -import useLazyRenderedList from 'src/shared/lists/useLazyRenderedList'; - -import ClustersLeaderboardListItem from './ClustersLeaderboardListItem'; - -interface Props { - data: Array; - isLoading?: boolean; - resetKey?: string; -} - -const ClustersLeaderboardList = ({ data, isLoading, resetKey }: Props) => { - const { cutRef, renderedItemsNum } = useLazyRenderedList({ list: data, isEnabled: !isLoading, resetKey }); - - return ( - <> - - { data.slice(0, renderedItemsNum).map((item, index) => ( - - )) } - - - - ); -}; - -export default React.memo(ClustersLeaderboardList); diff --git a/src/features/name-services/clusters/pages/index/ClustersLeaderboardListItem.tsx b/src/features/name-services/clusters/pages/index/ClustersLeaderboardListItem.tsx deleted file mode 100644 index 9f471d25ec8..00000000000 --- a/src/features/name-services/clusters/pages/index/ClustersLeaderboardListItem.tsx +++ /dev/null @@ -1,67 +0,0 @@ -// SPDX-License-Identifier: LicenseRef-Blockscout - -import React from 'react'; - -import type { ClustersLeaderboardObject } from 'src/features/name-services/clusters/types/api'; - -import ClustersEntity from 'src/features/name-services/clusters/components/ClustersEntity'; - -import ListItemMobileGrid from 'src/shared/lists/ListItemMobileGrid'; - -import { Skeleton } from 'src/toolkit/chakra/skeleton'; - -interface Props { - item: ClustersLeaderboardObject; - isLoading?: boolean; -} - -const ClustersLeaderboardListItem = ({ item, isLoading }: Props) => { - return ( - - - Rank - - - - #{ item.rank } - - - - - Cluster name - - - - - - - Names - - - - { item.nameCount } - - - - - Backing - - - - { (parseFloat(item.totalWeiAmount) / 1e18).toFixed(2) } ETH - - - - - Network presence - - - - { item.chainIds.length } { item.chainIds.length === 1 ? 'chain' : 'chains' } - - - - ); -}; - -export default React.memo(ClustersLeaderboardListItem); diff --git a/src/features/name-services/clusters/pages/index/ClustersLeaderboardTable.tsx b/src/features/name-services/clusters/pages/index/ClustersLeaderboardTable.tsx index 8ae84cd5f24..3bea8530d37 100644 --- a/src/features/name-services/clusters/pages/index/ClustersLeaderboardTable.tsx +++ b/src/features/name-services/clusters/pages/index/ClustersLeaderboardTable.tsx @@ -21,14 +21,14 @@ const ClustersLeaderboardTable = ({ data, isLoading, top, resetKey }: Props) => const { cutRef, renderedItemsNum } = useLazyRenderedList({ list: data, isEnabled: !isLoading, resetKey }); return ( - + - Rank - Cluster name - Names - Total backing - Active chains + Rank + Cluster name + Names + Total backing + Active chains diff --git a/src/features/name-services/common/pages/NameServices.pw.tsx b/src/features/name-services/common/pages/NameServices.pw.tsx index b9489a44359..9317f00b9ec 100644 --- a/src/features/name-services/common/pages/NameServices.pw.tsx +++ b/src/features/name-services/common/pages/NameServices.pw.tsx @@ -5,7 +5,7 @@ import { clustersLeaderboardMock } from 'src/features/name-services/clusters/moc import * as ensDomainMock from 'src/features/name-services/domains/mocks/domain'; import { ENVS_MAP } from 'playwright/fixtures/mockEnvs'; -import { test, expect, devices } from 'playwright/lib'; +import { test, expect } from 'playwright/lib'; import NameServices from './NameServices'; @@ -50,15 +50,6 @@ test.describe('domains', () => { await expect(component).toHaveScreenshot({ timeout: 10_000 }); }); - test.describe('mobile', () => { - test.use({ viewport: devices['iPhone 13 Pro'].viewport }); - - test('default view', async({ render }) => { - const component = await render(, { hooksConfig }); - await expect(component).toHaveScreenshot({ timeout: 10_000 }); - }); - }); - test('filters', async({ render, page }) => { const component = await render(, { hooksConfig }); @@ -125,18 +116,4 @@ test.describe('directories', () => { const component = await render(, { hooksConfig: hooksConfig.leaderboard }); await expect(component).toHaveScreenshot(); }); - - test.describe('mobile', () => { - test.use({ viewport: devices['iPhone 13 Pro'].viewport }); - - test('directory view', async({ render }) => { - const component = await render(, { hooksConfig: hooksConfig.directories }); - await expect(component).toHaveScreenshot(); - }); - - test('leaderboard view', async({ render }) => { - const component = await render(, { hooksConfig: hooksConfig.leaderboard }); - await expect(component).toHaveScreenshot(); - }); - }); }); diff --git a/src/features/name-services/common/pages/__screenshots__/NameServices.pw.tsx_default_directories-directory-view-1.png b/src/features/name-services/common/pages/__screenshots__/NameServices.pw.tsx_default_directories-directory-view-1.png index b1142c53a92..b8a24629b78 100644 Binary files a/src/features/name-services/common/pages/__screenshots__/NameServices.pw.tsx_default_directories-directory-view-1.png and b/src/features/name-services/common/pages/__screenshots__/NameServices.pw.tsx_default_directories-directory-view-1.png differ diff --git a/src/features/name-services/common/pages/__screenshots__/NameServices.pw.tsx_default_directories-leaderboard-view-1.png b/src/features/name-services/common/pages/__screenshots__/NameServices.pw.tsx_default_directories-leaderboard-view-1.png index 87488a035f6..19e173bd78c 100644 Binary files a/src/features/name-services/common/pages/__screenshots__/NameServices.pw.tsx_default_directories-leaderboard-view-1.png and b/src/features/name-services/common/pages/__screenshots__/NameServices.pw.tsx_default_directories-leaderboard-view-1.png differ diff --git a/src/features/name-services/common/pages/__screenshots__/NameServices.pw.tsx_default_directories-mobile-directory-view-1.png b/src/features/name-services/common/pages/__screenshots__/NameServices.pw.tsx_default_directories-mobile-directory-view-1.png deleted file mode 100644 index 5b38481c99b..00000000000 Binary files a/src/features/name-services/common/pages/__screenshots__/NameServices.pw.tsx_default_directories-mobile-directory-view-1.png and /dev/null differ diff --git a/src/features/name-services/common/pages/__screenshots__/NameServices.pw.tsx_default_directories-mobile-leaderboard-view-1.png b/src/features/name-services/common/pages/__screenshots__/NameServices.pw.tsx_default_directories-mobile-leaderboard-view-1.png deleted file mode 100644 index 160a7ebfe11..00000000000 Binary files a/src/features/name-services/common/pages/__screenshots__/NameServices.pw.tsx_default_directories-mobile-leaderboard-view-1.png and /dev/null differ diff --git a/src/features/name-services/common/pages/__screenshots__/NameServices.pw.tsx_default_domains-default-view-1.png b/src/features/name-services/common/pages/__screenshots__/NameServices.pw.tsx_default_domains-default-view-1.png index 9a1b572a4ac..c000be969f7 100644 Binary files a/src/features/name-services/common/pages/__screenshots__/NameServices.pw.tsx_default_domains-default-view-1.png and b/src/features/name-services/common/pages/__screenshots__/NameServices.pw.tsx_default_domains-default-view-1.png differ diff --git a/src/features/name-services/common/pages/__screenshots__/NameServices.pw.tsx_default_domains-filters-1.png b/src/features/name-services/common/pages/__screenshots__/NameServices.pw.tsx_default_domains-filters-1.png index dbcda4bf79a..c14b567a5b9 100644 Binary files a/src/features/name-services/common/pages/__screenshots__/NameServices.pw.tsx_default_domains-filters-1.png and b/src/features/name-services/common/pages/__screenshots__/NameServices.pw.tsx_default_domains-filters-1.png differ diff --git a/src/features/name-services/common/pages/__screenshots__/NameServices.pw.tsx_default_domains-mobile-default-view-1.png b/src/features/name-services/common/pages/__screenshots__/NameServices.pw.tsx_default_domains-mobile-default-view-1.png deleted file mode 100644 index 191ab012129..00000000000 Binary files a/src/features/name-services/common/pages/__screenshots__/NameServices.pw.tsx_default_domains-mobile-default-view-1.png and /dev/null differ diff --git a/src/features/name-services/domains/components/EnsEntity.pw.tsx b/src/features/name-services/domains/components/EnsEntity.pw.tsx index b4f6c02de2e..269205597ad 100644 --- a/src/features/name-services/domains/components/EnsEntity.pw.tsx +++ b/src/features/name-services/domains/components/EnsEntity.pw.tsx @@ -81,7 +81,16 @@ test.describe('tooltip test', () => { />, ); - await component.getByAltText(`${ domainMock.protocolA.title } protocol icon`).first().hover(); + const protocolIcon = component.getByAltText(`${ domainMock.protocolA.title } protocol icon`).first(); + await protocolIcon.hover(); + + // The first hover mounts the lazy tooltip, which remounts the icon and briefly swaps it with a skeleton. + // With a still cursor the browser may leave the hover state on the removed skeleton, so the tooltip + // never opens; re-entering the settled icon opens it through a regular pointer event. + await expect(protocolIcon).toHaveAttribute('data-part', 'trigger'); + await expect(protocolIcon).toBeVisible(); + await page.mouse.move(599, 399); + await protocolIcon.hover(); await expect(page.getByText(domainMock.protocolA.description)).toBeVisible(); await expect(page).toHaveScreenshot(); diff --git a/src/features/name-services/domains/components/SearchBarSuggestDomain.tsx b/src/features/name-services/domains/components/SearchBarSuggestDomain.tsx index 76854cd67af..0d7b4c3a6ee 100644 --- a/src/features/name-services/domains/components/SearchBarSuggestDomain.tsx +++ b/src/features/name-services/domains/components/SearchBarSuggestDomain.tsx @@ -12,10 +12,11 @@ import { toBech32Address } from 'src/slices/address/utils/bech32'; import * as EnsEntity from 'src/features/name-services/domains/components/EnsEntity'; import dayjs from 'src/shared/date-and-time/dayjs'; -import HashStringShortenDynamic from 'src/shared/texts/HashStringShortenDynamic'; import highlightText from 'src/shared/texts/highlight-text'; import SpriteIcon from 'src/sprite/SpriteIcon'; +import { Truncate } from 'src/toolkit/components/truncation/Truncate'; + const SearchBarSuggestDomain = ({ data, isMobile, searchTerm, addressFormat }: ItemsProps) => { @@ -51,7 +52,7 @@ const SearchBarSuggestDomain = whiteSpace="nowrap" color="text.secondary" > - + ) : null; diff --git a/src/features/name-services/domains/components/__screenshots__/EnsEntity.pw.tsx_default_loading-1.png b/src/features/name-services/domains/components/__screenshots__/EnsEntity.pw.tsx_default_loading-1.png index 2c83d443c41..1f3bdb94f8c 100644 Binary files a/src/features/name-services/domains/components/__screenshots__/EnsEntity.pw.tsx_default_loading-1.png and b/src/features/name-services/domains/components/__screenshots__/EnsEntity.pw.tsx_default_loading-1.png differ diff --git a/src/features/name-services/domains/hooks/useCheckDomainNameParam.ts b/src/features/name-services/domains/hooks/useCheckDomainNameParam.ts index c4135244ff8..4518f6074bd 100644 --- a/src/features/name-services/domains/hooks/useCheckDomainNameParam.ts +++ b/src/features/name-services/domains/hooks/useCheckDomainNameParam.ts @@ -15,7 +15,6 @@ export default function useCheckDomainNameParam(hashOrDomainName: string) { const router = useRouter(); const maybeDomainName = DOMAIN_NAME_REGEXP.test(hashOrDomainName); const isQueryEnabled = feature.isEnabled && feature.ens.isEnabled && maybeDomainName; - const [ isLoading, setIsLoading ] = React.useState(isQueryEnabled); const domainLookupQuery = useApiQuery('bens:domains_lookup', { queryParams: { @@ -28,24 +27,13 @@ export default function useCheckDomainNameParam(hashOrDomainName: string) { }, }); - React.useEffect(() => { - if (domainLookupQuery.isPending) { - return; - } + const firstDomainAddress = domainLookupQuery.data?.items[0]?.resolved_address?.hash; - const firstDomainAddress = domainLookupQuery.data?.items[0]?.resolved_address?.hash; + React.useEffect(() => { if (firstDomainAddress) { router.replace({ pathname: '/address/[hash]', query: { hash: firstDomainAddress } }); - } else { - setIsLoading(false); - } - }, [ domainLookupQuery.isPending, domainLookupQuery.data, router ]); - - React.useEffect(() => { - if (!maybeDomainName) { - setIsLoading(false); } - }, [ maybeDomainName ]); + }, [ firstDomainAddress, router ]); - return isLoading; + return isQueryEnabled && (domainLookupQuery.isPending || Boolean(firstDomainAddress)); } diff --git a/src/features/name-services/domains/pages/details/NameDomain.pw.tsx b/src/features/name-services/domains/pages/details/NameDomain.pw.tsx index 076a978edea..1d4e1a22c66 100644 --- a/src/features/name-services/domains/pages/details/NameDomain.pw.tsx +++ b/src/features/name-services/domains/pages/details/NameDomain.pw.tsx @@ -27,7 +27,7 @@ test('details tab', async({ render, mockTextAd, mockApiResponse, mockAssetRespon await expect(component).toHaveScreenshot(); }); -test('history tab +@mobile', async({ render, mockTextAd, mockApiResponse, mockAssetResponse }) => { +test('history tab', async({ render, mockTextAd, mockApiResponse, mockAssetResponse }) => { await mockTextAd(); await mockApiResponse('bens:domain_info', ensDomainMock.ensDomainA, { pathParams: { name: ensDomainMock.ensDomainA.name }, diff --git a/src/features/name-services/domains/pages/details/__screenshots__/NameDomain.pw.tsx_default_details-tab-1.png b/src/features/name-services/domains/pages/details/__screenshots__/NameDomain.pw.tsx_default_details-tab-1.png index c0cca32a9ed..5b011366a09 100644 Binary files a/src/features/name-services/domains/pages/details/__screenshots__/NameDomain.pw.tsx_default_details-tab-1.png and b/src/features/name-services/domains/pages/details/__screenshots__/NameDomain.pw.tsx_default_details-tab-1.png differ diff --git a/src/features/name-services/domains/pages/details/__screenshots__/NameDomain.pw.tsx_default_history-tab-1.png b/src/features/name-services/domains/pages/details/__screenshots__/NameDomain.pw.tsx_default_history-tab-1.png new file mode 100644 index 00000000000..42e61d92648 Binary files /dev/null and b/src/features/name-services/domains/pages/details/__screenshots__/NameDomain.pw.tsx_default_history-tab-1.png differ diff --git a/src/features/name-services/domains/pages/details/__screenshots__/NameDomain.pw.tsx_default_history-tab-mobile-1.png b/src/features/name-services/domains/pages/details/__screenshots__/NameDomain.pw.tsx_default_history-tab-mobile-1.png deleted file mode 100644 index e11042fdc84..00000000000 Binary files a/src/features/name-services/domains/pages/details/__screenshots__/NameDomain.pw.tsx_default_history-tab-mobile-1.png and /dev/null differ diff --git a/src/features/name-services/domains/pages/details/__screenshots__/NameDomain.pw.tsx_mobile_history-tab-mobile-1.png b/src/features/name-services/domains/pages/details/__screenshots__/NameDomain.pw.tsx_mobile_history-tab-mobile-1.png deleted file mode 100644 index 42cfac40fc3..00000000000 Binary files a/src/features/name-services/domains/pages/details/__screenshots__/NameDomain.pw.tsx_mobile_history-tab-mobile-1.png and /dev/null differ diff --git a/src/features/name-services/domains/pages/details/history/NameDomainHistory.tsx b/src/features/name-services/domains/pages/details/history/NameDomainHistory.tsx index 3e554088f97..44f7dd6dbed 100644 --- a/src/features/name-services/domains/pages/details/history/NameDomainHistory.tsx +++ b/src/features/name-services/domains/pages/details/history/NameDomainHistory.tsx @@ -1,6 +1,6 @@ // SPDX-License-Identifier: LicenseRef-Blockscout -import { Box } from '@chakra-ui/react'; +import { createListCollection } from '@chakra-ui/react'; import { useRouter } from 'next/router'; import React from 'react'; @@ -8,29 +8,39 @@ import type * as bens from '@blockscout/bens-types'; import useApiQuery from 'src/api/hooks/useApiQuery'; +import ActionBar from 'src/shell/page/action-bar/ActionBar'; + import { ENS_DOMAIN_EVENT } from 'src/features/name-services/domains/stubs'; import config from 'src/config'; +import useIsMobile from 'src/shared/hooks/useIsMobile'; import DataList from 'src/shared/lists/DataList'; import getQueryParamString from 'src/shared/router/get-query-param-string'; +import Sort from 'src/shared/sort/Sort'; + +import { TableContainerScrollable } from 'src/toolkit/chakra/table'; -import NameDomainHistoryList from './NameDomainHistoryList'; import NameDomainHistoryTable from './NameDomainHistoryTable'; -import { getNextSortValue, type Sort, type SortField } from './utils'; +import { getNextSortValue, SORT_OPTIONS, type Sort as SortValue, type SortField } from './utils'; const feature = config.features.nameServices; const availableProtocols = feature.isEnabled && feature.ens.isEnabled ? feature.ens.protocols : []; +const sortCollection = createListCollection({ + items: SORT_OPTIONS, +}); + interface Props { domain: bens.DetailedDomain | undefined; } const NameDomainHistory = ({ domain }: Props) => { const router = useRouter(); + const isMobile = useIsMobile(); const domainName = getQueryParamString(router.query.name); const protocolId = getQueryParamString(router.query.protocol_id) || availableProtocols[0]; - const [ sort, setSort ] = React.useState('default'); + const [ sort, setSort ] = React.useState('default'); const { isPlaceholderData, isError, data } = useApiQuery('bens:domain_events', { pathParams: { name: domainName }, @@ -52,25 +62,33 @@ const NameDomainHistory = ({ domain }: Props) => { } }, [ isPlaceholderData ]); + const handleSortValueChange = React.useCallback(({ value }: { value: Array }) => { + setSort(value[0] as SortValue); + }, []); + const content = data?.items ? ( - <> - - - - - - - + + + + ) : null; + + const actionBar = isMobile ? ( + + + ) : null; return ( @@ -78,6 +96,7 @@ const NameDomainHistory = ({ domain }: Props) => { isError={ isError } itemsNum={ data?.items.length } emptyText="There are no events for this domain." + actionBar={ actionBar } > { content } diff --git a/src/features/name-services/domains/pages/details/history/NameDomainHistoryList.tsx b/src/features/name-services/domains/pages/details/history/NameDomainHistoryList.tsx deleted file mode 100644 index f10d859561d..00000000000 --- a/src/features/name-services/domains/pages/details/history/NameDomainHistoryList.tsx +++ /dev/null @@ -1,31 +0,0 @@ -// SPDX-License-Identifier: LicenseRef-Blockscout - -import { Box } from '@chakra-ui/react'; -import React from 'react'; - -import type * as bens from '@blockscout/bens-types'; - -import NameDomainHistoryListItem from './NameDomainHistoryListItem'; - -interface Props { - items: Array; - domain: bens.DetailedDomain | undefined; - isLoading?: boolean; -} - -const NameDomainHistoryList = ({ items, domain, isLoading }: Props) => { - return ( - - { items.map((item, index) => ( - - )) } - - ); -}; - -export default React.memo(NameDomainHistoryList); diff --git a/src/features/name-services/domains/pages/details/history/NameDomainHistoryListItem.tsx b/src/features/name-services/domains/pages/details/history/NameDomainHistoryListItem.tsx deleted file mode 100644 index d24cdd909d1..00000000000 --- a/src/features/name-services/domains/pages/details/history/NameDomainHistoryListItem.tsx +++ /dev/null @@ -1,72 +0,0 @@ -// SPDX-License-Identifier: LicenseRef-Blockscout - -import { route } from 'nextjs-routes'; -import React from 'react'; - -import type * as bens from '@blockscout/bens-types'; - -import AddressEntity from 'src/slices/address/components/entity/AddressEntity'; -import TxEntity from 'src/slices/tx/components/entity/TxEntity'; - -import config from 'src/config'; -import TimeWithTooltip from 'src/shared/date-and-time/TimeWithTooltip'; -import ListItemMobileGrid from 'src/shared/lists/ListItemMobileGrid'; - -import { Badge } from 'src/toolkit/chakra/badge'; -import { stripTrailingSlash } from 'src/toolkit/utils/url'; - -interface Props { - event: bens.DomainEvent; - domain: bens.DetailedDomain | undefined; - isLoading?: boolean; -} - -const NameDomainHistoryListItem = ({ isLoading, domain, event }: Props) => { - const isProtocolBaseChain = stripTrailingSlash(domain?.protocol?.deployment_blockscout_base_url ?? '') === config.app.baseUrl; - const txEntityProps = { - link: { external: !isProtocolBaseChain ? true : false }, - href: !isProtocolBaseChain ? ( - stripTrailingSlash(domain?.protocol?.deployment_blockscout_base_url ?? '') + - route({ pathname: '/tx/[hash]', query: { hash: event.transaction_hash } }) - ) : undefined, - }; - - return ( - - Txn hash - - - - - Age - - - - - { event.from_address && ( - <> - From - - - - - ) } - - { event.action && ( - <> - Method - - { event.action } - - - ) } - - ); -}; - -export default React.memo(NameDomainHistoryListItem); diff --git a/src/features/name-services/domains/pages/details/history/NameDomainHistoryTable.tsx b/src/features/name-services/domains/pages/details/history/NameDomainHistoryTable.tsx index c05bfcbb72c..2648fba40ab 100644 --- a/src/features/name-services/domains/pages/details/history/NameDomainHistoryTable.tsx +++ b/src/features/name-services/domains/pages/details/history/NameDomainHistoryTable.tsx @@ -24,7 +24,7 @@ const NameDomainHistoryTable = ({ items, domain, isLoading, sort, onSortToggle } const sortedItems = React.useMemo(() => items.slice().sort(sortFn(sort)), [ items, sort ]); return ( - + Txn hash diff --git a/src/features/name-services/domains/pages/details/history/utils.ts b/src/features/name-services/domains/pages/details/history/utils.ts index 8e2e58429c3..3c32fe8ed08 100644 --- a/src/features/name-services/domains/pages/details/history/utils.ts +++ b/src/features/name-services/domains/pages/details/history/utils.ts @@ -5,9 +5,17 @@ import type * as bens from '@blockscout/bens-types'; import getNextSortValueShared from 'src/shared/sort/get-next-sort-value'; import { collator } from 'src/shared/texts/collator'; +import type { SelectOption } from 'src/toolkit/chakra/select'; + export type SortField = 'timestamp'; export type Sort = `${ SortField }-asc` | `${ SortField }-desc` | 'default'; +export const SORT_OPTIONS: Array> = [ + { label: 'Default', value: 'default' }, + { label: 'Timestamp descending', value: 'timestamp-desc' }, + { label: 'Timestamp ascending', value: 'timestamp-asc' }, +]; + const SORT_SEQUENCE: Record> = { timestamp: [ 'timestamp-desc', 'timestamp-asc', 'default' ], }; diff --git a/src/features/name-services/domains/pages/index/NameDomains.tsx b/src/features/name-services/domains/pages/index/NameDomains.tsx index 8f180a90335..1b76cec6f43 100644 --- a/src/features/name-services/domains/pages/index/NameDomains.tsx +++ b/src/features/name-services/domains/pages/index/NameDomains.tsx @@ -1,28 +1,28 @@ // SPDX-License-Identifier: LicenseRef-Blockscout -import { Box } from '@chakra-ui/react'; -import { useRouter } from 'next/router'; import React from 'react'; import type { EnsDomainLookupFiltersOptions, EnsLookupSorting } from 'src/features/name-services/domains/types/api'; import useApiQuery from 'src/api/hooks/useApiQuery'; +import type { PaginationFilters } from 'src/api/resources'; import { ENS_DOMAIN } from 'src/features/name-services/domains/stubs'; import config from 'src/config'; -import useDebounce from 'src/shared/hooks/useDebounce'; import DataList from 'src/shared/lists/DataList'; -import useQueryWithPages from 'src/shared/pagination/useQueryWithPages'; +import useApiPaginatedQuery from 'src/shared/pagination/useApiPaginatedQuery'; +import { useDebouncedFilterChange } from 'src/shared/pagination/useDebouncedFilterChange'; +import { usePaginationParams } from 'src/shared/pagination/usePaginationParams'; import { generateListStub } from 'src/shared/pagination/utils'; import getQueryParamString from 'src/shared/router/get-query-param-string'; import getSortParamsFromValue from 'src/shared/sort/get-sort-params-from-value'; import getSortValueFromQuery from 'src/shared/sort/get-sort-value-from-query'; +import { TableContainerScrollable } from 'src/toolkit/chakra/table'; import { ADDRESS_REGEXP } from 'src/toolkit/utils/regexp'; import NameDomainsActionBar from './NameDomainsActionBar'; -import NameDomainsList from './NameDomainsList'; import NameDomainsTable from './NameDomainsTable'; import type { Sort, SortField } from './utils'; import { SORT_OPTIONS, getNextSortValue } from './utils'; @@ -30,69 +30,75 @@ import { SORT_OPTIONS, getNextSortValue } from './utils'; const feature = config.features.nameServices; const availableProtocols = feature.isEnabled && feature.ens.isEnabled ? feature.ens.protocols : []; -const NameDomains = () => { - const router = useRouter(); - - const q = getQueryParamString(router.query.name) || getQueryParamString(router.query.address); - const ownedBy = getQueryParamString(router.query.owned_by); - const resolvedTo = getQueryParamString(router.query.resolved_to); - const onlyActive = getQueryParamString(router.query.only_active); - - const protocols = (() => { - if (router.query.protocols) { - const valueFromQuery = getQueryParamString(router.query.protocols) - .split(',') - .filter(Boolean) - .filter((protocol) => availableProtocols.includes(protocol)); - - if (valueFromQuery.length > 0) { - return valueFromQuery; - } - } +const NO_PROTOCOLS: Array = []; +const ADDRESS_SEARCH_DEFAULT_FILTERS: EnsDomainLookupFiltersOptions = [ 'owned_by', 'resolved_to' ]; + +type AddressesLookupFilters = PaginationFilters<'bens:addresses_lookup'>; + +interface LookupState { + readonly searchTerm: string; + readonly filterValue: EnsDomainLookupFiltersOptions; + readonly protocols: Array; +} - return []; - })(); +// The resource types these as booleans; read from the URL they are the strings 'true' / 'false'. +function isFalseParam(value: boolean | string | Array | undefined): boolean { + return String(value) === 'false'; +} - const initialFilters: EnsDomainLookupFiltersOptions = [ - ownedBy === 'true' ? 'owned_by' as const : undefined, - resolvedTo === 'true' ? 'resolved_to' as const : undefined, - onlyActive === 'false' ? 'with_inactive' as const : undefined, +function getFilterValueFromQuery(filters: AddressesLookupFilters, isAddressSearch: boolean): EnsDomainLookupFiltersOptions { + return [ + isAddressSearch && !isFalseParam(filters.owned_by) ? 'owned_by' as const : undefined, + isAddressSearch && !isFalseParam(filters.resolved_to) ? 'resolved_to' as const : undefined, + isFalseParam(filters.only_active) ? 'with_inactive' as const : undefined, ].filter(Boolean); - const initialSort = getSortValueFromQuery(router.query, SORT_OPTIONS); +} + +function getProtocolsFromQuery(value: string | Array | undefined): Array { + const protocols = getQueryParamString(value) + .split(',') + .filter(Boolean) + .filter((protocol) => availableProtocols.includes(protocol)); + + return protocols.length > 0 ? protocols : NO_PROTOCOLS; +} + +const NameDomains = () => { + const { filters: addressesFilters, sorting } = usePaginationParams('bens:addresses_lookup'); + const { filters: domainsFilters } = usePaginationParams('bens:domains_lookup'); - const [ searchTerm, setSearchTerm ] = React.useState(q || ''); - const [ filterValue, setFilterValue ] = React.useState(initialFilters); - const [ sort, setSort ] = React.useState(initialSort ?? 'default'); - const [ protocolsFilter, setProtocolsFilter ] = React.useState>(protocols); + const searchTerm = getQueryParamString(domainsFilters.name) || getQueryParamString(addressesFilters.address); + const isAddressSearch = ADDRESS_REGEXP.test(searchTerm); + const filterValue = React.useMemo(() => getFilterValueFromQuery(addressesFilters, isAddressSearch), [ addressesFilters, isAddressSearch ]); + const protocolsParam = addressesFilters.protocols; + const protocolsFilter = React.useMemo(() => getProtocolsFromQuery(protocolsParam), [ protocolsParam ]); + const sort = getSortValueFromQuery({ ...sorting }, SORT_OPTIONS) ?? 'default'; - const debouncedSearchTerm = useDebounce(searchTerm, 300); - const isAddressSearch = React.useMemo(() => ADDRESS_REGEXP.test(debouncedSearchTerm), [ debouncedSearchTerm ]); - const sortParams = getSortParamsFromValue(sort); + const onlyActive = !filterValue.includes('with_inactive'); + const requestedProtocols = protocolsFilter.length > 0 ? protocolsFilter : availableProtocols; - const addressesLookupQuery = useQueryWithPages({ + const addressesLookupQuery = useApiPaginatedQuery({ resourceName: 'bens:addresses_lookup', - filters: { - address: debouncedSearchTerm, + queryParams: { + address: searchTerm, resolved_to: filterValue.includes('resolved_to'), owned_by: filterValue.includes('owned_by'), - only_active: !filterValue.includes('with_inactive'), - protocols: protocolsFilter.length > 0 ? protocolsFilter : availableProtocols, + only_active: onlyActive, + protocols: requestedProtocols, }, - sorting: sortParams, options: { enabled: isAddressSearch, placeholderData: generateListStub<'bens:addresses_lookup'>(ENS_DOMAIN, 50, { next_page_params: undefined }), }, }); - const domainsLookupQuery = useQueryWithPages({ + const domainsLookupQuery = useApiPaginatedQuery({ resourceName: 'bens:domains_lookup', - filters: { - name: debouncedSearchTerm, - only_active: !filterValue.includes('with_inactive'), - protocols: protocolsFilter.length > 0 ? protocolsFilter : availableProtocols, + queryParams: { + name: searchTerm, + only_active: onlyActive, + protocols: requestedProtocols, }, - sorting: sortParams, options: { enabled: !isAddressSearch, placeholderData: generateListStub<'bens:domains_lookup'>(ENS_DOMAIN, 50, { next_page_params: undefined }), @@ -102,129 +108,68 @@ const NameDomains = () => { const protocolsQuery = useApiQuery('bens:protocols'); const query = isAddressSearch ? addressesLookupQuery : domainsLookupQuery; - const { data, isError, isPlaceholderData: isLoading, onFilterChange, onSortingChange, queryHash } = query; + const { data, isError, isInitialLoading, isTransitioning, onFilterChange, onSortingChange, queryHash } = query; - React.useEffect(() => { - const hasInactiveFilter = filterValue.some((value) => value === 'with_inactive'); - if (isAddressSearch) { - setFilterValue([ 'owned_by' as const, 'resolved_to' as const, hasInactiveFilter ? 'with_inactive' as const : undefined ].filter(Boolean)); + const pushLookupState = React.useCallback((next: LookupState) => { + if (ADDRESS_REGEXP.test(next.searchTerm)) { onFilterChange<'bens:addresses_lookup'>({ - address: debouncedSearchTerm, - resolved_to: true, - owned_by: true, - only_active: !hasInactiveFilter, - protocols: protocolsFilter, + address: next.searchTerm, + resolved_to: next.filterValue.includes('resolved_to'), + owned_by: next.filterValue.includes('owned_by'), + only_active: !next.filterValue.includes('with_inactive'), + protocols: next.protocols, }); - } else { - setFilterValue([ hasInactiveFilter ? 'with_inactive' as const : undefined ].filter(Boolean)); - onFilterChange<'bens:domains_lookup'>({ - name: debouncedSearchTerm, - only_active: !hasInactiveFilter, - protocols: protocolsFilter, - }); - } - // should run only the type of search changes - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [ isAddressSearch ]); - - const handleSortToggle = React.useCallback((field: SortField) => { - if (isLoading) { return; } - if (field) { - setSort((prevValue) => { - const nextSortValue = getNextSortValue(field)(prevValue); - onSortingChange(getSortParamsFromValue(nextSortValue)); - return nextSortValue; - }); - } - }, [ isLoading, onSortingChange ]); - - const handleSearchTermChange = React.useCallback((value: string) => { - setSearchTerm(value); - const isAddressSearch = ADDRESS_REGEXP.test(value); - if (isAddressSearch) { - onFilterChange<'bens:addresses_lookup'>({ - address: value, - resolved_to: filterValue.includes('resolved_to'), - owned_by: filterValue.includes('owned_by'), - only_active: !filterValue.includes('with_inactive'), - protocols: protocolsFilter, - }); - } else { - onFilterChange<'bens:domains_lookup'>({ - name: value, - only_active: !filterValue.includes('with_inactive'), - protocols: protocolsFilter, - }); - } - }, [ onFilterChange, filterValue, protocolsFilter ]); + onFilterChange<'bens:domains_lookup'>({ + name: next.searchTerm, + only_active: !next.filterValue.includes('with_inactive'), + protocols: next.protocols, + }); + }, [ onFilterChange ]); + + const handleSearchTermChange = useDebouncedFilterChange((value) => { + const switchesToAddressSearch = !isAddressSearch && ADDRESS_REGEXP.test(value); + const nextFilterValue = switchesToAddressSearch ? + [ ...ADDRESS_SEARCH_DEFAULT_FILTERS, ...filterValue.filter((item) => item === 'with_inactive') ] : + filterValue; + pushLookupState({ searchTerm: value, filterValue: nextFilterValue, protocols: protocolsFilter }); + }); const handleFilterValueChange = React.useCallback((value: EnsDomainLookupFiltersOptions) => { - setFilterValue(value); - - const isAddressSearch = ADDRESS_REGEXP.test(debouncedSearchTerm); - if (isAddressSearch) { - onFilterChange<'bens:addresses_lookup'>({ - address: debouncedSearchTerm, - resolved_to: value.includes('resolved_to'), - owned_by: value.includes('owned_by'), - only_active: !value.includes('with_inactive'), - protocols: protocolsFilter, - }); - } else { - onFilterChange<'bens:domains_lookup'>({ - name: debouncedSearchTerm, - only_active: !value.includes('with_inactive'), - protocols: protocolsFilter, - }); - } - }, [ debouncedSearchTerm, onFilterChange, protocolsFilter ]); + pushLookupState({ searchTerm, filterValue: value, protocols: protocolsFilter }); + }, [ pushLookupState, searchTerm, protocolsFilter ]); const handleProtocolsFilterChange = React.useCallback((nextValue: Array) => { - setProtocolsFilter(nextValue); + pushLookupState({ searchTerm, filterValue, protocols: nextValue }); + }, [ pushLookupState, searchTerm, filterValue ]); - const isAddressSearch = ADDRESS_REGEXP.test(debouncedSearchTerm); - if (isAddressSearch) { - onFilterChange<'bens:addresses_lookup'>({ - address: debouncedSearchTerm, - resolved_to: filterValue.includes('resolved_to'), - owned_by: filterValue.includes('owned_by'), - only_active: !filterValue.includes('with_inactive'), - protocols: nextValue, - }); - } else { - onFilterChange<'bens:domains_lookup'>({ - name: debouncedSearchTerm, - only_active: !filterValue.includes('with_inactive'), - protocols: nextValue, - }); + const handleSortChange = React.useCallback((nextValue: Sort) => { + onSortingChange(getSortParamsFromValue(nextValue)); + }, [ onSortingChange ]); + + const handleSortToggle = React.useCallback((field: SortField) => { + if (isInitialLoading || !field) { + return; } - }, [ debouncedSearchTerm, filterValue, onFilterChange ]); - const hasActiveFilters = Boolean(debouncedSearchTerm) || filterValue.length > 0 || + handleSortChange(getNextSortValue(field)(sort)); + }, [ isInitialLoading, handleSortChange, sort ]); + + const hasActiveFilters = Boolean(searchTerm) || filterValue.length > 0 || (protocolsQuery.data && availableProtocols.length > 1 ? protocolsFilter.length > 0 : false); const content = data?.items ? ( - <> - - - - - - - + + + ) : null; const protocolsData = React.useMemo(() => { @@ -233,7 +178,7 @@ const NameDomains = () => { const actionBar = ( { protocolsFilterValue={ protocolsFilter } onProtocolsFilterChange={ handleProtocolsFilterChange } sort={ sort } - onSortChange={ setSort } + onSortChange={ handleSortChange } isAddressSearch={ isAddressSearch } pagination={ query.pagination } /> @@ -258,6 +203,7 @@ const NameDomains = () => { term: 'name domain', }} actionBar={ actionBar } + isTransitioning={ isTransitioning } > { content } diff --git a/src/features/name-services/domains/pages/index/NameDomainsList.tsx b/src/features/name-services/domains/pages/index/NameDomainsList.tsx deleted file mode 100644 index 62cbffb0a18..00000000000 --- a/src/features/name-services/domains/pages/index/NameDomainsList.tsx +++ /dev/null @@ -1,37 +0,0 @@ -// SPDX-License-Identifier: LicenseRef-Blockscout - -import { Box } from '@chakra-ui/react'; -import React from 'react'; - -import type * as bens from '@blockscout/bens-types'; - -import useLazyRenderedList from 'src/shared/lists/useLazyRenderedList'; - -import NameDomainsListItem from './NameDomainsListItem'; - -interface Props { - items: Array; - isLoading?: boolean; - resetKey?: string; -} - -const NameDomainsList = ({ items, isLoading, resetKey }: Props) => { - const { cutRef, renderedItemsNum } = useLazyRenderedList({ list: items, isEnabled: !isLoading, resetKey }); - - return ( - <> - - { items.slice(0, renderedItemsNum).map((item, index) => ( - - )) } - - - - ); -}; - -export default React.memo(NameDomainsList); diff --git a/src/features/name-services/domains/pages/index/NameDomainsListItem.tsx b/src/features/name-services/domains/pages/index/NameDomainsListItem.tsx deleted file mode 100644 index 3e3bb499fc0..00000000000 --- a/src/features/name-services/domains/pages/index/NameDomainsListItem.tsx +++ /dev/null @@ -1,83 +0,0 @@ -// SPDX-License-Identifier: LicenseRef-Blockscout - -import React from 'react'; - -import type * as bens from '@blockscout/bens-types'; - -import AddressEntity from 'src/slices/address/components/entity/AddressEntity'; - -import EnsEntity from 'src/features/name-services/domains/components/EnsEntity'; -import NameDomainExpiryStatus from 'src/features/name-services/domains/components/NameDomainExpiryStatus'; - -import dayjs from 'src/shared/date-and-time/dayjs'; -import Time from 'src/shared/date-and-time/Time'; -import ListItemMobileGrid from 'src/shared/lists/ListItemMobileGrid'; - -import { Skeleton } from 'src/toolkit/chakra/skeleton'; - -interface Props extends bens.Domain { - isLoading: boolean; -} - -const NameDomainsListItem = ({ - name, - isLoading, - resolved_address: resolvedAddress, - registration_date: registrationDate, - expiry_date: expiryDate, - protocol, - protocol_dapp_url: protocolDappUrl, - protocol_dapp_logo: protocolDappLogo, -}: Props) => { - - const protocolDapp = React.useMemo(() => { - return { - url: protocolDappUrl, - logo: protocolDappLogo, - }; - }, [ protocolDappUrl, protocolDappLogo ]); - - return ( - - Domain - - - - - { resolvedAddress && ( - <> - Address - - - - - ) } - - { registrationDate && ( - <> - Registered on - - - - - - ) } - - { expiryDate && ( - <> - Expiration date - - - - - - ) } - - ); -}; - -export default React.memo(NameDomainsListItem); diff --git a/src/features/name-services/domains/pages/index/NameDomainsTable.tsx b/src/features/name-services/domains/pages/index/NameDomainsTable.tsx index 0c0c441beb9..7e8475f1155 100644 --- a/src/features/name-services/domains/pages/index/NameDomainsTable.tsx +++ b/src/features/name-services/domains/pages/index/NameDomainsTable.tsx @@ -29,7 +29,7 @@ const NameDomainsTable = ({ items, isLoading, sort, onSortToggle, resetKey }: Pr return ( - + Domain diff --git a/src/features/op-interop/components/__screenshots__/AddressEntityInterop.pw.tsx_dark-color-mode_with-chain-icon-stub-dark-mode-1.png b/src/features/op-interop/components/__screenshots__/AddressEntityInterop.pw.tsx_dark-color-mode_with-chain-icon-stub-dark-mode-1.png index bab192184b7..8b22031b7c5 100644 Binary files a/src/features/op-interop/components/__screenshots__/AddressEntityInterop.pw.tsx_dark-color-mode_with-chain-icon-stub-dark-mode-1.png and b/src/features/op-interop/components/__screenshots__/AddressEntityInterop.pw.tsx_dark-color-mode_with-chain-icon-stub-dark-mode-1.png differ diff --git a/src/features/op-interop/components/__screenshots__/AddressEntityInterop.pw.tsx_default_with-chain-icon-stub-dark-mode-1.png b/src/features/op-interop/components/__screenshots__/AddressEntityInterop.pw.tsx_default_with-chain-icon-stub-dark-mode-1.png index 08b2de7b3c3..45b28005ff8 100644 Binary files a/src/features/op-interop/components/__screenshots__/AddressEntityInterop.pw.tsx_default_with-chain-icon-stub-dark-mode-1.png and b/src/features/op-interop/components/__screenshots__/AddressEntityInterop.pw.tsx_default_with-chain-icon-stub-dark-mode-1.png differ diff --git a/src/features/op-interop/pages/messages/InteropMessages.pw.tsx b/src/features/op-interop/pages/messages/InteropMessages.pw.tsx index 78beefd0bdc..0fe1b6ea2da 100644 --- a/src/features/op-interop/pages/messages/InteropMessages.pw.tsx +++ b/src/features/op-interop/pages/messages/InteropMessages.pw.tsx @@ -3,7 +3,6 @@ import React from 'react'; import * as interopMessageMock from 'src/features/op-interop/mocks/interop'; import { test, expect } from 'playwright/lib'; -import * as pwConfig from 'playwright/utils/config'; import InteropMessages from './InteropMessages'; @@ -29,15 +28,3 @@ test('default view', async({ render, mockTextAd, mockAssetResponse, mockApiRespo const component = await render(); await expect(component).toHaveScreenshot({ maxDiffPixels: 30 }); }); - -test.describe('mobile', () => { - test.use({ viewport: pwConfig.viewport.mobile }); - test('default view', async({ render, mockTextAd, mockAssetResponse, mockApiResponse }) => { - await mockTextAd(); - await mockAssetResponse(interopMessageMock.chain.chain_logo as string, './playwright/mocks/image_s.jpg'); - await mockApiResponse('core:optimistic_l2_interop_messages', MESSAGES_RESPONSE); - await mockApiResponse('core:optimistic_l2_interop_messages_count', 4000000); - const component = await render(); - await expect(component).toHaveScreenshot({ maxDiffPixels: 30 }); - }); -}); diff --git a/src/features/op-interop/pages/messages/InteropMessages.tsx b/src/features/op-interop/pages/messages/InteropMessages.tsx index ab1dd467fb8..95f7ae68dca 100644 --- a/src/features/op-interop/pages/messages/InteropMessages.tsx +++ b/src/features/op-interop/pages/messages/InteropMessages.tsx @@ -1,6 +1,5 @@ // SPDX-License-Identifier: LicenseRef-Blockscout -import { Box } from '@chakra-ui/react'; import React from 'react'; import useApiQuery from 'src/api/hooks/useApiQuery'; @@ -12,16 +11,16 @@ import { INTEROP_MESSAGE } from 'src/features/op-interop/stubs'; import DataList from 'src/shared/lists/DataList'; import StickyPaginationWithText from 'src/shared/pagination/StickyPaginationWithText'; -import useQueryWithPages from 'src/shared/pagination/useQueryWithPages'; +import useApiPaginatedQuery from 'src/shared/pagination/useApiPaginatedQuery'; import { generateListStub } from 'src/shared/pagination/utils'; import { Skeleton } from 'src/toolkit/chakra/skeleton'; +import { TableContainerScrollable } from 'src/toolkit/chakra/table'; -import InteropMessagesListItem from './InteropMessagesListItem'; import InteropMessagesTable from './InteropMessagesTable'; const InteropMessages = () => { - const interopMessagesQuery = useQueryWithPages({ + const interopMessagesQuery = useApiPaginatedQuery({ resourceName: 'core:optimistic_l2_interop_messages', options: { placeholderData: generateListStub<'core:optimistic_l2_interop_messages'>(INTEROP_MESSAGE, 50, { next_page_params: { @@ -44,7 +43,7 @@ const InteropMessages = () => { } return ( - + A total of { countQuery.data?.toLocaleString() } messages found ); @@ -53,24 +52,13 @@ const InteropMessages = () => { const actionBar = ; const content = ( - <> - - { interopMessagesQuery.data?.items.map((item, index) => ( - - )) } - - - - - + + + ); return ( @@ -84,6 +72,7 @@ const InteropMessages = () => { itemsNum={ interopMessagesQuery.data?.items.length } emptyText="There are no interop messages." actionBar={ actionBar } + isTransitioning={ interopMessagesQuery.isTransitioning } > { content } diff --git a/src/features/op-interop/pages/messages/InteropMessagesListItem.tsx b/src/features/op-interop/pages/messages/InteropMessagesListItem.tsx deleted file mode 100644 index acf5b18d83f..00000000000 --- a/src/features/op-interop/pages/messages/InteropMessagesListItem.tsx +++ /dev/null @@ -1,75 +0,0 @@ -// SPDX-License-Identifier: LicenseRef-Blockscout - -import { Flex, HStack, Text, Grid } from '@chakra-ui/react'; -import React from 'react'; - -import type { InteropMessage } from 'src/features/op-interop/types/api'; - -import AddressEntity from 'src/slices/address/components/entity/AddressEntity'; -import AddressFromToIcon from 'src/slices/address/components/from-to/AddressFromToIcon'; - -import AddressEntityInterop from 'src/features/op-interop/components/AddressEntityInterop'; -import InteropMessageDestinationTx from 'src/features/op-interop/components/InteropMessageDestinationTx'; -import InteropMessageSourceTx from 'src/features/op-interop/components/InteropMessageSourceTx'; -import InteropMessageStatus from 'src/features/op-interop/components/InteropMessageStatus'; - -import TimeWithTooltip from 'src/shared/date-and-time/TimeWithTooltip'; -import ListItemMobile from 'src/shared/lists/ListItemMobile'; - -import InteropMessageAdditionalInfo from './InteropMessageAdditionalInfo'; - -interface Props { - item: InteropMessage; - isLoading?: boolean; -} - -const InteropMessagesListItem = ({ item, isLoading }: Props) => { - return ( - - - - - - - - #{ item.nonce } - - - - Source tx - - Destination tx - - - - { item.init_chain !== undefined ? ( - - ) : ( - - ) } - - { item.relay_chain !== undefined ? ( - - ) : ( - - ) } - - - - ); -}; - -export default React.memo(InteropMessagesListItem); diff --git a/src/features/op-interop/pages/messages/InteropMessagesTable.tsx b/src/features/op-interop/pages/messages/InteropMessagesTable.tsx index 01c4d1a5fac..f59d918069e 100644 --- a/src/features/op-interop/pages/messages/InteropMessagesTable.tsx +++ b/src/features/op-interop/pages/messages/InteropMessagesTable.tsx @@ -18,7 +18,7 @@ interface Props { const InteropMessagesTable = ({ items, top, isLoading }: Props) => { return ( - + @@ -38,7 +38,7 @@ const InteropMessagesTable = ({ items, top, isLoading }: Props) => { { items?.map((item, index) => ( diff --git a/src/features/op-interop/pages/messages/__screenshots__/InteropMessages.pw.tsx_default_default-view-1.png b/src/features/op-interop/pages/messages/__screenshots__/InteropMessages.pw.tsx_default_default-view-1.png index 2905cad77c2..959a4517400 100644 Binary files a/src/features/op-interop/pages/messages/__screenshots__/InteropMessages.pw.tsx_default_default-view-1.png and b/src/features/op-interop/pages/messages/__screenshots__/InteropMessages.pw.tsx_default_default-view-1.png differ diff --git a/src/features/op-interop/pages/messages/__screenshots__/InteropMessages.pw.tsx_default_mobile-default-view-1.png b/src/features/op-interop/pages/messages/__screenshots__/InteropMessages.pw.tsx_default_mobile-default-view-1.png deleted file mode 100644 index 029cce8a045..00000000000 Binary files a/src/features/op-interop/pages/messages/__screenshots__/InteropMessages.pw.tsx_default_mobile-default-view-1.png and /dev/null differ diff --git a/src/features/rewards/components/RewardsActivityTracker.tsx b/src/features/rewards/components/RewardsActivityTracker.ts similarity index 100% rename from src/features/rewards/components/RewardsActivityTracker.tsx rename to src/features/rewards/components/RewardsActivityTracker.ts diff --git a/src/features/rewards/components/login/RewardsLoginModal.tsx b/src/features/rewards/components/login/RewardsLoginModal.tsx index 7cd8c40405b..a720b2af621 100644 --- a/src/features/rewards/components/login/RewardsLoginModal.tsx +++ b/src/features/rewards/components/login/RewardsLoginModal.tsx @@ -14,7 +14,7 @@ import { useDisclosure } from 'src/toolkit/hooks/useDisclosure'; import CongratsStepContent from './steps/CongratsStepContent'; import LoginStepContent from './steps/LoginStepContent'; -const MIXPANEL_CONFIG = { +const ANALYTICS_CONFIG = { account_link_info: { source: 'Merits' as const, }, @@ -91,7 +91,7 @@ const RewardsLoginModal = () => { ) } diff --git a/src/features/rewards/pages/dashboard/RewardsDashboardCard.tsx b/src/features/rewards/pages/dashboard/RewardsDashboardCard.tsx index 55d8e78405b..061039131a6 100644 --- a/src/features/rewards/pages/dashboard/RewardsDashboardCard.tsx +++ b/src/features/rewards/pages/dashboard/RewardsDashboardCard.tsx @@ -52,7 +52,7 @@ const RewardsDashboardCard = ({ { title && ( { title } - { hint && } + { Boolean(hint) && } { availableSoon && Available soon } ) } diff --git a/src/features/rewards/pages/dashboard/RewardsDashboardCardValue.tsx b/src/features/rewards/pages/dashboard/RewardsDashboardCardValue.tsx index b2b44abf850..be7349bb056 100644 --- a/src/features/rewards/pages/dashboard/RewardsDashboardCardValue.tsx +++ b/src/features/rewards/pages/dashboard/RewardsDashboardCardValue.tsx @@ -23,7 +23,7 @@ const RewardsDashboardCardValue = ({ label, value, withIcon, hint, isLoading, bo { label && ( - { hint && } + { Boolean(hint) && } { label } diff --git a/src/features/rewards/pages/dashboard/__screenshots__/RewardsDashboard.pw.tsx_dark-color-mode_activity-tab-dark-mode-mobile-1.png b/src/features/rewards/pages/dashboard/__screenshots__/RewardsDashboard.pw.tsx_dark-color-mode_activity-tab-dark-mode-mobile-1.png index 18e6ddd16c0..b5a2bf02845 100644 Binary files a/src/features/rewards/pages/dashboard/__screenshots__/RewardsDashboard.pw.tsx_dark-color-mode_activity-tab-dark-mode-mobile-1.png and b/src/features/rewards/pages/dashboard/__screenshots__/RewardsDashboard.pw.tsx_dark-color-mode_activity-tab-dark-mode-mobile-1.png differ diff --git a/src/features/rewards/pages/dashboard/__screenshots__/RewardsDashboard.pw.tsx_dark-color-mode_referrals-tab-dark-mode-mobile-1.png b/src/features/rewards/pages/dashboard/__screenshots__/RewardsDashboard.pw.tsx_dark-color-mode_referrals-tab-dark-mode-mobile-1.png index 62c06bcb365..8e96bf96783 100644 Binary files a/src/features/rewards/pages/dashboard/__screenshots__/RewardsDashboard.pw.tsx_dark-color-mode_referrals-tab-dark-mode-mobile-1.png and b/src/features/rewards/pages/dashboard/__screenshots__/RewardsDashboard.pw.tsx_dark-color-mode_referrals-tab-dark-mode-mobile-1.png differ diff --git a/src/features/rewards/pages/dashboard/__screenshots__/RewardsDashboard.pw.tsx_dark-color-mode_resources-tab-dark-mode-mobile-1.png b/src/features/rewards/pages/dashboard/__screenshots__/RewardsDashboard.pw.tsx_dark-color-mode_resources-tab-dark-mode-mobile-1.png index 8d273a72e72..011e4b7b7e2 100644 Binary files a/src/features/rewards/pages/dashboard/__screenshots__/RewardsDashboard.pw.tsx_dark-color-mode_resources-tab-dark-mode-mobile-1.png and b/src/features/rewards/pages/dashboard/__screenshots__/RewardsDashboard.pw.tsx_dark-color-mode_resources-tab-dark-mode-mobile-1.png differ diff --git a/src/features/rewards/pages/dashboard/__screenshots__/RewardsDashboard.pw.tsx_default_activity-tab-dark-mode-mobile-1.png b/src/features/rewards/pages/dashboard/__screenshots__/RewardsDashboard.pw.tsx_default_activity-tab-dark-mode-mobile-1.png index 1f904776e5d..3e3185cbecd 100644 Binary files a/src/features/rewards/pages/dashboard/__screenshots__/RewardsDashboard.pw.tsx_default_activity-tab-dark-mode-mobile-1.png and b/src/features/rewards/pages/dashboard/__screenshots__/RewardsDashboard.pw.tsx_default_activity-tab-dark-mode-mobile-1.png differ diff --git a/src/features/rewards/pages/dashboard/__screenshots__/RewardsDashboard.pw.tsx_default_referrals-tab-dark-mode-mobile-1.png b/src/features/rewards/pages/dashboard/__screenshots__/RewardsDashboard.pw.tsx_default_referrals-tab-dark-mode-mobile-1.png index 5cd89cb7ec8..617437b377e 100644 Binary files a/src/features/rewards/pages/dashboard/__screenshots__/RewardsDashboard.pw.tsx_default_referrals-tab-dark-mode-mobile-1.png and b/src/features/rewards/pages/dashboard/__screenshots__/RewardsDashboard.pw.tsx_default_referrals-tab-dark-mode-mobile-1.png differ diff --git a/src/features/rewards/pages/dashboard/__screenshots__/RewardsDashboard.pw.tsx_default_resources-tab-dark-mode-mobile-1.png b/src/features/rewards/pages/dashboard/__screenshots__/RewardsDashboard.pw.tsx_default_resources-tab-dark-mode-mobile-1.png index 25919f67854..fd949a73c2d 100644 Binary files a/src/features/rewards/pages/dashboard/__screenshots__/RewardsDashboard.pw.tsx_default_resources-tab-dark-mode-mobile-1.png and b/src/features/rewards/pages/dashboard/__screenshots__/RewardsDashboard.pw.tsx_default_resources-tab-dark-mode-mobile-1.png differ diff --git a/src/features/rewards/pages/dashboard/__screenshots__/RewardsDashboard.pw.tsx_default_with-error-1.png b/src/features/rewards/pages/dashboard/__screenshots__/RewardsDashboard.pw.tsx_default_with-error-1.png index 8a2e1efc74a..5cf21e322de 100644 Binary files a/src/features/rewards/pages/dashboard/__screenshots__/RewardsDashboard.pw.tsx_default_with-error-1.png and b/src/features/rewards/pages/dashboard/__screenshots__/RewardsDashboard.pw.tsx_default_with-error-1.png differ diff --git a/src/features/rewards/pages/dashboard/__screenshots__/RewardsDashboard.pw.tsx_mobile_activity-tab-dark-mode-mobile-1.png b/src/features/rewards/pages/dashboard/__screenshots__/RewardsDashboard.pw.tsx_mobile_activity-tab-dark-mode-mobile-1.png index 5940304976a..d4e65988ac9 100644 Binary files a/src/features/rewards/pages/dashboard/__screenshots__/RewardsDashboard.pw.tsx_mobile_activity-tab-dark-mode-mobile-1.png and b/src/features/rewards/pages/dashboard/__screenshots__/RewardsDashboard.pw.tsx_mobile_activity-tab-dark-mode-mobile-1.png differ diff --git a/src/features/rewards/pages/dashboard/__screenshots__/RewardsDashboard.pw.tsx_mobile_referrals-tab-dark-mode-mobile-1.png b/src/features/rewards/pages/dashboard/__screenshots__/RewardsDashboard.pw.tsx_mobile_referrals-tab-dark-mode-mobile-1.png index 7c4d7924a20..c1c061e6509 100644 Binary files a/src/features/rewards/pages/dashboard/__screenshots__/RewardsDashboard.pw.tsx_mobile_referrals-tab-dark-mode-mobile-1.png and b/src/features/rewards/pages/dashboard/__screenshots__/RewardsDashboard.pw.tsx_mobile_referrals-tab-dark-mode-mobile-1.png differ diff --git a/src/features/rewards/pages/dashboard/__screenshots__/RewardsDashboard.pw.tsx_mobile_resources-tab-dark-mode-mobile-1.png b/src/features/rewards/pages/dashboard/__screenshots__/RewardsDashboard.pw.tsx_mobile_resources-tab-dark-mode-mobile-1.png index 1271bfa9c0e..1cc47e8693b 100644 Binary files a/src/features/rewards/pages/dashboard/__screenshots__/RewardsDashboard.pw.tsx_mobile_resources-tab-dark-mode-mobile-1.png and b/src/features/rewards/pages/dashboard/__screenshots__/RewardsDashboard.pw.tsx_mobile_resources-tab-dark-mode-mobile-1.png differ diff --git a/src/features/rollup/arbitrum/components/ArbitrumL2Messages.tsx b/src/features/rollup/arbitrum/components/ArbitrumL2Messages.tsx index c6459b9d79e..f4c81ecc235 100644 --- a/src/features/rollup/arbitrum/components/ArbitrumL2Messages.tsx +++ b/src/features/rollup/arbitrum/components/ArbitrumL2Messages.tsx @@ -1,6 +1,5 @@ // SPDX-License-Identifier: LicenseRef-Blockscout -import { Box } from '@chakra-ui/react'; import React from 'react'; import useApiQuery from 'src/api/hooks/useApiQuery'; @@ -12,14 +11,14 @@ import { layerLabels } from 'src/features/rollup/common/utils/layer'; import DataList from 'src/shared/lists/DataList'; import StickyPaginationWithText from 'src/shared/pagination/StickyPaginationWithText'; -import useQueryWithPages from 'src/shared/pagination/useQueryWithPages'; +import useApiPaginatedQuery from 'src/shared/pagination/useApiPaginatedQuery'; import { generateListStub } from 'src/shared/pagination/utils'; import { Skeleton } from 'src/toolkit/chakra/skeleton'; +import { TableContainerScrollable } from 'src/toolkit/chakra/table'; import { rightLineArrow, nbsp } from 'src/toolkit/utils/htmlEntities'; import { ARBITRUM_MESSAGES_ITEM } from '../stubs'; -import ArbitrumL2MessagesList from './ArbitrumL2MessagesList'; import ArbitrumL2MessagesTable from './ArbitrumL2MessagesTable'; export type MessagesDirection = 'from-rollup' | 'to-rollup'; @@ -30,7 +29,7 @@ interface Props { const ArbitrumL2Messages = ({ direction }: Props) => { const type = direction === 'from-rollup' ? 'withdrawals' : 'deposits'; - const { data, isError, isPlaceholderData, pagination, queryHash } = useQueryWithPages({ + const { data, isError, isInitialLoading, isTransitioning, pagination, queryHash } = useApiPaginatedQuery({ resourceName: 'core:arbitrum_l2_messages', pathParams: { direction }, options: { @@ -50,25 +49,15 @@ const ArbitrumL2Messages = ({ direction }: Props) => { }); const content = data?.items ? ( - <> - - - - - - - + + + ) : null; const text = (() => { @@ -101,6 +90,7 @@ const ArbitrumL2Messages = ({ direction }: Props) => { itemsNum={ data?.items.length } emptyText={ `There are no ${ type }.` } actionBar={ actionBar } + isTransitioning={ isTransitioning } > { content } diff --git a/src/features/rollup/arbitrum/components/ArbitrumL2MessagesList.tsx b/src/features/rollup/arbitrum/components/ArbitrumL2MessagesList.tsx deleted file mode 100644 index b16825a2b72..00000000000 --- a/src/features/rollup/arbitrum/components/ArbitrumL2MessagesList.tsx +++ /dev/null @@ -1,40 +0,0 @@ -// SPDX-License-Identifier: LicenseRef-Blockscout - -import { Box } from '@chakra-ui/react'; -import React from 'react'; - -import type { schemas } from '@blockscout/api-types'; - -import useLazyRenderedList from 'src/shared/lists/useLazyRenderedList'; - -import type { MessagesDirection } from './ArbitrumL2Messages'; -import ArbitrumL2MessagesListItem from './ArbitrumL2MessagesListItem'; - -type Props = { - items: Array; - direction: MessagesDirection; - isLoading?: boolean; - resetKey?: string; -}; - -const ArbitrumL2MessagesList = ({ items, direction, isLoading, resetKey }: Props) => { - const { cutRef, renderedItemsNum } = useLazyRenderedList({ list: items, isEnabled: !isLoading, resetKey }); - - return ( - <> - - { items.slice(0, renderedItemsNum).map((item, index) => ( - - )) } - - - - ); -}; - -export default ArbitrumL2MessagesList; diff --git a/src/features/rollup/arbitrum/components/ArbitrumL2MessagesListItem.tsx b/src/features/rollup/arbitrum/components/ArbitrumL2MessagesListItem.tsx deleted file mode 100644 index 8658a5b7ef1..00000000000 --- a/src/features/rollup/arbitrum/components/ArbitrumL2MessagesListItem.tsx +++ /dev/null @@ -1,132 +0,0 @@ -// SPDX-License-Identifier: LicenseRef-Blockscout - -import { chakra } from '@chakra-ui/react'; -import { route } from 'nextjs-routes'; -import React from 'react'; - -import type { schemas } from '@blockscout/api-types'; - -import AddressEntity from 'src/slices/address/components/entity/AddressEntity'; -import TxEntity from 'src/slices/tx/components/entity/TxEntity'; - -import BlockEntityL1 from 'src/features/rollup/common/components/BlockEntityL1'; -import TxEntityL1 from 'src/features/rollup/common/components/TxEntityL1'; -import { layerLabels } from 'src/features/rollup/common/utils/layer'; - -import config from 'src/config'; -import TimeWithTooltip from 'src/shared/date-and-time/TimeWithTooltip'; -import ListItemMobileGrid from 'src/shared/lists/ListItemMobileGrid'; - -import { Link } from 'src/toolkit/chakra/link'; -import { Skeleton } from 'src/toolkit/chakra/skeleton'; - -import type { MessagesDirection } from './ArbitrumL2Messages'; -import ArbitrumL2MessageStatus from './ArbitrumL2MessageStatus'; - -const rollupFeature = config.features.rollup; - -type Props = { item: schemas['ArbitrumMessage']; isLoading?: boolean; direction: MessagesDirection }; - -const ArbitrumL2MessagesListItem = ({ item, isLoading, direction }: Props) => { - if (!rollupFeature.isEnabled || rollupFeature.type !== 'arbitrum') { - return null; - } - - const l1TxHash = direction === 'from-rollup' ? item.completion_transaction_hash : item.origination_transaction_hash; - const l2TxHash = direction === 'from-rollup' ? item.origination_transaction_hash : item.completion_transaction_hash; - - return ( - - - { direction === 'to-rollup' && ( - <> - { layerLabels.parent } block - - { item.origination_transaction_block_number ? ( - - ) : N/A } - - - ) } - - { direction === 'from-rollup' && item.origination_address_hash && ( - <> - From - - - - - ) } - - Message # - - - { item.id } - - - - { layerLabels.current } transaction - - { l2TxHash ? ( - - ) : ( - - N/A - - ) } - - - { item.origination_timestamp && ( - <> - Age - - - - - ) } - - Status - - { item.status === 'confirmed' && direction === 'from-rollup' && item.origination_transaction_hash ? - Ready for relay : - } - - - { layerLabels.parent } transaction - - { l1TxHash ? ( - - ) : ( - - N/A - - ) } - - - - ); -}; - -export default ArbitrumL2MessagesListItem; diff --git a/src/features/rollup/arbitrum/pages/batch-details/ArbitrumL2TxnBatch.tsx b/src/features/rollup/arbitrum/pages/batch-details/ArbitrumL2TxnBatch.tsx index f3e393f5fb9..502793c7c10 100644 --- a/src/features/rollup/arbitrum/pages/batch-details/ArbitrumL2TxnBatch.tsx +++ b/src/features/rollup/arbitrum/pages/batch-details/ArbitrumL2TxnBatch.tsx @@ -18,7 +18,7 @@ import throwOnAbsentParamError from 'src/shared/errors/throw-on-absent-param-err import throwOnResourceLoadError from 'src/shared/errors/throw-on-resource-load-error'; import useIsMobile from 'src/shared/hooks/useIsMobile'; import Pagination from 'src/shared/pagination/Pagination'; -import useQueryWithPages from 'src/shared/pagination/useQueryWithPages'; +import useApiPaginatedQuery from 'src/shared/pagination/useApiPaginatedQuery'; import { generateListStub } from 'src/shared/pagination/utils'; import getQueryParamString from 'src/shared/router/get-query-param-string'; @@ -45,7 +45,7 @@ const ArbitrumL2TxnBatch = () => { const batchQuery = useBatchQuery(); - const batchTxsQuery = useQueryWithPages({ + const batchTxsQuery = useApiPaginatedQuery({ resourceName: 'core:arbitrum_l2_txn_batch_txs', pathParams: { number: String(batchQuery.data?.number) }, options: { @@ -59,7 +59,7 @@ const ArbitrumL2TxnBatch = () => { }, }); - const batchBlocksQuery = useQueryWithPages({ + const batchBlocksQuery = useApiPaginatedQuery({ resourceName: 'core:arbitrum_l2_txn_batch_blocks', pathParams: { number: String(batchQuery.data?.number) }, options: { diff --git a/src/features/rollup/arbitrum/pages/batch-details/ArbitrumL2TxnBatchDetails.tsx b/src/features/rollup/arbitrum/pages/batch-details/ArbitrumL2TxnBatchDetails.tsx index 90ae0e28326..57ca9bde56e 100644 --- a/src/features/rollup/arbitrum/pages/batch-details/ArbitrumL2TxnBatchDetails.tsx +++ b/src/features/rollup/arbitrum/pages/batch-details/ArbitrumL2TxnBatchDetails.tsx @@ -21,11 +21,11 @@ import DetailedInfoTimestamp from 'src/shared/detailed-info/DetailedInfoTimestam import isCustomAppError from 'src/shared/errors/is-custom-app-error'; import throwOnResourceLoadError from 'src/shared/errors/throw-on-resource-load-error'; import CopyToClipboard from 'src/shared/texts/CopyToClipboard'; -import HashStringShortenDynamic from 'src/shared/texts/HashStringShortenDynamic'; import { CollapsibleDetails } from 'src/toolkit/chakra/collapsible'; import { Link } from 'src/toolkit/chakra/link'; import { Skeleton } from 'src/toolkit/chakra/skeleton'; +import { Truncate } from 'src/toolkit/components/truncation/Truncate'; import ArbitrumL2TxnBatchDA from '../../components/ArbitrumL2TxnBatchDA'; import ArbitrumL2TxnBatchDetailsAnyTrustDA from './ArbitrumL2TxnBatchDetailsAnyTrustDA'; @@ -145,7 +145,7 @@ const ArbitrumL2TxnBatchDetails = ({ query }: Props) => { ) } - { data.commitment_transaction.block_number && ( + { data.commitment_transaction.block_number !== null && ( <> { - + @@ -196,7 +196,7 @@ const ArbitrumL2TxnBatchDetails = ({ query }: Props) => { - + diff --git a/src/features/rollup/arbitrum/pages/batch-details/ArbitrumL2TxnBatchDetailsAnyTrustDA.tsx b/src/features/rollup/arbitrum/pages/batch-details/ArbitrumL2TxnBatchDetailsAnyTrustDA.tsx index b8dac2f1caf..c44c2c8a1ca 100644 --- a/src/features/rollup/arbitrum/pages/batch-details/ArbitrumL2TxnBatchDetailsAnyTrustDA.tsx +++ b/src/features/rollup/arbitrum/pages/batch-details/ArbitrumL2TxnBatchDetailsAnyTrustDA.tsx @@ -9,10 +9,11 @@ import dayjs from 'src/shared/date-and-time/dayjs'; import * as DetailedInfo from 'src/shared/detailed-info/DetailedInfo'; import DetailsTimestamp from 'src/shared/detailed-info/DetailedInfoTimestamp'; import CopyToClipboard from 'src/shared/texts/CopyToClipboard'; -import HashStringShorten from 'src/shared/texts/HashStringShorten'; import TextSeparator from 'src/shared/texts/TextSeparator'; import SpriteIcon from 'src/sprite/SpriteIcon'; +import { Truncate } from 'src/toolkit/components/truncation/Truncate'; + type Props = { data: schemas['ArbitrumDataAvailabilityAnytrust']; }; @@ -90,7 +91,7 @@ const ArbitrumL2TxnBatchDetailsAnyTrustDA = ({ data }: Props) => { { signer.proof ? ( - + ) : '-' } @@ -115,7 +116,7 @@ const ArbitrumL2TxnBatchDetailsAnyTrustDA = ({ data }: Props) => { Proof { signer.proof ? ( - + ) : '-' } diff --git a/src/features/rollup/arbitrum/pages/batch-details/ArbitrumL2TxnBatchDetailsCelestiaDA.tsx b/src/features/rollup/arbitrum/pages/batch-details/ArbitrumL2TxnBatchDetailsCelestiaDA.tsx index 45c6278263f..78265a07ee1 100644 --- a/src/features/rollup/arbitrum/pages/batch-details/ArbitrumL2TxnBatchDetailsCelestiaDA.tsx +++ b/src/features/rollup/arbitrum/pages/batch-details/ArbitrumL2TxnBatchDetailsCelestiaDA.tsx @@ -10,7 +10,8 @@ import CeleniumLink from 'src/features/rollup/common/components/CeleniumLink'; import config from 'src/config'; import * as DetailedInfo from 'src/shared/detailed-info/DetailedInfo'; import CopyToClipboard from 'src/shared/texts/CopyToClipboard'; -import HashStringShortenDynamic from 'src/shared/texts/HashStringShortenDynamic'; + +import { Truncate } from 'src/toolkit/components/truncation/Truncate'; const feature = config.features.rollup; @@ -39,10 +40,10 @@ const ArbitrumL2TxnBatchDetailsCelestiaDA = ({ data }: Props) => { - + - { feature.isEnabled && feature.DA.celestia.namespace && data.height && ( + { feature.isEnabled && feature.DA.celestia.namespace && data.height !== null && ( { const component = await render(); await expect(component).toHaveScreenshot(); }); - -test.describe('mobile', () => { - test.use({ viewport: devices['iPhone 13 Pro'].viewport }); - test('base view', async({ render, mockEnvs, mockTextAd, mockApiResponse }) => { - test.slow(); - await mockEnvs(ENVS_MAP.arbitrumRollup); - await mockTextAd(); - await mockApiResponse('core:arbitrum_l2_txn_batches', arbitrumTxnBatchesMock.baseResponse); - await mockApiResponse('core:arbitrum_l2_txn_batches_count', 9927); - - const component = await render(); - await expect(component).toHaveScreenshot(); - }); -}); diff --git a/src/features/rollup/arbitrum/pages/batches/ArbitrumL2TxnBatches.tsx b/src/features/rollup/arbitrum/pages/batches/ArbitrumL2TxnBatches.tsx index 3d5670b6545..7cc4e475e91 100644 --- a/src/features/rollup/arbitrum/pages/batches/ArbitrumL2TxnBatches.tsx +++ b/src/features/rollup/arbitrum/pages/batches/ArbitrumL2TxnBatches.tsx @@ -1,6 +1,6 @@ // SPDX-License-Identifier: LicenseRef-Blockscout -import { Box, Text } from '@chakra-ui/react'; +import { Text } from '@chakra-ui/react'; import React from 'react'; import useApiQuery from 'src/api/hooks/useApiQuery'; @@ -10,17 +10,17 @@ import PageTitle from 'src/shell/page/title/PageTitle'; import DataList from 'src/shared/lists/DataList'; import StickyPaginationWithText from 'src/shared/pagination/StickyPaginationWithText'; -import useQueryWithPages from 'src/shared/pagination/useQueryWithPages'; +import useApiPaginatedQuery from 'src/shared/pagination/useApiPaginatedQuery'; import { generateListStub } from 'src/shared/pagination/utils'; import { Skeleton } from 'src/toolkit/chakra/skeleton'; +import { TableContainerScrollable } from 'src/toolkit/chakra/table'; import { ARBITRUM_L2_TXN_BATCHES_ITEM } from '../../stubs'; -import ArbitrumL2TxnBatchesList from './ArbitrumL2TxnBatchesList'; import ArbitrumL2TxnBatchesTable from './ArbitrumL2TxnBatchesTable'; const ArbitrumL2TxnBatches = () => { - const { data, isError, isPlaceholderData, pagination, queryHash } = useQueryWithPages({ + const { data, isError, isInitialLoading, isTransitioning, pagination, queryHash } = useApiPaginatedQuery({ resourceName: 'core:arbitrum_l2_txn_batches', options: { placeholderData: generateListStub<'core:arbitrum_l2_txn_batches'>( @@ -43,19 +43,14 @@ const ArbitrumL2TxnBatches = () => { }); const content = data?.items ? ( - <> - - - - - - - + + + ) : null; const text = (() => { @@ -64,7 +59,7 @@ const ArbitrumL2TxnBatches = () => { } return ( - + Txn batch #{ data.items[0].number } to #{ data.items[data.items.length - 1].number } @@ -83,6 +78,7 @@ const ArbitrumL2TxnBatches = () => { itemsNum={ data?.items.length } emptyText="There are no txn batches." actionBar={ actionBar } + isTransitioning={ isTransitioning } > { content } diff --git a/src/features/rollup/arbitrum/pages/batches/ArbitrumL2TxnBatchesList.tsx b/src/features/rollup/arbitrum/pages/batches/ArbitrumL2TxnBatchesList.tsx deleted file mode 100644 index beadbd33fdc..00000000000 --- a/src/features/rollup/arbitrum/pages/batches/ArbitrumL2TxnBatchesList.tsx +++ /dev/null @@ -1,37 +0,0 @@ -// SPDX-License-Identifier: LicenseRef-Blockscout - -import { Box } from '@chakra-ui/react'; -import React from 'react'; - -import type { schemas } from '@blockscout/api-types'; - -import useLazyRenderedList from 'src/shared/lists/useLazyRenderedList'; - -import ArbitrumL2TxnBatchesListItem from './ArbitrumL2TxnBatchesListItem'; - -type Props = { - items: Array; - isLoading?: boolean; - resetKey?: string; -}; - -const ArbitrumL2TxnBatchesList = ({ items, isLoading, resetKey }: Props) => { - const { cutRef, renderedItemsNum } = useLazyRenderedList({ list: items, isEnabled: !isLoading, resetKey }); - - return ( - <> - - { items.slice(0, renderedItemsNum).map((item, index) => ( - - )) } - - - - ); -}; - -export default ArbitrumL2TxnBatchesList; diff --git a/src/features/rollup/arbitrum/pages/batches/ArbitrumL2TxnBatchesListItem.tsx b/src/features/rollup/arbitrum/pages/batches/ArbitrumL2TxnBatchesListItem.tsx deleted file mode 100644 index f35eec32d50..00000000000 --- a/src/features/rollup/arbitrum/pages/batches/ArbitrumL2TxnBatchesListItem.tsx +++ /dev/null @@ -1,115 +0,0 @@ -// SPDX-License-Identifier: LicenseRef-Blockscout - -import { route } from 'nextjs-routes'; -import React from 'react'; - -import type { schemas } from '@blockscout/api-types'; - -import BatchEntityL2 from 'src/features/rollup/common/components/BatchEntityL2'; -import BlockEntityL1 from 'src/features/rollup/common/components/BlockEntityL1'; -import TxEntityL1 from 'src/features/rollup/common/components/TxEntityL1'; -import { layerLabels } from 'src/features/rollup/common/utils/layer'; - -import config from 'src/config'; -import TimeWithTooltip from 'src/shared/date-and-time/TimeWithTooltip'; -import ListItemMobileGrid from 'src/shared/lists/ListItemMobileGrid'; - -import { Link } from 'src/toolkit/chakra/link'; -import { Skeleton } from 'src/toolkit/chakra/skeleton'; - -import ArbitrumL2TxnBatchDA from '../../components/ArbitrumL2TxnBatchDA'; -import ArbitrumL2TxnBatchStatus from '../../components/ArbitrumL2TxnBatchStatus'; - -const rollupFeature = config.features.rollup; - -type Props = { item: schemas['ArbitrumBatchForList']; isLoading?: boolean }; - -const ArbitrumL2TxnBatchesListItem = ({ item, isLoading }: Props) => { - if (!rollupFeature.isEnabled || rollupFeature.type !== 'arbitrum') { - return null; - } - - return ( - - - Batch # - - - - - { layerLabels.parent } status - - - - - { item.commitment_transaction.block_number && ( - <> - { layerLabels.parent } block - - - - - ) } - - { item.blocks_count && ( - <> - Block count - - { item.blocks_count.toLocaleString() } - - - ) } - - { item.commitment_transaction.hash && ( - <> - { layerLabels.parent } transaction - - - - - ) } - - Age - - - - - Txn count - - - { item.transactions_count.toLocaleString() } - - - - Data container - - - - - - ); -}; - -export default ArbitrumL2TxnBatchesListItem; diff --git a/src/features/rollup/arbitrum/pages/batches/__screenshots__/ArbitrumL2TxnBatches.pw.tsx_default_base-view-1.png b/src/features/rollup/arbitrum/pages/batches/__screenshots__/ArbitrumL2TxnBatches.pw.tsx_default_base-view-1.png index 9c43990b2f3..f3b793d65ef 100644 Binary files a/src/features/rollup/arbitrum/pages/batches/__screenshots__/ArbitrumL2TxnBatches.pw.tsx_default_base-view-1.png and b/src/features/rollup/arbitrum/pages/batches/__screenshots__/ArbitrumL2TxnBatches.pw.tsx_default_base-view-1.png differ diff --git a/src/features/rollup/arbitrum/pages/batches/__screenshots__/ArbitrumL2TxnBatches.pw.tsx_default_mobile-base-view-1.png b/src/features/rollup/arbitrum/pages/batches/__screenshots__/ArbitrumL2TxnBatches.pw.tsx_default_mobile-base-view-1.png deleted file mode 100644 index 27e0ade3f45..00000000000 Binary files a/src/features/rollup/arbitrum/pages/batches/__screenshots__/ArbitrumL2TxnBatches.pw.tsx_default_mobile-base-view-1.png and /dev/null differ diff --git a/src/features/rollup/arbitrum/pages/deposits/ArbitrumL2Deposits.pw.tsx b/src/features/rollup/arbitrum/pages/deposits/ArbitrumL2Deposits.pw.tsx index debafc1697a..5c1e932b653 100644 --- a/src/features/rollup/arbitrum/pages/deposits/ArbitrumL2Deposits.pw.tsx +++ b/src/features/rollup/arbitrum/pages/deposits/ArbitrumL2Deposits.pw.tsx @@ -6,7 +6,7 @@ import { test, expect } from 'playwright/lib'; import * as depositsMock from '../../mocks/deposits'; import ArbitrumL2Deposits from './ArbitrumL2Deposits'; -test('base view +@mobile', async({ render, mockApiResponse, mockEnvs, mockTextAd }) => { +test('base view', async({ render, mockApiResponse, mockEnvs, mockTextAd }) => { test.slow(); await mockTextAd(); await mockEnvs(ENVS_MAP.arbitrumRollup); diff --git a/src/features/rollup/arbitrum/pages/deposits/__screenshots__/ArbitrumL2Deposits.pw.tsx_default_base-view-1.png b/src/features/rollup/arbitrum/pages/deposits/__screenshots__/ArbitrumL2Deposits.pw.tsx_default_base-view-1.png new file mode 100644 index 00000000000..9e05ffa2365 Binary files /dev/null and b/src/features/rollup/arbitrum/pages/deposits/__screenshots__/ArbitrumL2Deposits.pw.tsx_default_base-view-1.png differ diff --git a/src/features/rollup/arbitrum/pages/deposits/__screenshots__/ArbitrumL2Deposits.pw.tsx_default_base-view-mobile-1.png b/src/features/rollup/arbitrum/pages/deposits/__screenshots__/ArbitrumL2Deposits.pw.tsx_default_base-view-mobile-1.png deleted file mode 100644 index a2b26af7599..00000000000 Binary files a/src/features/rollup/arbitrum/pages/deposits/__screenshots__/ArbitrumL2Deposits.pw.tsx_default_base-view-mobile-1.png and /dev/null differ diff --git a/src/features/rollup/arbitrum/pages/deposits/__screenshots__/ArbitrumL2Deposits.pw.tsx_mobile_base-view-mobile-1.png b/src/features/rollup/arbitrum/pages/deposits/__screenshots__/ArbitrumL2Deposits.pw.tsx_mobile_base-view-mobile-1.png deleted file mode 100644 index d85dc9807c1..00000000000 Binary files a/src/features/rollup/arbitrum/pages/deposits/__screenshots__/ArbitrumL2Deposits.pw.tsx_mobile_base-view-mobile-1.png and /dev/null differ diff --git a/src/features/rollup/arbitrum/pages/home/LatestArbitrumDeposits.pw.tsx b/src/features/rollup/arbitrum/pages/home/LatestArbitrumDeposits.pw.tsx index 09e3258797a..e9a5f310b86 100644 --- a/src/features/rollup/arbitrum/pages/home/LatestArbitrumDeposits.pw.tsx +++ b/src/features/rollup/arbitrum/pages/home/LatestArbitrumDeposits.pw.tsx @@ -6,7 +6,7 @@ import { test, expect } from 'playwright/lib'; import * as depositMock from '../../mocks/deposits'; import LatestArbitrumDeposits from './LatestArbitrumDeposits'; -test('default view +@mobile', async({ render, mockApiResponse, mockEnvs }) => { +test('default view', async({ render, mockApiResponse, mockEnvs }) => { await mockEnvs(ENVS_MAP.arbitrumRollup); mockApiResponse('core:homepage_arbitrum_deposits', depositMock.latestDepositsResponse); const component = await render(); diff --git a/src/features/rollup/arbitrum/pages/home/LatestArbitrumDeposits.tsx b/src/features/rollup/arbitrum/pages/home/LatestArbitrumDeposits.tsx index b238411393a..b75490b5f91 100644 --- a/src/features/rollup/arbitrum/pages/home/LatestArbitrumDeposits.tsx +++ b/src/features/rollup/arbitrum/pages/home/LatestArbitrumDeposits.tsx @@ -13,14 +13,12 @@ import LatestTxsFallback from 'src/slices/home/pages/index/txs/LatestTxsFallback import LatestDeposits from 'src/features/rollup/common/pages/home/LatestDeposits'; -import useIsMobile from 'src/shared/hooks/useIsMobile'; import useGradualIncrement from 'src/shared/numbers/useGradualIncrement'; import { ARBITRUM_MESSAGES_ITEM } from '../../stubs'; const LatestArbitrumDeposits = () => { - const isMobile = useIsMobile(); - const itemsCount = isMobile ? 2 : 5; + const itemsCount = 5; const { data, isPlaceholderData, isError } = useApiQuery('core:homepage_arbitrum_deposits', { queryOptions: { placeholderData: { items: Array(itemsCount).fill(ARBITRUM_MESSAGES_ITEM) }, diff --git a/src/features/rollup/arbitrum/pages/home/__screenshots__/LatestArbitrumDeposits.pw.tsx_default_default-view-1.png b/src/features/rollup/arbitrum/pages/home/__screenshots__/LatestArbitrumDeposits.pw.tsx_default_default-view-1.png new file mode 100644 index 00000000000..e29f3089448 Binary files /dev/null and b/src/features/rollup/arbitrum/pages/home/__screenshots__/LatestArbitrumDeposits.pw.tsx_default_default-view-1.png differ diff --git a/src/features/rollup/arbitrum/pages/home/__screenshots__/LatestArbitrumDeposits.pw.tsx_default_default-view-mobile-1.png b/src/features/rollup/arbitrum/pages/home/__screenshots__/LatestArbitrumDeposits.pw.tsx_default_default-view-mobile-1.png deleted file mode 100644 index 9ee4962174f..00000000000 Binary files a/src/features/rollup/arbitrum/pages/home/__screenshots__/LatestArbitrumDeposits.pw.tsx_default_default-view-mobile-1.png and /dev/null differ diff --git a/src/features/rollup/arbitrum/pages/home/__screenshots__/LatestArbitrumDeposits.pw.tsx_mobile_default-view-mobile-1.png b/src/features/rollup/arbitrum/pages/home/__screenshots__/LatestArbitrumDeposits.pw.tsx_mobile_default-view-mobile-1.png deleted file mode 100644 index 291a1526e92..00000000000 Binary files a/src/features/rollup/arbitrum/pages/home/__screenshots__/LatestArbitrumDeposits.pw.tsx_mobile_default-view-mobile-1.png and /dev/null differ diff --git a/src/features/rollup/arbitrum/pages/home/__screenshots__/LatestArbitrumL2Batches.pw.tsx_dark-color-mode_default-view-mobile-dark-mode-1.png b/src/features/rollup/arbitrum/pages/home/__screenshots__/LatestArbitrumL2Batches.pw.tsx_dark-color-mode_default-view-mobile-dark-mode-1.png index 3a69b80e703..6e8de471063 100644 Binary files a/src/features/rollup/arbitrum/pages/home/__screenshots__/LatestArbitrumL2Batches.pw.tsx_dark-color-mode_default-view-mobile-dark-mode-1.png and b/src/features/rollup/arbitrum/pages/home/__screenshots__/LatestArbitrumL2Batches.pw.tsx_dark-color-mode_default-view-mobile-dark-mode-1.png differ diff --git a/src/features/rollup/arbitrum/pages/home/__screenshots__/LatestArbitrumL2Batches.pw.tsx_default_default-view-mobile-dark-mode-1.png b/src/features/rollup/arbitrum/pages/home/__screenshots__/LatestArbitrumL2Batches.pw.tsx_default_default-view-mobile-dark-mode-1.png index a877937de79..39d4890503e 100644 Binary files a/src/features/rollup/arbitrum/pages/home/__screenshots__/LatestArbitrumL2Batches.pw.tsx_default_default-view-mobile-dark-mode-1.png and b/src/features/rollup/arbitrum/pages/home/__screenshots__/LatestArbitrumL2Batches.pw.tsx_default_default-view-mobile-dark-mode-1.png differ diff --git a/src/features/rollup/arbitrum/pages/home/__screenshots__/LatestArbitrumL2Batches.pw.tsx_mobile_default-view-mobile-dark-mode-1.png b/src/features/rollup/arbitrum/pages/home/__screenshots__/LatestArbitrumL2Batches.pw.tsx_mobile_default-view-mobile-dark-mode-1.png index 4c459a5c7c5..443ea36ef13 100644 Binary files a/src/features/rollup/arbitrum/pages/home/__screenshots__/LatestArbitrumL2Batches.pw.tsx_mobile_default-view-mobile-dark-mode-1.png and b/src/features/rollup/arbitrum/pages/home/__screenshots__/LatestArbitrumL2Batches.pw.tsx_mobile_default-view-mobile-dark-mode-1.png differ diff --git a/src/features/rollup/arbitrum/pages/txn-withdrawals/ArbitrumL2TxnWithdrawals.pw.tsx b/src/features/rollup/arbitrum/pages/txn-withdrawals/ArbitrumL2TxnWithdrawals.pw.tsx index 062b7b6714c..5e668d641e6 100644 --- a/src/features/rollup/arbitrum/pages/txn-withdrawals/ArbitrumL2TxnWithdrawals.pw.tsx +++ b/src/features/rollup/arbitrum/pages/txn-withdrawals/ArbitrumL2TxnWithdrawals.pw.tsx @@ -14,7 +14,7 @@ const hooksConfig = { }, }; -test('base view +@mobile', async({ render, mockApiResponse, mockEnvs, mockTextAd }) => { +test('base view', async({ render, mockApiResponse, mockEnvs, mockTextAd }) => { await mockTextAd(); await mockEnvs(ENVS_MAP.arbitrumRollup); await mockApiResponse( diff --git a/src/features/rollup/arbitrum/pages/txn-withdrawals/ArbitrumL2TxnWithdrawals.tsx b/src/features/rollup/arbitrum/pages/txn-withdrawals/ArbitrumL2TxnWithdrawals.tsx index 7a56030a7c5..2c83e8d88f9 100644 --- a/src/features/rollup/arbitrum/pages/txn-withdrawals/ArbitrumL2TxnWithdrawals.tsx +++ b/src/features/rollup/arbitrum/pages/txn-withdrawals/ArbitrumL2TxnWithdrawals.tsx @@ -1,6 +1,6 @@ // SPDX-License-Identifier: LicenseRef-Blockscout -import { Box, chakra, Text } from '@chakra-ui/react'; +import { chakra, Text } from '@chakra-ui/react'; import { useRouter } from 'next/router'; import React from 'react'; @@ -13,12 +13,12 @@ import { layerLabels } from 'src/features/rollup/common/utils/layer'; import DataList from 'src/shared/lists/DataList'; import getQueryParamString from 'src/shared/router/get-query-param-string'; +import { TableContainerScrollable } from 'src/toolkit/chakra/table'; import { FilterInput } from 'src/toolkit/components/filters/FilterInput'; import { FormFieldError } from 'src/toolkit/components/forms/components/FormFieldError'; import { TRANSACTION_HASH_REGEXP } from 'src/toolkit/components/forms/validators/transaction'; import { ARBITRUM_L2_TXN_WITHDRAWALS_ITEM } from '../../stubs'; -import ArbitrumL2TxnWithdrawalsList from './ArbitrumL2TxnWithdrawalsList'; import ArbitrumL2TxnWithdrawalsTable from './ArbitrumL2TxnWithdrawalsTable'; const ArbitrumL2TxnWithdrawals = () => { @@ -70,14 +70,9 @@ const ArbitrumL2TxnWithdrawals = () => { }, [ handleSearchInputBlur ]); const content = data?.items ? ( - <> - - - - - - - + + + ) : null; return ( diff --git a/src/features/rollup/arbitrum/pages/txn-withdrawals/ArbitrumL2TxnWithdrawalsList.tsx b/src/features/rollup/arbitrum/pages/txn-withdrawals/ArbitrumL2TxnWithdrawalsList.tsx deleted file mode 100644 index e890fb60b04..00000000000 --- a/src/features/rollup/arbitrum/pages/txn-withdrawals/ArbitrumL2TxnWithdrawalsList.tsx +++ /dev/null @@ -1,31 +0,0 @@ -// SPDX-License-Identifier: LicenseRef-Blockscout - -import { Box } from '@chakra-ui/react'; -import React from 'react'; - -import type { schemas } from '@blockscout/api-types'; - -import ArbitrumL2TxnWithdrawalsListItem from './ArbitrumL2TxnWithdrawalsListItem'; - -interface Props { - data: Array; - isLoading: boolean; - txHash: string | undefined; -} - -const ArbitrumL2TxnWithdrawalsList = ({ data, isLoading, txHash }: Props) => { - return ( - - { data.map((item, index) => ( - - )) } - - ); -}; - -export default React.memo(ArbitrumL2TxnWithdrawalsList); diff --git a/src/features/rollup/arbitrum/pages/txn-withdrawals/ArbitrumL2TxnWithdrawalsListItem.tsx b/src/features/rollup/arbitrum/pages/txn-withdrawals/ArbitrumL2TxnWithdrawalsListItem.tsx deleted file mode 100644 index 5f7392b6e5a..00000000000 --- a/src/features/rollup/arbitrum/pages/txn-withdrawals/ArbitrumL2TxnWithdrawalsListItem.tsx +++ /dev/null @@ -1,76 +0,0 @@ -// SPDX-License-Identifier: LicenseRef-Blockscout - -import React from 'react'; - -import type { schemas } from '@blockscout/api-types'; - -import AddressEntityL1 from 'src/features/rollup/common/components/AddressEntityL1'; - -import config from 'src/config'; -import ListItemMobileGrid from 'src/shared/lists/ListItemMobileGrid'; - -import { Skeleton } from 'src/toolkit/chakra/skeleton'; - -import ArbitrumL2MessageStatus from '../../components/ArbitrumL2MessageStatus'; -import ArbitrumL2TxnWithdrawalsClaimButton from './ArbitrumL2TxnWithdrawalsClaimButton'; -import ArbitrumL2TxnWithdrawalsValue from './ArbitrumL2TxnWithdrawalsValue'; - -const rollupFeature = config.features.rollup; - -interface Props { - data: schemas['ArbitrumWithdrawal']; - isLoading: boolean; - txHash: string | undefined; -} - -const ArbitrumL2TxnWithdrawalsListItem = ({ data, isLoading, txHash }: Props) => { - if (!rollupFeature.isEnabled || rollupFeature.type !== 'arbitrum') { - return null; - } - - return ( - - - Message # - - { data.id } - - - Receiver - - - - - Value - - - - - - - Status - - - { (data.status === 'confirmed' || data.status === 'relayed') && ( - - ) } - - - - ); -}; - -export default ArbitrumL2TxnWithdrawalsListItem; diff --git a/src/features/rollup/arbitrum/pages/txn-withdrawals/ArbitrumL2TxnWithdrawalsTable.tsx b/src/features/rollup/arbitrum/pages/txn-withdrawals/ArbitrumL2TxnWithdrawalsTable.tsx index a07aacc914a..0c0339d6c1c 100644 --- a/src/features/rollup/arbitrum/pages/txn-withdrawals/ArbitrumL2TxnWithdrawalsTable.tsx +++ b/src/features/rollup/arbitrum/pages/txn-withdrawals/ArbitrumL2TxnWithdrawalsTable.tsx @@ -16,7 +16,7 @@ interface Props { const ArbitrumL2TxnWithdrawalsTable = ({ data, txHash, isLoading }: Props) => { return ( - + Message # diff --git a/src/features/rollup/arbitrum/pages/txn-withdrawals/__screenshots__/ArbitrumL2TxnWithdrawals.pw.tsx_default_base-view-mobile-1.png b/src/features/rollup/arbitrum/pages/txn-withdrawals/__screenshots__/ArbitrumL2TxnWithdrawals.pw.tsx_default_base-view-1.png similarity index 100% rename from src/features/rollup/arbitrum/pages/txn-withdrawals/__screenshots__/ArbitrumL2TxnWithdrawals.pw.tsx_default_base-view-mobile-1.png rename to src/features/rollup/arbitrum/pages/txn-withdrawals/__screenshots__/ArbitrumL2TxnWithdrawals.pw.tsx_default_base-view-1.png diff --git a/src/features/rollup/arbitrum/pages/txn-withdrawals/__screenshots__/ArbitrumL2TxnWithdrawals.pw.tsx_mobile_base-view-mobile-1.png b/src/features/rollup/arbitrum/pages/txn-withdrawals/__screenshots__/ArbitrumL2TxnWithdrawals.pw.tsx_mobile_base-view-mobile-1.png deleted file mode 100644 index 56f4c764714..00000000000 Binary files a/src/features/rollup/arbitrum/pages/txn-withdrawals/__screenshots__/ArbitrumL2TxnWithdrawals.pw.tsx_mobile_base-view-mobile-1.png and /dev/null differ diff --git a/src/features/rollup/arbitrum/pages/withdrawals/ArbitrumL2Withdrawals.pw.tsx b/src/features/rollup/arbitrum/pages/withdrawals/ArbitrumL2Withdrawals.pw.tsx index 04458b19cf1..9708541b2b0 100644 --- a/src/features/rollup/arbitrum/pages/withdrawals/ArbitrumL2Withdrawals.pw.tsx +++ b/src/features/rollup/arbitrum/pages/withdrawals/ArbitrumL2Withdrawals.pw.tsx @@ -6,7 +6,7 @@ import { test, expect } from 'playwright/lib'; import * as depositsMock from '../../mocks/withdrawals'; import ArbitrumL2Withdrawals from './ArbitrumL2Withdrawals'; -test('base view +@mobile', async({ render, mockApiResponse, mockEnvs, mockTextAd }) => { +test('base view', async({ render, mockApiResponse, mockEnvs, mockTextAd }) => { test.slow(); await mockTextAd(); await mockEnvs(ENVS_MAP.arbitrumRollup); diff --git a/src/features/rollup/arbitrum/pages/withdrawals/__screenshots__/ArbitrumL2Withdrawals.pw.tsx_default_base-view-1.png b/src/features/rollup/arbitrum/pages/withdrawals/__screenshots__/ArbitrumL2Withdrawals.pw.tsx_default_base-view-1.png new file mode 100644 index 00000000000..bfe4cdfd9b5 Binary files /dev/null and b/src/features/rollup/arbitrum/pages/withdrawals/__screenshots__/ArbitrumL2Withdrawals.pw.tsx_default_base-view-1.png differ diff --git a/src/features/rollup/arbitrum/pages/withdrawals/__screenshots__/ArbitrumL2Withdrawals.pw.tsx_default_base-view-mobile-1.png b/src/features/rollup/arbitrum/pages/withdrawals/__screenshots__/ArbitrumL2Withdrawals.pw.tsx_default_base-view-mobile-1.png deleted file mode 100644 index 780b452012d..00000000000 Binary files a/src/features/rollup/arbitrum/pages/withdrawals/__screenshots__/ArbitrumL2Withdrawals.pw.tsx_default_base-view-mobile-1.png and /dev/null differ diff --git a/src/features/rollup/arbitrum/pages/withdrawals/__screenshots__/ArbitrumL2Withdrawals.pw.tsx_mobile_base-view-mobile-1.png b/src/features/rollup/arbitrum/pages/withdrawals/__screenshots__/ArbitrumL2Withdrawals.pw.tsx_mobile_base-view-mobile-1.png deleted file mode 100644 index 753776dc422..00000000000 Binary files a/src/features/rollup/arbitrum/pages/withdrawals/__screenshots__/ArbitrumL2Withdrawals.pw.tsx_mobile_base-view-mobile-1.png and /dev/null differ diff --git a/src/features/rollup/common/pages/home/LatestDeposits.tsx b/src/features/rollup/common/pages/home/LatestDeposits.tsx index aa041bd3535..6728aae0e85 100644 --- a/src/features/rollup/common/pages/home/LatestDeposits.tsx +++ b/src/features/rollup/common/pages/home/LatestDeposits.tsx @@ -1,11 +1,6 @@ // SPDX-License-Identifier: LicenseRef-Blockscout -import { - Box, - Flex, - Grid, - GridItem, -} from '@chakra-ui/react'; +import { Box, Flex, VStack } from '@chakra-ui/react'; import { route } from 'nextjs-routes'; import React from 'react'; @@ -18,10 +13,12 @@ import TxEntityL1 from 'src/features/rollup/common/components/TxEntityL1'; import { layerLabels } from 'src/features/rollup/common/utils/layer'; import TimeWithTooltip from 'src/shared/date-and-time/TimeWithTooltip'; -import useIsMobile from 'src/shared/hooks/useIsMobile'; import { Link } from 'src/toolkit/chakra/link'; import { Skeleton } from 'src/toolkit/chakra/skeleton'; +import { TableBody, TableCell, TableContainerScrollable, TableRoot, TableRow } from 'src/toolkit/chakra/table'; + +const LATEST_DEPOSITS_TABLE_MIN_WIDTH = '750px'; interface DepositsItem { l1BlockNumber: number | null; @@ -43,8 +40,6 @@ interface ItemProps { }; const LatestDepositsItem = ({ item, isLoading }: ItemProps) => { - const isMobile = useIsMobile(); - const l1BlockLink = item.l1BlockNumber ? ( { ) : ( @@ -81,7 +76,7 @@ const LatestDepositsItem = ({ item, isLoading }: ItemProps) => { ) : ( { /> ); - const content = (() => { - if (isMobile) { - return ( - <> - - { l1BlockLink } - { item.timestamp ? ( - - ) : } - - - - { layerLabels.parent } txn - - { l1TxLink } - - { layerLabels.current } txn - - { l2TxLink } - - - ); - } - - return ( - - { l1BlockLink } - - { layerLabels.parent } txn - - { l1TxLink } - { item.timestamp ? ( - - ) : } - - { layerLabels.current } txn - - { l2TxLink } - - ); - })(); - return ( - - { content } - + + + + { l1BlockLink } + { item.timestamp && ( + + ) } + + + + + + { layerLabels.parent } txn + + + { layerLabels.current } txn + + + + + + { l1TxLink } + { l2TxLink } + + + ); }; @@ -166,22 +129,29 @@ const LatestDeposits = ({ isLoading, items, showSocketErrorAlert, socketItemsNum const depositsUrl = route({ pathname: '/deposits' }); return ( <> - - - { items.map(((item, index) => ( - + + - ))) } + + + { items.map(((item, index) => ( + + ))) } + + + View all deposits diff --git a/src/features/rollup/common/pages/home/LatestDepositsItem.tsx b/src/features/rollup/common/pages/home/LatestDepositsItem.tsx deleted file mode 100644 index fd81f9f3f7b..00000000000 --- a/src/features/rollup/common/pages/home/LatestDepositsItem.tsx +++ /dev/null @@ -1,129 +0,0 @@ -// SPDX-License-Identifier: LicenseRef-Blockscout - -import { - Box, - Flex, - Grid, -} from '@chakra-ui/react'; -import React from 'react'; - -import TxEntity from 'src/slices/tx/components/entity/TxEntity'; - -import BlockEntityL1 from 'src/features/rollup/common/components/BlockEntityL1'; -import TxEntityL1 from 'src/features/rollup/common/components/TxEntityL1'; -import { layerLabels } from 'src/features/rollup/common/utils/layer'; - -import TimeWithTooltip from 'src/shared/date-and-time/TimeWithTooltip'; -import useIsMobile from 'src/shared/hooks/useIsMobile'; - -import { Skeleton } from 'src/toolkit/chakra/skeleton'; - -type Props = { - l1BlockNumber: number; - l1TxHash: string; - l2TxHash: string | null; - timestamp: string | null; - isLoading?: boolean; -}; - -const LatestDepositsItem = ({ l1BlockNumber, l1TxHash, l2TxHash, timestamp, isLoading }: Props) => { - const isMobile = useIsMobile(); - - const l1BlockLink = ( - - ); - - const l1TxLink = ( - - ); - - const l2TxLink = l2TxHash ? ( - - ) : null; - - const content = (() => { - if (isMobile) { - return ( - <> - - { l1BlockLink } - - - - - { layerLabels.parent } txn - - { l1TxLink } - - { layerLabels.current } txn - - { l2TxLink } - - - ); - } - - return ( - - { l1BlockLink } - - { layerLabels.parent } txn - - { l1TxLink } - - - { layerLabels.current } txn - - { l2TxLink } - - ); - })(); - - return ( - - { content } - - ); -}; - -export default React.memo(LatestDepositsItem); diff --git a/src/features/rollup/optimism/mocks/dispute-games.ts b/src/features/rollup/optimism/mocks/dispute-games.ts index adb336ceabc..60b063827f6 100644 --- a/src/features/rollup/optimism/mocks/dispute-games.ts +++ b/src/features/rollup/optimism/mocks/dispute-games.ts @@ -8,7 +8,11 @@ export const data: operations['OptimismController.games']['json'] = { game_type: 0, index: 6662, l2_block_number: 12542890, +<<<<<<< HEAD l2_timestamp: null, +======= + l2_timestamp: '2022-05-27T01:13:00.000000Z', +>>>>>>> v2.12.0 resolved_at: null, status: 'In progress', }, @@ -18,7 +22,11 @@ export const data: operations['OptimismController.games']['json'] = { game_type: 0, index: 6662, l2_block_number: 12542890, +<<<<<<< HEAD l2_timestamp: null, +======= + l2_timestamp: '2022-05-27T01:13:00.000000Z', +>>>>>>> v2.12.0 resolved_at: '2022-05-27T01:13:48.000000Z', status: 'Defender wins', }, diff --git a/src/features/rollup/optimism/pages/batch-details/OptimisticL2TxnBatch.tsx b/src/features/rollup/optimism/pages/batch-details/OptimisticL2TxnBatch.tsx index 1da3d87f0e6..c50411ccf82 100644 --- a/src/features/rollup/optimism/pages/batch-details/OptimisticL2TxnBatch.tsx +++ b/src/features/rollup/optimism/pages/batch-details/OptimisticL2TxnBatch.tsx @@ -18,7 +18,7 @@ import throwOnAbsentParamError from 'src/shared/errors/throw-on-absent-param-err import throwOnResourceLoadError from 'src/shared/errors/throw-on-resource-load-error'; import useIsMobile from 'src/shared/hooks/useIsMobile'; import Pagination from 'src/shared/pagination/Pagination'; -import useQueryWithPages from 'src/shared/pagination/useQueryWithPages'; +import useApiPaginatedQuery from 'src/shared/pagination/useApiPaginatedQuery'; import { generateListStub } from 'src/shared/pagination/utils'; import getQueryParamString from 'src/shared/router/get-query-param-string'; @@ -45,7 +45,7 @@ const OptimisticL2TxnBatch = () => { const batchQuery = useBatchQuery(); - const batchTxsQuery = useQueryWithPages({ + const batchTxsQuery = useApiPaginatedQuery({ resourceName: 'core:optimistic_l2_txn_batch_txs', pathParams: { number: String(batchQuery.data?.number) }, options: { @@ -58,7 +58,7 @@ const OptimisticL2TxnBatch = () => { }, }); - const batchBlocksQuery = useQueryWithPages({ + const batchBlocksQuery = useApiPaginatedQuery({ resourceName: 'core:optimistic_l2_txn_batch_blocks', pathParams: { number: String(batchQuery.data?.number) }, options: { diff --git a/src/features/rollup/optimism/pages/batch-details/OptimisticL2TxnBatchBlobCelestia.tsx b/src/features/rollup/optimism/pages/batch-details/OptimisticL2TxnBatchBlobCelestia.tsx index 4b4aeef71ab..1426c3eb005 100644 --- a/src/features/rollup/optimism/pages/batch-details/OptimisticL2TxnBatchBlobCelestia.tsx +++ b/src/features/rollup/optimism/pages/batch-details/OptimisticL2TxnBatchBlobCelestia.tsx @@ -11,7 +11,8 @@ import { layerLabels } from 'src/features/rollup/common/utils/layer'; import DetailedInfoTimestamp from 'src/shared/detailed-info/DetailedInfoTimestamp'; import CopyToClipboard from 'src/shared/texts/CopyToClipboard'; -import HashStringShortenDynamic from 'src/shared/texts/HashStringShortenDynamic'; + +import { Truncate } from 'src/toolkit/components/truncation/Truncate'; import OptimisticL2TxnBatchBlobWrapper from './OptimisticL2TxnBatchBlobWrapper'; @@ -29,7 +30,7 @@ const OptimisticL2TxnBatchBlobCelestia = ({ blobs, isLoading }: Props) => { Commitment - + diff --git a/src/features/rollup/optimism/pages/batch-details/OptimisticL2TxnBatchBlobEigenda.tsx b/src/features/rollup/optimism/pages/batch-details/OptimisticL2TxnBatchBlobEigenda.tsx index 686e11867a9..f0ecca2f1a1 100644 --- a/src/features/rollup/optimism/pages/batch-details/OptimisticL2TxnBatchBlobEigenda.tsx +++ b/src/features/rollup/optimism/pages/batch-details/OptimisticL2TxnBatchBlobEigenda.tsx @@ -10,7 +10,8 @@ import { layerLabels } from 'src/features/rollup/common/utils/layer'; import DetailedInfoTimestamp from 'src/shared/detailed-info/DetailedInfoTimestamp'; import CopyToClipboard from 'src/shared/texts/CopyToClipboard'; -import HashStringShortenDynamic from 'src/shared/texts/HashStringShortenDynamic'; + +import { Truncate } from 'src/toolkit/components/truncation/Truncate'; import OptimisticL2TxnBatchBlobWrapper from './OptimisticL2TxnBatchBlobWrapper'; @@ -28,7 +29,7 @@ const OptimisticL2TxnBatchBlobEigenda = ({ blobs, isLoading }: Props) => { Cert - + diff --git a/src/features/rollup/optimism/pages/batch-details/__screenshots__/OptimisticL2TxnBatchDetails.pw.tsx_default_EIP-4844-blob-container-mobile-1.png b/src/features/rollup/optimism/pages/batch-details/__screenshots__/OptimisticL2TxnBatchDetails.pw.tsx_default_EIP-4844-blob-container-mobile-1.png index f8b71aa4046..762f581b786 100644 Binary files a/src/features/rollup/optimism/pages/batch-details/__screenshots__/OptimisticL2TxnBatchDetails.pw.tsx_default_EIP-4844-blob-container-mobile-1.png and b/src/features/rollup/optimism/pages/batch-details/__screenshots__/OptimisticL2TxnBatchDetails.pw.tsx_default_EIP-4844-blob-container-mobile-1.png differ diff --git a/src/features/rollup/optimism/pages/batch-details/__screenshots__/OptimisticL2TxnBatchDetails.pw.tsx_default_Eigenda-blob-container-mobile-1.png b/src/features/rollup/optimism/pages/batch-details/__screenshots__/OptimisticL2TxnBatchDetails.pw.tsx_default_Eigenda-blob-container-mobile-1.png index 63f790da5d8..36fdf820edd 100644 Binary files a/src/features/rollup/optimism/pages/batch-details/__screenshots__/OptimisticL2TxnBatchDetails.pw.tsx_default_Eigenda-blob-container-mobile-1.png and b/src/features/rollup/optimism/pages/batch-details/__screenshots__/OptimisticL2TxnBatchDetails.pw.tsx_default_Eigenda-blob-container-mobile-1.png differ diff --git a/src/features/rollup/optimism/pages/batch-details/__screenshots__/OptimisticL2TxnBatchDetails.pw.tsx_default_call-data-blob-container-mobile-1.png b/src/features/rollup/optimism/pages/batch-details/__screenshots__/OptimisticL2TxnBatchDetails.pw.tsx_default_call-data-blob-container-mobile-1.png index 5a33e1b8e7f..d7463d8df27 100644 Binary files a/src/features/rollup/optimism/pages/batch-details/__screenshots__/OptimisticL2TxnBatchDetails.pw.tsx_default_call-data-blob-container-mobile-1.png and b/src/features/rollup/optimism/pages/batch-details/__screenshots__/OptimisticL2TxnBatchDetails.pw.tsx_default_call-data-blob-container-mobile-1.png differ diff --git a/src/features/rollup/optimism/pages/batch-details/__screenshots__/OptimisticL2TxnBatchDetails.pw.tsx_default_celestia-blob-container-mobile-1.png b/src/features/rollup/optimism/pages/batch-details/__screenshots__/OptimisticL2TxnBatchDetails.pw.tsx_default_celestia-blob-container-mobile-1.png index f7cbe767c2f..454e2c2b041 100644 Binary files a/src/features/rollup/optimism/pages/batch-details/__screenshots__/OptimisticL2TxnBatchDetails.pw.tsx_default_celestia-blob-container-mobile-1.png and b/src/features/rollup/optimism/pages/batch-details/__screenshots__/OptimisticL2TxnBatchDetails.pw.tsx_default_celestia-blob-container-mobile-1.png differ diff --git a/src/features/rollup/optimism/pages/batch-details/__screenshots__/OptimisticL2TxnBatchDetails.pw.tsx_mobile_EIP-4844-blob-container-mobile-1.png b/src/features/rollup/optimism/pages/batch-details/__screenshots__/OptimisticL2TxnBatchDetails.pw.tsx_mobile_EIP-4844-blob-container-mobile-1.png index 17c0a931063..a2065e889de 100644 Binary files a/src/features/rollup/optimism/pages/batch-details/__screenshots__/OptimisticL2TxnBatchDetails.pw.tsx_mobile_EIP-4844-blob-container-mobile-1.png and b/src/features/rollup/optimism/pages/batch-details/__screenshots__/OptimisticL2TxnBatchDetails.pw.tsx_mobile_EIP-4844-blob-container-mobile-1.png differ diff --git a/src/features/rollup/optimism/pages/batch-details/__screenshots__/OptimisticL2TxnBatchDetails.pw.tsx_mobile_Eigenda-blob-container-mobile-1.png b/src/features/rollup/optimism/pages/batch-details/__screenshots__/OptimisticL2TxnBatchDetails.pw.tsx_mobile_Eigenda-blob-container-mobile-1.png index 0e45d43f8ad..4135b433db8 100644 Binary files a/src/features/rollup/optimism/pages/batch-details/__screenshots__/OptimisticL2TxnBatchDetails.pw.tsx_mobile_Eigenda-blob-container-mobile-1.png and b/src/features/rollup/optimism/pages/batch-details/__screenshots__/OptimisticL2TxnBatchDetails.pw.tsx_mobile_Eigenda-blob-container-mobile-1.png differ diff --git a/src/features/rollup/optimism/pages/batch-details/__screenshots__/OptimisticL2TxnBatchDetails.pw.tsx_mobile_call-data-blob-container-mobile-1.png b/src/features/rollup/optimism/pages/batch-details/__screenshots__/OptimisticL2TxnBatchDetails.pw.tsx_mobile_call-data-blob-container-mobile-1.png index e97ef3bf48b..e0a629510ce 100644 Binary files a/src/features/rollup/optimism/pages/batch-details/__screenshots__/OptimisticL2TxnBatchDetails.pw.tsx_mobile_call-data-blob-container-mobile-1.png and b/src/features/rollup/optimism/pages/batch-details/__screenshots__/OptimisticL2TxnBatchDetails.pw.tsx_mobile_call-data-blob-container-mobile-1.png differ diff --git a/src/features/rollup/optimism/pages/batch-details/__screenshots__/OptimisticL2TxnBatchDetails.pw.tsx_mobile_celestia-blob-container-mobile-1.png b/src/features/rollup/optimism/pages/batch-details/__screenshots__/OptimisticL2TxnBatchDetails.pw.tsx_mobile_celestia-blob-container-mobile-1.png index 8ab97caee23..9f235d38828 100644 Binary files a/src/features/rollup/optimism/pages/batch-details/__screenshots__/OptimisticL2TxnBatchDetails.pw.tsx_mobile_celestia-blob-container-mobile-1.png and b/src/features/rollup/optimism/pages/batch-details/__screenshots__/OptimisticL2TxnBatchDetails.pw.tsx_mobile_celestia-blob-container-mobile-1.png differ diff --git a/src/features/rollup/optimism/pages/batch-details/useBatchQuery.tsx b/src/features/rollup/optimism/pages/batch-details/useBatchQuery.ts similarity index 100% rename from src/features/rollup/optimism/pages/batch-details/useBatchQuery.tsx rename to src/features/rollup/optimism/pages/batch-details/useBatchQuery.ts diff --git a/src/features/rollup/optimism/pages/batches/OptimisticL2TxnBatches.pw.tsx b/src/features/rollup/optimism/pages/batches/OptimisticL2TxnBatches.pw.tsx index 3b3c9479391..0f33c877e4e 100644 --- a/src/features/rollup/optimism/pages/batches/OptimisticL2TxnBatches.pw.tsx +++ b/src/features/rollup/optimism/pages/batches/OptimisticL2TxnBatches.pw.tsx @@ -7,10 +7,7 @@ import { test, expect } from 'playwright/lib'; import OptimisticL2TxnBatches from './OptimisticL2TxnBatches'; -test('base view +@mobile', async({ render, mockTextAd, mockEnvs, mockApiResponse }) => { - // test on mobile is flaky - // my assumption is there is not enough time to calculate hashes truncation so component is unstable - // so I raised the test timeout to check if it helps +test('base view', async({ render, mockTextAd, mockEnvs, mockApiResponse }) => { test.slow(); await mockTextAd(); await mockEnvs(ENVS_MAP.optimisticRollup); diff --git a/src/features/rollup/optimism/pages/batches/OptimisticL2TxnBatches.tsx b/src/features/rollup/optimism/pages/batches/OptimisticL2TxnBatches.tsx index 7f57e717e5b..3ed74c91621 100644 --- a/src/features/rollup/optimism/pages/batches/OptimisticL2TxnBatches.tsx +++ b/src/features/rollup/optimism/pages/batches/OptimisticL2TxnBatches.tsx @@ -1,6 +1,6 @@ // SPDX-License-Identifier: LicenseRef-Blockscout -import { Box, Text } from '@chakra-ui/react'; +import { Text } from '@chakra-ui/react'; import React from 'react'; import useApiQuery from 'src/api/hooks/useApiQuery'; @@ -8,19 +8,19 @@ import useApiQuery from 'src/api/hooks/useApiQuery'; import { ACTION_BAR_HEIGHT_DESKTOP } from 'src/shell/page/action-bar/ActionBar'; import PageTitle from 'src/shell/page/title/PageTitle'; -import OptimisticL2TxnBatchesList from 'src/features/rollup/optimism/pages/batches/OptimisticL2TxnBatchesList'; import OptimisticL2TxnBatchesTable from 'src/features/rollup/optimism/pages/batches/OptimisticL2TxnBatchesTable'; import { L2_TXN_BATCHES_ITEM } from 'src/features/rollup/optimism/stubs'; import DataList from 'src/shared/lists/DataList'; import StickyPaginationWithText from 'src/shared/pagination/StickyPaginationWithText'; -import useQueryWithPages from 'src/shared/pagination/useQueryWithPages'; +import useApiPaginatedQuery from 'src/shared/pagination/useApiPaginatedQuery'; import { generateListStub } from 'src/shared/pagination/utils'; import { Skeleton } from 'src/toolkit/chakra/skeleton'; +import { TableContainerScrollable } from 'src/toolkit/chakra/table'; const OptimisticL2TxnBatches = () => { - const { data, isError, isPlaceholderData, pagination, queryHash } = useQueryWithPages({ + const { data, isError, isInitialLoading, isTransitioning, pagination, queryHash } = useApiPaginatedQuery({ resourceName: 'core:optimistic_l2_txn_batches', options: { placeholderData: generateListStub<'core:optimistic_l2_txn_batches'>( @@ -43,19 +43,14 @@ const OptimisticL2TxnBatches = () => { }); const content = data?.items ? ( - <> - - - - - - - + + + ) : null; const text = (() => { @@ -64,7 +59,7 @@ const OptimisticL2TxnBatches = () => { } return ( - + Txn batch #{ data.items[0].number } to #{ data.items[data.items.length - 1].number } @@ -83,6 +78,7 @@ const OptimisticL2TxnBatches = () => { itemsNum={ data?.items?.length } emptyText="There are no txn batches." actionBar={ actionBar } + isTransitioning={ isTransitioning } > { content } diff --git a/src/features/rollup/optimism/pages/batches/OptimisticL2TxnBatchesList.tsx b/src/features/rollup/optimism/pages/batches/OptimisticL2TxnBatchesList.tsx deleted file mode 100644 index a81b9ca24d6..00000000000 --- a/src/features/rollup/optimism/pages/batches/OptimisticL2TxnBatchesList.tsx +++ /dev/null @@ -1,37 +0,0 @@ -// SPDX-License-Identifier: LicenseRef-Blockscout - -import { Box } from '@chakra-ui/react'; -import React from 'react'; - -import type { schemas } from '@blockscout/api-types'; - -import useLazyRenderedList from 'src/shared/lists/useLazyRenderedList'; - -import OptimisticL2TxnBatchesListItem from './OptimisticL2TxnBatchesListItem'; - -type Props = { - items: Array; - isLoading?: boolean; - resetKey?: string; -}; - -const OptimisticL2TxnBatchesList = ({ items, isLoading, resetKey }: Props) => { - const { cutRef, renderedItemsNum } = useLazyRenderedList({ list: items, isEnabled: !isLoading, resetKey }); - - return ( - <> - - { items.slice(0, renderedItemsNum).map((item, index) => ( - - )) } - - - - ); -}; - -export default OptimisticL2TxnBatchesList; diff --git a/src/features/rollup/optimism/pages/batches/OptimisticL2TxnBatchesListItem.tsx b/src/features/rollup/optimism/pages/batches/OptimisticL2TxnBatchesListItem.tsx deleted file mode 100644 index ce35be4eaef..00000000000 --- a/src/features/rollup/optimism/pages/batches/OptimisticL2TxnBatchesListItem.tsx +++ /dev/null @@ -1,89 +0,0 @@ -// SPDX-License-Identifier: LicenseRef-Blockscout - -import { route } from 'nextjs-routes'; -import React from 'react'; - -import type { schemas } from '@blockscout/api-types'; - -import BatchEntityL2 from 'src/features/rollup/common/components/BatchEntityL2'; -import { layerLabels } from 'src/features/rollup/common/utils/layer'; -import OptimisticL2TxnBatchDA from 'src/features/rollup/optimism/components/TxnBatchDA'; - -import config from 'src/config'; -import TimeWithTooltip from 'src/shared/date-and-time/TimeWithTooltip'; -import ListItemMobileGrid from 'src/shared/lists/ListItemMobileGrid'; - -import { Link } from 'src/toolkit/chakra/link'; -import { Skeleton } from 'src/toolkit/chakra/skeleton'; - -const rollupFeature = config.features.rollup; - -type Props = { item: schemas['OptimismBatch']; isLoading?: boolean }; - -const OptimisticL2TxnBatchesListItem = ({ item, isLoading }: Props) => { - if (!rollupFeature.isEnabled || rollupFeature.type !== 'optimistic') { - return null; - } - - return ( - - - Batch ID - - - - - { item.batch_data_container && ( - <> - - Storage - - - - - - ) } - - Age - - - - - { layerLabels.parent } txn count - - - { item.l1_transaction_hashes.length } - - - - { layerLabels.current } blocks - - - { item.l2_end_block_number - item.l2_start_block_number + 1 } - - - - Txn - - - { item.transactions_count } - - - - - ); -}; - -export default OptimisticL2TxnBatchesListItem; diff --git a/src/features/rollup/optimism/pages/batches/__screenshots__/OptimisticL2TxnBatches.pw.tsx_default_base-view-1.png b/src/features/rollup/optimism/pages/batches/__screenshots__/OptimisticL2TxnBatches.pw.tsx_default_base-view-1.png new file mode 100644 index 00000000000..f3eb3715d4e Binary files /dev/null and b/src/features/rollup/optimism/pages/batches/__screenshots__/OptimisticL2TxnBatches.pw.tsx_default_base-view-1.png differ diff --git a/src/features/rollup/optimism/pages/batches/__screenshots__/OptimisticL2TxnBatches.pw.tsx_default_base-view-mobile-1.png b/src/features/rollup/optimism/pages/batches/__screenshots__/OptimisticL2TxnBatches.pw.tsx_default_base-view-mobile-1.png deleted file mode 100644 index de1d2599ffc..00000000000 Binary files a/src/features/rollup/optimism/pages/batches/__screenshots__/OptimisticL2TxnBatches.pw.tsx_default_base-view-mobile-1.png and /dev/null differ diff --git a/src/features/rollup/optimism/pages/batches/__screenshots__/OptimisticL2TxnBatches.pw.tsx_mobile_base-view-mobile-1.png b/src/features/rollup/optimism/pages/batches/__screenshots__/OptimisticL2TxnBatches.pw.tsx_mobile_base-view-mobile-1.png deleted file mode 100644 index 734370e5222..00000000000 Binary files a/src/features/rollup/optimism/pages/batches/__screenshots__/OptimisticL2TxnBatches.pw.tsx_mobile_base-view-mobile-1.png and /dev/null differ diff --git a/src/features/rollup/optimism/pages/deposits/OptimisticDepositsList.tsx b/src/features/rollup/optimism/pages/deposits/OptimisticDepositsList.tsx deleted file mode 100644 index 811cc3bf7c5..00000000000 --- a/src/features/rollup/optimism/pages/deposits/OptimisticDepositsList.tsx +++ /dev/null @@ -1,37 +0,0 @@ -// SPDX-License-Identifier: LicenseRef-Blockscout - -import { Box } from '@chakra-ui/react'; -import React from 'react'; - -import type { schemas } from '@blockscout/api-types'; - -import useLazyRenderedList from 'src/shared/lists/useLazyRenderedList'; - -import OptimisticDepositsListItem from './OptimisticDepositsListItem'; - -type Props = { - items: Array; - isLoading?: boolean; - resetKey?: string; -}; - -const OptimisticDepositsList = ({ items, isLoading, resetKey }: Props) => { - const { cutRef, renderedItemsNum } = useLazyRenderedList({ list: items, isEnabled: !isLoading, resetKey }); - - return ( - <> - - { items.slice(0, renderedItemsNum).map((item, index) => ( - - )) } - - - - ); -}; - -export default OptimisticDepositsList; diff --git a/src/features/rollup/optimism/pages/deposits/OptimisticDepositsListItem.tsx b/src/features/rollup/optimism/pages/deposits/OptimisticDepositsListItem.tsx deleted file mode 100644 index b6f10d769d8..00000000000 --- a/src/features/rollup/optimism/pages/deposits/OptimisticDepositsListItem.tsx +++ /dev/null @@ -1,89 +0,0 @@ -// SPDX-License-Identifier: LicenseRef-Blockscout - -import BigNumber from 'bignumber.js'; -import React from 'react'; - -import type { schemas } from '@blockscout/api-types'; - -import TxEntity from 'src/slices/tx/components/entity/TxEntity'; - -import AddressEntityL1 from 'src/features/rollup/common/components/AddressEntityL1'; -import BlockEntityL1 from 'src/features/rollup/common/components/BlockEntityL1'; -import TxEntityL1 from 'src/features/rollup/common/components/TxEntityL1'; -import { layerLabels } from 'src/features/rollup/common/utils/layer'; - -import config from 'src/config'; -import TimeWithTooltip from 'src/shared/date-and-time/TimeWithTooltip'; -import ListItemMobileGrid from 'src/shared/lists/ListItemMobileGrid'; - -import { Skeleton } from 'src/toolkit/chakra/skeleton'; - -const rollupFeature = config.features.rollup; - -type Props = { item: schemas['OptimismDeposit']; isLoading?: boolean }; - -const OptimisticDepositsListItem = ({ item, isLoading }: Props) => { - if (!rollupFeature.isEnabled || rollupFeature.type !== 'optimistic') { - return null; - } - - return ( - - - { layerLabels.parent } block No - - - - - { layerLabels.current } txn hash - - - - - Age - - - - - { layerLabels.parent } txn hash - - - - - { layerLabels.parent } txn origin - - - - - Gas limit - - { BigNumber(item.l2_transaction_gas_limit).toFormat() } - - - - ); -}; - -export default OptimisticDepositsListItem; diff --git a/src/features/rollup/optimism/pages/deposits/OptimisticL2Deposits.pw.tsx b/src/features/rollup/optimism/pages/deposits/OptimisticL2Deposits.pw.tsx index d3d2917292c..c21ca12179d 100644 --- a/src/features/rollup/optimism/pages/deposits/OptimisticL2Deposits.pw.tsx +++ b/src/features/rollup/optimism/pages/deposits/OptimisticL2Deposits.pw.tsx @@ -7,10 +7,7 @@ import { test, expect } from 'playwright/lib'; import OptimisticL2Deposits from './OptimisticL2Deposits'; -test('base view +@mobile', async({ render, mockEnvs, mockTextAd, mockApiResponse }) => { - // test on mobile is flaky - // my assumption is there is not enough time to calculate hashes truncation so component is unstable - // so I raised the test timeout to check if it helps +test('base view', async({ render, mockEnvs, mockTextAd, mockApiResponse }) => { test.slow(); await mockEnvs(ENVS_MAP.optimisticRollup); await mockTextAd(); diff --git a/src/features/rollup/optimism/pages/deposits/OptimisticL2Deposits.tsx b/src/features/rollup/optimism/pages/deposits/OptimisticL2Deposits.tsx index 3c83a9a0a8a..46c271ebaa6 100644 --- a/src/features/rollup/optimism/pages/deposits/OptimisticL2Deposits.tsx +++ b/src/features/rollup/optimism/pages/deposits/OptimisticL2Deposits.tsx @@ -1,6 +1,5 @@ // SPDX-License-Identifier: LicenseRef-Blockscout -import { Box } from '@chakra-ui/react'; import React from 'react'; import useApiQuery from 'src/api/hooks/useApiQuery'; @@ -9,20 +8,20 @@ import { ACTION_BAR_HEIGHT_DESKTOP } from 'src/shell/page/action-bar/ActionBar'; import PageTitle from 'src/shell/page/title/PageTitle'; import { layerLabels } from 'src/features/rollup/common/utils/layer'; -import OptimisticDepositsList from 'src/features/rollup/optimism/pages/deposits/OptimisticDepositsList'; import OptimisticDepositsTable from 'src/features/rollup/optimism/pages/deposits/OptimisticDepositsTable'; import { L2_DEPOSIT_ITEM } from 'src/features/rollup/optimism/stubs'; import DataList from 'src/shared/lists/DataList'; import StickyPaginationWithText from 'src/shared/pagination/StickyPaginationWithText'; -import useQueryWithPages from 'src/shared/pagination/useQueryWithPages'; +import useApiPaginatedQuery from 'src/shared/pagination/useApiPaginatedQuery'; import { generateListStub } from 'src/shared/pagination/utils'; import { Skeleton } from 'src/toolkit/chakra/skeleton'; +import { TableContainerScrollable } from 'src/toolkit/chakra/table'; import { rightLineArrow, nbsp } from 'src/toolkit/utils/htmlEntities'; const OptimisticL2Deposits = () => { - const { data, isError, isPlaceholderData, pagination, queryHash } = useQueryWithPages({ + const { data, isError, isInitialLoading, isTransitioning, pagination, queryHash } = useApiPaginatedQuery({ resourceName: 'core:optimistic_l2_deposits', options: { placeholderData: generateListStub<'core:optimistic_l2_deposits'>( @@ -46,19 +45,14 @@ const OptimisticL2Deposits = () => { }); const content = data?.items ? ( - <> - - - - - - - + + + ) : null; const text = (() => { @@ -83,6 +77,7 @@ const OptimisticL2Deposits = () => { itemsNum={ data?.items?.length } emptyText="There are no deposits." actionBar={ actionBar } + isTransitioning={ isTransitioning } > { content } diff --git a/src/features/rollup/optimism/pages/deposits/__screenshots__/OptimisticL2Deposits.pw.tsx_default_base-view-1.png b/src/features/rollup/optimism/pages/deposits/__screenshots__/OptimisticL2Deposits.pw.tsx_default_base-view-1.png new file mode 100644 index 00000000000..a67e14b2961 Binary files /dev/null and b/src/features/rollup/optimism/pages/deposits/__screenshots__/OptimisticL2Deposits.pw.tsx_default_base-view-1.png differ diff --git a/src/features/rollup/optimism/pages/deposits/__screenshots__/OptimisticL2Deposits.pw.tsx_default_base-view-mobile-1.png b/src/features/rollup/optimism/pages/deposits/__screenshots__/OptimisticL2Deposits.pw.tsx_default_base-view-mobile-1.png deleted file mode 100644 index 0638b8fe3ba..00000000000 Binary files a/src/features/rollup/optimism/pages/deposits/__screenshots__/OptimisticL2Deposits.pw.tsx_default_base-view-mobile-1.png and /dev/null differ diff --git a/src/features/rollup/optimism/pages/deposits/__screenshots__/OptimisticL2Deposits.pw.tsx_mobile_base-view-mobile-1.png b/src/features/rollup/optimism/pages/deposits/__screenshots__/OptimisticL2Deposits.pw.tsx_mobile_base-view-mobile-1.png deleted file mode 100644 index 30a0fc058a6..00000000000 Binary files a/src/features/rollup/optimism/pages/deposits/__screenshots__/OptimisticL2Deposits.pw.tsx_mobile_base-view-mobile-1.png and /dev/null differ diff --git a/src/features/rollup/optimism/pages/dispute-games/OptimisticL2DisputeGames.pw.tsx b/src/features/rollup/optimism/pages/dispute-games/OptimisticL2DisputeGames.pw.tsx index 8c229ca5c81..07b3b324900 100644 --- a/src/features/rollup/optimism/pages/dispute-games/OptimisticL2DisputeGames.pw.tsx +++ b/src/features/rollup/optimism/pages/dispute-games/OptimisticL2DisputeGames.pw.tsx @@ -7,7 +7,7 @@ import { test, expect } from 'playwright/lib'; import OptimisticL2DisputeGames from './OptimisticL2DisputeGames'; -test('base view +@mobile', async({ render, mockEnvs, mockTextAd, mockApiResponse }) => { +test('base view', async({ render, mockEnvs, mockTextAd, mockApiResponse }) => { test.slow(); await mockEnvs(ENVS_MAP.optimisticRollup); await mockTextAd(); diff --git a/src/features/rollup/optimism/pages/dispute-games/OptimisticL2DisputeGames.tsx b/src/features/rollup/optimism/pages/dispute-games/OptimisticL2DisputeGames.tsx index ef07f004723..43988dde042 100644 --- a/src/features/rollup/optimism/pages/dispute-games/OptimisticL2DisputeGames.tsx +++ b/src/features/rollup/optimism/pages/dispute-games/OptimisticL2DisputeGames.tsx @@ -1,6 +1,6 @@ // SPDX-License-Identifier: LicenseRef-Blockscout -import { Box, Text } from '@chakra-ui/react'; +import { Text } from '@chakra-ui/react'; import React from 'react'; import useApiQuery from 'src/api/hooks/useApiQuery'; @@ -12,16 +12,16 @@ import { L2_DISPUTE_GAMES_ITEM } from 'src/features/rollup/optimism/stubs'; import DataList from 'src/shared/lists/DataList'; import StickyPaginationWithText from 'src/shared/pagination/StickyPaginationWithText'; -import useQueryWithPages from 'src/shared/pagination/useQueryWithPages'; +import useApiPaginatedQuery from 'src/shared/pagination/useApiPaginatedQuery'; import { generateListStub } from 'src/shared/pagination/utils'; import { Skeleton } from 'src/toolkit/chakra/skeleton'; +import { TableContainerScrollable } from 'src/toolkit/chakra/table'; -import OptimisticL2DisputeGamesList from './OptimisticL2DisputeGamesList'; import OptimisticL2DisputeGamesTable from './OptimisticL2DisputeGamesTable'; const OptimisticL2DisputeGames = () => { - const { data, isError, isPlaceholderData, pagination, queryHash } = useQueryWithPages({ + const { data, isError, isInitialLoading, isTransitioning, pagination, queryHash } = useApiPaginatedQuery({ resourceName: 'core:optimistic_l2_dispute_games', options: { placeholderData: generateListStub<'core:optimistic_l2_dispute_games'>( @@ -44,19 +44,14 @@ const OptimisticL2DisputeGames = () => { }); const content = data?.items ? ( - <> - - - - - - - + + + ) : null; const text = (() => { @@ -65,7 +60,7 @@ const OptimisticL2DisputeGames = () => { } return ( - + Dispute game index #{ data.items[0].index } to #{ data.items[data.items.length - 1].index } @@ -84,6 +79,7 @@ const OptimisticL2DisputeGames = () => { itemsNum={ data?.items.length } emptyText="There are no dispute games." actionBar={ actionBar } + isTransitioning={ isTransitioning } > { content } diff --git a/src/features/rollup/optimism/pages/dispute-games/OptimisticL2DisputeGamesList.tsx b/src/features/rollup/optimism/pages/dispute-games/OptimisticL2DisputeGamesList.tsx deleted file mode 100644 index ef6b07bb4b6..00000000000 --- a/src/features/rollup/optimism/pages/dispute-games/OptimisticL2DisputeGamesList.tsx +++ /dev/null @@ -1,37 +0,0 @@ -// SPDX-License-Identifier: LicenseRef-Blockscout - -import { Box } from '@chakra-ui/react'; -import React from 'react'; - -import type { schemas } from '@blockscout/api-types'; - -import useLazyRenderedList from 'src/shared/lists/useLazyRenderedList'; - -import OptimisticL2DisputeGamesListItem from './OptimisticL2DisputeGamesListItem'; - -type Props = { - items: Array; - isLoading?: boolean; - resetKey?: string; -}; - -const OptimisticL2DisputeGamesList = ({ items, isLoading, resetKey }: Props) => { - const { cutRef, renderedItemsNum } = useLazyRenderedList({ list: items, isEnabled: !isLoading, resetKey }); - - return ( - <> - - { items.slice(0, renderedItemsNum).map((item, index) => ( - - )) } - - - - ); -}; - -export default OptimisticL2DisputeGamesList; diff --git a/src/features/rollup/optimism/pages/dispute-games/OptimisticL2DisputeGamesTableItem.tsx b/src/features/rollup/optimism/pages/dispute-games/OptimisticL2DisputeGamesTableItem.tsx index 4a64fcef501..fa8c14ecdb6 100644 --- a/src/features/rollup/optimism/pages/dispute-games/OptimisticL2DisputeGamesTableItem.tsx +++ b/src/features/rollup/optimism/pages/dispute-games/OptimisticL2DisputeGamesTableItem.tsx @@ -9,10 +9,12 @@ import config from 'src/config'; import { getFeaturePayload } from 'src/config/utils/features'; import TimeWithTooltip from 'src/shared/date-and-time/TimeWithTooltip'; import CopyToClipboard from 'src/shared/texts/CopyToClipboard'; -import HashStringShorten from 'src/shared/texts/HashStringShorten'; import { Skeleton } from 'src/toolkit/chakra/skeleton'; import { TableCell, TableRow } from 'src/toolkit/chakra/table'; +import { Truncate } from 'src/toolkit/components/truncation/Truncate'; + +import OptimisticL2DisputeGameL2Position from './OptimisticL2DisputeGameL2Position'; import OptimisticL2DisputeGameL2Position from './OptimisticL2DisputeGameL2Position'; @@ -34,7 +36,7 @@ const OptimisticL2DisputeGamesTableItem = ({ item, isLoading }: Props) => { - + diff --git a/src/features/rollup/optimism/pages/dispute-games/__screenshots__/OptimisticL2DisputeGames.pw.tsx_default_base-view-1.png b/src/features/rollup/optimism/pages/dispute-games/__screenshots__/OptimisticL2DisputeGames.pw.tsx_default_base-view-1.png new file mode 100644 index 00000000000..537facaf6b8 Binary files /dev/null and b/src/features/rollup/optimism/pages/dispute-games/__screenshots__/OptimisticL2DisputeGames.pw.tsx_default_base-view-1.png differ diff --git a/src/features/rollup/optimism/pages/home/LatestOptimisticDeposits.pw.tsx b/src/features/rollup/optimism/pages/home/LatestOptimisticDeposits.pw.tsx index 00ddda31171..34a3ac7f93e 100644 --- a/src/features/rollup/optimism/pages/home/LatestOptimisticDeposits.pw.tsx +++ b/src/features/rollup/optimism/pages/home/LatestOptimisticDeposits.pw.tsx @@ -7,7 +7,7 @@ import { test, expect } from 'playwright/lib'; import LatestOptimisticDeposits from './LatestOptimisticDeposits'; -test('default view +@mobile +@dark-mode', async({ render, mockApiResponse, mockEnvs }) => { +test('default view +@dark-mode', async({ render, mockApiResponse, mockEnvs }) => { await mockEnvs(ENVS_MAP.optimisticRollup); mockApiResponse('core:homepage_optimistic_deposits', depositMock.data.items); const component = await render(); diff --git a/src/features/rollup/optimism/pages/home/LatestOptimisticDeposits.tsx b/src/features/rollup/optimism/pages/home/LatestOptimisticDeposits.tsx index 4b4c0a0b00d..a93f264ee55 100644 --- a/src/features/rollup/optimism/pages/home/LatestOptimisticDeposits.tsx +++ b/src/features/rollup/optimism/pages/home/LatestOptimisticDeposits.tsx @@ -14,12 +14,10 @@ import LatestTxsFallback from 'src/slices/home/pages/index/txs/LatestTxsFallback import LatestDeposits from 'src/features/rollup/common/pages/home/LatestDeposits'; import { L2_DEPOSIT_ITEM } from 'src/features/rollup/optimism/stubs'; -import useIsMobile from 'src/shared/hooks/useIsMobile'; import useGradualIncrement from 'src/shared/numbers/useGradualIncrement'; const LatestOptimisticDeposits = () => { - const isMobile = useIsMobile(); - const itemsCount = isMobile ? 2 : 5; + const itemsCount = 5; const { data, isPlaceholderData, isError } = useApiQuery('core:homepage_optimistic_deposits', { queryOptions: { placeholderData: Array(itemsCount).fill(L2_DEPOSIT_ITEM), diff --git a/src/features/rollup/optimism/pages/home/__screenshots__/LatestOptimisticDeposits.pw.tsx_dark-color-mode_default-view-dark-mode-1.png b/src/features/rollup/optimism/pages/home/__screenshots__/LatestOptimisticDeposits.pw.tsx_dark-color-mode_default-view-dark-mode-1.png new file mode 100644 index 00000000000..a30809aec75 Binary files /dev/null and b/src/features/rollup/optimism/pages/home/__screenshots__/LatestOptimisticDeposits.pw.tsx_dark-color-mode_default-view-dark-mode-1.png differ diff --git a/src/features/rollup/optimism/pages/home/__screenshots__/LatestOptimisticDeposits.pw.tsx_dark-color-mode_default-view-mobile-dark-mode-1.png b/src/features/rollup/optimism/pages/home/__screenshots__/LatestOptimisticDeposits.pw.tsx_dark-color-mode_default-view-mobile-dark-mode-1.png deleted file mode 100644 index 83f2acce8cc..00000000000 Binary files a/src/features/rollup/optimism/pages/home/__screenshots__/LatestOptimisticDeposits.pw.tsx_dark-color-mode_default-view-mobile-dark-mode-1.png and /dev/null differ diff --git a/src/features/rollup/optimism/pages/home/__screenshots__/LatestOptimisticDeposits.pw.tsx_default_default-view-dark-mode-1.png b/src/features/rollup/optimism/pages/home/__screenshots__/LatestOptimisticDeposits.pw.tsx_default_default-view-dark-mode-1.png new file mode 100644 index 00000000000..df05d2f40f0 Binary files /dev/null and b/src/features/rollup/optimism/pages/home/__screenshots__/LatestOptimisticDeposits.pw.tsx_default_default-view-dark-mode-1.png differ diff --git a/src/features/rollup/optimism/pages/home/__screenshots__/LatestOptimisticDeposits.pw.tsx_default_default-view-mobile-dark-mode-1.png b/src/features/rollup/optimism/pages/home/__screenshots__/LatestOptimisticDeposits.pw.tsx_default_default-view-mobile-dark-mode-1.png deleted file mode 100644 index afca6fb4d8d..00000000000 Binary files a/src/features/rollup/optimism/pages/home/__screenshots__/LatestOptimisticDeposits.pw.tsx_default_default-view-mobile-dark-mode-1.png and /dev/null differ diff --git a/src/features/rollup/optimism/pages/home/__screenshots__/LatestOptimisticDeposits.pw.tsx_mobile_default-view-mobile-dark-mode-1.png b/src/features/rollup/optimism/pages/home/__screenshots__/LatestOptimisticDeposits.pw.tsx_mobile_default-view-mobile-dark-mode-1.png deleted file mode 100644 index f904a66ad65..00000000000 Binary files a/src/features/rollup/optimism/pages/home/__screenshots__/LatestOptimisticDeposits.pw.tsx_mobile_default-view-mobile-dark-mode-1.png and /dev/null differ diff --git a/src/features/rollup/optimism/pages/output-roots/OptimisticL2OutputRoots.pw.tsx b/src/features/rollup/optimism/pages/output-roots/OptimisticL2OutputRoots.pw.tsx index 53ced8135e2..c0c5788876c 100644 --- a/src/features/rollup/optimism/pages/output-roots/OptimisticL2OutputRoots.pw.tsx +++ b/src/features/rollup/optimism/pages/output-roots/OptimisticL2OutputRoots.pw.tsx @@ -7,7 +7,7 @@ import { test, expect } from 'playwright/lib'; import OptimisticL2OutputRoots from './OptimisticL2OutputRoots'; -test('base view +@mobile', async({ render, mockEnvs, mockTextAd, mockApiResponse }) => { +test('base view', async({ render, mockEnvs, mockTextAd, mockApiResponse }) => { test.slow(); await mockEnvs(ENVS_MAP.optimisticRollup); await mockTextAd(); diff --git a/src/features/rollup/optimism/pages/output-roots/OptimisticL2OutputRoots.tsx b/src/features/rollup/optimism/pages/output-roots/OptimisticL2OutputRoots.tsx index fa61b0782eb..05fb8d94cda 100644 --- a/src/features/rollup/optimism/pages/output-roots/OptimisticL2OutputRoots.tsx +++ b/src/features/rollup/optimism/pages/output-roots/OptimisticL2OutputRoots.tsx @@ -1,6 +1,6 @@ // SPDX-License-Identifier: LicenseRef-Blockscout -import { Box, Text } from '@chakra-ui/react'; +import { Text } from '@chakra-ui/react'; import React from 'react'; import useApiQuery from 'src/api/hooks/useApiQuery'; @@ -13,16 +13,16 @@ import { L2_OUTPUT_ROOTS_ITEM } from 'src/features/rollup/optimism/stubs'; import DataList from 'src/shared/lists/DataList'; import StickyPaginationWithText from 'src/shared/pagination/StickyPaginationWithText'; -import useQueryWithPages from 'src/shared/pagination/useQueryWithPages'; +import useApiPaginatedQuery from 'src/shared/pagination/useApiPaginatedQuery'; import { generateListStub } from 'src/shared/pagination/utils'; import { Skeleton } from 'src/toolkit/chakra/skeleton'; +import { TableContainerScrollable } from 'src/toolkit/chakra/table'; -import OptimisticL2OutputRootsList from './OptimisticL2OutputRootsList'; import OptimisticL2OutputRootsTable from './OptimisticL2OutputRootsTable'; const OptimisticL2OutputRoots = () => { - const { data, isError, isPlaceholderData, pagination, queryHash } = useQueryWithPages({ + const { data, isError, isInitialLoading, isTransitioning, pagination, queryHash } = useApiPaginatedQuery({ resourceName: 'core:optimistic_l2_output_roots', options: { placeholderData: generateListStub<'core:optimistic_l2_output_roots'>( @@ -45,19 +45,14 @@ const OptimisticL2OutputRoots = () => { }); const content = data?.items ? ( - <> - - - - - - - + + + ) : null; const text = (() => { @@ -66,7 +61,7 @@ const OptimisticL2OutputRoots = () => { } return ( - + { layerLabels.current } output index #{ data.items[0].l2_output_index } to #{ data.items[data.items.length - 1].l2_output_index } @@ -85,6 +80,7 @@ const OptimisticL2OutputRoots = () => { itemsNum={ data?.items.length } emptyText="There are no output roots." actionBar={ actionBar } + isTransitioning={ isTransitioning } > { content } diff --git a/src/features/rollup/optimism/pages/output-roots/OptimisticL2OutputRootsList.tsx b/src/features/rollup/optimism/pages/output-roots/OptimisticL2OutputRootsList.tsx deleted file mode 100644 index fd0de54bfa7..00000000000 --- a/src/features/rollup/optimism/pages/output-roots/OptimisticL2OutputRootsList.tsx +++ /dev/null @@ -1,37 +0,0 @@ -// SPDX-License-Identifier: LicenseRef-Blockscout - -import { Box } from '@chakra-ui/react'; -import React from 'react'; - -import type { schemas } from '@blockscout/api-types'; - -import useLazyRenderedList from 'src/shared/lists/useLazyRenderedList'; - -import OptimisticL2OutputRootsListItem from './OptimisticL2OutputRootsListItem'; - -type Props = { - items: Array; - isLoading?: boolean; - resetKey?: string; -}; - -const OptimisticL2OutputRootsList = ({ items, isLoading, resetKey }: Props) => { - const { cutRef, renderedItemsNum } = useLazyRenderedList({ list: items, isEnabled: !isLoading, resetKey }); - - return ( - <> - - { items.slice(0, renderedItemsNum).map((item, index) => ( - - )) } - - - - ); -}; - -export default OptimisticL2OutputRootsList; diff --git a/src/features/rollup/optimism/pages/output-roots/OptimisticL2OutputRootsListItem.tsx b/src/features/rollup/optimism/pages/output-roots/OptimisticL2OutputRootsListItem.tsx deleted file mode 100644 index d76f123cc92..00000000000 --- a/src/features/rollup/optimism/pages/output-roots/OptimisticL2OutputRootsListItem.tsx +++ /dev/null @@ -1,79 +0,0 @@ -// SPDX-License-Identifier: LicenseRef-Blockscout - -import { Flex } from '@chakra-ui/react'; -import React from 'react'; - -import type { schemas } from '@blockscout/api-types'; - -import BlockEntityL2 from 'src/features/rollup/common/components/BlockEntityL2'; -import TxEntityL1 from 'src/features/rollup/common/components/TxEntityL1'; -import { layerLabels } from 'src/features/rollup/common/utils/layer'; - -import config from 'src/config'; -import TimeWithTooltip from 'src/shared/date-and-time/TimeWithTooltip'; -import ListItemMobileGrid from 'src/shared/lists/ListItemMobileGrid'; -import CopyToClipboard from 'src/shared/texts/CopyToClipboard'; -import HashStringShorten from 'src/shared/texts/HashStringShorten'; - -import { Skeleton } from 'src/toolkit/chakra/skeleton'; - -const rollupFeature = config.features.rollup; - -type Props = { item: schemas['OptimismOutputRoot']; isLoading?: boolean }; - -const OptimisticL2OutputRootsListItem = ({ item, isLoading }: Props) => { - if (!rollupFeature.isEnabled || rollupFeature.type !== 'optimistic') { - return null; - } - - return ( - - - { layerLabels.current } output index - - { item.l2_output_index } - - - Age - - - - - { layerLabels.current } block # - - - - - { layerLabels.parent } txn hash - - - - - Output root - - - - - - - - - - - ); -}; - -export default OptimisticL2OutputRootsListItem; diff --git a/src/features/rollup/optimism/pages/output-roots/OptimisticL2OutputRootsTable.tsx b/src/features/rollup/optimism/pages/output-roots/OptimisticL2OutputRootsTable.tsx index 226d5851990..491d55057a2 100644 --- a/src/features/rollup/optimism/pages/output-roots/OptimisticL2OutputRootsTable.tsx +++ b/src/features/rollup/optimism/pages/output-roots/OptimisticL2OutputRootsTable.tsx @@ -40,7 +40,7 @@ const OptimisticL2OutputRootsTable = ({ items, top, isLoading, resetKey }: Props { items.slice(0, renderedItemsNum).map((item, index) => ( diff --git a/src/features/rollup/optimism/pages/output-roots/OptimisticL2OutputRootsTableItem.tsx b/src/features/rollup/optimism/pages/output-roots/OptimisticL2OutputRootsTableItem.tsx index fe25002298e..3ee04f67eb0 100644 --- a/src/features/rollup/optimism/pages/output-roots/OptimisticL2OutputRootsTableItem.tsx +++ b/src/features/rollup/optimism/pages/output-roots/OptimisticL2OutputRootsTableItem.tsx @@ -11,10 +11,10 @@ import TxEntityL1 from 'src/features/rollup/common/components/TxEntityL1'; import config from 'src/config'; import TimeWithTooltip from 'src/shared/date-and-time/TimeWithTooltip'; import CopyToClipboard from 'src/shared/texts/CopyToClipboard'; -import HashStringShorten from 'src/shared/texts/HashStringShorten'; import { Skeleton } from 'src/toolkit/chakra/skeleton'; import { TableCell, TableRow } from 'src/toolkit/chakra/table'; +import { Truncate } from 'src/toolkit/components/truncation/Truncate'; const rollupFeature = config.features.rollup; @@ -58,7 +58,7 @@ const OptimisticL2OutputRootsTableItem = ({ item, isLoading }: Props) => { - + diff --git a/src/features/rollup/optimism/pages/output-roots/__screenshots__/OptimisticL2OutputRoots.pw.tsx_default_base-view-1.png b/src/features/rollup/optimism/pages/output-roots/__screenshots__/OptimisticL2OutputRoots.pw.tsx_default_base-view-1.png new file mode 100644 index 00000000000..be90e13b889 Binary files /dev/null and b/src/features/rollup/optimism/pages/output-roots/__screenshots__/OptimisticL2OutputRoots.pw.tsx_default_base-view-1.png differ diff --git a/src/features/rollup/optimism/pages/output-roots/__screenshots__/OptimisticL2OutputRoots.pw.tsx_default_base-view-mobile-1.png b/src/features/rollup/optimism/pages/output-roots/__screenshots__/OptimisticL2OutputRoots.pw.tsx_default_base-view-mobile-1.png deleted file mode 100644 index 45eca3685f3..00000000000 Binary files a/src/features/rollup/optimism/pages/output-roots/__screenshots__/OptimisticL2OutputRoots.pw.tsx_default_base-view-mobile-1.png and /dev/null differ diff --git a/src/features/rollup/optimism/pages/output-roots/__screenshots__/OptimisticL2OutputRoots.pw.tsx_mobile_base-view-mobile-1.png b/src/features/rollup/optimism/pages/output-roots/__screenshots__/OptimisticL2OutputRoots.pw.tsx_mobile_base-view-mobile-1.png deleted file mode 100644 index 2c5260181ea..00000000000 Binary files a/src/features/rollup/optimism/pages/output-roots/__screenshots__/OptimisticL2OutputRoots.pw.tsx_mobile_base-view-mobile-1.png and /dev/null differ diff --git a/src/features/rollup/optimism/pages/withdrawals/OptimisticL2Withdrawals.pw.tsx b/src/features/rollup/optimism/pages/withdrawals/OptimisticL2Withdrawals.pw.tsx index d5827e29bdf..b5b99950c7a 100644 --- a/src/features/rollup/optimism/pages/withdrawals/OptimisticL2Withdrawals.pw.tsx +++ b/src/features/rollup/optimism/pages/withdrawals/OptimisticL2Withdrawals.pw.tsx @@ -7,10 +7,7 @@ import { test, expect } from 'playwright/lib'; import OptimisticL2Withdrawals from './OptimisticL2Withdrawals'; -test('base view +@mobile', async({ render, mockTextAd, mockEnvs, mockApiResponse }) => { - // test on mobile is flaky - // my assumption is there is not enough time to calculate hashes truncation so component is unstable - // so I raised the test timeout to check if it helps +test('base view', async({ render, mockTextAd, mockEnvs, mockApiResponse }) => { test.slow(); await mockTextAd(); await mockEnvs(ENVS_MAP.optimisticRollup); diff --git a/src/features/rollup/optimism/pages/withdrawals/OptimisticL2Withdrawals.tsx b/src/features/rollup/optimism/pages/withdrawals/OptimisticL2Withdrawals.tsx index 2fdcc2c850a..867e849215f 100644 --- a/src/features/rollup/optimism/pages/withdrawals/OptimisticL2Withdrawals.tsx +++ b/src/features/rollup/optimism/pages/withdrawals/OptimisticL2Withdrawals.tsx @@ -1,6 +1,5 @@ // SPDX-License-Identifier: LicenseRef-Blockscout -import { Box } from '@chakra-ui/react'; import React from 'react'; import useApiQuery from 'src/api/hooks/useApiQuery'; @@ -9,20 +8,20 @@ import { ACTION_BAR_HEIGHT_DESKTOP } from 'src/shell/page/action-bar/ActionBar'; import PageTitle from 'src/shell/page/title/PageTitle'; import { layerLabels } from 'src/features/rollup/common/utils/layer'; -import OptimisticL2WithdrawalsList from 'src/features/rollup/optimism/pages/withdrawals/OptimisticL2WithdrawalsList'; import OptimisticL2WithdrawalsTable from 'src/features/rollup/optimism/pages/withdrawals/OptimisticL2WithdrawalsTable'; import { L2_WITHDRAWAL_ITEM } from 'src/features/rollup/optimism/stubs'; import DataList from 'src/shared/lists/DataList'; import StickyPaginationWithText from 'src/shared/pagination/StickyPaginationWithText'; -import useQueryWithPages from 'src/shared/pagination/useQueryWithPages'; +import useApiPaginatedQuery from 'src/shared/pagination/useApiPaginatedQuery'; import { generateListStub } from 'src/shared/pagination/utils'; import { Skeleton } from 'src/toolkit/chakra/skeleton'; +import { TableContainerScrollable } from 'src/toolkit/chakra/table'; import { rightLineArrow, nbsp } from 'src/toolkit/utils/htmlEntities'; const OptimisticL2Withdrawals = () => { - const { data, isError, isPlaceholderData, pagination, queryHash } = useQueryWithPages({ + const { data, isError, isInitialLoading, isTransitioning, pagination, queryHash } = useApiPaginatedQuery({ resourceName: 'core:optimistic_l2_withdrawals', options: { placeholderData: generateListStub<'core:optimistic_l2_withdrawals'>( @@ -45,19 +44,14 @@ const OptimisticL2Withdrawals = () => { }); const content = data?.items ? ( - <> - - - - - - - + + + ) : null; const text = (() => { @@ -85,6 +79,7 @@ const OptimisticL2Withdrawals = () => { itemsNum={ data?.items?.length } emptyText="There are no withdrawals." actionBar={ actionBar } + isTransitioning={ isTransitioning } > { content } diff --git a/src/features/rollup/optimism/pages/withdrawals/OptimisticL2WithdrawalsList.tsx b/src/features/rollup/optimism/pages/withdrawals/OptimisticL2WithdrawalsList.tsx deleted file mode 100644 index c3748b132f2..00000000000 --- a/src/features/rollup/optimism/pages/withdrawals/OptimisticL2WithdrawalsList.tsx +++ /dev/null @@ -1,37 +0,0 @@ -// SPDX-License-Identifier: LicenseRef-Blockscout - -import { Box } from '@chakra-ui/react'; -import React from 'react'; - -import type { schemas } from '@blockscout/api-types'; - -import useLazyRenderedList from 'src/shared/lists/useLazyRenderedList'; - -import OptimisticL2WithdrawalsListItem from './OptimisticL2WithdrawalsListItem'; - -type Props = { - items: Array; - isLoading?: boolean; - resetKey?: string; -}; - -const OptimisticL2WithdrawalsList = ({ items, isLoading, resetKey }: Props) => { - const { cutRef, renderedItemsNum } = useLazyRenderedList({ list: items, isEnabled: !isLoading, resetKey }); - - return ( - <> - - { items.slice(0, renderedItemsNum).map((item, index) => ( - - )) } - - - - ); -}; - -export default OptimisticL2WithdrawalsList; diff --git a/src/features/rollup/optimism/pages/withdrawals/OptimisticL2WithdrawalsListItem.tsx b/src/features/rollup/optimism/pages/withdrawals/OptimisticL2WithdrawalsListItem.tsx deleted file mode 100644 index c9460826e52..00000000000 --- a/src/features/rollup/optimism/pages/withdrawals/OptimisticL2WithdrawalsListItem.tsx +++ /dev/null @@ -1,108 +0,0 @@ -// SPDX-License-Identifier: LicenseRef-Blockscout - -import React from 'react'; - -import type { schemas } from '@blockscout/api-types'; - -import AddressEntity from 'src/slices/address/components/entity/AddressEntity'; -import TxEntity from 'src/slices/tx/components/entity/TxEntity'; - -import TxEntityL1 from 'src/features/rollup/common/components/TxEntityL1'; -import { layerLabels } from 'src/features/rollup/common/utils/layer'; - -import config from 'src/config'; -import dayjs from 'src/shared/date-and-time/dayjs'; -import TimeWithTooltip from 'src/shared/date-and-time/TimeWithTooltip'; -import ListItemMobileGrid from 'src/shared/lists/ListItemMobileGrid'; - -import { Skeleton } from 'src/toolkit/chakra/skeleton'; - -import OptimisticL2WithdrawalsItemStatus from './OptimisticL2WithdrawalsItemStatus'; - -const rollupFeature = config.features.rollup; - -type Props = { item: schemas['OptimismWithdrawal']; isLoading?: boolean }; - -const OptimisticL2WithdrawalsListItem = ({ item, isLoading }: Props) => { - const timeToEnd = item.challenge_period_end ? dayjs(item.challenge_period_end).fromNow(true) + ' left' : null; - - if (!rollupFeature.isEnabled || rollupFeature.type !== 'optimistic') { - return null; - } - - return ( - - - Msg nonce - - - { item.msg_nonce_version + '-' + item.msg_nonce } - - - - { item.from && ( - <> - From - - - - - ) } - - { layerLabels.current } txn hash - - - - - { item.l2_timestamp && ( - <> - Age - - - - - ) } - - Status - - - - - { item.l1_transaction_hash && ( - <> - { layerLabels.parent } txn hash - - - - - ) } - - { timeToEnd && ( - <> - Time left - { timeToEnd } - - ) } - - - ); -}; - -export default OptimisticL2WithdrawalsListItem; diff --git a/src/features/rollup/optimism/pages/withdrawals/__screenshots__/OptimisticL2Withdrawals.pw.tsx_default_base-view-1.png b/src/features/rollup/optimism/pages/withdrawals/__screenshots__/OptimisticL2Withdrawals.pw.tsx_default_base-view-1.png new file mode 100644 index 00000000000..6d7422b83e6 Binary files /dev/null and b/src/features/rollup/optimism/pages/withdrawals/__screenshots__/OptimisticL2Withdrawals.pw.tsx_default_base-view-1.png differ diff --git a/src/features/rollup/optimism/pages/withdrawals/__screenshots__/OptimisticL2Withdrawals.pw.tsx_default_base-view-mobile-1.png b/src/features/rollup/optimism/pages/withdrawals/__screenshots__/OptimisticL2Withdrawals.pw.tsx_default_base-view-mobile-1.png deleted file mode 100644 index d1759e4435e..00000000000 Binary files a/src/features/rollup/optimism/pages/withdrawals/__screenshots__/OptimisticL2Withdrawals.pw.tsx_default_base-view-mobile-1.png and /dev/null differ diff --git a/src/features/rollup/optimism/pages/withdrawals/__screenshots__/OptimisticL2Withdrawals.pw.tsx_mobile_base-view-mobile-1.png b/src/features/rollup/optimism/pages/withdrawals/__screenshots__/OptimisticL2Withdrawals.pw.tsx_mobile_base-view-mobile-1.png deleted file mode 100644 index a0f51211c0a..00000000000 Binary files a/src/features/rollup/optimism/pages/withdrawals/__screenshots__/OptimisticL2Withdrawals.pw.tsx_mobile_base-view-mobile-1.png and /dev/null differ diff --git a/src/features/rollup/scroll/pages/batch-details/ScrollL2TxnBatch.tsx b/src/features/rollup/scroll/pages/batch-details/ScrollL2TxnBatch.tsx index 8770efeec74..ea300871049 100644 --- a/src/features/rollup/scroll/pages/batch-details/ScrollL2TxnBatch.tsx +++ b/src/features/rollup/scroll/pages/batch-details/ScrollL2TxnBatch.tsx @@ -21,7 +21,7 @@ import throwOnAbsentParamError from 'src/shared/errors/throw-on-absent-param-err import throwOnResourceLoadError from 'src/shared/errors/throw-on-resource-load-error'; import useIsMobile from 'src/shared/hooks/useIsMobile'; import Pagination from 'src/shared/pagination/Pagination'; -import useQueryWithPages from 'src/shared/pagination/useQueryWithPages'; +import useApiPaginatedQuery from 'src/shared/pagination/useApiPaginatedQuery'; import { generateListStub } from 'src/shared/pagination/utils'; import getQueryParamString from 'src/shared/router/get-query-param-string'; @@ -51,7 +51,7 @@ const ScrollL2TxnBatch = () => { }, }); - const batchTxsQuery = useQueryWithPages({ + const batchTxsQuery = useApiPaginatedQuery({ resourceName: 'core:scroll_l2_txn_batch_txs', pathParams: { number }, options: { @@ -65,7 +65,7 @@ const ScrollL2TxnBatch = () => { }, }); - const batchBlocksQuery = useQueryWithPages({ + const batchBlocksQuery = useApiPaginatedQuery({ resourceName: 'core:scroll_l2_txn_batch_blocks', pathParams: { number }, options: { diff --git a/src/features/rollup/scroll/pages/batch-details/__screenshots__/ScrollL2TxnBatch.pw.tsx_default_base-view-1.png b/src/features/rollup/scroll/pages/batch-details/__screenshots__/ScrollL2TxnBatch.pw.tsx_default_base-view-1.png index f160d2874f0..9a87389bd3d 100644 Binary files a/src/features/rollup/scroll/pages/batch-details/__screenshots__/ScrollL2TxnBatch.pw.tsx_default_base-view-1.png and b/src/features/rollup/scroll/pages/batch-details/__screenshots__/ScrollL2TxnBatch.pw.tsx_default_base-view-1.png differ diff --git a/src/features/rollup/scroll/pages/batch-details/__screenshots__/ScrollL2TxnBatch.pw.tsx_default_mobile-base-view-1.png b/src/features/rollup/scroll/pages/batch-details/__screenshots__/ScrollL2TxnBatch.pw.tsx_default_mobile-base-view-1.png index 0695c2f30f1..8382bb0e65d 100644 Binary files a/src/features/rollup/scroll/pages/batch-details/__screenshots__/ScrollL2TxnBatch.pw.tsx_default_mobile-base-view-1.png and b/src/features/rollup/scroll/pages/batch-details/__screenshots__/ScrollL2TxnBatch.pw.tsx_default_mobile-base-view-1.png differ diff --git a/src/features/rollup/scroll/pages/batches/ScrollL2TxnBatches.pw.tsx b/src/features/rollup/scroll/pages/batches/ScrollL2TxnBatches.pw.tsx index e52d24b89c5..659d6246e41 100644 --- a/src/features/rollup/scroll/pages/batches/ScrollL2TxnBatches.pw.tsx +++ b/src/features/rollup/scroll/pages/batches/ScrollL2TxnBatches.pw.tsx @@ -3,7 +3,7 @@ import React from 'react'; import * as scrollTxnBatchesMock from 'src/features/rollup/scroll/mocks/txn-batches'; import { ENVS_MAP } from 'playwright/fixtures/mockEnvs'; -import { test, expect, devices } from 'playwright/lib'; +import { test, expect } from 'playwright/lib'; import ScrollL2TxnBatches from './ScrollL2TxnBatches'; @@ -17,18 +17,3 @@ test('base view', async({ render, mockEnvs, mockTextAd, mockApiResponse }) => { const component = await render(); await expect(component).toHaveScreenshot(); }); - -test.describe('mobile', () => { - test.use({ viewport: devices['iPhone 13 Pro'].viewport }); - - test('base view', async({ render, mockEnvs, mockTextAd, mockApiResponse }) => { - test.slow(); - await mockEnvs(ENVS_MAP.scrollRollup); - await mockTextAd(); - await mockApiResponse('core:scroll_l2_txn_batches', scrollTxnBatchesMock.baseResponse); - await mockApiResponse('core:scroll_l2_txn_batches_count', 9927); - - const component = await render(); - await expect(component).toHaveScreenshot(); - }); -}); diff --git a/src/features/rollup/scroll/pages/batches/ScrollL2TxnBatches.tsx b/src/features/rollup/scroll/pages/batches/ScrollL2TxnBatches.tsx index fd4994c65a4..82c42dac8f3 100644 --- a/src/features/rollup/scroll/pages/batches/ScrollL2TxnBatches.tsx +++ b/src/features/rollup/scroll/pages/batches/ScrollL2TxnBatches.tsx @@ -1,6 +1,6 @@ // SPDX-License-Identifier: LicenseRef-Blockscout -import { Box, Text } from '@chakra-ui/react'; +import { Text } from '@chakra-ui/react'; import React from 'react'; import useApiQuery from 'src/api/hooks/useApiQuery'; @@ -12,16 +12,16 @@ import { SCROLL_L2_TXN_BATCH } from 'src/features/rollup/scroll/stubs'; import DataList from 'src/shared/lists/DataList'; import StickyPaginationWithText from 'src/shared/pagination/StickyPaginationWithText'; -import useQueryWithPages from 'src/shared/pagination/useQueryWithPages'; +import useApiPaginatedQuery from 'src/shared/pagination/useApiPaginatedQuery'; import { generateListStub } from 'src/shared/pagination/utils'; import { Skeleton } from 'src/toolkit/chakra/skeleton'; +import { TableContainerScrollable } from 'src/toolkit/chakra/table'; -import ScrollL2TxnBatchesList from './ScrollL2TxnBatchesList'; import ScrollL2TxnBatchesTable from './ScrollL2TxnBatchesTable'; const ScrollL2TxnBatches = () => { - const { data, isError, isPlaceholderData, pagination, queryHash } = useQueryWithPages({ + const { data, isError, isInitialLoading, isTransitioning, pagination, queryHash } = useApiPaginatedQuery({ resourceName: 'core:scroll_l2_txn_batches', options: { placeholderData: generateListStub<'core:scroll_l2_txn_batches'>( @@ -44,19 +44,14 @@ const ScrollL2TxnBatches = () => { }); const content = data?.items ? ( - <> - - - - - - - + + + ) : null; const text = (() => { @@ -65,7 +60,7 @@ const ScrollL2TxnBatches = () => { } return ( - + Txn batch #{ data.items[0].number } to #{ data.items[data.items.length - 1].number } @@ -84,6 +79,7 @@ const ScrollL2TxnBatches = () => { itemsNum={ data?.items?.length } emptyText="There are no txn batches." actionBar={ actionBar } + isTransitioning={ isTransitioning } > { content } diff --git a/src/features/rollup/scroll/pages/batches/ScrollL2TxnBatchesList.tsx b/src/features/rollup/scroll/pages/batches/ScrollL2TxnBatchesList.tsx deleted file mode 100644 index 84f85a3d00e..00000000000 --- a/src/features/rollup/scroll/pages/batches/ScrollL2TxnBatchesList.tsx +++ /dev/null @@ -1,37 +0,0 @@ -// SPDX-License-Identifier: LicenseRef-Blockscout - -import { Box } from '@chakra-ui/react'; -import React from 'react'; - -import type { schemas } from '@blockscout/api-types'; - -import useLazyRenderedList from 'src/shared/lists/useLazyRenderedList'; - -import ScrollL2TxnBatchesListItem from './ScrollL2TxnBatchesListItem'; - -type Props = { - items: Array; - isLoading?: boolean; - resetKey?: string; -}; - -const ScrollL2TxnBatchesList = ({ items, isLoading, resetKey }: Props) => { - const { cutRef, renderedItemsNum } = useLazyRenderedList({ list: items, isEnabled: !isLoading, resetKey }); - - return ( - <> - - { items.slice(0, renderedItemsNum).map((item, index) => ( - - )) } - - - - ); -}; - -export default ScrollL2TxnBatchesList; diff --git a/src/features/rollup/scroll/pages/batches/ScrollL2TxnBatchesListItem.tsx b/src/features/rollup/scroll/pages/batches/ScrollL2TxnBatchesListItem.tsx deleted file mode 100644 index 0632993092a..00000000000 --- a/src/features/rollup/scroll/pages/batches/ScrollL2TxnBatchesListItem.tsx +++ /dev/null @@ -1,132 +0,0 @@ -// SPDX-License-Identifier: LicenseRef-Blockscout - -import { route } from 'nextjs-routes'; -import React from 'react'; - -import type { schemas } from '@blockscout/api-types'; - -import BatchEntityL2 from 'src/features/rollup/common/components/BatchEntityL2'; -import BlockEntityL1 from 'src/features/rollup/common/components/BlockEntityL1'; -import TxEntityL1 from 'src/features/rollup/common/components/TxEntityL1'; -import ScrollL2TxnBatchDA from 'src/features/rollup/scroll/components/ScrollL2TxnBatchDA'; -import ScrollL2TxnBatchStatus from 'src/features/rollup/scroll/components/ScrollL2TxnBatchStatus'; - -import config from 'src/config'; -import TimeWithTooltip from 'src/shared/date-and-time/TimeWithTooltip'; -import ListItemMobileGrid from 'src/shared/lists/ListItemMobileGrid'; - -import { Link } from 'src/toolkit/chakra/link'; -import { Skeleton } from 'src/toolkit/chakra/skeleton'; - -const rollupFeature = config.features.rollup; - -type Props = { item: schemas['ScrollBatch']; isLoading?: boolean }; - -const ScrollL2TxnBatchesListItem = ({ item, isLoading }: Props) => { - if (!rollupFeature.isEnabled || rollupFeature.type !== 'scroll') { - return null; - } - - return ( - - - Batch # - - - - - Data availability - - - - - Status - - - - - Committed block - - - - - Committed txn hash - - - - - Age - - - - - Finalized block - - { item.confirmation_transaction.block_number ? ( - - ) : Pending } - - - Finalized txn hash - - { item.confirmation_transaction.hash ? ( - - ) : Pending } - - - Blocks count - - - { (item.end_block_number && item.start_block_number ? item.end_block_number - item.start_block_number + 1 : 0).toLocaleString() } - - - - { typeof item.transactions_count === 'number' ? ( - <> - Txn count - - - { item.transactions_count.toLocaleString() } - - - - ) : null } - - - ); -}; - -export default ScrollL2TxnBatchesListItem; diff --git a/src/features/rollup/scroll/pages/batches/ScrollL2TxnBatchesTable.tsx b/src/features/rollup/scroll/pages/batches/ScrollL2TxnBatchesTable.tsx index 163c1e26ee5..5e338174ed6 100644 --- a/src/features/rollup/scroll/pages/batches/ScrollL2TxnBatchesTable.tsx +++ b/src/features/rollup/scroll/pages/batches/ScrollL2TxnBatchesTable.tsx @@ -22,7 +22,7 @@ const ScrollL2TxnBatchesTable = ({ items, top, isLoading, resetKey }: Props) => const { cutRef, renderedItemsNum } = useLazyRenderedList({ list: items, isEnabled: !isLoading, resetKey }); return ( - + Batch # diff --git a/src/features/rollup/scroll/pages/batches/ScrollL2TxnBatchesTableItem.tsx b/src/features/rollup/scroll/pages/batches/ScrollL2TxnBatchesTableItem.tsx index 0d50880d6b5..bc04fc60fa4 100644 --- a/src/features/rollup/scroll/pages/batches/ScrollL2TxnBatchesTableItem.tsx +++ b/src/features/rollup/scroll/pages/batches/ScrollL2TxnBatchesTableItem.tsx @@ -47,6 +47,7 @@ const ScrollL2TxnBatchesTableItem = ({ item, isLoading }: Props) => { @@ -55,6 +56,7 @@ const ScrollL2TxnBatchesTableItem = ({ item, isLoading }: Props) => { isLoading={ isLoading } truncation="constant_long" noCopy + noIcon /> @@ -70,6 +72,7 @@ const ScrollL2TxnBatchesTableItem = ({ item, isLoading }: Props) => { ) : Pending } @@ -80,6 +83,7 @@ const ScrollL2TxnBatchesTableItem = ({ item, isLoading }: Props) => { isLoading={ isLoading } truncation="constant_long" noCopy + noIcon /> ) : Pending } diff --git a/src/features/rollup/scroll/pages/batches/__screenshots__/ScrollL2TxnBatches.pw.tsx_default_base-view-1.png b/src/features/rollup/scroll/pages/batches/__screenshots__/ScrollL2TxnBatches.pw.tsx_default_base-view-1.png index b5a0f1f2fc3..f8e0b5f2a3e 100644 Binary files a/src/features/rollup/scroll/pages/batches/__screenshots__/ScrollL2TxnBatches.pw.tsx_default_base-view-1.png and b/src/features/rollup/scroll/pages/batches/__screenshots__/ScrollL2TxnBatches.pw.tsx_default_base-view-1.png differ diff --git a/src/features/rollup/scroll/pages/batches/__screenshots__/ScrollL2TxnBatches.pw.tsx_default_mobile-base-view-1.png b/src/features/rollup/scroll/pages/batches/__screenshots__/ScrollL2TxnBatches.pw.tsx_default_mobile-base-view-1.png deleted file mode 100644 index 64c55f7dd9c..00000000000 Binary files a/src/features/rollup/scroll/pages/batches/__screenshots__/ScrollL2TxnBatches.pw.tsx_default_mobile-base-view-1.png and /dev/null differ diff --git a/src/features/rollup/scroll/pages/deposits/ScrollL2Deposits.pw.tsx b/src/features/rollup/scroll/pages/deposits/ScrollL2Deposits.pw.tsx index 450ac0cdc7b..7ab44849f7c 100644 --- a/src/features/rollup/scroll/pages/deposits/ScrollL2Deposits.pw.tsx +++ b/src/features/rollup/scroll/pages/deposits/ScrollL2Deposits.pw.tsx @@ -3,7 +3,7 @@ import React from 'react'; import * as messagesMock from 'src/features/rollup/scroll/mocks/messages'; import { ENVS_MAP } from 'playwright/fixtures/mockEnvs'; -import { test, expect, devices } from 'playwright/lib'; +import { test, expect } from 'playwright/lib'; import ScrollL2Deposits from './ScrollL2Deposits'; @@ -17,18 +17,3 @@ test('base view', async({ render, mockApiResponse, mockEnvs, mockTextAd }) => { await expect(component).toHaveScreenshot(); }); - -test.describe('mobile', () => { - test.use({ viewport: devices['iPhone 13 Pro'].viewport }); - - test('base view', async({ render, mockApiResponse, mockEnvs, mockTextAd }) => { - await mockTextAd(); - await mockEnvs(ENVS_MAP.scrollRollup); - await mockApiResponse('core:scroll_l2_deposits', messagesMock.baseResponse); - await mockApiResponse('core:scroll_l2_deposits_count', 3971111); - - const component = await render(); - - await expect(component).toHaveScreenshot(); - }); -}); diff --git a/src/features/rollup/scroll/pages/deposits/ScrollL2Deposits.tsx b/src/features/rollup/scroll/pages/deposits/ScrollL2Deposits.tsx index 46c8b682951..70a671caeda 100644 --- a/src/features/rollup/scroll/pages/deposits/ScrollL2Deposits.tsx +++ b/src/features/rollup/scroll/pages/deposits/ScrollL2Deposits.tsx @@ -1,6 +1,5 @@ // SPDX-License-Identifier: LicenseRef-Blockscout -import { Box } from '@chakra-ui/react'; import React from 'react'; import useApiQuery from 'src/api/hooks/useApiQuery'; @@ -13,17 +12,17 @@ import { SCROLL_L2_MESSAGE_ITEM } from 'src/features/rollup/scroll/stubs'; import DataList from 'src/shared/lists/DataList'; import StickyPaginationWithText from 'src/shared/pagination/StickyPaginationWithText'; -import useQueryWithPages from 'src/shared/pagination/useQueryWithPages'; +import useApiPaginatedQuery from 'src/shared/pagination/useApiPaginatedQuery'; import { generateListStub } from 'src/shared/pagination/utils'; import { Skeleton } from 'src/toolkit/chakra/skeleton'; +import { TableContainerScrollable } from 'src/toolkit/chakra/table'; import { rightLineArrow, nbsp } from 'src/toolkit/utils/htmlEntities'; -import ScrollL2DepositsList from './ScrollL2DepositsList'; import ScrollL2DepositsTable from './ScrollL2DepositsTable'; const ScrollL2Deposits = () => { - const { data, isError, isPlaceholderData, pagination, queryHash } = useQueryWithPages({ + const { data, isError, isInitialLoading, isTransitioning, pagination, queryHash } = useApiPaginatedQuery({ resourceName: 'core:scroll_l2_deposits', options: { placeholderData: generateListStub<'core:scroll_l2_deposits'>( @@ -41,19 +40,14 @@ const ScrollL2Deposits = () => { }); const content = data?.items ? ( - <> - - - - - - - + + + ) : null; const text = (() => { @@ -81,6 +75,7 @@ const ScrollL2Deposits = () => { itemsNum={ data?.items?.length } emptyText="There are no deposits." actionBar={ actionBar } + isTransitioning={ isTransitioning } > { content } diff --git a/src/features/rollup/scroll/pages/deposits/ScrollL2DepositsList.tsx b/src/features/rollup/scroll/pages/deposits/ScrollL2DepositsList.tsx deleted file mode 100644 index 26214a6e95d..00000000000 --- a/src/features/rollup/scroll/pages/deposits/ScrollL2DepositsList.tsx +++ /dev/null @@ -1,37 +0,0 @@ -// SPDX-License-Identifier: LicenseRef-Blockscout - -import { Box } from '@chakra-ui/react'; -import React from 'react'; - -import type { schemas } from '@blockscout/api-types'; - -import useLazyRenderedList from 'src/shared/lists/useLazyRenderedList'; - -import ScrollL2DepositsListItem from './ScrollL2DepositsListItem'; - -type Props = { - items: Array; - isLoading?: boolean; - resetKey?: string; -}; - -const ScrollL2DepositsList = ({ items, isLoading, resetKey }: Props) => { - const { cutRef, renderedItemsNum } = useLazyRenderedList({ list: items, isEnabled: !isLoading, resetKey }); - - return ( - <> - - { items.slice(0, renderedItemsNum).map((item, index) => ( - - )) } - - - - ); -}; - -export default ScrollL2DepositsList; diff --git a/src/features/rollup/scroll/pages/deposits/ScrollL2DepositsListItem.tsx b/src/features/rollup/scroll/pages/deposits/ScrollL2DepositsListItem.tsx deleted file mode 100644 index 1775c5bfee4..00000000000 --- a/src/features/rollup/scroll/pages/deposits/ScrollL2DepositsListItem.tsx +++ /dev/null @@ -1,102 +0,0 @@ -// SPDX-License-Identifier: LicenseRef-Blockscout - -import { chakra } from '@chakra-ui/react'; -import React from 'react'; - -import type { schemas } from '@blockscout/api-types'; - -import TxEntity from 'src/slices/tx/components/entity/TxEntity'; - -import BlockEntityL1 from 'src/features/rollup/common/components/BlockEntityL1'; -import TxEntityL1 from 'src/features/rollup/common/components/TxEntityL1'; -import { layerLabels } from 'src/features/rollup/common/utils/layer'; - -import config from 'src/config'; -import TimeWithTooltip from 'src/shared/date-and-time/TimeWithTooltip'; -import ListItemMobileGrid from 'src/shared/lists/ListItemMobileGrid'; -import NativeCoinValue from 'src/shared/values/entity/NativeCoinValue'; - -import { Skeleton } from 'src/toolkit/chakra/skeleton'; - -const rollupFeature = config.features.rollup; - -type Props = { item: schemas['ScrollBridge']; isLoading?: boolean }; - -const ScrollL2DepositsListItem = ({ item, isLoading }: Props) => { - if (!rollupFeature.isEnabled || rollupFeature.type !== 'scroll') { - return null; - } - - return ( - - - { item.origination_transaction_block_number && ( - <> - { layerLabels.parent } block - - - - ) } - - Index - - - { item.id } - - - - { item.origination_transaction_hash && ( - <> - { layerLabels.parent } txn hash - - - - - ) } - - Age - - - - - { layerLabels.current } txn hash - - { item.completion_transaction_hash ? ( - - ) : ( - - Pending Claim - - ) } - - - Value - - - - - - ); -}; - -export default ScrollL2DepositsListItem; diff --git a/src/features/rollup/scroll/pages/deposits/__screenshots__/ScrollL2Deposits.pw.tsx_default_base-view-1.png b/src/features/rollup/scroll/pages/deposits/__screenshots__/ScrollL2Deposits.pw.tsx_default_base-view-1.png index bf842b28670..4b3fe0b1029 100644 Binary files a/src/features/rollup/scroll/pages/deposits/__screenshots__/ScrollL2Deposits.pw.tsx_default_base-view-1.png and b/src/features/rollup/scroll/pages/deposits/__screenshots__/ScrollL2Deposits.pw.tsx_default_base-view-1.png differ diff --git a/src/features/rollup/scroll/pages/deposits/__screenshots__/ScrollL2Deposits.pw.tsx_default_mobile-base-view-1.png b/src/features/rollup/scroll/pages/deposits/__screenshots__/ScrollL2Deposits.pw.tsx_default_mobile-base-view-1.png deleted file mode 100644 index 7b08c6cb782..00000000000 Binary files a/src/features/rollup/scroll/pages/deposits/__screenshots__/ScrollL2Deposits.pw.tsx_default_mobile-base-view-1.png and /dev/null differ diff --git a/src/features/rollup/scroll/pages/withdrawals/ScrollL2Withdrawals.pw.tsx b/src/features/rollup/scroll/pages/withdrawals/ScrollL2Withdrawals.pw.tsx index 8a2a76e86b2..3c0ec570be5 100644 --- a/src/features/rollup/scroll/pages/withdrawals/ScrollL2Withdrawals.pw.tsx +++ b/src/features/rollup/scroll/pages/withdrawals/ScrollL2Withdrawals.pw.tsx @@ -3,7 +3,7 @@ import React from 'react'; import * as messagesMock from 'src/features/rollup/scroll/mocks/messages'; import { ENVS_MAP } from 'playwright/fixtures/mockEnvs'; -import { test, expect, devices } from 'playwright/lib'; +import { test, expect } from 'playwright/lib'; import ScrollL2Withdrawals from './ScrollL2Withdrawals'; @@ -17,18 +17,3 @@ test('base view', async({ render, mockApiResponse, mockEnvs, mockTextAd }) => { await expect(component).toHaveScreenshot(); }); - -test.describe('mobile', () => { - test.use({ viewport: devices['iPhone 13 Pro'].viewport }); - - test('base view', async({ render, mockApiResponse, mockEnvs, mockTextAd }) => { - await mockTextAd(); - await mockEnvs(ENVS_MAP.scrollRollup); - await mockApiResponse('core:scroll_l2_withdrawals', messagesMock.baseResponse); - await mockApiResponse('core:scroll_l2_withdrawals_count', 3971111); - - const component = await render(); - - await expect(component).toHaveScreenshot(); - }); -}); diff --git a/src/features/rollup/scroll/pages/withdrawals/ScrollL2Withdrawals.tsx b/src/features/rollup/scroll/pages/withdrawals/ScrollL2Withdrawals.tsx index 3b698bc6414..cd450d70b3b 100644 --- a/src/features/rollup/scroll/pages/withdrawals/ScrollL2Withdrawals.tsx +++ b/src/features/rollup/scroll/pages/withdrawals/ScrollL2Withdrawals.tsx @@ -1,6 +1,5 @@ // SPDX-License-Identifier: LicenseRef-Blockscout -import { Box } from '@chakra-ui/react'; import React from 'react'; import useApiQuery from 'src/api/hooks/useApiQuery'; @@ -13,17 +12,17 @@ import { SCROLL_L2_MESSAGE_ITEM } from 'src/features/rollup/scroll/stubs'; import DataList from 'src/shared/lists/DataList'; import StickyPaginationWithText from 'src/shared/pagination/StickyPaginationWithText'; -import useQueryWithPages from 'src/shared/pagination/useQueryWithPages'; +import useApiPaginatedQuery from 'src/shared/pagination/useApiPaginatedQuery'; import { generateListStub } from 'src/shared/pagination/utils'; import { Skeleton } from 'src/toolkit/chakra/skeleton'; +import { TableContainerScrollable } from 'src/toolkit/chakra/table'; import { rightLineArrow, nbsp } from 'src/toolkit/utils/htmlEntities'; -import ScrollL2WithdrawalsList from './ScrollL2WithdrawalsList'; import ScrollL2WithdrawalsTable from './ScrollL2WithdrawalsTable'; const ScrollL2Withdrawals = () => { - const { data, isError, isPlaceholderData, pagination, queryHash } = useQueryWithPages({ + const { data, isError, isInitialLoading, isTransitioning, pagination, queryHash } = useApiPaginatedQuery({ resourceName: 'core:scroll_l2_withdrawals', options: { placeholderData: generateListStub<'core:scroll_l2_withdrawals'>( @@ -41,19 +40,14 @@ const ScrollL2Withdrawals = () => { }); const content = data?.items ? ( - <> - - - - - - - + + + ) : null; const text = (() => { @@ -81,6 +75,7 @@ const ScrollL2Withdrawals = () => { itemsNum={ data?.items?.length } emptyText="There are no withdrawals." actionBar={ actionBar } + isTransitioning={ isTransitioning } > { content } diff --git a/src/features/rollup/scroll/pages/withdrawals/ScrollL2WithdrawalsList.tsx b/src/features/rollup/scroll/pages/withdrawals/ScrollL2WithdrawalsList.tsx deleted file mode 100644 index 7b9537bae1b..00000000000 --- a/src/features/rollup/scroll/pages/withdrawals/ScrollL2WithdrawalsList.tsx +++ /dev/null @@ -1,37 +0,0 @@ -// SPDX-License-Identifier: LicenseRef-Blockscout - -import { Box } from '@chakra-ui/react'; -import React from 'react'; - -import type { schemas } from '@blockscout/api-types'; - -import useLazyRenderedList from 'src/shared/lists/useLazyRenderedList'; - -import ScrollL2WithdrawalsListItem from './ScrollL2WithdrawalsListItem'; - -type Props = { - items: Array; - isLoading?: boolean; - resetKey?: string; -}; - -const ScrollL2WithdrawalsList = ({ items, isLoading, resetKey }: Props) => { - const { cutRef, renderedItemsNum } = useLazyRenderedList({ list: items, isEnabled: !isLoading, resetKey }); - - return ( - <> - - { items.slice(0, renderedItemsNum).map((item, index) => ( - - )) } - - - - ); -}; - -export default ScrollL2WithdrawalsList; diff --git a/src/features/rollup/scroll/pages/withdrawals/ScrollL2WithdrawalsListItem.tsx b/src/features/rollup/scroll/pages/withdrawals/ScrollL2WithdrawalsListItem.tsx deleted file mode 100644 index 8c1e8d4d155..00000000000 --- a/src/features/rollup/scroll/pages/withdrawals/ScrollL2WithdrawalsListItem.tsx +++ /dev/null @@ -1,104 +0,0 @@ -// SPDX-License-Identifier: LicenseRef-Blockscout - -import { chakra } from '@chakra-ui/react'; -import React from 'react'; - -import type { schemas } from '@blockscout/api-types'; - -import BlockEntity from 'src/slices/block/components/entity/BlockEntity'; -import TxEntity from 'src/slices/tx/components/entity/TxEntity'; - -import TxEntityL1 from 'src/features/rollup/common/components/TxEntityL1'; -import { layerLabels } from 'src/features/rollup/common/utils/layer'; - -import config from 'src/config'; -import TimeWithTooltip from 'src/shared/date-and-time/TimeWithTooltip'; -import ListItemMobileGrid from 'src/shared/lists/ListItemMobileGrid'; -import NativeCoinValue from 'src/shared/values/entity/NativeCoinValue'; - -import { Skeleton } from 'src/toolkit/chakra/skeleton'; - -const rollupFeature = config.features.rollup; - -type Props = { item: schemas['ScrollBridge']; isLoading?: boolean }; - -const ScrollL2WithdrawalsListItem = ({ item, isLoading }: Props) => { - if (!rollupFeature.isEnabled || rollupFeature.type !== 'scroll') { - return null; - } - - return ( - - - { item.origination_transaction_block_number && ( - <> - { layerLabels.current } block - - - - - ) - } - - Index - - - { item.id } - - - - { item.origination_transaction_hash && ( - <> - { layerLabels.current } txn hash - - - - - ) } - - Age - - - - - { layerLabels.parent } txn hash - - { item.completion_transaction_hash ? ( - - ) : ( - - Pending Claim - - ) } - - - Value - - - - - - ); -}; - -export default ScrollL2WithdrawalsListItem; diff --git a/src/features/rollup/scroll/pages/withdrawals/__screenshots__/ScrollL2Withdrawals.pw.tsx_default_base-view-1.png b/src/features/rollup/scroll/pages/withdrawals/__screenshots__/ScrollL2Withdrawals.pw.tsx_default_base-view-1.png index 3f06b264dfa..ff9d3aa4421 100644 Binary files a/src/features/rollup/scroll/pages/withdrawals/__screenshots__/ScrollL2Withdrawals.pw.tsx_default_base-view-1.png and b/src/features/rollup/scroll/pages/withdrawals/__screenshots__/ScrollL2Withdrawals.pw.tsx_default_base-view-1.png differ diff --git a/src/features/rollup/scroll/pages/withdrawals/__screenshots__/ScrollL2Withdrawals.pw.tsx_default_mobile-base-view-1.png b/src/features/rollup/scroll/pages/withdrawals/__screenshots__/ScrollL2Withdrawals.pw.tsx_default_mobile-base-view-1.png deleted file mode 100644 index 16a5658fe27..00000000000 Binary files a/src/features/rollup/scroll/pages/withdrawals/__screenshots__/ScrollL2Withdrawals.pw.tsx_default_mobile-base-view-1.png and /dev/null differ diff --git a/src/features/rollup/shibarium/pages/deposits/DepositsList.tsx b/src/features/rollup/shibarium/pages/deposits/DepositsList.tsx deleted file mode 100644 index c8ca27956f6..00000000000 --- a/src/features/rollup/shibarium/pages/deposits/DepositsList.tsx +++ /dev/null @@ -1,37 +0,0 @@ -// SPDX-License-Identifier: LicenseRef-Blockscout - -import { Box } from '@chakra-ui/react'; -import React from 'react'; - -import type { ShibariumDepositsItem } from 'src/features/rollup/shibarium/types/api'; - -import useLazyRenderedList from 'src/shared/lists/useLazyRenderedList'; - -import DepositsListItem from './DepositsListItem'; - -type Props = { - items: Array; - isLoading?: boolean; - resetKey?: string; -}; - -const DepositsList = ({ items, isLoading, resetKey }: Props) => { - const { cutRef, renderedItemsNum } = useLazyRenderedList({ list: items, isEnabled: !isLoading, resetKey }); - - return ( - <> - - { items.slice(0, renderedItemsNum).map((item, index) => ( - - )) } - - - - ); -}; - -export default DepositsList; diff --git a/src/features/rollup/shibarium/pages/deposits/DepositsListItem.tsx b/src/features/rollup/shibarium/pages/deposits/DepositsListItem.tsx deleted file mode 100644 index 03cfc0e14b8..00000000000 --- a/src/features/rollup/shibarium/pages/deposits/DepositsListItem.tsx +++ /dev/null @@ -1,84 +0,0 @@ -// SPDX-License-Identifier: LicenseRef-Blockscout - -import React from 'react'; - -import type { ShibariumDepositsItem } from 'src/features/rollup/shibarium/types/api'; - -import AddressStringOrParam from 'src/slices/address/components/entity/AddressStringOrParam'; -import TxEntity from 'src/slices/tx/components/entity/TxEntity'; - -import BlockEntityL1 from 'src/features/rollup/common/components/BlockEntityL1'; -import TxEntityL1 from 'src/features/rollup/common/components/TxEntityL1'; -import { layerLabels } from 'src/features/rollup/common/utils/layer'; - -import config from 'src/config'; -import TimeWithTooltip from 'src/shared/date-and-time/TimeWithTooltip'; -import ListItemMobileGrid from 'src/shared/lists/ListItemMobileGrid'; - -const feature = config.features.rollup; - -type Props = { item: ShibariumDepositsItem; isLoading?: boolean }; - -const DepositsListItem = ({ item, isLoading }: Props) => { - if (!(feature.isEnabled && feature.type === 'shibarium')) { - return null; - } - - return ( - - - { layerLabels.parent } block No - - - - - { layerLabels.parent } txn hash - - - - - { layerLabels.current } txn hash - - - - - User - - - - - Age - - - - - - ); -}; - -export default DepositsListItem; diff --git a/src/features/rollup/shibarium/pages/deposits/DepositsTable.tsx b/src/features/rollup/shibarium/pages/deposits/DepositsTable.tsx index 3310dcbf401..78ef699baa9 100644 --- a/src/features/rollup/shibarium/pages/deposits/DepositsTable.tsx +++ b/src/features/rollup/shibarium/pages/deposits/DepositsTable.tsx @@ -42,7 +42,7 @@ const DepositsTable = ({ items, top, isLoading, resetKey }: Props) => { { items.slice(0, renderedItemsNum).map((item, index) => ( - + )) } diff --git a/src/features/rollup/shibarium/pages/deposits/ShibariumDeposits.pw.tsx b/src/features/rollup/shibarium/pages/deposits/ShibariumDeposits.pw.tsx index 558cae3bc68..758083dfe4a 100644 --- a/src/features/rollup/shibarium/pages/deposits/ShibariumDeposits.pw.tsx +++ b/src/features/rollup/shibarium/pages/deposits/ShibariumDeposits.pw.tsx @@ -7,7 +7,7 @@ import { test, expect } from 'playwright/lib'; import ShibariumDeposits from './ShibariumDeposits'; -test('base view +@mobile', async({ render, mockApiResponse, mockEnvs, mockTextAd }) => { +test('base view', async({ render, mockApiResponse, mockEnvs, mockTextAd }) => { test.slow(); await mockTextAd(); await mockEnvs(ENVS_MAP.shibariumRollup); diff --git a/src/features/rollup/shibarium/pages/deposits/ShibariumDeposits.tsx b/src/features/rollup/shibarium/pages/deposits/ShibariumDeposits.tsx index 56435a0c9bb..3936d7ccad4 100644 --- a/src/features/rollup/shibarium/pages/deposits/ShibariumDeposits.tsx +++ b/src/features/rollup/shibarium/pages/deposits/ShibariumDeposits.tsx @@ -1,6 +1,5 @@ // SPDX-License-Identifier: LicenseRef-Blockscout -import { Box } from '@chakra-ui/react'; import React from 'react'; import useApiQuery from 'src/api/hooks/useApiQuery'; @@ -13,17 +12,17 @@ import { SHIBARIUM_DEPOSIT_ITEM } from 'src/features/rollup/shibarium/stubs'; import DataList from 'src/shared/lists/DataList'; import StickyPaginationWithText from 'src/shared/pagination/StickyPaginationWithText'; -import useQueryWithPages from 'src/shared/pagination/useQueryWithPages'; +import useApiPaginatedQuery from 'src/shared/pagination/useApiPaginatedQuery'; import { generateListStub } from 'src/shared/pagination/utils'; import { Skeleton } from 'src/toolkit/chakra/skeleton'; +import { TableContainerScrollable } from 'src/toolkit/chakra/table'; import { rightLineArrow, nbsp } from 'src/toolkit/utils/htmlEntities'; -import DepositsList from './DepositsList'; import DepositsTable from './DepositsTable'; const ShibariumDeposits = () => { - const { data, isError, isPlaceholderData, pagination, queryHash } = useQueryWithPages({ + const { data, isError, isInitialLoading, isTransitioning, pagination, queryHash } = useApiPaginatedQuery({ resourceName: 'core:shibarium_deposits', options: { placeholderData: generateListStub<'core:shibarium_deposits'>( @@ -46,19 +45,14 @@ const ShibariumDeposits = () => { }); const content = data?.items ? ( - <> - - - - - - - + + + ) : null; const text = (() => { @@ -86,6 +80,7 @@ const ShibariumDeposits = () => { itemsNum={ data?.items.length } emptyText="There are no deposits." actionBar={ actionBar } + isTransitioning={ isTransitioning } > { content } diff --git a/src/features/rollup/shibarium/pages/deposits/__screenshots__/ShibariumDeposits.pw.tsx_default_base-view-1.png b/src/features/rollup/shibarium/pages/deposits/__screenshots__/ShibariumDeposits.pw.tsx_default_base-view-1.png new file mode 100644 index 00000000000..cf31515db0f Binary files /dev/null and b/src/features/rollup/shibarium/pages/deposits/__screenshots__/ShibariumDeposits.pw.tsx_default_base-view-1.png differ diff --git a/src/features/rollup/shibarium/pages/deposits/__screenshots__/ShibariumDeposits.pw.tsx_default_base-view-mobile-1.png b/src/features/rollup/shibarium/pages/deposits/__screenshots__/ShibariumDeposits.pw.tsx_default_base-view-mobile-1.png deleted file mode 100644 index 0d0874f83bd..00000000000 Binary files a/src/features/rollup/shibarium/pages/deposits/__screenshots__/ShibariumDeposits.pw.tsx_default_base-view-mobile-1.png and /dev/null differ diff --git a/src/features/rollup/shibarium/pages/deposits/__screenshots__/ShibariumDeposits.pw.tsx_mobile_base-view-mobile-1.png b/src/features/rollup/shibarium/pages/deposits/__screenshots__/ShibariumDeposits.pw.tsx_mobile_base-view-mobile-1.png deleted file mode 100644 index 25322dda97f..00000000000 Binary files a/src/features/rollup/shibarium/pages/deposits/__screenshots__/ShibariumDeposits.pw.tsx_mobile_base-view-mobile-1.png and /dev/null differ diff --git a/src/features/rollup/shibarium/pages/withdrawals/ShibariumWithdrawals.pw.tsx b/src/features/rollup/shibarium/pages/withdrawals/ShibariumWithdrawals.pw.tsx index 289830a62b0..9c174c567d8 100644 --- a/src/features/rollup/shibarium/pages/withdrawals/ShibariumWithdrawals.pw.tsx +++ b/src/features/rollup/shibarium/pages/withdrawals/ShibariumWithdrawals.pw.tsx @@ -7,10 +7,7 @@ import { test, expect } from 'playwright/lib'; import ShibariumWithdrawals from './ShibariumWithdrawals'; -test('base view +@mobile', async({ render, mockApiResponse, mockEnvs, mockTextAd }) => { - // test on mobile is flaky - // my assumption is there is not enough time to calculate hashes truncation so component is unstable - // so I raised the test timeout to check if it helps +test('base view', async({ render, mockApiResponse, mockEnvs, mockTextAd }) => { test.slow(); await mockTextAd(); diff --git a/src/features/rollup/shibarium/pages/withdrawals/ShibariumWithdrawals.tsx b/src/features/rollup/shibarium/pages/withdrawals/ShibariumWithdrawals.tsx index 79f94d6ba6e..ea1b3a948c2 100644 --- a/src/features/rollup/shibarium/pages/withdrawals/ShibariumWithdrawals.tsx +++ b/src/features/rollup/shibarium/pages/withdrawals/ShibariumWithdrawals.tsx @@ -1,6 +1,5 @@ // SPDX-License-Identifier: LicenseRef-Blockscout -import { Box } from '@chakra-ui/react'; import React from 'react'; import useApiQuery from 'src/api/hooks/useApiQuery'; @@ -13,17 +12,17 @@ import { SHIBARIUM_WITHDRAWAL_ITEM } from 'src/features/rollup/shibarium/stubs'; import DataList from 'src/shared/lists/DataList'; import StickyPaginationWithText from 'src/shared/pagination/StickyPaginationWithText'; -import useQueryWithPages from 'src/shared/pagination/useQueryWithPages'; +import useApiPaginatedQuery from 'src/shared/pagination/useApiPaginatedQuery'; import { generateListStub } from 'src/shared/pagination/utils'; import { Skeleton } from 'src/toolkit/chakra/skeleton'; +import { TableContainerScrollable } from 'src/toolkit/chakra/table'; import { rightLineArrow, nbsp } from 'src/toolkit/utils/htmlEntities'; -import WithdrawalsList from './WithdrawalsList'; import WithdrawalsTable from './WithdrawalsTable'; const ShibariumWithdrawals = () => { - const { data, isError, isPlaceholderData, pagination, queryHash } = useQueryWithPages({ + const { data, isError, isInitialLoading, isTransitioning, pagination, queryHash } = useApiPaginatedQuery({ resourceName: 'core:shibarium_withdrawals', options: { placeholderData: generateListStub<'core:shibarium_withdrawals'>( @@ -46,19 +45,14 @@ const ShibariumWithdrawals = () => { }); const content = data?.items ? ( - <> - - - - - - - + + + ) : null; const text = (() => { @@ -83,6 +77,7 @@ const ShibariumWithdrawals = () => { itemsNum={ data?.items.length } emptyText="There are no withdrawals." actionBar={ actionBar } + isTransitioning={ isTransitioning } > { content } diff --git a/src/features/rollup/shibarium/pages/withdrawals/WithdrawalsList.tsx b/src/features/rollup/shibarium/pages/withdrawals/WithdrawalsList.tsx deleted file mode 100644 index 0921cde61bd..00000000000 --- a/src/features/rollup/shibarium/pages/withdrawals/WithdrawalsList.tsx +++ /dev/null @@ -1,37 +0,0 @@ -// SPDX-License-Identifier: LicenseRef-Blockscout - -import { Box } from '@chakra-ui/react'; -import React from 'react'; - -import type { ShibariumWithdrawalsItem } from 'src/features/rollup/shibarium/types/api'; - -import useLazyRenderedList from 'src/shared/lists/useLazyRenderedList'; - -import WithdrawalsListItem from './WithdrawalsListItem'; - -type Props = { - items: Array; - isLoading?: boolean; - resetKey?: string; -}; - -const WithdrawalsList = ({ items, isLoading, resetKey }: Props) => { - const { cutRef, renderedItemsNum } = useLazyRenderedList({ list: items, isEnabled: !isLoading, resetKey }); - - return ( - <> - - { items.slice(0, renderedItemsNum).map((item, index) => ( - - )) } - - - - ); -}; - -export default WithdrawalsList; diff --git a/src/features/rollup/shibarium/pages/withdrawals/WithdrawalsListItem.tsx b/src/features/rollup/shibarium/pages/withdrawals/WithdrawalsListItem.tsx deleted file mode 100644 index a13c530c2a2..00000000000 --- a/src/features/rollup/shibarium/pages/withdrawals/WithdrawalsListItem.tsx +++ /dev/null @@ -1,83 +0,0 @@ -// SPDX-License-Identifier: LicenseRef-Blockscout - -import React from 'react'; - -import type { ShibariumWithdrawalsItem } from 'src/features/rollup/shibarium/types/api'; - -import AddressStringOrParam from 'src/slices/address/components/entity/AddressStringOrParam'; -import BlockEntity from 'src/slices/block/components/entity/BlockEntity'; -import TxEntity from 'src/slices/tx/components/entity/TxEntity'; - -import TxEntityL1 from 'src/features/rollup/common/components/TxEntityL1'; -import { layerLabels } from 'src/features/rollup/common/utils/layer'; - -import config from 'src/config'; -import TimeWithTooltip from 'src/shared/date-and-time/TimeWithTooltip'; -import ListItemMobileGrid from 'src/shared/lists/ListItemMobileGrid'; - -const feature = config.features.rollup; - -type Props = { item: ShibariumWithdrawalsItem; isLoading?: boolean }; - -const WithdrawalsListItem = ({ item, isLoading }: Props) => { - if (!(feature.isEnabled && feature.type === 'shibarium')) { - return null; - } - - return ( - - - { layerLabels.current } block No - - - - - { layerLabels.current } txn hash - - - - - { layerLabels.parent } txn hash - - - - - User - - - - Age - - - - - - ); -}; - -export default WithdrawalsListItem; diff --git a/src/features/rollup/shibarium/pages/withdrawals/WithdrawalsTable.tsx b/src/features/rollup/shibarium/pages/withdrawals/WithdrawalsTable.tsx index b1d52fe58b2..5061df929a2 100644 --- a/src/features/rollup/shibarium/pages/withdrawals/WithdrawalsTable.tsx +++ b/src/features/rollup/shibarium/pages/withdrawals/WithdrawalsTable.tsx @@ -42,7 +42,7 @@ const WithdrawalsTable = ({ items, top, isLoading, resetKey }: Props) => { { items.slice(0, renderedItemsNum).map((item, index) => ( - + )) } diff --git a/src/features/rollup/shibarium/pages/withdrawals/__screenshots__/ShibariumWithdrawals.pw.tsx_default_base-view-1.png b/src/features/rollup/shibarium/pages/withdrawals/__screenshots__/ShibariumWithdrawals.pw.tsx_default_base-view-1.png new file mode 100644 index 00000000000..d84b41a00e5 Binary files /dev/null and b/src/features/rollup/shibarium/pages/withdrawals/__screenshots__/ShibariumWithdrawals.pw.tsx_default_base-view-1.png differ diff --git a/src/features/rollup/shibarium/pages/withdrawals/__screenshots__/ShibariumWithdrawals.pw.tsx_default_base-view-mobile-1.png b/src/features/rollup/shibarium/pages/withdrawals/__screenshots__/ShibariumWithdrawals.pw.tsx_default_base-view-mobile-1.png deleted file mode 100644 index 0fac76aacf8..00000000000 Binary files a/src/features/rollup/shibarium/pages/withdrawals/__screenshots__/ShibariumWithdrawals.pw.tsx_default_base-view-mobile-1.png and /dev/null differ diff --git a/src/features/rollup/shibarium/pages/withdrawals/__screenshots__/ShibariumWithdrawals.pw.tsx_mobile_base-view-mobile-1.png b/src/features/rollup/shibarium/pages/withdrawals/__screenshots__/ShibariumWithdrawals.pw.tsx_mobile_base-view-mobile-1.png deleted file mode 100644 index 508ff7f4d3a..00000000000 Binary files a/src/features/rollup/shibarium/pages/withdrawals/__screenshots__/ShibariumWithdrawals.pw.tsx_mobile_base-view-mobile-1.png and /dev/null differ diff --git a/src/features/rollup/zk-sync/pages/batch-details/ZkSyncL2TxnBatch.tsx b/src/features/rollup/zk-sync/pages/batch-details/ZkSyncL2TxnBatch.tsx index c4751db40c2..b9e8358e946 100644 --- a/src/features/rollup/zk-sync/pages/batch-details/ZkSyncL2TxnBatch.tsx +++ b/src/features/rollup/zk-sync/pages/batch-details/ZkSyncL2TxnBatch.tsx @@ -19,7 +19,7 @@ import throwOnAbsentParamError from 'src/shared/errors/throw-on-absent-param-err import throwOnResourceLoadError from 'src/shared/errors/throw-on-resource-load-error'; import useIsMobile from 'src/shared/hooks/useIsMobile'; import Pagination from 'src/shared/pagination/Pagination'; -import useQueryWithPages from 'src/shared/pagination/useQueryWithPages'; +import useApiPaginatedQuery from 'src/shared/pagination/useApiPaginatedQuery'; import { generateListStub } from 'src/shared/pagination/utils'; import getQueryParamString from 'src/shared/router/get-query-param-string'; @@ -49,7 +49,7 @@ const ZkSyncL2TxnBatch = () => { }, }); - const batchTxsQuery = useQueryWithPages({ + const batchTxsQuery = useApiPaginatedQuery({ resourceName: 'core:zksync_l2_txn_batch_txs', pathParams: { number }, options: { diff --git a/src/features/rollup/zk-sync/pages/batch-details/ZkSyncL2TxnBatchDetails.tsx b/src/features/rollup/zk-sync/pages/batch-details/ZkSyncL2TxnBatchDetails.tsx index c3262969496..926a84fbf66 100644 --- a/src/features/rollup/zk-sync/pages/batch-details/ZkSyncL2TxnBatchDetails.tsx +++ b/src/features/rollup/zk-sync/pages/batch-details/ZkSyncL2TxnBatchDetails.tsx @@ -29,7 +29,7 @@ import GasPriceValue from 'src/shared/values/entity/GasPriceValue'; import { CollapsibleDetails } from 'src/toolkit/chakra/collapsible'; import { Link } from 'src/toolkit/chakra/link'; import { Skeleton } from 'src/toolkit/chakra/skeleton'; -import { TruncatedText } from 'src/toolkit/components/truncation/TruncatedText'; +import { Truncate } from 'src/toolkit/components/truncation/Truncate'; import ZkSyncL2TxnBatchHashesInfo from './ZkSyncL2TxnBatchHashesInfo'; @@ -148,7 +148,7 @@ const ZkSyncL2TxnBatchDetails = ({ query }: Props) => { flexWrap="nowrap" alignSelf="flex-start" > - + diff --git a/src/features/rollup/zk-sync/pages/batch-details/__screenshots__/ZkSyncL2TxnBatch.pw.tsx_default_base-view-1.png b/src/features/rollup/zk-sync/pages/batch-details/__screenshots__/ZkSyncL2TxnBatch.pw.tsx_default_base-view-1.png index 82c32aa1af7..316c9cda3a1 100644 Binary files a/src/features/rollup/zk-sync/pages/batch-details/__screenshots__/ZkSyncL2TxnBatch.pw.tsx_default_base-view-1.png and b/src/features/rollup/zk-sync/pages/batch-details/__screenshots__/ZkSyncL2TxnBatch.pw.tsx_default_base-view-1.png differ diff --git a/src/features/rollup/zk-sync/pages/batch-details/__screenshots__/ZkSyncL2TxnBatch.pw.tsx_default_mobile-base-view-1.png b/src/features/rollup/zk-sync/pages/batch-details/__screenshots__/ZkSyncL2TxnBatch.pw.tsx_default_mobile-base-view-1.png index 3cbbc41d135..646097066be 100644 Binary files a/src/features/rollup/zk-sync/pages/batch-details/__screenshots__/ZkSyncL2TxnBatch.pw.tsx_default_mobile-base-view-1.png and b/src/features/rollup/zk-sync/pages/batch-details/__screenshots__/ZkSyncL2TxnBatch.pw.tsx_default_mobile-base-view-1.png differ diff --git a/src/features/rollup/zk-sync/pages/batches/ZkSyncL2TxnBatches.pw.tsx b/src/features/rollup/zk-sync/pages/batches/ZkSyncL2TxnBatches.pw.tsx index 0d57fce99bf..4dd6ed118f9 100644 --- a/src/features/rollup/zk-sync/pages/batches/ZkSyncL2TxnBatches.pw.tsx +++ b/src/features/rollup/zk-sync/pages/batches/ZkSyncL2TxnBatches.pw.tsx @@ -7,7 +7,7 @@ import { test, expect } from 'playwright/lib'; import ZkSyncL2TxnBatches from './ZkSyncL2TxnBatches'; -test('base view +@mobile', async({ render, mockEnvs, mockTextAd, mockApiResponse }) => { +test('base view', async({ render, mockEnvs, mockTextAd, mockApiResponse }) => { test.slow(); await mockEnvs(ENVS_MAP.zkSyncRollup); await mockTextAd(); diff --git a/src/features/rollup/zk-sync/pages/batches/ZkSyncL2TxnBatches.tsx b/src/features/rollup/zk-sync/pages/batches/ZkSyncL2TxnBatches.tsx index 0c32185a5dd..ed109186d88 100644 --- a/src/features/rollup/zk-sync/pages/batches/ZkSyncL2TxnBatches.tsx +++ b/src/features/rollup/zk-sync/pages/batches/ZkSyncL2TxnBatches.tsx @@ -1,6 +1,6 @@ // SPDX-License-Identifier: LicenseRef-Blockscout -import { Box, Text } from '@chakra-ui/react'; +import { Text } from '@chakra-ui/react'; import React from 'react'; import useApiQuery from 'src/api/hooks/useApiQuery'; @@ -12,16 +12,16 @@ import { ZKSYNC_L2_TXN_BATCHES_ITEM } from 'src/features/rollup/zk-sync/stubs'; import DataList from 'src/shared/lists/DataList'; import StickyPaginationWithText from 'src/shared/pagination/StickyPaginationWithText'; -import useQueryWithPages from 'src/shared/pagination/useQueryWithPages'; +import useApiPaginatedQuery from 'src/shared/pagination/useApiPaginatedQuery'; import { generateListStub } from 'src/shared/pagination/utils'; import { Skeleton } from 'src/toolkit/chakra/skeleton'; +import { TableContainerScrollable } from 'src/toolkit/chakra/table'; -import ZkSyncTxnBatchesList from './ZkSyncTxnBatchesList'; import ZkSyncTxnBatchesTable from './ZkSyncTxnBatchesTable'; const ZkSyncL2TxnBatches = () => { - const { data, isError, isPlaceholderData, pagination, queryHash } = useQueryWithPages({ + const { data, isError, isInitialLoading, isTransitioning, pagination, queryHash } = useApiPaginatedQuery({ resourceName: 'core:zksync_l2_txn_batches', options: { placeholderData: generateListStub<'core:zksync_l2_txn_batches'>( @@ -44,19 +44,14 @@ const ZkSyncL2TxnBatches = () => { }); const content = data?.items ? ( - <> - - - - - - - + + + ) : null; const text = (() => { @@ -65,7 +60,7 @@ const ZkSyncL2TxnBatches = () => { } return ( - + Txn batch #{ data.items[0].number } to #{ data.items[data.items.length - 1].number } @@ -84,6 +79,7 @@ const ZkSyncL2TxnBatches = () => { itemsNum={ data?.items.length } emptyText="There are no txn batches." actionBar={ actionBar } + isTransitioning={ isTransitioning } > { content } diff --git a/src/features/rollup/zk-sync/pages/batches/ZkSyncTxnBatchesList.tsx b/src/features/rollup/zk-sync/pages/batches/ZkSyncTxnBatchesList.tsx deleted file mode 100644 index f8c9f99d43b..00000000000 --- a/src/features/rollup/zk-sync/pages/batches/ZkSyncTxnBatchesList.tsx +++ /dev/null @@ -1,37 +0,0 @@ -// SPDX-License-Identifier: LicenseRef-Blockscout - -import { Box } from '@chakra-ui/react'; -import React from 'react'; - -import type { ZkSyncBatchesItem } from 'src/features/rollup/zk-sync/types/api'; - -import useLazyRenderedList from 'src/shared/lists/useLazyRenderedList'; - -import ZkSyncTxnBatchesListItem from './ZkSyncTxnBatchesListItem'; - -type Props = { - items: Array; - isLoading?: boolean; - resetKey?: string; -}; - -const ZkSyncTxnBatchesList = ({ items, isLoading, resetKey }: Props) => { - const { cutRef, renderedItemsNum } = useLazyRenderedList({ list: items, isEnabled: !isLoading, resetKey }); - - return ( - <> - - { items.slice(0, renderedItemsNum).map((item, index) => ( - - )) } - - - - ); -}; - -export default ZkSyncTxnBatchesList; diff --git a/src/features/rollup/zk-sync/pages/batches/ZkSyncTxnBatchesListItem.tsx b/src/features/rollup/zk-sync/pages/batches/ZkSyncTxnBatchesListItem.tsx deleted file mode 100644 index 27e324432d6..00000000000 --- a/src/features/rollup/zk-sync/pages/batches/ZkSyncTxnBatchesListItem.tsx +++ /dev/null @@ -1,98 +0,0 @@ -// SPDX-License-Identifier: LicenseRef-Blockscout - -import { Text } from '@chakra-ui/react'; -import { route } from 'nextjs-routes'; -import React from 'react'; - -import type { ZkSyncBatchesItem } from 'src/features/rollup/zk-sync/types/api'; - -import BatchEntityL2 from 'src/features/rollup/common/components/BatchEntityL2'; -import TxEntityL1 from 'src/features/rollup/common/components/TxEntityL1'; -import ZkSyncL2TxnBatchStatus from 'src/features/rollup/zk-sync/components/ZkSyncL2TxnBatchStatus'; - -import config from 'src/config'; -import TimeWithTooltip from 'src/shared/date-and-time/TimeWithTooltip'; -import ListItemMobileGrid from 'src/shared/lists/ListItemMobileGrid'; - -import { Link } from 'src/toolkit/chakra/link'; - -const rollupFeature = config.features.rollup; - -type Props = { item: ZkSyncBatchesItem; isLoading?: boolean }; - -const ZkSyncTxnBatchesListItem = ({ item, isLoading }: Props) => { - if (!rollupFeature.isEnabled || rollupFeature.type !== 'zkSync') { - return null; - } - - return ( - - - Batch # - - - - - Status - - - - - Age - - - - - Txn count - - - { item.transactions_count } - - - - Commit tx - - { item.commit_transaction_hash ? ( - - ) : Pending } - - - Prove tx - - { item.prove_transaction_hash ? ( - - ) : Pending } - - - - ); -}; - -export default ZkSyncTxnBatchesListItem; diff --git a/src/features/rollup/zk-sync/pages/batches/__screenshots__/ZkSyncL2TxnBatches.pw.tsx_default_base-view-mobile-1.png b/src/features/rollup/zk-sync/pages/batches/__screenshots__/ZkSyncL2TxnBatches.pw.tsx_default_base-view-1.png similarity index 100% rename from src/features/rollup/zk-sync/pages/batches/__screenshots__/ZkSyncL2TxnBatches.pw.tsx_default_base-view-mobile-1.png rename to src/features/rollup/zk-sync/pages/batches/__screenshots__/ZkSyncL2TxnBatches.pw.tsx_default_base-view-1.png diff --git a/src/features/rollup/zk-sync/pages/batches/__screenshots__/ZkSyncL2TxnBatches.pw.tsx_mobile_base-view-mobile-1.png b/src/features/rollup/zk-sync/pages/batches/__screenshots__/ZkSyncL2TxnBatches.pw.tsx_mobile_base-view-mobile-1.png deleted file mode 100644 index f507f75f084..00000000000 Binary files a/src/features/rollup/zk-sync/pages/batches/__screenshots__/ZkSyncL2TxnBatches.pw.tsx_mobile_base-view-mobile-1.png and /dev/null differ diff --git a/src/features/safe/hooks/useIsSafeAddress.tsx b/src/features/safe/hooks/useIsSafeAddress.ts similarity index 100% rename from src/features/safe/hooks/useIsSafeAddress.tsx rename to src/features/safe/hooks/useIsSafeAddress.ts diff --git a/src/features/solidity-scan/components/__screenshots__/SolidityscanReport.pw.tsx_mobile_average-report-dark-mode-mobile-2.png b/src/features/solidity-scan/components/__screenshots__/SolidityscanReport.pw.tsx_mobile_average-report-dark-mode-mobile-2.png index bea3d07e961..10fa7f228af 100644 Binary files a/src/features/solidity-scan/components/__screenshots__/SolidityscanReport.pw.tsx_mobile_average-report-dark-mode-mobile-2.png and b/src/features/solidity-scan/components/__screenshots__/SolidityscanReport.pw.tsx_mobile_average-report-dark-mode-mobile-2.png differ diff --git a/src/features/tx-authorization/pages/tx/TxAuthorizations.tsx b/src/features/tx-authorization/pages/tx/TxAuthorizations.tsx index 8a5763f0ca8..fa0c9143d94 100644 --- a/src/features/tx-authorization/pages/tx/TxAuthorizations.tsx +++ b/src/features/tx-authorization/pages/tx/TxAuthorizations.tsx @@ -1,6 +1,5 @@ // SPDX-License-Identifier: LicenseRef-Blockscout -import { Box } from '@chakra-ui/react'; import React from 'react'; import TxPendingAlert from 'src/slices/tx/components/TxPendingAlert'; @@ -9,7 +8,8 @@ import type { TxQuery } from 'src/slices/tx/hooks/useTxQuery'; import DataList from 'src/shared/lists/DataList'; -import TxAuthorizationsList from './TxAuthorizationsList'; +import { TableContainerScrollable } from 'src/toolkit/chakra/table'; + import TxAuthorizationsTable from './TxAuthorizationsTable'; interface Props { @@ -23,22 +23,13 @@ const TxAuthorizations = ({ txQuery }: Props) => { } const content = txQuery.data?.authorization_list ? ( - <> - - - - - - - + + + ) : null; return ( diff --git a/src/features/tx-authorization/pages/tx/TxAuthorizationsList.tsx b/src/features/tx-authorization/pages/tx/TxAuthorizationsList.tsx deleted file mode 100644 index 55a87032128..00000000000 --- a/src/features/tx-authorization/pages/tx/TxAuthorizationsList.tsx +++ /dev/null @@ -1,35 +0,0 @@ -// SPDX-License-Identifier: LicenseRef-Blockscout - -import { Box } from '@chakra-ui/react'; -import React from 'react'; - -import type { schemas } from '@blockscout/api-types'; - -import useLazyRenderedList from 'src/shared/lists/useLazyRenderedList'; - -import TxAuthorizationsListItem from './TxAuthorizationsListItem'; - -interface Props { - data: Array; - isLoading?: boolean; - resetKey?: string; -} - -const TxAuthorizationsList = ({ data, isLoading, resetKey }: Props) => { - const { cutRef, renderedItemsNum } = useLazyRenderedList({ list: data, isEnabled: !isLoading, resetKey }); - - return ( - - { data.slice(0, renderedItemsNum).map((item, index) => ( - - )) } - - - ); -}; - -export default TxAuthorizationsList; diff --git a/src/features/tx-authorization/pages/tx/TxAuthorizationsListItem.tsx b/src/features/tx-authorization/pages/tx/TxAuthorizationsListItem.tsx deleted file mode 100644 index a5806a3c970..00000000000 --- a/src/features/tx-authorization/pages/tx/TxAuthorizationsListItem.tsx +++ /dev/null @@ -1,49 +0,0 @@ -// SPDX-License-Identifier: LicenseRef-Blockscout - -import { HStack } from '@chakra-ui/react'; -import React from 'react'; - -import type { schemas } from '@blockscout/api-types'; - -import AddressEntity from 'src/slices/address/components/entity/AddressEntity'; - -import TxAuthorizationStatus from 'src/features/tx-authorization/components/TxAuthorizationStatus'; - -import config from 'src/config'; -import ListItemMobile from 'src/shared/lists/ListItemMobile'; - -import { Skeleton } from 'src/toolkit/chakra/skeleton'; - -interface Props { - data: schemas['SignedAuthorization']; - isLoading?: boolean; -} - -const TxAuthorizationsListItem = ({ data, isLoading }: Props) => { - return ( - - - Authority - - - - Delegated address - - - - Chain - { data.chain_id === Number(config.chain.id) ? 'this' : 'any' } - - - Nonce - { data.nonce } - - - Status - - - - ); -}; - -export default TxAuthorizationsListItem; diff --git a/src/features/tx-authorization/pages/tx/TxAuthorizationsTable.tsx b/src/features/tx-authorization/pages/tx/TxAuthorizationsTable.tsx index 53b677aa92a..e9202cea849 100644 --- a/src/features/tx-authorization/pages/tx/TxAuthorizationsTable.tsx +++ b/src/features/tx-authorization/pages/tx/TxAuthorizationsTable.tsx @@ -23,7 +23,7 @@ const TxAuthorizationsTable = ({ data, isLoading, resetKey }: Props) => { return ( - + Authority diff --git a/src/features/tx-interpretation/common/components/TxInterpretation.tsx b/src/features/tx-interpretation/common/components/TxInterpretation.tsx index 1104f344bae..c27d779e7f7 100644 --- a/src/features/tx-interpretation/common/components/TxInterpretation.tsx +++ b/src/features/tx-interpretation/common/components/TxInterpretation.tsx @@ -20,7 +20,7 @@ import TokenEntity from 'src/slices/token/components/entity/TokenEntity'; import EnsEntity from 'src/features/name-services/domains/components/EnsEntity'; import config from 'src/config'; -import * as mixpanel from 'src/services/mixpanel'; +import * as analytics from 'src/shared/analytics'; import dayjs from 'src/shared/date-and-time/dayjs'; import ChainIcon from 'src/shared/external-chains/ChainIcon'; import getChainTooltipText from 'src/shared/external-chains/get-chain-tooltip-text'; @@ -61,15 +61,15 @@ const TxInterpretationElementByType = ( { variable, addressDataMap }: { variable?: NonStringTxInterpretationVariable; addressDataMap?: Record }, ) => { const onAddressClick = React.useCallback(() => { - mixpanel.logEvent(mixpanel.EventTypes.TX_INTERPRETATION_INTERACTION, { Type: 'Address click' }); + analytics.logEvent(analytics.EventTypes.TX_INTERPRETATION_INTERACTION, { Type: 'Address click' }); }, []); const onTokenClick = React.useCallback(() => { - mixpanel.logEvent(mixpanel.EventTypes.TX_INTERPRETATION_INTERACTION, { Type: 'Token click' }); + analytics.logEvent(analytics.EventTypes.TX_INTERPRETATION_INTERACTION, { Type: 'Token click' }); }, []); const onDomainClick = React.useCallback(() => { - mixpanel.logEvent(mixpanel.EventTypes.TX_INTERPRETATION_INTERACTION, { Type: 'Domain click' }); + analytics.logEvent(analytics.EventTypes.TX_INTERPRETATION_INTERACTION, { Type: 'Domain click' }); }, []); if (!variable) { diff --git a/src/features/tx-interpretation/common/utils/address-to-plain-text.ts b/src/features/tx-interpretation/common/utils/address-to-plain-text.ts index be5ff2758a8..884d2f2051c 100644 --- a/src/features/tx-interpretation/common/utils/address-to-plain-text.ts +++ b/src/features/tx-interpretation/common/utils/address-to-plain-text.ts @@ -3,7 +3,7 @@ import type { AddressNameSource } from 'src/slices/address/utils/get-address-name'; import getAddressName from 'src/slices/address/utils/get-address-name'; -import shortenString from 'src/shared/texts/shorten-string'; +import { shortenString } from 'src/toolkit/utils/texts'; // What `truncation="constant"` resolves to in `AddressEntity`. const HASH_CHAR_NUMBER = 8; diff --git a/src/features/tx-interpretation/noves/components/NovesTokenTooltipContent.tsx b/src/features/tx-interpretation/noves/components/NovesTokenTooltipContent.tsx index 4ac602157d9..5bc0c2745ea 100644 --- a/src/features/tx-interpretation/noves/components/NovesTokenTooltipContent.tsx +++ b/src/features/tx-interpretation/noves/components/NovesTokenTooltipContent.tsx @@ -7,9 +7,9 @@ import React from 'react'; import type { NovesNft, NovesToken } from 'src/features/tx-interpretation/noves/types/api'; import CopyToClipboard from 'src/shared/texts/CopyToClipboard'; -import shortenString from 'src/shared/texts/shorten-string'; import { HEX_REGEXP } from 'src/toolkit/utils/regexp'; +import { shortenString } from 'src/toolkit/utils/texts'; interface Props { amount?: string; diff --git a/src/features/tx-interpretation/noves/pages/address/AddressAccountHistory.tsx b/src/features/tx-interpretation/noves/pages/address/AddressAccountHistory.tsx index 5f5fc389e14..2738e395e50 100644 --- a/src/features/tx-interpretation/noves/pages/address/AddressAccountHistory.tsx +++ b/src/features/tx-interpretation/noves/pages/address/AddressAccountHistory.tsx @@ -1,5 +1,6 @@ // SPDX-License-Identifier: LicenseRef-Blockscout +import { omit } from 'es-toolkit'; import { useRouter } from 'next/router'; import React from 'react'; @@ -14,7 +15,7 @@ import { getFromToValue } from 'src/features/tx-interpretation/noves/utils/from- import useIsMounted from 'src/shared/hooks/useIsMounted'; import DataList from 'src/shared/lists/DataList'; import Pagination from 'src/shared/pagination/Pagination'; -import useQueryWithPages from 'src/shared/pagination/useQueryWithPages'; +import useApiPaginatedQuery from 'src/shared/pagination/useApiPaginatedQuery'; import { generateListStub } from 'src/shared/pagination/utils'; import getFilterValueFromQuery from 'src/shared/router/get-filter-value-from-query'; import getQueryParamString from 'src/shared/router/get-query-param-string'; @@ -26,6 +27,8 @@ import AddressAccountHistoryTable from './AddressAccountHistoryTable'; const getFilterValue = (getFilterValueFromQuery).bind(null, NovesHistoryFilterValues); +const FILTER_FIELDS = [ 'filter' ]; + type Props = { shouldRender?: boolean; isQueryEnabled?: boolean; @@ -36,10 +39,9 @@ const AddressAccountHistory = ({ shouldRender = true, isQueryEnabled = true }: P const isMounted = useIsMounted(); const currentAddress = getQueryParamString(router.query.hash).toLowerCase(); + const filterValue = getFilterValue(router.query.filter); - const [ filterValue, setFilterValue ] = React.useState(getFilterValue(router.query.filter)); - - const { data, isError, pagination, isPlaceholderData, queryHash } = useQueryWithPages({ + const { data, isError, pagination, isInitialLoading, isTransitioning, queryHash } = useApiPaginatedQuery({ resourceName: 'core:noves_address_history', pathParams: { address: currentAddress }, options: { @@ -49,10 +51,12 @@ const AddressAccountHistory = ({ shouldRender = true, isQueryEnabled = true }: P }); const handleFilterChange = React.useCallback((val: string | Array) => { - - const newVal = getFilterValue(val); - setFilterValue(newVal); - }, [ ]); + const nextValue = getFilterValue(val); + router.push({ + pathname: router.pathname, + query: nextValue ? { ...router.query, filter: nextValue } : omit(router.query, FILTER_FIELDS), + }, undefined, { shallow: true }); + }, [ router ]); if (!isMounted || !shouldRender) { return null; @@ -71,14 +75,14 @@ const AddressAccountHistory = ({ shouldRender = true, isQueryEnabled = true }: P ); - const filteredData = isPlaceholderData ? data?.items : data?.items.filter(i => filterValue ? getFromToValue(i, currentAddress) === filterValue : i); + const filteredData = isInitialLoading ? data?.items : data?.items.filter(i => filterValue ? getFromToValue(i, currentAddress) === filterValue : i); const content = filteredData ? ( @@ -94,6 +98,7 @@ const AddressAccountHistory = ({ shouldRender = true, isQueryEnabled = true }: P emptyStateProps={{ description: 'No match found for current filter', }} + isTransitioning={ isTransitioning } > { content } diff --git a/src/features/tx-interpretation/noves/pages/address/AddressAccountHistoryListItem.tsx b/src/features/tx-interpretation/noves/pages/address/AddressAccountHistoryListItem.tsx deleted file mode 100644 index 7e5a69df3d1..00000000000 --- a/src/features/tx-interpretation/noves/pages/address/AddressAccountHistoryListItem.tsx +++ /dev/null @@ -1,74 +0,0 @@ -// SPDX-License-Identifier: LicenseRef-Blockscout - -import { Box, Flex, Text } from '@chakra-ui/react'; -import React, { useMemo } from 'react'; - -import type { NovesResponseData } from 'src/features/tx-interpretation/noves/types/api'; - -import NovesFromTo from 'src/features/tx-interpretation/noves/components/NovesFromTo'; - -import TimeWithTooltip from 'src/shared/date-and-time/TimeWithTooltip'; -import ListItemMobile from 'src/shared/lists/ListItemMobile'; -import SpriteIcon from 'src/sprite/SpriteIcon'; - -import { Link } from 'src/toolkit/chakra/link'; -import { Skeleton } from 'src/toolkit/chakra/skeleton'; -import { SECOND } from 'src/toolkit/utils/consts'; - -type Props = { - isPlaceholderData: boolean; - tx: NovesResponseData; - currentAddress: string; -}; - -const AddressAccountHistoryListItem = (props: Props) => { - - const parsedDescription = useMemo(() => { - const description = props.tx.classificationData.description; - - return description.endsWith('.') ? description.substring(0, description.length - 1) : description; - }, [ props.tx.classificationData.description ]); - - return ( - - - - - - - - Action - - - - - - - - { parsedDescription } - - - - - - - - ); -}; - -export default React.memo(AddressAccountHistoryListItem); diff --git a/src/features/tx-interpretation/noves/pages/address/AddressAccountHistoryTable.tsx b/src/features/tx-interpretation/noves/pages/address/AddressAccountHistoryTable.tsx index f595dc762ee..091ae0214cf 100644 --- a/src/features/tx-interpretation/noves/pages/address/AddressAccountHistoryTable.tsx +++ b/src/features/tx-interpretation/noves/pages/address/AddressAccountHistoryTable.tsx @@ -13,12 +13,12 @@ import AddressAccountHistoryTableItem from './AddressAccountHistoryTableItem'; interface Props { items: Array; currentAddress: string; - isPlaceholderData?: boolean; + isLoading?: boolean; resetKey?: string; } -const AddressAccountHistoryTable = ({ items, currentAddress, isPlaceholderData, resetKey }: Props) => { - const { cutRef, renderedItemsNum } = useLazyRenderedList({ list: items, isEnabled: !isPlaceholderData, resetKey }); +const AddressAccountHistoryTable = ({ items, currentAddress, isLoading, resetKey }: Props) => { + const { cutRef, renderedItemsNum } = useLazyRenderedList({ list: items, isEnabled: !isLoading, resetKey }); return ( @@ -38,10 +38,10 @@ const AddressAccountHistoryTable = ({ items, currentAddress, isPlaceholderData, { items.slice(0, renderedItemsNum).map((item, i) => ( )) } diff --git a/src/features/tx-interpretation/noves/pages/address/AddressAccountHistoryTableItem.tsx b/src/features/tx-interpretation/noves/pages/address/AddressAccountHistoryTableItem.tsx index 5de2ebfdc6b..5a85e8487b2 100644 --- a/src/features/tx-interpretation/noves/pages/address/AddressAccountHistoryTableItem.tsx +++ b/src/features/tx-interpretation/noves/pages/address/AddressAccountHistoryTableItem.tsx @@ -16,7 +16,7 @@ import { TableCell, TableRow } from 'src/toolkit/chakra/table'; import { SECOND } from 'src/toolkit/utils/consts'; type Props = { - isPlaceholderData: boolean; + isLoading: boolean; tx: NovesResponseData; currentAddress: string; }; @@ -34,14 +34,14 @@ const AddressAccountHistoryTableItem = (props: Props) => { - + { - + diff --git a/src/features/tx-interpretation/noves/pages/tx-asset-flows/TxAssetFlows.tsx b/src/features/tx-interpretation/noves/pages/tx-asset-flows/TxAssetFlows.tsx index 2b7d9218219..05251f5ed48 100644 --- a/src/features/tx-interpretation/noves/pages/tx-asset-flows/TxAssetFlows.tsx +++ b/src/features/tx-interpretation/noves/pages/tx-asset-flows/TxAssetFlows.tsx @@ -18,9 +18,9 @@ import DataList from 'src/shared/lists/DataList'; import Pagination from 'src/shared/pagination/Pagination'; import { Skeleton } from 'src/toolkit/chakra/skeleton'; +import { TableContainerScrollable } from 'src/toolkit/chakra/table'; import { generateFlowViewData } from '../../utils/generateFlowViewData'; -import TxAssetFlowsList from './TxAssetFlowsList'; import TxAssetFlowsTable from './TxAssetFlowsTable'; interface FlowViewProps { @@ -78,23 +78,13 @@ export default function TxAssetFlows(props: FlowViewProps) { ); const content = data ? ( - <> - - - - - - - - + + + ) : null; return ( diff --git a/src/features/tx-interpretation/noves/pages/tx-asset-flows/TxAssetFlowsList.tsx b/src/features/tx-interpretation/noves/pages/tx-asset-flows/TxAssetFlowsList.tsx deleted file mode 100644 index 2222813d84e..00000000000 --- a/src/features/tx-interpretation/noves/pages/tx-asset-flows/TxAssetFlowsList.tsx +++ /dev/null @@ -1,34 +0,0 @@ -// SPDX-License-Identifier: LicenseRef-Blockscout - -import { Box } from '@chakra-ui/react'; -import React from 'react'; - -import useLazyRenderedList from 'src/shared/lists/useLazyRenderedList'; - -import type { NovesFlowViewItem } from '../../utils/generateFlowViewData'; -import TxAssetFlowsListItem from './TxAssetFlowsListItem'; - -interface Props { - items: Array; - isPlaceholderData?: boolean; - resetKey?: string; -} - -const TxAssetFlowsList = ({ items, isPlaceholderData, resetKey }: Props) => { - const { cutRef, renderedItemsNum } = useLazyRenderedList({ list: items, isEnabled: !isPlaceholderData, resetKey }); - - return ( - - { items.slice(0, renderedItemsNum).map((item, i) => ( - - )) } - - - ); -}; - -export default React.memo(TxAssetFlowsList); diff --git a/src/features/tx-interpretation/noves/pages/tx-asset-flows/TxAssetFlowsListItem.tsx b/src/features/tx-interpretation/noves/pages/tx-asset-flows/TxAssetFlowsListItem.tsx deleted file mode 100644 index f406fd846ef..00000000000 --- a/src/features/tx-interpretation/noves/pages/tx-asset-flows/TxAssetFlowsListItem.tsx +++ /dev/null @@ -1,51 +0,0 @@ -// SPDX-License-Identifier: LicenseRef-Blockscout - -import { Box, Text } from '@chakra-ui/react'; -import React from 'react'; - -import NovesFromTo from 'src/features/tx-interpretation/noves/components/NovesFromTo'; - -import ListItemMobile from 'src/shared/lists/ListItemMobile'; -import SpriteIcon from 'src/sprite/SpriteIcon'; - -import { Skeleton } from 'src/toolkit/chakra/skeleton'; - -import NovesActionSnippet from '../../components/NovesActionSnippet'; -import type { NovesFlowViewItem } from '../../utils/generateFlowViewData'; - -type Props = { - isPlaceholderData: boolean; - item: NovesFlowViewItem; -}; - -const TxAssetFlowsListItem = (props: Props) => { - - return ( - - - - - - - - Action - - - - - - - - - - - - ); -}; - -export default React.memo(TxAssetFlowsListItem); diff --git a/src/features/tx-interpretation/noves/pages/tx-asset-flows/TxAssetFlowsTable.tsx b/src/features/tx-interpretation/noves/pages/tx-asset-flows/TxAssetFlowsTable.tsx index 45c3c398cc0..6f97336f021 100644 --- a/src/features/tx-interpretation/noves/pages/tx-asset-flows/TxAssetFlowsTable.tsx +++ b/src/features/tx-interpretation/noves/pages/tx-asset-flows/TxAssetFlowsTable.tsx @@ -19,7 +19,7 @@ const TxAssetFlowsTable = ({ items, isPlaceholderData, resetKey }: Props) => { const { cutRef, renderedItemsNum } = useLazyRenderedList({ list: items, isEnabled: !isPlaceholderData, resetKey }); return ( - + diff --git a/src/features/user-ops/components/SearchBarSuggestUserOp.tsx b/src/features/user-ops/components/SearchBarSuggestUserOp.tsx index a0de7bda759..032cfe956f5 100644 --- a/src/features/user-ops/components/SearchBarSuggestUserOp.tsx +++ b/src/features/user-ops/components/SearchBarSuggestUserOp.tsx @@ -9,13 +9,14 @@ import type { ItemsProps } from 'src/slices/search/components/search-bar/SearchB import * as UserOpEntity from 'src/features/user-ops/components/entity/UserOpEntity'; import Time from 'src/shared/date-and-time/Time'; -import HashStringShortenDynamic from 'src/shared/texts/HashStringShortenDynamic'; + +import { Truncate } from 'src/toolkit/components/truncation/Truncate'; const SearchBarSuggestUserOp = ({ data, isMobile }: ItemsProps) => { const icon = ; const hash = ( - + ); diff --git a/src/features/user-ops/components/entity/__screenshots__/UserOpEntity.pw.tsx_default_loading-1.png b/src/features/user-ops/components/entity/__screenshots__/UserOpEntity.pw.tsx_default_loading-1.png index fc48d621cea..f06fa010cc3 100644 Binary files a/src/features/user-ops/components/entity/__screenshots__/UserOpEntity.pw.tsx_default_loading-1.png and b/src/features/user-ops/components/entity/__screenshots__/UserOpEntity.pw.tsx_default_loading-1.png differ diff --git a/src/features/user-ops/pages/address/AddressUserOps.tsx b/src/features/user-ops/pages/address/AddressUserOps.tsx index 9d260ff44bb..9b9e50f1ff1 100644 --- a/src/features/user-ops/pages/address/AddressUserOps.tsx +++ b/src/features/user-ops/pages/address/AddressUserOps.tsx @@ -7,7 +7,7 @@ import UserOpsContent from 'src/features/user-ops/pages/index/UserOpsContent'; import { USER_OPS_ITEM } from 'src/features/user-ops/stubs'; import useIsMounted from 'src/shared/hooks/useIsMounted'; -import useQueryWithPages from 'src/shared/pagination/useQueryWithPages'; +import useApiPaginatedQuery from 'src/shared/pagination/useApiPaginatedQuery'; import { generateListStub } from 'src/shared/pagination/utils'; import getQueryParamString from 'src/shared/router/get-query-param-string'; @@ -23,7 +23,7 @@ const AddressUserOps = ({ scrollRef, shouldRender = true, isQueryEnabled = true const hash = getQueryParamString(router.query.hash); - const userOpsQuery = useQueryWithPages({ + const userOpsQuery = useApiPaginatedQuery({ resourceName: 'core:user_ops', scrollRef, options: { @@ -33,7 +33,7 @@ const AddressUserOps = ({ scrollRef, shouldRender = true, isQueryEnabled = true page_size: 50, } }), }, - filters: { sender: hash }, + queryParams: { sender: hash }, }); if (!isMounted || !shouldRender) { diff --git a/src/features/user-ops/pages/details/UserOp.tsx b/src/features/user-ops/pages/details/UserOp.tsx index 36ce0befd81..eadb9e5a5aa 100644 --- a/src/features/user-ops/pages/details/UserOp.tsx +++ b/src/features/user-ops/pages/details/UserOp.tsx @@ -18,6 +18,7 @@ import TxLogs from 'src/slices/tx/pages/details/logs/TxLogs'; import TextAd from 'src/features/ads/text/components/TextAd'; import { USER_OP } from 'src/features/user-ops/stubs'; +import ApiDegradationAlert from 'src/shared/api-degradation/ApiDegradationAlert'; import throwOnAbsentParamError from 'src/shared/errors/throw-on-absent-param-error'; import throwOnResourceLoadError from 'src/shared/errors/throw-on-resource-load-error'; import getQueryParamString from 'src/shared/router/get-query-param-string'; @@ -74,16 +75,23 @@ const UserOp = () => { } }, [ userOpQuery.data ]); - const tabs: Array = React.useMemo(() => ([ - { id: 'index', title: 'Details', component: }, - { - id: 'token_transfers', - title: 'Token transfers', - component: , - }, - { id: 'logs', title: 'Logs', component: }, - { id: 'raw', title: 'Raw', component: }, - ]), [ userOpQuery, txQuery, filterTokenTransfersByLogIndex, filterLogsByLogIndex ]); + const tabs: Array = React.useMemo(() => { + // the transaction is served by the RPC node while the API catches up — nothing to list until then + const txDependentTab = (component: React.ReactNode) => txQuery.isDegradedData ? + : + component; + + return [ + { id: 'index', title: 'Details', component: }, + { + id: 'token_transfers', + title: 'Token transfers', + component: txDependentTab(), + }, + { id: 'logs', title: 'Logs', component: txDependentTab() }, + { id: 'raw', title: 'Raw', component: }, + ]; + }, [ userOpQuery, txQuery, filterTokenTransfersByLogIndex, filterLogsByLogIndex ]); throwOnAbsentParamError(hash); throwOnResourceLoadError(userOpQuery); diff --git a/src/features/user-ops/pages/details/UserOpCallDataSwitch.tsx b/src/features/user-ops/pages/details/UserOpCallDataSwitch.tsx index 30aa39fe273..21bbbdfd849 100644 --- a/src/features/user-ops/pages/details/UserOpCallDataSwitch.tsx +++ b/src/features/user-ops/pages/details/UserOpCallDataSwitch.tsx @@ -18,12 +18,10 @@ const UserOpCallDataSwitch = ({ className, initialValue, isDisabled, onChange, i const [ isChecked, setIsChecked ] = React.useState(initialValue ?? false); const handleChange = React.useCallback(() => { - setIsChecked((prevValue) => { - const nextValue = !prevValue; - onChange(nextValue); - return nextValue; - }); - }, [ onChange ]); + const nextValue = !isChecked; + setIsChecked(nextValue); + onChange(nextValue); + }, [ isChecked, onChange ]); return ( diff --git a/src/features/user-ops/pages/details/__screenshots__/UserOp.pw.tsx_default_base-view-1.png b/src/features/user-ops/pages/details/__screenshots__/UserOp.pw.tsx_default_base-view-1.png index 14675f48b8e..7ac2561affa 100644 Binary files a/src/features/user-ops/pages/details/__screenshots__/UserOp.pw.tsx_default_base-view-1.png and b/src/features/user-ops/pages/details/__screenshots__/UserOp.pw.tsx_default_base-view-1.png differ diff --git a/src/features/user-ops/pages/details/__screenshots__/UserOp.pw.tsx_default_mobile-base-view-1.png b/src/features/user-ops/pages/details/__screenshots__/UserOp.pw.tsx_default_mobile-base-view-1.png index cde88d78c96..48dad637102 100644 Binary files a/src/features/user-ops/pages/details/__screenshots__/UserOp.pw.tsx_default_mobile-base-view-1.png and b/src/features/user-ops/pages/details/__screenshots__/UserOp.pw.tsx_default_mobile-base-view-1.png differ diff --git a/src/features/user-ops/pages/index/UserOps.pw.tsx b/src/features/user-ops/pages/index/UserOps.pw.tsx index 302e332ef11..23575c21d65 100644 --- a/src/features/user-ops/pages/index/UserOps.pw.tsx +++ b/src/features/user-ops/pages/index/UserOps.pw.tsx @@ -9,7 +9,7 @@ import { test, expect } from 'playwright/lib'; import UserOps from './UserOps'; -test('base view +@mobile', async({ render, mockEnvs, mockTextAd, mockApiResponse }) => { +test('base view', async({ render, mockEnvs, mockTextAd, mockApiResponse }) => { test.slow(); await mockEnvs(ENVS_MAP.userOps); await mockTextAd(); diff --git a/src/features/user-ops/pages/index/UserOps.tsx b/src/features/user-ops/pages/index/UserOps.tsx index 23aafc00cbb..32239e47ef8 100644 --- a/src/features/user-ops/pages/index/UserOps.tsx +++ b/src/features/user-ops/pages/index/UserOps.tsx @@ -6,13 +6,13 @@ import PageTitle from 'src/shell/page/title/PageTitle'; import { USER_OPS_ITEM } from 'src/features/user-ops/stubs'; -import useQueryWithPages from 'src/shared/pagination/useQueryWithPages'; +import useApiPaginatedQuery from 'src/shared/pagination/useApiPaginatedQuery'; import { generateListStub } from 'src/shared/pagination/utils'; import UserOpsContent from './UserOpsContent'; const UserOps = () => { - const query = useQueryWithPages({ + const query = useApiPaginatedQuery({ resourceName: 'core:user_ops', options: { placeholderData: generateListStub<'core:user_ops'>(USER_OPS_ITEM, 50, { next_page_params: { diff --git a/src/features/user-ops/pages/index/UserOpsContent.tsx b/src/features/user-ops/pages/index/UserOpsContent.tsx index 45ce1f66b84..b13a3e42b87 100644 --- a/src/features/user-ops/pages/index/UserOpsContent.tsx +++ b/src/features/user-ops/pages/index/UserOpsContent.tsx @@ -7,14 +7,14 @@ import ActionBar, { ACTION_BAR_HEIGHT_DESKTOP } from 'src/shell/page/action-bar/ import ApiFetchAlert from 'src/shared/alerts/ApiFetchAlert'; import DataList from 'src/shared/lists/DataList'; import Pagination from 'src/shared/pagination/Pagination'; -import type { QueryWithPagesResult } from 'src/shared/pagination/useQueryWithPages'; +import type { ApiPaginatedQueryResult } from 'src/shared/pagination/useApiPaginatedQuery'; import { TableContainerScrollable } from 'src/toolkit/chakra/table'; import UserOpsTable from './UserOpsTable'; type Props = { - query: QueryWithPagesResult<'core:user_ops'>; + query: ApiPaginatedQueryResult<'core:user_ops'>; showTx?: boolean; showSender?: boolean; }; @@ -30,7 +30,7 @@ const UserOpsContent = ({ query, showTx = true, showSender = true }: Props) => { { itemsNum={ query.data?.items?.length } emptyText="There are no user operations." actionBar={ actionBar } + isTransitioning={ query.isTransitioning } > { content } diff --git a/src/features/user-ops/pages/index/UserOpsList.tsx b/src/features/user-ops/pages/index/UserOpsList.tsx deleted file mode 100644 index a626e3d6e79..00000000000 --- a/src/features/user-ops/pages/index/UserOpsList.tsx +++ /dev/null @@ -1,42 +0,0 @@ -// SPDX-License-Identifier: LicenseRef-Blockscout - -import { Box } from '@chakra-ui/react'; -import React from 'react'; - -import type { schemas } from '@blockscout/api-types'; -import type { ClusterChainConfig } from 'src/features/multichain/types/client'; - -import useLazyRenderedList from 'src/shared/lists/useLazyRenderedList'; - -import UserOpsListItem from './UserOpsListItem'; - -type Props = { - items: Array; - isLoading?: boolean; - showTx: boolean; - showSender: boolean; - chainData?: ClusterChainConfig; - resetKey?: string; -}; - -const UserOpsList = ({ items, isLoading, showTx, showSender, chainData, resetKey }: Props) => { - const { cutRef, renderedItemsNum } = useLazyRenderedList({ list: items, isEnabled: !isLoading, resetKey }); - - return ( - - { items.slice(0, renderedItemsNum).map((item, index) => ( - - )) } - - - ); -}; - -export default React.memo(UserOpsList); diff --git a/src/features/user-ops/pages/index/UserOpsListItem.tsx b/src/features/user-ops/pages/index/UserOpsListItem.tsx deleted file mode 100644 index 9890ec164b6..00000000000 --- a/src/features/user-ops/pages/index/UserOpsListItem.tsx +++ /dev/null @@ -1,109 +0,0 @@ -// SPDX-License-Identifier: LicenseRef-Blockscout - -import React from 'react'; - -import type { schemas } from '@blockscout/api-types'; -import type { ClusterChainConfig } from 'src/features/multichain/types/client'; - -import AddressStringOrParam from 'src/slices/address/components/entity/AddressStringOrParam'; -import BlockEntity from 'src/slices/block/components/entity/BlockEntity'; -import TxEntity from 'src/slices/tx/components/entity/TxEntity'; - -import { useMultichainContext } from 'src/features/multichain/context'; -import UserOpEntity from 'src/features/user-ops/components/entity/UserOpEntity'; -import UserOpStatus from 'src/features/user-ops/components/UserOpStatus'; - -import config from 'src/config'; -import TimeWithTooltip from 'src/shared/date-and-time/TimeWithTooltip'; -import ListItemMobileGrid from 'src/shared/lists/ListItemMobileGrid'; -import NativeCoinValue from 'src/shared/values/entity/NativeCoinValue'; - -type Props = { - item: schemas['UserOperationInList']; - isLoading?: boolean; - showTx: boolean; - showSender: boolean; - chainData?: ClusterChainConfig; -}; - -const UserOpsListItem = ({ item, isLoading, showTx, showSender, chainData }: Props) => { - const multichainContext = useMultichainContext(); - const chainConfig = (multichainContext?.chain.app_config || config); - - return ( - - - User op hash - - - - - Age - - - - - Status - - - - - { showSender && ( - <> - Sender - - - - - ) } - - { showTx && ( - <> - Tx hash - - - - - ) } - - Block - - - - - { !chainConfig.slices.tx.hiddenFields?.tx_fee && ( - <> - Fee - - - - - ) } - - - ); -}; - -export default UserOpsListItem; diff --git a/src/features/user-ops/pages/index/__screenshots__/UserOps.pw.tsx_default_base-view-1.png b/src/features/user-ops/pages/index/__screenshots__/UserOps.pw.tsx_default_base-view-1.png new file mode 100644 index 00000000000..bf4a38b94ae Binary files /dev/null and b/src/features/user-ops/pages/index/__screenshots__/UserOps.pw.tsx_default_base-view-1.png differ diff --git a/src/features/user-ops/pages/index/__screenshots__/UserOps.pw.tsx_default_base-view-mobile-1.png b/src/features/user-ops/pages/index/__screenshots__/UserOps.pw.tsx_default_base-view-mobile-1.png deleted file mode 100644 index 3078ef18bf2..00000000000 Binary files a/src/features/user-ops/pages/index/__screenshots__/UserOps.pw.tsx_default_base-view-mobile-1.png and /dev/null differ diff --git a/src/features/user-ops/pages/index/__screenshots__/UserOps.pw.tsx_mobile_base-view-mobile-1.png b/src/features/user-ops/pages/index/__screenshots__/UserOps.pw.tsx_mobile_base-view-mobile-1.png deleted file mode 100644 index 016b348a195..00000000000 Binary files a/src/features/user-ops/pages/index/__screenshots__/UserOps.pw.tsx_mobile_base-view-mobile-1.png and /dev/null differ diff --git a/src/features/user-ops/pages/tx/TxUserOps.tsx b/src/features/user-ops/pages/tx/TxUserOps.tsx index c34da96d0f8..9ebd1fe9a2b 100644 --- a/src/features/user-ops/pages/tx/TxUserOps.tsx +++ b/src/features/user-ops/pages/tx/TxUserOps.tsx @@ -10,7 +10,7 @@ import UserOpsContent from 'src/features/user-ops/pages/index/UserOpsContent'; import { USER_OPS_ITEM } from 'src/features/user-ops/stubs'; import ApiFetchAlert from 'src/shared/alerts/ApiFetchAlert'; -import useQueryWithPages from 'src/shared/pagination/useQueryWithPages'; +import useApiPaginatedQuery from 'src/shared/pagination/useApiPaginatedQuery'; import { generateListStub } from 'src/shared/pagination/utils'; interface Props { @@ -18,14 +18,14 @@ interface Props { } const TxUserOps = ({ txQuery }: Props) => { - const userOpsQuery = useQueryWithPages({ + const userOpsQuery = useApiPaginatedQuery({ resourceName: 'core:user_ops', options: { enabled: !txQuery.isPlaceholderData && Boolean(txQuery.data?.status && txQuery.data?.hash), // most often there is only one user op in one tx placeholderData: generateListStub<'core:user_ops'>(USER_OPS_ITEM, 1, { next_page_params: null }), }, - filters: { transaction_hash: txQuery.data?.hash }, + queryParams: { transaction_hash: txQuery.data?.hash }, }); if (!txQuery.isPending && !txQuery.isPlaceholderData && !txQuery.isError && !txQuery.data.status) { diff --git a/src/features/web3-wallet/components/TokenAddToWallet.tsx b/src/features/web3-wallet/components/TokenAddToWallet.tsx index 7d28da2ed4f..d5b8330c456 100644 --- a/src/features/web3-wallet/components/TokenAddToWallet.tsx +++ b/src/features/web3-wallet/components/TokenAddToWallet.tsx @@ -13,7 +13,7 @@ import useProvider from 'src/features/web3-wallet/hooks/useProvider'; import useSwitchOrAddChain from 'src/features/web3-wallet/hooks/useSwitchOrAddChain'; import config from 'src/config'; -import * as mixpanel from 'src/services/mixpanel'; +import * as analytics from 'src/shared/analytics'; import useIsMobile from 'src/shared/hooks/useIsMobile'; import SpriteIcon from 'src/sprite/SpriteIcon'; @@ -99,7 +99,7 @@ const TokenAddToWallet = ({ className, token, tokenId, isLoading, variant = 'ico await trackUsage('add_token'); - mixpanel.logEvent(mixpanel.EventTypes.ADD_TO_WALLET, { + analytics.logEvent(analytics.EventTypes.ADD_TO_WALLET, { Target: 'token', Wallet: wallet, Token: token.symbol || '', diff --git a/src/features/web3-wallet/hooks/useAddChainClick.ts b/src/features/web3-wallet/hooks/useAddChainClick.ts index 2e1825b8681..3916b36fe6a 100644 --- a/src/features/web3-wallet/hooks/useAddChainClick.ts +++ b/src/features/web3-wallet/hooks/useAddChainClick.ts @@ -2,7 +2,7 @@ import React from 'react'; -import * as mixpanel from 'src/services/mixpanel'; +import * as analytics from 'src/shared/analytics'; import { toaster } from 'src/toolkit/chakra/toaster'; @@ -34,7 +34,7 @@ export default function useAddChainClick({ source, onSuccess }: Props) { description: 'Successfully added network to your wallet', }); - mixpanel.logEvent(mixpanel.EventTypes.ADD_TO_WALLET, { + analytics.logEvent(analytics.EventTypes.ADD_TO_WALLET, { Target: 'network', Wallet: wallet, Source: source, diff --git a/src/pages/_document.tsx b/src/pages/_document.tsx index 9baf9230508..538338e3110 100644 --- a/src/pages/_document.tsx +++ b/src/pages/_document.tsx @@ -12,6 +12,8 @@ import * as serverTiming from 'src/server/utils/serverTiming'; import config from 'src/config'; import { SPRITE_URL } from 'src/sprite/SpriteInjector'; +import { ColorModeProvider } from 'src/toolkit/chakra/color-mode'; + const marketplaceFeature = config.features.marketplace; const usercentrics = config.services.usercentrics; @@ -41,6 +43,11 @@ class MyDocument extends Document { return ( + { /* Color mode script: without children the provider renders only its inline script, which sets the theme */ } + { /* class on before the first paint. The copy rendered by the app provider sits after the global styles */ } + { /* in , and Firefox paints the page white before reaching it. */ } + + { /* Early-fetch primer: fire the page's first-render API requests before the JS bundle boots */ } { primerScript &&