UNPKG

rapidoc

Version:

RapiDoc - Open API spec viewer with built in console

151 lines (137 loc) • 11.6 kB
# `src/` — Custom Element Entry Points ## Overview Source root of the `rapidoc` package. It owns the three custom elements and the bundle entry: `rapidoc.ts` (`<rapi-doc>`, the full viewer), `rapidoc-mini.ts` (`<rapi-doc-mini>`, the embeddable subset) and `oauth-receiver.ts` (`<oauth-receiver>`, the OAuth 2 return page). `index.js` is the entry Vite builds into `dist/rapidoc-min.js`. Two Node packages build on this source and are never imported from it: `@rapidoc/prerender` (`packages/rapidoc-prerender`) renders these templates to HTML at build time, and `@rapidoc/mcp` (`packages/rapidoc-mcp`) publishes the MCP code in `utils/`. `rapi-flow.ts` (`<rapi-flow>`, the Arazzo workflow diagram) has its own entry, `flow.js`, built into `dist/rapidoc-flow-min.js` (`rapidoc/flow`), so `rapidoc-min.js` carries no Arazzo code. It does not own parsing, rendering or styling: a spec goes through `ProcessSpec` ([`utils/`](./utils/README.md)) into `this.resolvedSpec`, and `render()` hands it to [`templates/main-body-template.js`](./templates/README.md), which composes [`components/`](./components/README.md). Themes are in [`styles/`](./styles/README.md). Dependents: page authors (attributes, methods, events), `@rapidoc/portal` and `utils/bind-navbar.js` (navigation hooks), and the docs site, which renders the attribute, method and event reference from `docs/src/page-data/rapidoc-api.yaml`. ## Interface What `rapidoc-api.yaml` documents is the supported API, plus the portal hooks listed below. Every other member is internal; templates call internal members freely through `this`. - `index.js`: default export is the object `{ RapiDoc }` (not the class); named exports `RapiDocMini` and `OAuthReceiver`. Importing it registers all three elements. - `<rapi-doc>` (`[rapidoc.ts:RapiDoc]`): - Documented methods: `loadSpec`, `scrollToPath`, `setApiKey`, `setHttpUserNameAndPassword`, `setApiServer`, `generateMcpServer`, `downloadMcpServer`, `getMcpBudget`. - Portal hooks: `bindNavBar(sidebar, options)`, `scrollTo(elementId)` (alias of `scrollToPath`; it shadows `Element.prototype.scrollTo`, so `el.scrollTo({ top })` does not scroll), `expandAndGotoOperation`, `getTargetElement`, `resolveOperationAlias`. - Events: `before-render` (from `ProcessSpec`, `detail: { spec }`); `spec-loaded` (`detail` is the live `resolvedSpec`; not bubbling); `nav-active-change` (`detail: { id, elementId, operationId? }`; bubbles, composed). - `<rapi-doc-mini>` (`[rapidoc-mini.ts:RapiDocMini]`): `loadSpec`, `setApiKey`, `setHttpUserNameAndPassword`, `removeAllSecurityKeys`, `setApiServer`; event `spec-loaded`. - `<oauth-receiver>` (`[oauth-receiver.ts:OauthReceiver]`): plain `HTMLElement` for the page named by the `oauth-receiver` attribute; relays the provider's reply to the opener. - `<rapi-flow>` (`[rapi-flow.ts:RapiFlow]`): attributes `arazzo-url`, `spec-url` (replaces the first OpenAPI source's URL, resolved against the page), `workflow-id` (defaults to the first workflow), `for` (id of a `<rapi-doc>`: clicking a step calls its `scrollToPath`; without `spec-url`, that `<rapi-doc>`'s processed spec stands in for the first OpenAPI source, read again on each of its `spec-loaded` events), `view` (`expanded`, the default, or `compact`; the header's Compact / Expanded switch sets it), `show-view-toggle` (`"false"` hides that switch), `theme`, `color-scheme`; method `load`; events `flow-loaded` (`detail: { doc, problems }`), `step-click` (`detail: { workflowId, stepId, operation, workflowRef }`) and `view-change` (`detail: { view }`, fired only by the switch), all bubbling and composed; getter `arazzoDocument` (the parsed document, null until loaded). The element does not store the chosen view; a page that wants it remembered listens for `view-change`. The legend lists only the kinds of lines the current diagram draws. Hovering a step, one of its value lines or its rows in the outputs box highlights all of them. Clicking a step name in the outputs box (focusable, Enter works too) or a value line that reads a step selects that step and scrolls its nearest scrolling ancestor (or the window) to center it. Clicking a step selects it; clicking a step that calls another workflow of the same document switches to it. Errors and sources that failed to load are listed under the diagram; the graph's authoring warnings are not shown. **Run** builds the MCP manifest for the shown workflow (`utils/mcp/manifest.js` with the tied `<rapi-doc>`'s processed spec, else the first source this element loaded) and runs it in the browser with `utils/mcp/workflow-runtime.js`. The run panel under the legend is always open: the inputs form (defaults filled, values coerced by type), the Run workflow button, then a log of every step's request, response and outputs and the workflow's outputs or the error. While a step's request is out its box carries `rf-running`; results are not drawn on the diagram. When the tied `<rapi-doc>` has `mock-server="true"`, the form also takes the status codes its in-browser mock answers with (`503`, or a list used in turn or at random): they are set on the `<rapi-doc>`'s `mock-server-status-code` / `-strategy` for the run and restored after, and log lines say `mocked`. Credentials are the tied `<rapi-doc>`'s entered ones (API keys, Basic, Bearer and OAuth tokens); a standalone element shows one password field per security scheme of the first source instead (kept in memory only). A workflow the runtime cannot run says why instead of a form. ## Invariants - **String flags.** Boolean-like attributes are the strings `"true"` / `"false"`, compared exactly, never by truthiness. `[rapidoc.ts:connectedCallback]` replaces an unknown enumerated value with a fixed fallback so templates can compare with `===`. `<rapi-doc-mini>` normalizes a smaller set (`[rapidoc-mini.ts:connectedCallback]`): its `layout` falls back to `row`, `sort-endpoints-by` has no `none`, and it does not move `theme="light|dark"` into `color-scheme`. - **Rewrites in `<rapi-doc>`.** `theme="light"|"dark"` moves into `color-scheme`; `color-scheme="auto"` is resolved by `AutoColorSchemeController` before normalization; a missing or invalid `layout` is derived from `render-style` (`column` for `read`/`focused`, `row` for `view`) and written back, while an author-set `row`/`column` is kept; an empty `mcp` becomes `none`. `mcp` defaults to `"false"`: the Export MCP Server button shows only for `"true"` (or any value that is not `false`/`none`, such as the old language names). The export includes workflow tools when `<rapi-flow>` is loaded on the page: the one whose `for` names this element, else one without `for`, supplies its `arazzoDocument` (`options.arazzo` overrides). - **Spec loading** (`[rapidoc.ts:loadSpec]`). Each load takes a token from `_specLoadSeq`; a load whose token is no longer current applies nothing (no `resolvedSpec`, no `loadFailed`, no `spec-loaded`), so the newest call wins. `connectedCallback` owns the initial load: an inlined `<script type="application/json" slot="rapidoc-spec">` wins over `spec-url`, and the `spec-url` value present at connect is then skipped by `attributeChangedCallback`. An inlined spec that fails `JSON.parse` is ignored, with no fallback to `spec-url`. A falsy argument is ignored. On failure `loadFailed` is set and `resolvedSpec` cleared. `<rapi-doc-mini>` uses the same token but loads only from `attributeChangedCallback`. - **`spec-loaded`** (`[rapidoc.ts:afterSpecParsedAndValidated]`) fires once per applied load, after `updateComplete` resolves true in a loop; `updated()` must not set a reactive property on every cycle, or the event never fires. Initial navigation follows: `location.hash`, else `goto-path`, else (focused mode) `overview` or the first operation. - **Server selection.** `default-api-server` equal to `server-url` selects the added server; otherwise the spec server with that `url`; otherwise `resolvedSpec.servers[0]`. - **Section ids.** Operation ids come from `OperationSlugger` (`utils/slugger.js`); `[rapidoc.ts:getTargetElement]` also accepts a `data-op-slug`, a `<tag>--<op>` suffix or a method+path alias. `nav-active-change.detail.id` is the alias-resolved request in `scrollToPath` and the observed section's DOM id in `onIntersect`; consumers should not assume the two agree. - **Auto height.** Scroll handling treats the element as page-scrolled when `auto-height="true"` or the attribute is present with any value. - **DSD hydration** (`[rapidoc.ts:_clearDsdIfNeeded]`). With a pre-rendered shadow root, `render()` returns `noChange` until `resolvedSpec` is set, then replaces all shadow children before Lit's first render. - **`<oauth-receiver>`** does no validation; `templates/security-scheme-template.js` in the opener checks `state`, origin and source and must stay the sole validator. ## Side Effects - Importing `index.js` defines `rapi-doc`, `rapi-doc-mini` and `oauth-receiver` globally, and evaluates `utils/markdown-utils.js`, which configures the global `marked`. - `<rapi-doc>` `connectedCallback` sets inline size and margin on a zero-size parent `<body>`; adds one capturing `hashchange` listener and first-interaction listeners on `window` (both removed on disconnect). Programmatic scrolls add temporary `scroll` / `scrollend` listeners. - `[rapidoc.ts:render]` clones the page `<link>` whose `href` contains `css-file` (passed through `CSS.escape`) into the shadow root on each render. - `history.replaceState` rewrites the hash on navigation only when `update-route="true"`. - `mock-server="true"` makes `utils/mock-interceptor.js` replace `window.fetch` and `XMLHttpRequest` methods page-wide; disconnect restores them only when the flag is still `"true"`. - `AutoColorSchemeController` observes `<html>`/`<body>` and a `matchMedia` query while `color-scheme="auto"`, removed on disconnect. - `downloadMcpServer` zips the exported project (`utils/mcp/zip.js`) into a Blob URL and clicks a temporary `<a>`; the file input uses `FileReader`. - `console.error` on load or parse failure (not for superseded loads); `console.warn` when `scroll-container` is not an ancestor. - `<rapi-flow>` fetches `arazzo-url` and each OpenAPI source description (relative to the Arazzo URL); a source that fails to load becomes a warning, not an error. A run sends the workflow's requests from the browser with `fetch`, as Try-it does, so the API must allow cross-origin requests. The first OpenAPI source is not fetched while `for` names a `<rapi-doc>` and `spec-url` is unset; `<rapi-flow>` listens to that element's `spec-loaded` until it disconnects. - `<oauth-receiver>` clears its URL with `history.replaceState`, posts the reply to `window.opener` (when set) with `location.origin` as target, and to `BroadcastChannel("rapidoc-oauth")` when that global exists. ## Verification - `npm run test:unit` (repo root; tests in `tests/unit/`): `spec-load-sequence.test.js` (load tokens, inlined spec first, one load per `spec-url`), `semantic-markup.test.js` and `static-renderer.test.js` (static render path). These run in Node, not a browser. - `npm run test:e2e` / `npm run test:component` run the elements in a browser, e.g. `tests/e2e/dynamic-attributes.test.js`, `scroll-offset.test.js`, `color-scheme-auto.test.js`, `sections-routing.test.js`. - After changes: `npm run build:rapidoc`, `npm run lint`, `npm run format-fix`.