Skip to content

refactor: migrate PhishingController data fetching to PhishingDataService - #9914

Open
0xOhm wants to merge 44 commits into
mainfrom
refactor/psafe-593-phishing-data-service
Open

0xOhm wants to merge 44 commits into
mainfrom
refactor/psafe-593-phishing-data-service

Conversation

@0xOhm

@0xOhm 0xOhm commented Aug 20, 2026

Copy link
Copy Markdown

Description

Extracts all remote data fetching from PhishingController into a new PhishingDataService built on the BaseDataService pattern (PSAFE-593). CacheManager is removed in favor of the service's built-in caching, persistence, and request coalescing.

Changes

  • New PhishingDataService owning all four remote APIs (stalelist/hotlist, request-blocklist/C2, dapp scanning, token/address scanning) with generated method action types
  • PhishingController slimmed to detection/state logic; queries data through the service via the messenger
  • CacheManager removed — caching, persistence (StorageService), and request coalescing now come from BaseDataService
  • Response validation intentionally kept loose (e.g. token scan result_type as string, not enum) to match pre-refactor behavior — production already returns values outside the documented enum (Verified)

Verification

  • 501 unit tests / 7 suites, 100% stmt/fn/line coverage, 99.29% branch
  • Full monorepo build clean against the new API
  • Live production API contract: 9/9 endpoints accepted by validation
  • Outage simulation: detection fails open to stale lists (never off), bounded retries
  • Persistence lifecycle: rehydration after simulated MV3 SW restart with zero network; maxAge-expired verdicts discarded
  • Coalescing parity: 120-token chunking, miss-only fetch, zero-fetch cached batches

Client integration PRs

  • Extension and mobile integration PRs to follow (wiring + state migrations); they depend on this merging and publishing first.

🤖 Generated with Claude Code


Note

High Risk
Large breaking refactor of security-sensitive phishing and scan networking, new required wiring and state migrations, plus changed caching/persistence semantics for scan verdicts.

Overview
Introduces PhishingDataService on top of BaseDataService so stalelist/hotlist, C2 blocklist, URL/token/address scans, and approvals go through messenger actions with TanStack query caching, optional StorageService persistence, and batched bulk scans. PhishingController keeps list/detection logic only and calls the service; the in-controller CacheManager and persisted urlScanCache / tokenScanCache / addressScanCache state (plus related constructor options) are removed.

BaseDataService gains bounded wait for cache rehydration after init (hydrationTimeout, DEFAULT_HYDRATION_TIMEOUT), optional shouldHydrateQuery, persisted-cache shape validation with discard-on-failure, and a protected policy getter for subclasses.

Behavior fixes bundled with the move: partial bulkScanUrls / bulk token results instead of failing the whole batch, skip unrecognized hotlist diff list types, negative caching for tokens with no API row, stricter rejection of malformed responses (not cached), and transaction-driven token scans coalesced per chain.

Breaking for integrators: register PhishingDataService, delegate its actions, call init when persistence is enabled, and migrate persisted controller state away from the removed scan-cache fields.

Reviewed by Cursor Bugbot for commit c0b3196. Bugbot is set up for automated code reviews on this repo. Configure here.

…vice

Extract all remote data fetching from PhishingController into a new
PhishingDataService built on the BaseDataService pattern. Removes
CacheManager in favor of the service's built-in caching, persistence,
and request coalescing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@0xOhm

0xOhm commented Aug 25, 2026

Copy link
Copy Markdown
Author

@metamaskbot publish-previews

@github-actions

Copy link
Copy Markdown
Contributor

Preview builds have been published. Learn how to use preview builds in other projects.

