Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ const config = createConfig([
'merged-packages/**',
'scripts/create-package/package-template/**',
'.platform-api-docs/**',
// Code generated from OpenAPI documents by Kubb (`yarn codegen`).
'packages/*/src/generated/**',
],
},
{
Expand Down Expand Up @@ -305,6 +307,13 @@ const config = createConfig([
'import-x/no-nodejs-modules': 'off',
},
},
{
// Code generation tooling (Kubb configs and plugins) runs under Node.
files: ['packages/*/codegen/**/*.ts'],
rules: {
'import-x/no-nodejs-modules': 'off',
},
},
{
files: ['packages/wallet-cli/src/**/*.{js,ts}'],
rules: {
Expand Down
5 changes: 5 additions & 0 deletions knip.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@ const config: KnipConfig = {
ignoreDependencies: ['cockatiel'],
},
'packages/core-backend': {
// Scan the Kubb code generation tooling (`codegen/`) next to `src/` so
// the `@kubb/*` devDependencies used by the codegen runner and the
// custom plugins are seen.
entry: ['codegen/run.ts'],
project: ['src/**/*.ts', 'codegen/**/*.ts'],
ignoreDependencies: ['@metamask/keyring-internal-api'],
},
'packages/earn-controller': {
Expand Down
11 changes: 11 additions & 0 deletions packages/core-backend/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- Add generated Price API bindings under the new `@metamask/core-backend/price-api` entry point, produced from a vendored OpenAPI spec snapshot (`specs/price-api.json`) by Kubb with a custom `@metamask/superstruct` plugin ([#9446](https://github.com/MetaMask/core/pull/9446))
- TypeScript types (e.g. `GetV3SpotPricesQueryResponse`), `@metamask/superstruct` structs (e.g. `GetV3SpotPricesQueryResponseStruct`), and TanStack query-core bindings (e.g. `getV3SpotPricesQueryOptions`, `fetchV3SpotPrices`) that validate responses against the generated structs
- Add generated Price API test mocks under the new `@metamask/core-backend/mocks` entry point ([#9446](https://github.com/MetaMask/core/pull/9446))
- Faker-based mock data builders (e.g. `createCoinGeckoSpotPrice`) and MSW request handlers (e.g. `getV3SpotPricesHandler`, `handlers`)
- Using the mocks requires the new optional peer dependencies `@faker-js/faker` (`^9.9.0`) and `msw` (`^2.0.0`)
- Add `ApiRequestClient` contract (with `ApiRequestArgs`) and `PricesApiRequestClient`, an implementation backed by the shared `BaseApiClient` transport for use with the generated query-core bindings ([#9446](https://github.com/MetaMask/core/pull/9446))

### Changed

- `PricesApiClient` now validates responses against the generated `@metamask/superstruct` structs for the endpoints covered by the vendored Price API spec (supported networks, exchange rates, v1/v2/v3 spot prices, v1 single-token and v3 historical prices) ([#9446](https://github.com/MetaMask/core/pull/9446))
- Malformed responses now reject with a `StructError` instead of being returned as-is; unknown additional fields are still tolerated
- Bump `@metamask/messenger` from `^1.2.0` to `^2.0.0` ([#9392](https://github.com/MetaMask/core/pull/9392))

## [6.5.0]
Expand Down
59 changes: 59 additions & 0 deletions packages/core-backend/codegen/annotate-msw-handlers.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/**
* Post-processes the MSW handlers generated by `@kubb/plugin-msw`, adding
* explicit `HttpHandler` type annotations.
*
* Without the annotations, TypeScript's declaration emit fails with TS2742
* ("The inferred type of 'xHandler' cannot be named without a reference to
* 'msw/lib/core/handlers/HttpHandler'") because the inferred handler type
* resolves to a non-portable path inside the msw package.
*
* Runs as part of `yarn codegen` (see `package.json`).
*/

import { readdir, readFile, writeFile } from 'node:fs/promises';
import path from 'node:path';

const MSW_DIRECTORY = path.resolve(
import.meta.dirname,
'../src/generated/price-api/msw',
);

const HTTP_HANDLER_IMPORT = 'import type { HttpHandler } from "msw";';

const files = await readdir(MSW_DIRECTORY);

for (const file of files.filter((name) => name.endsWith('.ts'))) {
const filePath = path.join(MSW_DIRECTORY, file);
const source = await readFile(filePath, 'utf8');
if (source.includes(HTTP_HANDLER_IMPORT)) {
continue;
}

const annotated = source
// Annotate the return type of `export function xHandler(...) {`.
.replace(
/(export function \w+Handler\([\s\S]*?\)\)) \{/u,
'$1: HttpHandler {',
)
// Annotate the aggregated `handlers` array in `handlers.ts`.
.replace(
/export const handlers = (\[[\s\S]*?\]) as const/u,
'export const handlers: HttpHandler[] = $1',
);

if (annotated === source) {
continue;
}

// Add the type import after the last existing import (`handlers.ts` has no
// "msw" import of its own, so anchoring on the import block keeps this
// uniform).
const lines = annotated.split('\n');
const lastImportIndex = lines.reduce(
(last, line, index) => (line.startsWith('import ') ? index : last),
-1,
);
lines.splice(lastImportIndex + 1, 0, HTTP_HANDLER_IMPORT);

await writeFile(filePath, lines.join('\n'));
}
Loading