Skip to content

Commit 552b51e

Browse files
cryptodev-2smcmire
andauthored
feat: add @metamask/platform-api-docs package (MetaMask#8012)
## Explanation Adds `@metamask/platform-api-docs`, a publishable package that generates and serves a searchable site documenting the Platform API — the catalog of actions and events available to clients through the message bus. The package scans TypeScript source and `.d.cts` declaration files, finds every `*Messenger` type declaration, walks its `Actions` and `Events` type arguments to discover the capability types they reference, extracts JSDoc/handler/payload information for each, and renders the result as a Docusaurus site with per-namespace pages and local search. ### Features - **Anchored on Messenger declarations.** Extraction discovers `*Messenger` type aliases and walks `Messenger<Namespace, Actions, Events>` to find the capability types — eliminating false positives from unrelated types that happen to share an action/event-like shape. - **Two capability-type patterns supported.** Inline (`type FooAction = { type: '...'; handler: ... }`) and capability-type constructors (`type FooGetStateAction = ControllerGetStateAction<typeof name, State>`), including their `interface` variants. - **Union expansion.** When a Messenger references an umbrella union like `FooActions = FooGetAction | FooSetAction`, the walker descends through it without documenting the intermediate alias. - **Scans three layouts.** Configured `--scan-dir`s, `packages/*/src/` (`.ts`), and `node_modules/@metamask/*/dist/` (`.d.cts`). - **Source links resolve automatically.** Reads `git remote get-url origin` and `git symbolic-ref refs/remotes/origin/HEAD` to build `https://github.com/<owner>/<repo>/blob/<branch>/<path>#L<line>` URLs; falls back to `main` for shallow clones. - **Project label and commit SHA stamped on the site.** `--project-label Core` / `--project-label Extension` produces titles like `Platform API (Core)`, and the short Git commit is shown in the intro and navbar so engineers can tell how current the docs are. - **Dedup across packages.** When a capability is declared in two places (e.g. once as the home definition and once as a re-export), the version with JSDoc and from the matching namespace's package wins. - **Docusaurus site** with dark/light mode, offline search (no Algolia), and MDX-safe rendering of JSDoc. ### Usage From the core monorepo: ```bash yarn docs:platform-api:build # Generate docs and build static site yarn docs:platform-api:dev # Dev server with hot reload yarn docs:platform-api:serve # Build and serve ``` From client projects (Extension, Mobile), install `@metamask/platform-api-docs` as a devDependency and add a script: ```json { "scripts": { "docs:platform-api:build": "platform-api-docs --build --project-label MyProject" } } ``` ### Implementation - **`packages/platform-api-docs/`** — separate workspace from `@metamask/messenger-cli` (different deps and release cadence). - **CLI** built with yargs; runs Docusaurus through `execa`. Flags: `--build`, `--serve`, `--dev`, `--scan-dir` (additive, repeatable), `--output`, `--project-label`. Configuration is CLI-only — no `package.json` config block. - **AST parsing via ts-morph** instead of the raw TypeScript compiler API. Standard JSDoc extraction (`jsDoc.getDescription()` + `getTags()`) replaces the previous hand-rolled comment parser. - **Single-pass extraction.** The Messenger walk returns `TypeAliasDeclaration`/`InterfaceDeclaration` nodes directly tagged with `'action'`/`'event'` kind, so the main loop doesn't re-walk the source file looking up names. - **File discovery via the `glob` package**, with results sorted for deterministic output across filesystems. - **Test coverage**: 100% lines / ~93% branches. Defensive guards against AST shapes that don't appear in real messenger types are explicitly marked with `// istanbul ignore` comments. - **GitHub Actions workflow** (`deploy-platform-api-docs.yml`) builds docs on PRs (uploads the build as an artifact) and deploys to GitHub Pages on `main`. ## References - [WPC-82](https://consensyssoftware.atlassian.net/browse/WPC-82) ## Checklist - [x] I've updated the test suite for new or updated code as appropriate - [x] I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate - [x] I've communicated my changes to consumers by updating changelogs for packages I've changed - [ ] I've introduced breaking changes in this PR and have prepared draft pull requests for clients and consumer packages to resolve them [WPC-82]: https://consensyssoftware.atlassian.net/browse/WPC-82?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Low Risk** > Changes are additive (new package, docs tooling, and CI deploy) with no runtime impact on existing controllers or clients. > > **Overview** > Introduces **`@metamask/platform-api-docs`**, a new workspace package that scans TypeScript and `.d.cts` for `*Messenger` types, walks their `Actions`/`Events` unions, extracts JSDoc and handler/payload shapes (via **ts-morph**), and emits a **Docusaurus** site with per-namespace pages, deduplication, and optional GitHub source links. > > The **monorepo** gains root scripts (`docs:platform-api:build|dev|serve`), README/CODEOWNERS entries, `.gitignore` for `.platform-api-docs/`, and **eslint/knip** rules for the new package. **CI** adds `deploy-platform-api-docs.yml` (build on every run; publish to GitHub Pages under `/platform-api/` on `main` with `keep_files` and a token-exchange PAT) and wires that job into **`main.yml`**’s required `all-jobs-complete` gate. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit d5fdb00. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Co-authored-by: Elliot Winkler <elliot.winkler@gmail.com>
1 parent b54b8e2 commit 552b51e

41 files changed

Lines changed: 7705 additions & 209 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/CODEOWNERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@
100100
/packages/multichain-api-middleware @MetaMask/core-platform
101101
/packages/permission-controller @MetaMask/core-platform
102102
/packages/permission-log-controller @MetaMask/core-platform
103+
/packages/platform-api-docs @MetaMask/core-platform
103104
/packages/polling-controller @MetaMask/core-platform
104105
/packages/preferences-controller @MetaMask/core-platform
105106
/packages/rate-limit-controller @MetaMask/core-platform
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
name: Deploy Platform API Docs
2+
3+
on:
4+
workflow_call:
5+
6+
jobs:
7+
build-docs:
8+
name: Build Platform API docs
9+
runs-on: ubuntu-latest
10+
permissions:
11+
contents: read
12+
steps:
13+
- name: Checkout and setup environment
14+
uses: MetaMask/action-checkout-and-setup@v3
15+
with:
16+
is-high-risk-environment: true
17+
18+
- name: Generate and build Platform API docs
19+
# The site is published under the `/platform-api/` subdirectory of
20+
# the repo's GitHub Pages site so that other doc sites (e.g.
21+
# package API docs) can be hosted alongside under sibling paths.
22+
env:
23+
REPO_OWNER: ${{ github.repository_owner }}
24+
REPO_NAME: ${{ github.event.repository.name }}
25+
run: |
26+
yarn docs:platform-api:build \
27+
--site-url "https://${REPO_OWNER}.github.io" \
28+
--site-base-url "/${REPO_NAME}/platform-api/"
29+
30+
- name: Upload build artifact
31+
uses: actions/upload-artifact@v7
32+
with:
33+
name: ${{ github.ref == 'refs/heads/main' && github.event_name == 'push' && 'platform-api-docs-build' || 'platform-api-docs' }}
34+
path: .platform-api-docs/build/
35+
retention-days: ${{ github.ref == 'refs/heads/main' && github.event_name == 'push' && 1 || 7 }}
36+
37+
get-token:
38+
name: Get access token
39+
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
40+
needs: build-docs
41+
runs-on: ubuntu-latest
42+
environment: github-pages
43+
permissions:
44+
id-token: write
45+
outputs:
46+
token: ${{ steps.get-token.outputs.token }}
47+
steps:
48+
- name: Get access token
49+
id: get-token
50+
uses: MetaMask/github-tools/.github/actions/get-token@v1
51+
with:
52+
token-exchange-url: ${{ vars.TOKEN_EXCHANGE_URL }}
53+
permissions: |
54+
contents: write
55+
56+
deploy:
57+
name: Deploy to GitHub Pages
58+
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
59+
needs:
60+
- build-docs
61+
- get-token
62+
runs-on: ubuntu-latest
63+
environment: github-pages
64+
permissions:
65+
# Just for the initial `actions/checkout` — the actual push to
66+
# `gh-pages` uses the PAT from the `get-token` job, not GITHUB_TOKEN.
67+
contents: read
68+
steps:
69+
- name: Checkout
70+
uses: actions/checkout@v6
71+
with:
72+
fetch-depth: 1
73+
74+
- name: Download build artifact
75+
uses: actions/download-artifact@v8
76+
with:
77+
name: platform-api-docs-build
78+
path: build/
79+
80+
- name: Deploy to GitHub Pages
81+
uses: peaceiris/actions-gh-pages@4f9cc6602d3f66b9c108549d475ec49e8ef4d45e # v4.0.0
82+
with:
83+
# Use a PAT obtained via Token Exchange Service rather than the
84+
# default `GITHUB_TOKEN`. `GITHUB_TOKEN` can't be granted branch
85+
# protections that disallow direct human pushes while still letting
86+
# this workflow publish, which is what we want for `gh-pages`.
87+
personal_token: ${{ needs.get-token.outputs.token }}
88+
publish_dir: ./build
89+
destination_dir: platform-api
90+
# Preserve sibling subdirectories (e.g. /package-api/) so this
91+
# deploy only overwrites /platform-api/.
92+
keep_files: true

.github/workflows/main.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,14 @@ jobs:
7777
needs: check-workflows
7878
uses: ./.github/workflows/lint-build-test.yml
7979

80+
deploy-platform-api-docs:
81+
name: Deploy Platform API Docs
82+
needs: lint-build-test
83+
permissions:
84+
contents: read
85+
id-token: write
86+
uses: ./.github/workflows/deploy-platform-api-docs.yml
87+
8088
check-release:
8189
name: Check release
8290
needs: check-workflows
@@ -135,6 +143,7 @@ jobs:
135143
needs:
136144
- analyse-code
137145
- check-release
146+
- deploy-platform-api-docs
138147
- lint-build-test
139148
outputs:
140149
passed: ${{ steps.set-output.outputs.passed }}

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ packages/*/*.tsbuildinfo
3939
# AI
4040
.sisyphus/
4141

42+
# Platform API docs (generated)
43+
.platform-api-docs/
44+
4245
# Agent skills
4346
# Copy `.skills.local.example` to `.skills.local` and edit `SKILLS_DOMAINS=`.
4447
.skills.local

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ yarn skills --reset # clear saved local selection
106106
- [`@metamask/permission-log-controller`](packages/permission-log-controller)
107107
- [`@metamask/perps-controller`](packages/perps-controller)
108108
- [`@metamask/phishing-controller`](packages/phishing-controller)
109+
- [`@metamask/platform-api-docs`](packages/platform-api-docs)
109110
- [`@metamask/polling-controller`](packages/polling-controller)
110111
- [`@metamask/preferences-controller`](packages/preferences-controller)
111112
- [`@metamask/profile-metrics-controller`](packages/profile-metrics-controller)
@@ -206,6 +207,7 @@ linkStyle default opacity:0.5
206207
permission_log_controller(["@metamask/permission-log-controller"]);
207208
perps_controller(["@metamask/perps-controller"]);
208209
phishing_controller(["@metamask/phishing-controller"]);
210+
platform_api_docs(["@metamask/platform-api-docs"]);
209211
polling_controller(["@metamask/polling-controller"]);
210212
preferences_controller(["@metamask/preferences-controller"]);
211213
profile_metrics_controller(["@metamask/profile-metrics-controller"]);

eslint.config.mjs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ const config = createConfig([
8181
'.yarn/**',
8282
'merged-packages/**',
8383
'scripts/create-package/package-template/**',
84+
'.platform-api-docs/**',
8485
],
8586
},
8687
{
@@ -117,6 +118,7 @@ const config = createConfig([
117118
'**/tests/**/*.{js,ts}',
118119
'scripts/*.ts',
119120
'scripts/create-package/**/*.ts',
121+
'packages/platform-api-docs/**/*.ts',
120122
],
121123
extends: [nodejs],
122124
},
@@ -210,7 +212,7 @@ const config = createConfig([
210212
},
211213
},
212214
{
213-
files: ['scripts/*.ts'],
215+
files: ['scripts/*.ts', 'packages/platform-api-docs/src/cli.ts'],
214216
rules: {
215217
// Scripts may be self-executable and thus have hashbangs.
216218
'n/hashbang': 'off',
@@ -317,7 +319,10 @@ const config = createConfig([
317319
},
318320
},
319321
{
320-
files: ['packages/wallet-cli/src/**/*.test.{js,ts}'],
322+
files: [
323+
'packages/wallet-cli/src/**/*.test.{js,ts}',
324+
'packages/platform-api-docs/**/*.{js,ts}',
325+
],
321326
rules: {
322327
'jest/unbound-method': 'off',
323328
'n/no-process-env': 'off',

knip.config.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,30 @@ const config: KnipConfig = {
136136
'packages/phishing-controller': {
137137
ignoreDependencies: ['immer', 'punycode'],
138138
},
139+
'packages/platform-api-docs': {
140+
// This package has both a CLI (`src/`) and a Docusaurus site (`site/`).
141+
// Scan both so the CLI's deps (e.g. `glob`, `ts-morph`) and the site's
142+
// `@docusaurus/*` / `prism-react-renderer` imports in
143+
// `docusaurus.config.ts` are seen and neither gets flagged as unused.
144+
entry: ['site/docusaurus.config.ts'],
145+
project: ['src/**/*.{ts,tsx}', 'site/**/*.{ts,tsx}'],
146+
ignoreDependencies: [
147+
// Docusaurus runtime and React peers loaded by the framework at build
148+
// time; the `docusaurus` binary is invoked via execa, never imported
149+
// by source.
150+
'@docusaurus/core',
151+
'@docusaurus/plugin-content-docs',
152+
'@mdx-js/react',
153+
'react',
154+
'react-dom',
155+
// Loaded by docusaurus as a plugin name string (themes[0]); knip
156+
// doesn't trace string-referenced plugins.
157+
'@easyops-cn/docusaurus-search-local',
158+
// Pulled in transitively by `@docusaurus/preset-classic`; pinned here
159+
// so the framework's webpack/theme resolution finds a single version.
160+
'@docusaurus/theme-common',
161+
],
162+
},
139163
'packages/profile-metrics-controller': {
140164
ignoreDependencies: ['cockatiel'],
141165
},

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@
2222
"changelog:validate": "yarn workspaces foreach --all --no-private --parallel --interlaced --verbose run changelog:validate",
2323
"create-release-branch": "create-release-branch --formatter oxfmt",
2424
"create-package": "tsx scripts/create-package",
25+
"docs:platform-api:build": "yarn workspace @metamask/platform-api-docs cli ../.. --build --project-label Core",
26+
"docs:platform-api:dev": "yarn workspace @metamask/platform-api-docs cli ../.. --dev --project-label Core",
27+
"docs:platform-api:serve": "yarn workspace @metamask/platform-api-docs cli ../.. --serve --project-label Core",
2528
"lint": "yarn lint:eslint && echo && yarn lint:misc --check && yarn constraints && yarn lint:dependencies && yarn lint:teams && yarn messenger-action-types:check && yarn readme-content:check",
2629
"lint:dependencies": "knip --dependencies && yarn dedupe --check",
2730
"lint:dependencies:fix": "knip --dependencies && yarn dedupe",
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Changelog
2+
3+
All notable changes to this project will be documented in this file.
4+
5+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7+
8+
## [Unreleased]
9+
10+
### Added
11+
12+
- Initial release of the platform-api-docs package ([#8012](https://github.com/MetaMask/core/pull/8012))
13+
14+
[Unreleased]: https://github.com/MetaMask/core/

packages/platform-api-docs/LICENSE

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
This project is licensed under either of
2+
3+
* MIT license ([LICENSE.MIT](LICENSE.MIT))
4+
* Apache License, Version 2.0 ([LICENSE.APACHE2](LICENSE.APACHE2))
5+
6+
at your option.

0 commit comments

Comments
 (0)