Expand for full list of packages and versions.
@metamask-previews/account-tree-controller@7.6.1-preview-4758e65ff
@metamask-previews/accounts-controller@39.1.0-preview-4758e65ff
@metamask-previews/address-book-controller@7.1.2-preview-4758e65ff
@metamask-previews/ai-controllers@0.8.0-preview-4758e65ff
@metamask-previews/analytics-controller@2.0.0-preview-4758e65ff
@metamask-previews/analytics-data-regulation-controller@0.0.0-preview-4758e65ff
@metamask-previews/announcement-controller@8.1.0-preview-4758e65ff
@metamask-previews/app-metadata-controller@2.0.1-preview-4758e65ff
@metamask-previews/approval-controller@9.0.2-preview-4758e65ff
@metamask-previews/assets-controller@13.1.2-preview-4758e65ff
@metamask-previews/assets-controllers@111.1.0-preview-4758e65ff
@metamask-previews/authenticated-user-storage@3.0.1-preview-4758e65ff
@metamask-previews/base-controller@9.1.0-preview-4758e65ff
@metamask-previews/base-data-service@0.1.3-preview-4758e65ff
@metamask-previews/bitcoin-regtest-up@1.0.0-preview-4758e65ff
@metamask-previews/bridge-controller@79.1.0-preview-4758e65ff
@metamask-previews/bridge-status-controller@75.0.0-preview-4758e65ff
@metamask-previews/build-utils@3.0.4-preview-4758e65ff
@metamask-previews/chain-agnostic-permission@1.7.0-preview-4758e65ff
@metamask-previews/chomp-api-service@4.0.0-preview-4758e65ff
@metamask-previews/claims-controller@0.6.0-preview-4758e65ff
@metamask-previews/client-controller@1.0.1-preview-4758e65ff
@metamask-previews/client-utils@2.0.1-preview-4758e65ff
@metamask-previews/compliance-controller@2.1.0-preview-4758e65ff
@metamask-previews/composable-controller@12.0.1-preview-4758e65ff
@metamask-previews/config-registry-controller@2.0.1-preview-4758e65ff
@metamask-previews/connectivity-controller@0.3.0-preview-4758e65ff
@metamask-previews/controller-utils@12.3.0-preview-4758e65ff
@metamask-previews/core-backend@8.1.1-preview-4758e65ff
@metamask-previews/delegation-controller@3.0.2-preview-4758e65ff
@metamask-previews/earn-controller@12.2.4-preview-4758e65ff
@metamask-previews/eip-5792-middleware@3.0.5-preview-4758e65ff
@metamask-previews/eip-7702-internal-rpc-middleware@0.1.1-preview-4758e65ff
@metamask-previews/eip1193-permission-middleware@2.0.1-preview-4758e65ff
@metamask-previews/ens-controller@19.1.6-preview-4758e65ff
@metamask-previews/eth-block-tracker@15.0.1-preview-4758e65ff
@metamask-previews/eth-json-rpc-middleware@24.0.0-preview-4758e65ff
@metamask-previews/eth-json-rpc-provider@6.0.1-preview-4758e65ff
@metamask-previews/foundryup@1.0.1-preview-4758e65ff
@metamask-previews/gas-fee-controller@26.3.1-preview-4758e65ff
@metamask-previews/gator-permissions-controller@5.0.1-preview-4758e65ff
@metamask-previews/geolocation-controller@1.0.0-preview-4758e65ff
@metamask-previews/java-tron-up@1.0.0-preview-4758e65ff
@metamask-previews/json-rpc-engine@10.5.0-preview-4758e65ff
@metamask-previews/json-rpc-middleware-stream@8.0.8-preview-4758e65ff
@metamask-previews/keyring-controller@27.1.1-preview-4758e65ff
@metamask-previews/kyc-controller@0.0.0-preview-4758e65ff
@metamask-previews/local-node-utils@1.0.0-preview-4758e65ff
@metamask-previews/logging-controller@8.0.2-preview-4758e65ff
@metamask-previews/message-manager@14.1.2-preview-4758e65ff
@metamask-previews/messenger@2.0.0-preview-4758e65ff
@metamask-previews/messenger-cli@0.2.0-preview-4758e65ff
@metamask-previews/money-account-api-data-service@0.4.0-preview-4758e65ff
@metamask-previews/money-account-balance-service@2.4.1-preview-4758e65ff
@metamask-previews/money-account-controller@1.0.0-preview-4758e65ff
@metamask-previews/money-account-upgrade-controller@3.0.1-preview-4758e65ff
@metamask-previews/money-account-utils@1.1.0-preview-4758e65ff
@metamask-previews/multichain-account-service@13.0.1-preview-4758e65ff
@metamask-previews/multichain-api-middleware@4.0.2-preview-4758e65ff
@metamask-previews/multichain-network-controller@3.2.3-preview-4758e65ff
@metamask-previews/multichain-transactions-controller@7.1.2-preview-4758e65ff
@metamask-previews/name-controller@9.1.2-preview-4758e65ff
@metamask-previews/network-connection-banner-controller@0.1.2-preview-4758e65ff
@metamask-previews/network-controller@35.0.1-preview-4758e65ff
@metamask-previews/network-enablement-controller@6.0.3-preview-4758e65ff
@metamask-previews/notification-services-controller@26.0.1-preview-4758e65ff
@metamask-previews/passkey-controller@3.0.0-preview-4758e65ff
@metamask-previews/permission-controller@13.1.1-preview-4758e65ff
@metamask-previews/permission-log-controller@5.1.0-preview-4758e65ff
@metamask-previews/perps-controller@11.0.0-preview-4758e65ff
@metamask-previews/phishing-controller@17.3.1-preview-4758e65ff
@metamask-previews/platform-api-docs@0.0.0-preview-4758e65ff
@metamask-previews/polling-controller@16.0.9-preview-4758e65ff
@metamask-previews/preferences-controller@23.1.0-preview-4758e65ff
@metamask-previews/profile-metrics-controller@4.0.3-preview-4758e65ff
@metamask-previews/profile-sync-controller@29.0.0-preview-4758e65ff
@metamask-previews/ramps-controller@20.0.0-preview-4758e65ff
@metamask-previews/rate-limit-controller@7.0.1-preview-4758e65ff
@metamask-previews/react-data-query@0.2.2-preview-4758e65ff
@metamask-previews/remote-feature-flag-controller@5.0.0-preview-4758e65ff
@metamask-previews/sample-controllers@5.0.4-preview-4758e65ff
@metamask-previews/seedless-onboarding-controller@10.1.1-preview-4758e65ff
@metamask-previews/selected-network-controller@26.1.6-preview-4758e65ff
@metamask-previews/sentinel-api-service@1.0.0-preview-4758e65ff
@metamask-previews/shield-controller@6.0.0-preview-4758e65ff
@metamask-previews/signature-controller@39.2.9-preview-4758e65ff
@metamask-previews/smart-transactions-controller@25.1.0-preview-4758e65ff
@metamask-previews/snap-account-service@2.1.2-preview-4758e65ff
@metamask-previews/social-controllers@2.7.1-preview-4758e65ff
@metamask-previews/solana-test-validator-up@1.0.0-preview-4758e65ff
@metamask-previews/stellar-quickstart-up@0.0.0-preview-4758e65ff
@metamask-previews/storage-service@1.0.2-preview-4758e65ff
@metamask-previews/subscription-controller@7.0.0-preview-4758e65ff
@metamask-previews/transaction-controller@69.5.2-preview-4758e65ff
@metamask-previews/transaction-pay-controller@26.3.0-preview-4758e65ff
@metamask-previews/user-operation-controller@41.2.8-preview-4758e65ff
@metamask-previews/wallet@10.0.0-preview-4758e65ff
@metamask-previews/wallet-cli@0.0.0-preview-4758e65ff

Resolve conflicts from base-data-service 1.0.0 and phishing-controller 17.4.0.

- Bump `@metamask/base-data-service` to ^1.0.0 and `@tanstack/query-core`
  to ^5.62.16 to match main. query-core v4 had no lockfile resolution on
  main, which broke `yarn install --immutable` in CI.
- Rename `cacheTime` to `gcTime` in `getApprovals` for the query-core v5 API.
- Keep main's `getAddressScanSupportedChain` and
  `isAddressScanSupportedChainId`; drop `splitCacheHits`, whose
  `buildCacheKey` dependency this refactor removes and which was never
  exported or used.
