--- name: plugin-api-docs description: Use when regenerating, checking, or auditing the CODAP v3 Plugin API reference in v3/doc/plugin-api/ — the per-resource pages, the quick-reference tables, the JSON inventory, or the request schema. Also use to check whether the Data Interactive code and its documentation have drifted, and before a v3 release. --- # Plugin API reference tooling Keeps `v3/doc/plugin-api/` honest about `v3/src/data-interactive/`. Three scripts under `v3/scripts/plugin-api/`, each runnable on its own; npm scripts wrap them. This solves the same problem as `generate-log-events-csv` does for log events, and is built the same way: a deterministic compiler-API extractor, a merge step that rewrites only machine-owned content, and a checker. ## The scripts | npm script | What it does | |---|---| | `npm run plugin-api:extract` | Prints the inventory as JSON to stdout (the generator is what writes the file) | | `npm run plugin-api:generate` | Rewrites generated blocks in the docs, writes the request schema, reports drift | | `npm run plugin-api:lint` | Checks the docs against the code and the conventions | | `npm run plugin-api:check` | Both of the above in report-only mode — what CI runs | `markers.mjs` and `inventory.mjs` are shared helpers, not entry points: the first defines a well-formed generated-block marker, the second is the one way both scripts run the extractor. The generator consults `markers.mjs` *before* rewriting a page and skips any page whose markers it cannot prove safe; that file explains what a rewrite would otherwise destroy. Run them from `v3/`. ## When to use which **Before a release, or after touching `src/data-interactive/`:** ``` npm run plugin-api:generate && npm run plugin-api:lint ``` Then read the drift report. `CHANGED` means a table was stale and has been rewritten — review the diff. `NEW` means a resource exists in code that nothing documents and that is not in the baseline; either document it or add it to the baseline deliberately. **To see whether anything has drifted, without writing:** `npm run plugin-api:check`. ## What is generated and what is not Generated blocks are delimited in the markdown: ``` ... ``` The generator writes `actions`, `scope`, `adornment-types`, the three `quick-reference.md` tables, and a `values` block **only** when it declares its source interface: ``` ``` Everything else it leaves alone and reports. That is deliberate: resources do not map to value interfaces by name, so a block the tool cannot fill completely is better hand-written than half-generated. The lint checks those instead. A marker declares a block machine-owned even before the tool can fill it. Content hand-written inside one is provisional: it stands until the generator learns that block, then is replaced — reported as CHANGED, and as a `--check` failure, not silently. Never hand-edit a block the generator already writes — run `npm run plugin-api:generate` and read the "Hand-maintained (left alone)" list to see which is which. See `v3/doc/plugin-api/conventions.md` for the full rules. ## The undocumented baseline `doc/plugin-api/undocumented-baseline.txt` lists resources with no page yet. The drift check reports a resource only if it is undocumented *and* absent from the baseline, so the check is meaningful while resources still lack pages instead of permanently red. When a page lands, delete its line. The check reports stale entries, so the file cannot quietly rot. ## What the extractor deliberately does not know It reports facts, not judgment. It cannot produce, and will never overwrite: - why a resource exists, or when a plugin would reach for it - the condition that produces an error — only the string is derivable - worked examples - per-adornment measure-data shapes, assembled at runtime by each handler's `getAdditionalData` If one of those is wrong in the docs, a human wrote it and a human fixes it. ## Gotchas - The extractor resolves **one** level of factory indirection — several adornments get their handler from `univariateMeasureAdornmentBaseHandler`. Anything deeper is reported as unresolved rather than guessed; if you see `built by …()` in the inventory, the actions are unknown, not absent. - Value interfaces are resolved through `extends`, including `extends Partial` (which makes the inherited members optional) and the alias forms `type X = Y` / `type X = Partial`. Members carry `inherited` naming the interface they came from. This matters more than it sounds: every component type extends `V2Component`, and `DIAttribute` declares 2 members and inherits 22. A base the walker cannot find is listed in `unresolvedBases` rather than dropped. Unions and MST `Partial>` aliases denote no single member list and are skipped, not approximated. - "Documented" has one definition, in `coverage.mjs`, used by both the generator and the lint: a page's generated `actions` header names every resource that page covers, falling back to the filename for a single-resource page. A mention in prose never counts. - `scope` derives from two signals: whether the parser exempts the resource from `#default` defaulting, and whether the handler actually reads a data context. The exemption list alone is misleading — `adornment` has a context resolved and ignores it. - Error strings reach the docs with CODAP's `%@` i18n notation replaced by neutral `` placeholders. A resource page may name them meaningfully in its own hand-written table.