- Take main's `@metamask/transaction-controller` ^69.6.1 and move the
  ^69.5.2 bump entry under the released 17.4.0 changelog section.
@0xOhm

0xOhm commented Aug 26, 2026

Copy link
Copy Markdown
Author

@metamaskbot publish-previews

0xOhm added a commit to MetaMask/metamask-extension that referenced this pull request Aug 26, 2026
…tion

Resolve the migration-number collision and data-service registry conflict.

- Renumber our PhishingController cache-removal migration from 222 to 224.
  Main shipped 222 (delete persisted EnsController state) and 223 (move
  StorageService data to IndexedDB) in the meantime, so 222 was taken.
  `oldVersion` in the test moves from 221 to 223 to match.
- Register 223 and 224 in `migrations/index.js`.
- `DATA_SERVICES` keeps main's `MoneyAccountBalanceService` and
  `MoneyAccountApiDataService` plus its `createUIQueryClient` comment, and
  adds `PhishingDataService`.

Still pending, tracked separately: `@metamask/phishing-controller` is not
bumped yet, so this cannot pass typecheck against the published package
until MetaMask/core#9914 merges and releases.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@adonesky1

Copy link
Copy Markdown
Contributor

@metamaskbot publish-previews

@github-actions

Copy link
Copy Markdown
Contributor

Preview builds have been published. Learn how to use preview builds in other projects.

Expand for full list of packages and versions.
@metamask-previews/account-tree-controller@8.0.0-preview-5b293ee50
@metamask-previews/accounts-controller@39.1.1-preview-5b293ee50
@metamask-previews/address-book-controller@7.1.2-preview-5b293ee50
@metamask-previews/ai-controllers@0.8.0-preview-5b293ee50
@metamask-previews/analytics-controller@2.0.0-preview-5b293ee50
@metamask-previews/analytics-data-regulation-controller@0.0.0-preview-5b293ee50
@metamask-previews/announcement-controller@8.1.0-preview-5b293ee50
@metamask-previews/app-metadata-controller@2.0.1-preview-5b293ee50
@metamask-previews/approval-controller@9.0.2-preview-5b293ee50
@metamask-previews/assets-controller@14.0.2-preview-5b293ee50
@metamask-previews/assets-controllers@111.1.3-preview-5b293ee50
@metamask-previews/authenticated-user-storage@3.0.2-preview-5b293ee50
@metamask-previews/base-controller@9.1.0-preview-5b293ee50
@metamask-previews/base-data-service@1.0.0-preview-5b293ee50
@metamask-previews/bitcoin-regtest-up@1.0.0-preview-5b293ee50
@metamask-previews/bridge-controller@79.3.1-preview-5b293ee50
@metamask-previews/bridge-status-controller@75.2.1-preview-5b293ee50
@metamask-previews/build-utils@3.0.4-preview-5b293ee50
@metamask-previews/chain-agnostic-permission@1.7.0-preview-5b293ee50
@metamask-previews/chomp-api-service@4.0.1-preview-5b293ee50
@metamask-previews/claims-controller@0.6.1-preview-5b293ee50
@metamask-previews/client-controller@1.0.1-preview-5b293ee50
@metamask-previews/client-utils@2.1.1-preview-5b293ee50
@metamask-previews/compliance-controller@2.1.0-preview-5b293ee50
@metamask-previews/composable-controller@12.0.1-preview-5b293ee50
@metamask-previews/config-registry-controller@3.1.0-preview-5b293ee50
@metamask-previews/connectivity-controller@0.3.0-preview-5b293ee50
@metamask-previews/controller-utils@12.3.0-preview-5b293ee50
@metamask-previews/core-backend@9.0.0-preview-5b293ee50
@metamask-previews/delegation-controller@3.0.2-preview-5b293ee50
@metamask-previews/earn-controller@12.2.6-preview-5b293ee50
@metamask-previews/eip-5792-middleware@3.0.5-preview-5b293ee50
@metamask-previews/eip-7702-internal-rpc-middleware@0.1.1-preview-5b293ee50
@metamask-previews/eip1193-permission-middleware@2.0.1-preview-5b293ee50
@metamask-previews/eth-block-tracker@15.0.1-preview-5b293ee50
@metamask-previews/eth-json-rpc-middleware@24.0.1-preview-5b293ee50
@metamask-previews/eth-json-rpc-provider@6.0.1-preview-5b293ee50
@metamask-previews/foundryup@1.0.1-preview-5b293ee50
@metamask-previews/gas-fee-controller@26.3.2-preview-5b293ee50
@metamask-previews/gator-permissions-controller@5.0.2-preview-5b293ee50
@metamask-previews/geolocation-controller@1.0.0-preview-5b293ee50
@metamask-previews/java-tron-up@1.0.0-preview-5b293ee50
@metamask-previews/json-rpc-engine@10.5.0-preview-5b293ee50
@metamask-previews/json-rpc-middleware-stream@8.0.8-preview-5b293ee50
@metamask-previews/keyring-controller@27.1.1-preview-5b293ee50
@metamask-previews/kyc-controller@0.0.0-preview-5b293ee50
@metamask-previews/local-node-utils@1.0.0-preview-5b293ee50
@metamask-previews/logging-controller@9.0.0-preview-5b293ee50
@metamask-previews/message-manager@14.1.2-preview-5b293ee50
@metamask-previews/messenger@2.0.0-preview-5b293ee50
@metamask-previews/messenger-cli@0.2.0-preview-5b293ee50
@metamask-previews/money-account-api-data-service@0.4.1-preview-5b293ee50
@metamask-previews/money-account-balance-service@2.4.3-preview-5b293ee50
@metamask-previews/money-account-controller@1.0.0-preview-5b293ee50
@metamask-previews/money-account-upgrade-controller@3.0.2-preview-5b293ee50
@metamask-previews/money-account-utils@1.1.0-preview-5b293ee50
@metamask-previews/multichain-account-service@13.0.2-preview-5b293ee50
@metamask-previews/multichain-api-middleware@4.0.3-preview-5b293ee50
@metamask-previews/multichain-network-controller@3.2.4-preview-5b293ee50
@metamask-previews/multichain-transactions-controller@7.1.2-preview-5b293ee50
@metamask-previews/name-controller@9.1.2-preview-5b293ee50
@metamask-previews/network-connection-banner-controller@0.2.1-preview-5b293ee50
@metamask-previews/network-controller@36.0.0-preview-5b293ee50
@metamask-previews/network-enablement-controller@6.0.5-preview-5b293ee50
@metamask-previews/notification-services-controller@26.0.1-preview-5b293ee50
@metamask-previews/passkey-controller@3.1.0-preview-5b293ee50
@metamask-previews/permission-controller@13.1.1-preview-5b293ee50
@metamask-previews/permission-log-controller@5.1.0-preview-5b293ee50
@metamask-previews/perps-controller@13.0.0-preview-5b293ee50
@metamask-previews/phishing-controller@17.4.0-preview-5b293ee50
@metamask-previews/platform-api-docs@0.0.0-preview-5b293ee50
@metamask-previews/polling-controller@16.0.9-preview-5b293ee50
@metamask-previews/preferences-controller@23.1.0-preview-5b293ee50
@metamask-previews/profile-metrics-controller@4.0.3-preview-5b293ee50
@metamask-previews/profile-sync-controller@29.0.0-preview-5b293ee50
@metamask-previews/ramps-controller@20.0.0-preview-5b293ee50
@metamask-previews/rate-limit-controller@7.0.1-preview-5b293ee50
@metamask-previews/react-data-query@1.0.0-preview-5b293ee50
@metamask-previews/remote-feature-flag-controller@6.0.0-preview-5b293ee50
@metamask-previews/sample-controllers@5.0.6-preview-5b293ee50
@metamask-previews/seedless-onboarding-controller@10.1.1-preview-5b293ee50
@metamask-previews/selected-network-controller@26.1.7-preview-5b293ee50
@metamask-previews/sentinel-api-service@1.0.1-preview-5b293ee50
@metamask-previews/shield-controller@6.0.1-preview-5b293ee50
@metamask-previews/signature-controller@39.2.10-preview-5b293ee50
@metamask-previews/smart-transactions-controller@25.1.1-preview-5b293ee50
@metamask-previews/snap-account-service@2.1.2-preview-5b293ee50
@metamask-previews/social-controllers@2.8.0-preview-5b293ee50
@metamask-previews/solana-test-validator-up@1.0.0-preview-5b293ee50
@metamask-previews/stellar-quickstart-up@0.0.0-preview-5b293ee50
@metamask-previews/storage-service@1.0.2-preview-5b293ee50
@metamask-previews/subscription-controller@8.0.1-preview-5b293ee50
@metamask-previews/transaction-controller@69.6.1-preview-5b293ee50
@metamask-previews/transaction-pay-controller@27.0.0-preview-5b293ee50
@metamask-previews/user-operation-controller@41.2.9-preview-5b293ee50
@metamask-previews/wallet@12.0.2-preview-5b293ee50
@metamask-previews/wallet-cli@0.0.0-preview-5b293ee50

Comment thread packages/phishing-controller/src/PhishingDataService.ts Outdated
Comment thread packages/phishing-controller/src/PhishingDataService.ts
Comment thread packages/phishing-controller/src/PhishingDataService.ts Outdated
Comment thread packages/phishing-controller/src/PhishingDataService.ts Outdated
Comment thread packages/phishing-controller/src/PhishingDataService.ts Outdated
Comment thread packages/phishing-controller/src/PhishingDataService.ts Outdated
Comment thread packages/phishing-controller/src/PhishingDataService.test.ts

@adonesky1 adonesky1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I merged main into this branch to clear the conflicts (CHANGELOG, package.json, yarn.lock, all union resolutions). Worth knowing that #10007 landed on main while this was open and removed fetchQuery from every KycService POST for three reasons that all apply here: deduped in-flight writes, responses published on the messenger as cacheUpdated payloads, and the service policy retrying non-idempotent POSTs. Those are the same root cause behind the bulk dedupe and retry fan-out comments I left earlier.

Could we reconcile with that precedent, keeping fetchQuery for the GET list endpoints and scanUrl but calling #postJson directly for scanAddress and getApprovals? The bulk endpoints genuinely need per-item caching so they are a real exception, but it would help to say so explicitly in a comment.

Also note #6388 (C2 blocklist Array to Set) is now in the base. It only touched PhishingDetector.ts, which this PR does not modify, so the merge is clean there.

Comment thread packages/phishing-controller/src/PhishingDataService.ts
Comment thread packages/phishing-controller/src/PhishingDataService.ts
Comment thread packages/phishing-controller/src/PhishingController.ts Outdated
Comment thread packages/phishing-controller/CHANGELOG.md Outdated
Bulk URL scanning:

- Key bulk queries by the scan URL parameter rather than the bare hostname.
  Path-sensitive hosts (ipfs.io, github.io, and the other entries in
  PHISHING_DETECTION_PATH_BASED_ROOT_DOMAINS) collapsed into one cache entry
  per host, so only the first path in a batch was scanned and the rest
  inherited its verdict.
- Return the results that did resolve when some lookups fail, reporting the
  failures per URL, instead of discarding the whole batch. A single failed
  lookup previously threw away fresh cached BLOCK verdicts for unrelated URLs.
  The call still rejects when nothing could be resolved at all.
- Stop caching a "no result" verdict for URLs the API reported an error for.
  Those URLs were silently skipped for the following minute.
- Report invalid URLs instead of collapsing them onto a shared empty key.

Request policy:

- Disable retries by default. The previous in-controller implementation made
  a single request per call and the controller's timeouts are sized for one
  attempt, so inheriting maxRetries: 3 meant a timeout could fire mid-retry.
  Retries also amplified badly through the batch loaders: a failed batch
  rejects every item query in it, and each retried on its own, turning one
  failed request into many single-item requests against a failing host.

Caching:

- Validate responses inside fetchQuery via responseStruct. Validating after
  the fact meant a malformed 200 was committed to the cache, and persisted,
  before it was rejected, so every caller for the next minute got the same
  error with no request made.
- Set gcTime explicitly on scan queries. TanStack Query defaults gcTime to
  Infinity when it detects a server environment, which includes the MV3
  service worker, so the cache would grow unbounded; the cache this replaces
  was explicitly size-bounded.
- Do not retain fetched lists in the query cache. The controller keeps its
  own copy, and retaining them meant the multi-megabyte stalelist was
  rewritten to disk on every scan-triggered persist.
- Do not route getApprovals through the query cache. It is never cached
  (staleTime and gcTime are both 0), so the cache only served to publish
  account-specific approval data on the messenger, matching #10007.

Also corrects two changelog claims: PhishingDataService is not yet one of
@metamask/wallet's default instances, so init is not automatic.
`responseStruct` requires a `Struct<Json>`, which these structs are not
(`HotlistDiffsResponseStruct` infers `unknown[]`), so the build failed.
Validating inside the query function achieves the same result: a malformed
response throws before TanStack Query commits it, so it is never cached or
persisted, and the existing error messages are preserved.
@adonesky1

Copy link
Copy Markdown
Contributor

@metamaskbot publish-previews

@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Preview builds have been published. Learn how to use preview builds in other projects.

Expand for full list of packages and versions.
@metamask-previews/account-tree-controller@8.0.0-preview-1cf7cc7b3
@metamask-previews/accounts-controller@39.1.1-preview-1cf7cc7b3
@metamask-previews/address-book-controller@7.1.2-preview-1cf7cc7b3
@metamask-previews/ai-controllers@1.0.0-preview-1cf7cc7b3
@metamask-previews/analytics-controller@2.0.0-preview-1cf7cc7b3
@metamask-previews/analytics-data-regulation-controller@0.0.0-preview-1cf7cc7b3
@metamask-previews/announcement-controller@8.1.0-preview-1cf7cc7b3
@metamask-previews/app-metadata-controller@2.0.1-preview-1cf7cc7b3
@metamask-previews/approval-controller@9.0.2-preview-1cf7cc7b3
@metamask-previews/assets-controller@14.0.3-preview-1cf7cc7b3
@metamask-previews/assets-controllers@111.1.3-preview-1cf7cc7b3
@metamask-previews/authenticated-user-storage@3.0.2-preview-1cf7cc7b3
@metamask-previews/base-controller@9.1.0-preview-1cf7cc7b3
@metamask-previews/base-data-service@1.0.0-preview-1cf7cc7b3
@metamask-previews/bitcoin-regtest-up@1.0.0-preview-1cf7cc7b3
@metamask-previews/bridge-controller@80.1.1-preview-1cf7cc7b3
@metamask-previews/bridge-status-controller@75.4.0-preview-1cf7cc7b3
@metamask-previews/build-utils@3.0.4-preview-1cf7cc7b3
@metamask-previews/chain-agnostic-permission@1.7.0-preview-1cf7cc7b3
@metamask-previews/chomp-api-service@4.0.1-preview-1cf7cc7b3
@metamask-previews/claims-controller@0.6.1-preview-1cf7cc7b3
@metamask-previews/client-controller@1.0.1-preview-1cf7cc7b3
@metamask-previews/client-utils@2.1.1-preview-1cf7cc7b3
@metamask-previews/compliance-controller@2.1.0-preview-1cf7cc7b3
@metamask-previews/composable-controller@12.0.1-preview-1cf7cc7b3
@metamask-previews/config-registry-controller@3.1.0-preview-1cf7cc7b3
@metamask-previews/connectivity-controller@0.3.0-preview-1cf7cc7b3
@metamask-previews/controller-utils@12.3.0-preview-1cf7cc7b3
@metamask-previews/core-backend@9.0.0-preview-1cf7cc7b3
@metamask-previews/delegation-controller@3.0.2-preview-1cf7cc7b3
@metamask-previews/earn-controller@12.2.6-preview-1cf7cc7b3
@metamask-previews/eip-5792-middleware@3.0.5-preview-1cf7cc7b3
@metamask-previews/eip-7702-internal-rpc-middleware@0.1.1-preview-1cf7cc7b3
@metamask-previews/eip1193-permission-middleware@2.0.1-preview-1cf7cc7b3
@metamask-previews/eth-block-tracker@15.0.1-preview-1cf7cc7b3
@metamask-previews/eth-json-rpc-middleware@24.0.2-preview-1cf7cc7b3
@metamask-previews/eth-json-rpc-provider@6.0.1-preview-1cf7cc7b3
@metamask-previews/foundryup@1.0.1-preview-1cf7cc7b3
@metamask-previews/gas-fee-controller@26.3.2-preview-1cf7cc7b3
@metamask-previews/gator-permissions-controller@5.0.2-preview-1cf7cc7b3
@metamask-previews/geolocation-controller@1.0.0-preview-1cf7cc7b3
@metamask-previews/java-tron-up@1.0.0-preview-1cf7cc7b3
@metamask-previews/json-rpc-engine@10.5.0-preview-1cf7cc7b3
@metamask-previews/json-rpc-middleware-stream@8.0.8-preview-1cf7cc7b3
@metamask-previews/keyring-controller@27.1.1-preview-1cf7cc7b3
@metamask-previews/kyc-controller@0.0.0-preview-1cf7cc7b3
@metamask-previews/local-node-utils@1.0.0-preview-1cf7cc7b3
@metamask-previews/logging-controller@9.0.0-preview-1cf7cc7b3
@metamask-previews/message-manager@14.1.2-preview-1cf7cc7b3
@metamask-previews/messenger@2.0.0-preview-1cf7cc7b3
@metamask-previews/messenger-cli@0.2.0-preview-1cf7cc7b3
@metamask-previews/money-account-api-data-service@0.4.1-preview-1cf7cc7b3
@metamask-previews/money-account-balance-service@2.4.3-preview-1cf7cc7b3
@metamask-previews/money-account-controller@1.0.0-preview-1cf7cc7b3
@metamask-previews/money-account-upgrade-controller@3.0.2-preview-1cf7cc7b3
@metamask-previews/money-account-utils@1.1.0-preview-1cf7cc7b3
@metamask-previews/multichain-account-service@13.0.2-preview-1cf7cc7b3
@metamask-previews/multichain-api-middleware@4.0.3-preview-1cf7cc7b3
@metamask-previews/multichain-network-controller@3.2.4-preview-1cf7cc7b3
@metamask-previews/multichain-transactions-controller@7.1.2-preview-1cf7cc7b3
@metamask-previews/name-controller@9.1.2-preview-1cf7cc7b3
@metamask-previews/network-connection-banner-controller@0.2.1-preview-1cf7cc7b3
@metamask-previews/network-controller@36.0.0-preview-1cf7cc7b3
@metamask-previews/network-enablement-controller@6.0.5-preview-1cf7cc7b3
@metamask-previews/notification-services-controller@26.0.1-preview-1cf7cc7b3
@metamask-previews/passkey-controller@3.1.0-preview-1cf7cc7b3
@metamask-previews/permission-controller@13.1.1-preview-1cf7cc7b3
@metamask-previews/permission-log-controller@5.1.0-preview-1cf7cc7b3
@metamask-previews/perps-controller@15.0.0-preview-1cf7cc7b3
@metamask-previews/phishing-controller@17.4.0-preview-1cf7cc7b3
@metamask-previews/platform-api-docs@0.1.0-preview-1cf7cc7b3
@metamask-previews/polling-controller@16.0.9-preview-1cf7cc7b3
@metamask-previews/preferences-controller@23.1.0-preview-1cf7cc7b3
@metamask-previews/profile-metrics-controller@4.0.3-preview-1cf7cc7b3
@metamask-previews/profile-sync-controller@29.0.0-preview-1cf7cc7b3
@metamask-previews/ramps-controller@20.2.0-preview-1cf7cc7b3
@metamask-previews/rate-limit-controller@7.0.1-preview-1cf7cc7b3
@metamask-previews/react-data-query@1.0.0-preview-1cf7cc7b3
@metamask-previews/remote-feature-flag-controller@6.1.0-preview-1cf7cc7b3
@metamask-previews/sample-controllers@5.0.6-preview-1cf7cc7b3
@metamask-previews/seedless-onboarding-controller@10.1.1-preview-1cf7cc7b3
@metamask-previews/selected-network-controller@26.1.7-preview-1cf7cc7b3
@metamask-previews/sentinel-api-service@1.0.1-preview-1cf7cc7b3
@metamask-previews/shield-controller@6.0.1-preview-1cf7cc7b3
@metamask-previews/signature-controller@39.2.10-preview-1cf7cc7b3
@metamask-previews/smart-transactions-controller@26.0.0-preview-1cf7cc7b3
@metamask-previews/snap-account-service@2.1.2-preview-1cf7cc7b3
@metamask-previews/social-controllers@2.8.0-preview-1cf7cc7b3
@metamask-previews/solana-test-validator-up@1.0.0-preview-1cf7cc7b3
@metamask-previews/stellar-quickstart-up@0.0.0-preview-1cf7cc7b3
@metamask-previews/storage-service@1.0.2-preview-1cf7cc7b3
@metamask-previews/subscription-controller@8.0.1-preview-1cf7cc7b3
@metamask-previews/transaction-controller@69.7.0-preview-1cf7cc7b3
@metamask-previews/transaction-pay-controller@27.1.1-preview-1cf7cc7b3
@metamask-previews/user-operation-controller@41.2.9-preview-1cf7cc7b3
@metamask-previews/wallet@12.0.2-preview-1cf7cc7b3
@metamask-previews/wallet-cli@0.0.0-preview-1cf7cc7b3

…ishing-data-service

# Conflicts:
#	packages/phishing-controller/CHANGELOG.md
#	packages/phishing-controller/package.json
#	yarn.lock
@adonesky1
adonesky1 marked this pull request as ready for review September 9, 2026 20:36
@adonesky1
adonesky1 requested review from a team as code owners September 9, 2026 20:36

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

Comment thread packages/phishing-controller/src/PhishingDataService.ts

@mcmire mcmire left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have just started looking at this PR. Here are some comments for now. I will do another pass later.

Comment thread packages/phishing-controller/src/PhishingDataService.ts
gcTime: LIST_GC_TIME,
});

return jsonResponse as DataResultWrapper<PhishingStalelist>;

@mcmire mcmire Sep 9, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why the typecast? Ideally this.#validate should return the correct type (as it's the one ensuring that the data matches a known type).

(edit: I'll try to come back to this and make a better suggestion here)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The casts come from fetchQuery's TQueryFnData extends Json bound: superstruct optional() infers T | undefined, which isn't Json, and responseStruct needs a Struct<Json> for the same reason, so it can't take these structs without casting the raw response first. For the two structs without optionals (stalelist and address scan) the validated type satisfies the bound on its own, so I dropped the casts there in d04bcaa. Removing the rest means relaxing that bound in base-data-service; I'd do that as a follow-up unless you want it here.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gotcha. Yeah, we'll have to figure out a way to relax the types in BaseDataService. That's clearly out of scope here so these typecasts are fine.

cc @FrederikBolding

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

exactOptional may be what we are looking for here to be JSON compatible?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I revisited this and came to a different conclusion as you @adonesky1. I don't think it's due to the types in fetchQuery. I left some more comments here: #9914 (comment)

Comment thread packages/phishing-controller/CHANGELOG.md Outdated
Comment thread packages/phishing-controller/CHANGELOG.md Outdated
Comment thread packages/phishing-controller/CHANGELOG.md Outdated
Comment thread packages/phishing-controller/CHANGELOG.md Outdated
Comment thread packages/phishing-controller/CHANGELOG.md Outdated
Comment thread packages/phishing-controller/src/PhishingDataService.ts Outdated
adonesky1 and others added 7 commits September 10, 2026 11:44
Queries no longer wait indefinitely for StorageService:getItem after init();
the wait is bounded by a new hydrationTimeout (default 1s). Persisted caches
are shape-validated before hydration and a shouldHydrateQuery filter lets
services validate individual persisted queries.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
applyDiffs previously indexed listSets with the diff's list type and threw a
TypeError for any type it did not know, and the stricter hotlist struct in
this branch turned the same case into a rejected hotlist response that left
phishingLists empty. Unknown list types are now skipped per diff. Also adds
normalizeScanAddress for lowercasing EVM addresses in scan keys.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A single unrecognized verdict or malformed nested field previously rejected
the whole bulk URL, bulk token, or approvals response, dropping every BLOCK
and Malicious verdict in the batch. Entries are now validated individually:
malformed URL results are reported per URL, malformed token results and
approvals are omitted. Hotlist diffs may target unknown list types and the
C2 blocklist no longer requires lastFetchedAt, which the controller never
reads. Bulk-seeded cache entries now carry a hostname so scanUrl always
returns one.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The bounded rehydration wait added an asynchronous hop before every query
even for services that never call init(). That hop delays request timers
relative to callers' own timeouts; in the phishing controller's timeout
tests the request outlived the test, was aborted during teardown, and
TanStack then scheduled a real five-minute gcTime timer on the destroyed
query, keeping the Jest process alive. Only await rehydration when an
initialization promise exists.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Address lowercasing lived only in PhishingController, while the service's
query keys became public API for UI consumers. A mixed-case token or address
passed directly to the service missed the API's lowercase response keys and
negatively cached a null verdict. The service now lowercases EVM addresses
for both the request and the cache key; non-EVM addresses are unchanged.
Also corrects the scanToken JSDoc, which claimed concurrent calls coalesce.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ed scan results

With policyOptions.maxRetries enabled, each item query backed by a failed
batch retried on its own and de-batched into single-item requests (10 URLs
against a failing host produced 31 POSTs). The batch POST now runs under the
service policy and item-level batch errors are excluded from retries, so a
failed batch is retried intact. Persisted scan results are validated against
their endpoint shapes before hydration; entries that fail, and any query
other than a scan result, are discarded.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

@mcmire mcmire left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still working through this. Left some more comments.

Comment thread packages/phishing-controller/src/PhishingDataService.ts
Comment thread packages/base-data-service/src/BaseDataService.ts Outdated
init: RequestInit,
timeout?: number,
): Promise<Json> {
const controller = new AbortController();

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We already have an AbortController kept under this.#abortController. Why are we creating another one?

@adonesky1 adonesky1 Sep 14, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fetch only takes one cancel signal, but a request can be cancelled for up to three reasons: TanStack cancels that query (single-item requests only; batched requests aren't tied to one query), the service is destroyed (that's this.#abortController), or the request's own timeout fires. The per-request controller merges those into the one signal fetch accepts, and keeps the timeout separate so the error can say it was a timeout. Comment added in 8d5a1c9 and cff89ad.

To be clear this isn't shape I think we want long term. AbortSignal.any would be the one-liner but mobile's RN polyfill doesn't have it (its shim.js has to back-fill even AbortSignal.timeout), so it's the portable option for now. The better home is BaseDataService: hand each query function a signal covering both the query and the service's teardown, with an optional timeout built on cockatiel's timeout policy, which already models this and throws a typed TaskCancelledError. That would delete this wiring here and give every subclass the same guarantees. Happy to write that up as a follow-up if you agree with the direction.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh okay. So it sounds like one AbortController is service-level (and gets triggered when the service is destroyed) and the other is request-level (and may get triggered by TanStack Query or manually)? I'm asking because I'm wondering if this is a pattern that we need to port to BaseDataService.

Comment thread packages/phishing-controller/src/PhishingDataService.ts Outdated
adonesky1 and others added 9 commits September 11, 2026 12:47
…response casts

getApprovals now goes through fetchQuery with staleTime and gcTime of 0, the
same pattern KycService, ShieldApiService, and SubscriptionService use for
uncached reads. The precedent in #10007 bypasses fetchQuery for writes, not
for POST-shaped reads, so the direct executeWithPolicy path was a special case
with no benefit.

getStalelist and scanAddress no longer cast through Json: their structs have
no optional fields, so the validated type satisfies fetchQuery's Json bound
and flows through as the return type. The remaining casts are forced by
optional() fields inferring T | undefined, which fetchQuery's TQueryFnData
bound rejects; relaxing that lives in base-data-service.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Fold new-service behavior into the PhishingDataService entry, move fixes of
released behavior to Fixed, list the added dependencies, name the class and
method in ambiguous entries, and drop entries that only fixed iterations of
this branch that never shipped.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
# Conflicts:
#	packages/base-data-service/CHANGELOG.md
#	packages/phishing-controller/CHANGELOG.md
Replace the protected executeWithPolicy helper (introduced on this branch)
with a protected policy getter. Subclasses can still run uncached requests
under the shared retry and circuit-breaker policy, and can now also observe
its onBreak, onDegraded, and onRetry events, which hand-rolled services
elsewhere in the monorepo forward but BaseDataService subclasses could not
reach.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Each request combines the query's TanStack signal, the service-wide destroy
signal, and its own timeout into the single signal that fetch accepts.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The batch loader had two ways to send a batch: an explicit flush() called
by each bulk method, and a microtask fallback originally added for retried
item queries. Item queries are no longer retried, and once a client has
called init() every lookup runs after the explicit flush anyway, so the
deferred flush was already doing all the work in that configuration. Remove
the explicit flush and its API; every lookup made in the same turn is sent
in one batch.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Each query used to start its own hydration deadline. While storage hung,
every query paid the full timeout, and because the per-query timers fire as
separate macrotasks the item lookups of one bulk scan resumed in different
turns and were sent as single-item requests. One shared deadline per service
means all waiters resume together, batches stay intact, and no query waits
again once the deadline has passed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit dfa5fd2. Configure here.

Comment thread packages/phishing-controller/src/PhishingController.ts
The new @metamask/utils dependency was missing from the package tsconfigs and README graph, which failed lint:tsconfigs and readme-content:check.
timestamp: number;
};

const PersistedCacheStruct = objectType({

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think about grouping this consts with the others and moving it below DEFAULT_HYDRATION_TIMEOUT?

@mcmire mcmire Sep 14, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alternatively, now that we have this struct, it's a bit confusing to have this + PersistedCache. Usually we derive types from structs, but in this case, if we were to derive a type from PersistedCacheStruct and call it PersistedCache, it would be too broad.

Maybe we don't need this struct after all? (also see below)

*
* @param initialization - The pending rehydration.
*/
async #waitForInitialization(initialization: Promise<void>): Promise<void> {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are your thoughts on baking this into init itself? That way we only need one property to hold the initialization promise (this.#initializationPromise) and not two.

return;
}

if (!is(untypedCache, PersistedCacheStruct)) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Considering that PersistedCacheStruct and PersistedCache are similarly named, it might be tempting to make use of the struct to more idiomatically assign a type to cache rather than using a type assertion, e.g.:

    const [error, cache] = validate(untypedCache, PersistedCacheStruct);

    if (error) {
      await this.#externalMessenger.call(
        'StorageService:removeItem',
        this.name,
        STORAGE_SERVICE_KEY,
      );
      return;
    }

However, this would not work, because PersistedCacheStruct is more loosely defined than PersistedCache.

Maybe using a struct here is too heavy-handed and we can get away with simple checks?

Suggested change
if (!is(untypedCache, PersistedCacheStruct)) {
if (
!(
isPlainObject(untypedCache) &&
hasProperty(untypedCache, 'timestamp') &&
typeof untypedCache.timestamp === 'number' &&
hasProperty(untypedCache, 'state') &&
isPlainObject(untypedCache.state) &&
hasProperty(untypedCache.state, 'queries') &&
Array.isArray(untypedCache.state.queries) &&
hasProperty(untypedCache.state, 'mutations') &&
Array.isArray(untypedCache.state.mutations)
)
) {

We can also move this to an isPersistedCache function if it's too much to see here.

What do you think?

*
* @returns The service policy.
*/
protected get policy(): ServicePolicy {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it worth adding a comment explaining why this is a getter?

Suggested change
protected get policy(): ServicePolicy {
// A getter so that `policy` cannot be overwritten in a JavaScript project.
protected get policy(): ServicePolicy {

response: unknown,
struct: Struct<Type, Schema>,
endpointName: string,
): Infer<Struct<Type, Schema>> {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would this also work?

Suggested change
): Infer<Struct<Type, Schema>> {
): Type {

Comment on lines +1128 to +1136
} as Json;
},
// Live account state: always refetch and evict as soon as the call
// settles, as other data services do for uncached reads.
staleTime: 0,
gcTime: 0,
});

return jsonResponse as ApprovalsResponse;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar as above:

Suggested change
} as Json;
},
// Live account state: always refetch and evict as soon as the call
// settles, as other data services do for uncached reads.
staleTime: 0,
gcTime: 0,
});
return jsonResponse as ApprovalsResponse;
};
},
// Live account state: always refetch and evict as soon as the call
// settles, as other data services do for uncached reads.
staleTime: 0,
gcTime: 0,
});
return jsonResponse;

init: RequestInit,
timeout?: number,
): Promise<Json> {
const controller = new AbortController();

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh okay. So it sounds like one AbortController is service-level (and gets triggered when the service is destroyed) and the other is request-level (and may get triggered by TanStack Query or manually)? I'm asking because I'm wondering if this is a pattern that we need to port to BaseDataService.

*/
async #toJson(response: Response): Promise<Json> {
if (!response.ok) {
throw new HttpError(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: The HTTP status does not seem to correlate to converting the response to JSON. Maybe it's worth it to bake this into #fetchJson instead?

// cache rehydration) joins the same batch.
if (!flushScheduled) {
flushScheduled = true;
queueMicrotask(flush);

@mcmire mcmire Sep 14, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This whole function looks similar to reduceInBatchesSerially from assets-controller:

export async function reduceInBatchesSerially<Value, Result>({

However, this part is a bit different. Do we need to schedule the flush as a microtask because requests go through a query client? assets-controller also uses a query client under the hood, but it doesn't need to do this. So I'm wondering what the differences are between the approach we're taking here and the approach that is being taken in assets-controller to batch requests and whether the complexity is worth it.

You can see where it's being used here for instance:

const batchResults = await reduceInBatchesSerially<string, BatchResult[]>({

(Asking because I'm wondering if this is a pattern we need to add to base-data-service, so I want to know the problems we're solving and what the best approach is.)

// Persisted scan results are validated before they can be served
// from the cache; a caller-provided filter is applied on top.
shouldHydrateQuery: (query): boolean =>
isValidPersistedScanQuery(query) &&

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this line connected to? Is it the fact we are batching requests or is it the fact that we are validating requests inside of the queryFn?

Comment on lines +206 to +208
#initializationPromise?: Promise<void>;

#boundedInitialization?: Promise<void>;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

None of the changes to BaseDataService should be done in this PR, they should be opened as a separate PR IMO.

return;
}

if (!is(untypedCache, PersistedCacheStruct)) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this trying to guard against?

responseStruct?: TDataStruct;
}): Promise<TData> {
if (this.#initializationPromise) {
await this.#waitForInitialization(this.#initializationPromise);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the use-case here? The thinking originally was that persisted state would be loaded during init anyways before queries start to fire.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants