@wildo-ai/saas-technical-doc 1.1.4 → 1.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/build/csp-emit.d.ts.map +1 -1
- package/dist/esm/build/load-materialized-frontend-providers.d.ts.map +1 -1
- package/dist/esm/companion/application-documentation/application-administration-documentation.d.ts.map +1 -1
- package/dist/esm/companion/application-documentation/application-administration-documentation.js.map +1 -1
- package/dist/esm/companion/application-documentation/application-authentication-documentation.d.ts.map +1 -1
- package/dist/esm/companion/application-documentation/application-connection-documentation.d.ts +37 -0
- package/dist/esm/companion/application-documentation/application-connection-documentation.d.ts.map +1 -1
- package/dist/esm/companion/application-documentation/application-connection-documentation.js +46 -5
- package/dist/esm/companion/application-documentation/application-connection-documentation.js.map +1 -1
- package/dist/esm/companion/application-documentation/application-documentation-chapters.d.ts +101 -0
- package/dist/esm/companion/application-documentation/application-documentation-chapters.d.ts.map +1 -0
- package/dist/esm/companion/application-documentation/application-documentation-chapters.js +205 -0
- package/dist/esm/companion/application-documentation/application-documentation-chapters.js.map +1 -0
- package/dist/esm/companion/application-documentation/application-domain-documentation.d.ts.map +1 -1
- package/dist/esm/companion/application-documentation/application-domain-documentation.js.map +1 -1
- package/dist/esm/companion/application-documentation/application-integration-documentation.d.ts.map +1 -1
- package/dist/esm/companion/application-documentation/application-integration-documentation.js.map +1 -1
- package/dist/esm/companion/application-documentation/application-organization-role-documentation.d.ts.map +1 -1
- package/dist/esm/companion/application-documentation/application-organization-role-documentation.js.map +1 -1
- package/dist/esm/companion/application-documentation/application-organization-unit-resource-documentation.d.ts.map +1 -1
- package/dist/esm/companion/application-documentation/application-organization-unit-resource-documentation.js +3 -0
- package/dist/esm/companion/application-documentation/application-organization-unit-resource-documentation.js.map +1 -1
- package/dist/esm/companion/application-documentation/docs-api-origin-substitution.d.ts +34 -0
- package/dist/esm/companion/application-documentation/docs-api-origin-substitution.d.ts.map +1 -0
- package/dist/esm/companion/application-documentation/docs-api-origin-substitution.js +45 -0
- package/dist/esm/companion/application-documentation/docs-api-origin-substitution.js.map +1 -0
- package/dist/esm/companion/application-documentation/technical-documentation-engine-content-bundle.d.ts +24 -10
- package/dist/esm/companion/application-documentation/technical-documentation-engine-content-bundle.d.ts.map +1 -1
- package/dist/esm/companion/application-documentation/technical-documentation-engine-content-bundle.js +25 -15
- package/dist/esm/companion/application-documentation/technical-documentation-engine-content-bundle.js.map +1 -1
- package/dist/esm/companion/application-documentation/technical-documentation-operational-evidence.d.ts.map +1 -1
- package/dist/esm/companion/application-documentation/technical-documentation-private-derivation.d.ts +11 -3
- package/dist/esm/companion/application-documentation/technical-documentation-private-derivation.d.ts.map +1 -1
- package/dist/esm/companion/application-documentation/technical-documentation-private-derivation.js +9 -2
- package/dist/esm/companion/application-documentation/technical-documentation-private-derivation.js.map +1 -1
- package/dist/esm/companion/application-documentation/technical-documentation-publication-policy.d.ts.map +1 -1
- package/dist/esm/companion/application-documentation/technical-documentation-publication-policy.js +10 -4
- package/dist/esm/companion/application-documentation/technical-documentation-publication-policy.js.map +1 -1
- package/dist/esm/companion/content/application-consumer-documentation-content-loader.d.ts.map +1 -1
- package/dist/esm/companion/index.d.ts +2 -0
- package/dist/esm/companion/index.d.ts.map +1 -1
- package/dist/esm/companion/index.js +2 -0
- package/dist/esm/companion/index.js.map +1 -1
- package/dist/esm/companion/manual-controller-route-projection.d.ts.map +1 -1
- package/dist/esm/companion/manual-controller-route-projection.js.map +1 -1
- package/dist/esm/companion/openapi-example-derivation.d.ts +53 -0
- package/dist/esm/companion/openapi-example-derivation.d.ts.map +1 -0
- package/dist/esm/companion/openapi-example-derivation.js +229 -0
- package/dist/esm/companion/openapi-example-derivation.js.map +1 -0
- package/dist/esm/companion/openapi-generator.d.ts +8 -4
- package/dist/esm/companion/openapi-generator.d.ts.map +1 -1
- package/dist/esm/companion/openapi-generator.js +323 -33
- package/dist/esm/companion/openapi-generator.js.map +1 -1
- package/dist/esm/companion/operation-projection.schemas.d.ts +57 -1
- package/dist/esm/companion/operation-projection.schemas.d.ts.map +1 -1
- package/dist/esm/companion/operation-projection.schemas.js +29 -0
- package/dist/esm/companion/operation-projection.schemas.js.map +1 -1
- package/dist/esm/companion/publish-result.types.d.ts.map +1 -1
- package/dist/esm/companion/publish-result.types.js.map +1 -1
- package/dist/esm/companion/rendering/technical-documentation-authorized-bundle-reader.d.ts.map +1 -1
- package/dist/esm/companion/rendering/technical-documentation-docusaurus-renderer.d.ts.map +1 -1
- package/dist/esm/companion/rendering/technical-documentation-docusaurus-renderer.js +8 -0
- package/dist/esm/companion/rendering/technical-documentation-docusaurus-renderer.js.map +1 -1
- package/dist/esm/companion/rendering/technical-documentation-managed-tree-validator.d.ts.map +1 -1
- package/dist/esm/companion/rendering/technical-documentation-managed-tree-validator.js +20 -0
- package/dist/esm/companion/rendering/technical-documentation-managed-tree-validator.js.map +1 -1
- package/dist/esm/companion/rendering/technical-documentation-openapi-renderer.d.ts +2 -0
- package/dist/esm/companion/rendering/technical-documentation-openapi-renderer.d.ts.map +1 -1
- package/dist/esm/companion/rendering/technical-documentation-openapi-renderer.js +48 -31
- package/dist/esm/companion/rendering/technical-documentation-openapi-renderer.js.map +1 -1
- package/dist/esm/companion/rendering/technical-documentation-render-model.d.ts.map +1 -1
- package/dist/esm/companion/rendering/technical-documentation-render-model.js +26 -10
- package/dist/esm/companion/rendering/technical-documentation-render-model.js.map +1 -1
- package/dist/esm/companion/rendering/technical-documentation-search-index-renderer.d.ts.map +1 -1
- package/dist/esm/companion/rendering/technical-documentation-search-index-renderer.js.map +1 -1
- package/dist/esm/companion/spec-to-operation-doc.d.ts +52 -10
- package/dist/esm/companion/spec-to-operation-doc.d.ts.map +1 -1
- package/dist/esm/companion/spec-to-operation-doc.js +125 -7
- package/dist/esm/companion/spec-to-operation-doc.js.map +1 -1
- package/dist/esm/companion/technical-documentation-asset-path.d.ts.map +1 -1
- package/dist/esm/companion/technical-documentation-capture-execution-port.d.ts.map +1 -1
- package/dist/esm/companion/technical-documentation-capture-execution-port.js.map +1 -1
- package/dist/esm/companion/technical-documentation-diagram-definitions.d.ts.map +1 -1
- package/dist/esm/companion/technical-documentation-diagram-definitions.js.map +1 -1
- package/dist/esm/companion/technical-documentation-diagram-materializer.d.ts.map +1 -1
- package/dist/esm/companion/technical-documentation-diagram-materializer.js.map +1 -1
- package/dist/esm/companion/technical-documentation-placeholder-materializer.d.ts.map +1 -1
- package/dist/esm/companion/zod-to-openapi.d.ts.map +1 -1
- package/dist/esm/companion-exports.d.ts.map +1 -1
- package/dist/esm/config/define-tech-doc-config.d.ts.map +1 -1
- package/dist/esm/config/index.d.ts.map +1 -1
- package/dist/esm/config/wildo-tech-doc-config.schemas.d.ts.map +1 -1
- package/dist/esm/config/wildo-tech-doc-config.schemas.js.map +1 -1
- package/dist/esm/content/application-consumer-documentation-content-manifest.schemas.d.ts.map +1 -1
- package/dist/esm/content/application-consumer-documentation-content.techdoc.d.ts +27 -24
- package/dist/esm/content/application-consumer-documentation-content.techdoc.d.ts.map +1 -1
- package/dist/esm/content/application-consumer-documentation-content.techdoc.js +205 -82
- package/dist/esm/content/application-consumer-documentation-content.techdoc.js.map +1 -1
- package/dist/esm/content.exports.d.ts.map +1 -1
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/openapi/api-reference-link-index.d.ts +3 -0
- package/dist/esm/openapi/api-reference-link-index.d.ts.map +1 -1
- package/dist/esm/openapi/api-reference-link-index.js +26 -15
- package/dist/esm/openapi/api-reference-link-index.js.map +1 -1
- package/dist/esm/openapi/api-reference-pages.d.ts +55 -0
- package/dist/esm/openapi/api-reference-pages.d.ts.map +1 -0
- package/dist/esm/openapi/api-reference-pages.js +229 -0
- package/dist/esm/openapi/api-reference-pages.js.map +1 -0
- package/dist/esm/openapi/api-reference-search.d.ts +53 -0
- package/dist/esm/openapi/api-reference-search.d.ts.map +1 -0
- package/dist/esm/openapi/api-reference-search.js +100 -0
- package/dist/esm/openapi/api-reference-search.js.map +1 -0
- package/dist/esm/openapi/api-reference-targets.d.ts +11 -0
- package/dist/esm/openapi/api-reference-targets.d.ts.map +1 -1
- package/dist/esm/openapi/api-reference-targets.js +8 -0
- package/dist/esm/openapi/api-reference-targets.js.map +1 -1
- package/dist/esm/openapi/index.d.ts +2 -0
- package/dist/esm/openapi/index.d.ts.map +1 -1
- package/dist/esm/openapi/index.js +2 -0
- package/dist/esm/openapi/index.js.map +1 -1
- package/dist/esm/openapi/openapi-generation-output.schemas.d.ts.map +1 -1
- package/dist/esm/openapi-reference-model.exports.d.ts +2 -0
- package/dist/esm/openapi-reference-model.exports.d.ts.map +1 -1
- package/dist/esm/openapi-reference-model.exports.js +2 -0
- package/dist/esm/openapi-reference-model.exports.js.map +1 -1
- package/dist/esm/runtime/AuthExchangePage.d.ts +45 -4
- package/dist/esm/runtime/AuthExchangePage.d.ts.map +1 -1
- package/dist/esm/runtime/AuthExchangePage.js +45 -12
- package/dist/esm/runtime/AuthExchangePage.js.map +1 -1
- package/dist/esm/runtime/DocsAuthContext.d.ts +2 -2
- package/dist/esm/runtime/DocsAuthContext.d.ts.map +1 -1
- package/dist/esm/runtime/DocsAuthContext.js +8 -4
- package/dist/esm/runtime/DocsAuthContext.js.map +1 -1
- package/dist/esm/runtime/DocsFrontendProviders.d.ts +30 -0
- package/dist/esm/runtime/DocsFrontendProviders.d.ts.map +1 -0
- package/dist/esm/runtime/DocsFrontendProviders.js +39 -0
- package/dist/esm/runtime/DocsFrontendProviders.js.map +1 -0
- package/dist/esm/runtime/DocsProviderComponent.d.ts +41 -0
- package/dist/esm/runtime/DocsProviderComponent.d.ts.map +1 -0
- package/dist/esm/runtime/DocsProviderComponent.js +17 -0
- package/dist/esm/runtime/DocsProviderComponent.js.map +1 -0
- package/dist/esm/runtime/decode-jwt-claims.d.ts.map +1 -1
- package/dist/esm/runtime/docs-auth-client.d.ts.map +1 -1
- package/dist/esm/runtime/docs-auth-session.schemas.d.ts.map +1 -1
- package/dist/esm/runtime/documentation-site-translator.d.ts +53 -0
- package/dist/esm/runtime/documentation-site-translator.d.ts.map +1 -0
- package/dist/esm/runtime/documentation-site-translator.js +51 -0
- package/dist/esm/runtime/documentation-site-translator.js.map +1 -0
- package/dist/esm/runtime/frontend-provider-registry.techdoc.d.ts +1 -2
- package/dist/esm/runtime/frontend-provider-registry.techdoc.d.ts.map +1 -1
- package/dist/esm/runtime/frontend-provider-registry.techdoc.js.map +1 -1
- package/dist/esm/runtime/index.d.ts +8 -0
- package/dist/esm/runtime/index.d.ts.map +1 -1
- package/dist/esm/runtime/index.js +8 -0
- package/dist/esm/runtime/index.js.map +1 -1
- package/dist/esm/runtime/openapi-reference-conservation.d.ts.map +1 -1
- package/dist/esm/runtime/openapi-reference-model.d.ts +30 -0
- package/dist/esm/runtime/openapi-reference-model.d.ts.map +1 -1
- package/dist/esm/runtime/openapi-reference-model.js +85 -11
- package/dist/esm/runtime/openapi-reference-model.js.map +1 -1
- package/dist/esm/runtime/openapi-reference-navigation.d.ts +50 -0
- package/dist/esm/runtime/openapi-reference-navigation.d.ts.map +1 -0
- package/dist/esm/runtime/openapi-reference-navigation.js +46 -0
- package/dist/esm/runtime/openapi-reference-navigation.js.map +1 -0
- package/dist/esm/runtime/openapi-reference-samples.d.ts +40 -0
- package/dist/esm/runtime/openapi-reference-samples.d.ts.map +1 -0
- package/dist/esm/runtime/openapi-reference-samples.js +169 -0
- package/dist/esm/runtime/openapi-reference-samples.js.map +1 -0
- package/dist/esm/runtime/openapi-reference-styles.d.ts +28 -0
- package/dist/esm/runtime/openapi-reference-styles.d.ts.map +1 -0
- package/dist/esm/runtime/openapi-reference-styles.js +292 -0
- package/dist/esm/runtime/openapi-reference-styles.js.map +1 -0
- package/dist/esm/runtime/openapi-reference-view.d.ts +40 -5
- package/dist/esm/runtime/openapi-reference-view.d.ts.map +1 -1
- package/dist/esm/runtime/openapi-reference-view.js +828 -82
- package/dist/esm/runtime/openapi-reference-view.js.map +1 -1
- package/dist/esm/runtime/openapi-reference-words-context.d.ts +12 -0
- package/dist/esm/runtime/openapi-reference-words-context.d.ts.map +1 -0
- package/dist/esm/runtime/openapi-reference-words-context.js +30 -0
- package/dist/esm/runtime/openapi-reference-words-context.js.map +1 -0
- package/dist/esm/runtime/openapi-reference-words.d.ts +205 -0
- package/dist/esm/runtime/openapi-reference-words.d.ts.map +1 -0
- package/dist/esm/runtime/openapi-reference-words.js +162 -0
- package/dist/esm/runtime/openapi-reference-words.js.map +1 -0
- package/dist/esm/runtime/provider-component-registry.techdoc.d.ts +31 -0
- package/dist/esm/runtime/provider-component-registry.techdoc.d.ts.map +1 -0
- package/dist/esm/runtime/provider-component-registry.techdoc.js +35 -0
- package/dist/esm/runtime/provider-component-registry.techdoc.js.map +1 -0
- package/dist/esm/runtime/use-docs-auth-session.d.ts.map +1 -1
- package/dist/esm/runtime/use-docs-frontend-provider-registry.d.ts +1 -0
- package/dist/esm/runtime/use-docs-frontend-provider-registry.d.ts.map +1 -1
- package/dist/esm/runtime/use-docs-frontend-provider-registry.js +10 -2
- package/dist/esm/runtime/use-docs-frontend-provider-registry.js.map +1 -1
- package/dist/esm/runtime/use-docs-provider-component.d.ts +29 -0
- package/dist/esm/runtime/use-docs-provider-component.d.ts.map +1 -0
- package/dist/esm/runtime/use-docs-provider-component.js +42 -0
- package/dist/esm/runtime/use-docs-provider-component.js.map +1 -0
- package/dist/esm/runtime/use-docs-provider-scripts.d.ts +37 -0
- package/dist/esm/runtime/use-docs-provider-scripts.d.ts.map +1 -0
- package/dist/esm/runtime/use-docs-provider-scripts.js +47 -0
- package/dist/esm/runtime/use-docs-provider-scripts.js.map +1 -0
- package/dist/esm/runtime/use-docs-provider-sdks.d.ts.map +1 -1
- package/dist/esm/runtime/use-docs-provider-sdks.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +8 -24
|
@@ -1,6 +1,30 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { Fragment, useEffect, useMemo, useState } from 'react';
|
|
2
|
+
import { Fragment, createContext, useContext, useEffect, useMemo, useId, useRef, useState, } from 'react';
|
|
3
3
|
import { ApiReferenceSchemaCompositionKind } from './openapi-reference-model.js';
|
|
4
|
+
import { ApiReferenceSampleLanguage, buildRequestSamples, responseSamples } from './openapi-reference-samples.js';
|
|
5
|
+
import { apiReferenceResourceSlug } from './openapi-reference-navigation.js';
|
|
6
|
+
import { API_REFERENCE_STYLES } from './openapi-reference-styles.js';
|
|
7
|
+
import { ApiReferenceWordsProvider, renderList, renderSentence, useReferenceWords } from './openapi-reference-words-context.js';
|
|
8
|
+
import { DocumentationListKind } from './documentation-site-translator.js';
|
|
9
|
+
import { apiReferencePageHref, apiReferencePageSlugForFragment } from '../openapi/api-reference-pages.js';
|
|
10
|
+
const ApiDocumentConnectionContext = createContext({ servers: [], serverUrl: null, selectServer: () => undefined, securitySchemes: [] });
|
|
11
|
+
const SchemaResolutionContext = createContext({ componentsByName: new Map(), expanding: new Set() });
|
|
12
|
+
/** The component a reference node names, when it can be shown in place on this branch. */
|
|
13
|
+
function expandableReference(schema, resolution) {
|
|
14
|
+
if (schema.referenceName === null || resolution.expanding.has(schema.referenceName))
|
|
15
|
+
return null;
|
|
16
|
+
const resolved = resolution.componentsByName.get(schema.referenceName);
|
|
17
|
+
return resolved === undefined ? null : { name: schema.referenceName, schema: resolved };
|
|
18
|
+
}
|
|
19
|
+
/** The schema a presenter should describe: the referenced component when it expands, else the node itself. */
|
|
20
|
+
function displayedSchema(schema, resolution) {
|
|
21
|
+
return expandableReference(schema, resolution)?.schema ?? schema;
|
|
22
|
+
}
|
|
23
|
+
/** A heading whose level follows where it sits: the same operation is an `h3` on its page and an `h4` on the single-page reference. */
|
|
24
|
+
function Heading({ level, id, className, children }) {
|
|
25
|
+
const Tag = `h${Math.min(Math.max(level, 1), 6)}`;
|
|
26
|
+
return _jsx(Tag, { id: id, className: className, children: children });
|
|
27
|
+
}
|
|
4
28
|
/**
|
|
5
29
|
* Generic OpenAPI reference presentation for every generated application.
|
|
6
30
|
*
|
|
@@ -8,45 +32,251 @@ import { ApiReferenceSchemaCompositionKind } from './openapi-reference-model.js'
|
|
|
8
32
|
* Application routes, branding, and document loading remain outside this
|
|
9
33
|
* component; API semantics remain in OpenAPI rather than in React.
|
|
10
34
|
*/
|
|
11
|
-
|
|
35
|
+
/** The stylesheet and the two contexts every reference surface renders inside. */
|
|
36
|
+
function ReferenceProviders({ model, words, children }) {
|
|
37
|
+
const schemaResolution = useMemo(() => ({
|
|
38
|
+
componentsByName: new Map(model.componentSchemas.map((component) => [component.name, component.schema])),
|
|
39
|
+
expanding: new Set(),
|
|
40
|
+
}), [model]);
|
|
41
|
+
const [selectedServerUrl, setSelectedServerUrl] = useState(null);
|
|
42
|
+
const connection = useMemo(() => ({
|
|
43
|
+
servers: model.servers,
|
|
44
|
+
serverUrl: model.servers.find((server) => server.url === selectedServerUrl)?.url ?? model.servers[0]?.url ?? null,
|
|
45
|
+
selectServer: setSelectedServerUrl,
|
|
46
|
+
securitySchemes: model.securitySchemes,
|
|
47
|
+
}), [model, selectedServerUrl]);
|
|
48
|
+
return _jsx(ApiReferenceWordsProvider, { value: words, children: _jsx(SchemaResolutionContext.Provider, { value: schemaResolution, children: _jsxs(ApiDocumentConnectionContext.Provider, { value: connection, children: [_jsx("style", { children: API_REFERENCE_STYLES }), children] }) }) });
|
|
49
|
+
}
|
|
50
|
+
/* Shell: rail, drawer and sub-bar --------------------------------------------------------------- */
|
|
51
|
+
const RAIL_ID = 'wildo-api-ref-navigation';
|
|
52
|
+
function MenuIcon() {
|
|
53
|
+
return _jsx("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", "aria-hidden": "true", children: _jsx("path", { d: "M4 7h16M4 12h16M4 17h16" }) });
|
|
54
|
+
}
|
|
55
|
+
function CloseIcon() {
|
|
56
|
+
return _jsx("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", "aria-hidden": "true", children: _jsx("path", { d: "M6 6l12 12M18 6L6 18" }) });
|
|
57
|
+
}
|
|
58
|
+
function ChevronIcon() {
|
|
59
|
+
return _jsx("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.4", strokeLinecap: "round", "aria-hidden": "true", children: _jsx("path", { d: "M6 9l6 6 6-6" }) });
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* The page frame: the navigation rail beside the page content and, on narrower screens, a sticky
|
|
63
|
+
* sub-bar whose Menu button opens the rail as a drawer. Without navigation the content fills the
|
|
64
|
+
* width and no rail or sub-bar is drawn.
|
|
65
|
+
*/
|
|
66
|
+
function ReferenceShell({ navigation, subbarTitle, currentResource, activeFragment, children, }) {
|
|
67
|
+
const words = useReferenceWords();
|
|
68
|
+
const [isRailOpen, setIsRailOpen] = useState(false);
|
|
69
|
+
const menuButtonRef = useRef(null);
|
|
70
|
+
const railFilterRef = useRef(null);
|
|
71
|
+
/** Set when the drawer is dismissed (Escape, backdrop, Close), so focus goes back to the Menu button. */
|
|
72
|
+
const returnsFocusToMenu = useRef(false);
|
|
73
|
+
/**
|
|
74
|
+
* Focus moves AFTER the render that removes `inert` from the page content: the Menu button sits inside
|
|
75
|
+
* that content, and a browser refuses to focus anything in an inert subtree. Focusing in the same tick
|
|
76
|
+
* as `setIsRailOpen(false)` left focus on the drawer's filter input as it became hidden.
|
|
77
|
+
*/
|
|
78
|
+
useEffect(() => {
|
|
79
|
+
if (isRailOpen) {
|
|
80
|
+
railFilterRef.current?.focus();
|
|
81
|
+
const onKeyDown = (event) => {
|
|
82
|
+
if (event.key !== 'Escape')
|
|
83
|
+
return;
|
|
84
|
+
returnsFocusToMenu.current = true;
|
|
85
|
+
setIsRailOpen(false);
|
|
86
|
+
};
|
|
87
|
+
document.addEventListener('keydown', onKeyDown);
|
|
88
|
+
return () => document.removeEventListener('keydown', onKeyDown);
|
|
89
|
+
}
|
|
90
|
+
if (returnsFocusToMenu.current) {
|
|
91
|
+
returnsFocusToMenu.current = false;
|
|
92
|
+
menuButtonRef.current?.focus();
|
|
93
|
+
}
|
|
94
|
+
return undefined;
|
|
95
|
+
}, [isRailOpen]);
|
|
96
|
+
const closeRail = () => {
|
|
97
|
+
returnsFocusToMenu.current = true;
|
|
98
|
+
setIsRailOpen(false);
|
|
99
|
+
};
|
|
100
|
+
if (navigation === null) {
|
|
101
|
+
return _jsx("div", { className: "wildo-api-ref", children: _jsx("div", { className: "wildo-api-ref__shell", children: _jsx("div", { className: "wildo-api-ref__main", children: children }) }) });
|
|
102
|
+
}
|
|
103
|
+
return _jsx("div", { className: `wildo-api-ref${isRailOpen ? ' wildo-api-ref--rail-open' : ''}`, children: _jsxs("div", { className: "wildo-api-ref__shell wildo-api-ref__shell--with-rail", children: [_jsx("button", { type: "button", className: "wildo-api-ref__backdrop", "aria-label": words.closeNavigation, tabIndex: -1, onClick: closeRail }), _jsx(NavigationRail, { navigation: navigation, currentResource: currentResource, activeFragment: activeFragment, filterRef: railFilterRef, onClose: closeRail, onNavigate: () => setIsRailOpen(false) }), _jsxs("div", { className: "wildo-api-ref__main", inert: isRailOpen || undefined, children: [_jsxs("div", { className: "wildo-api-ref__subbar", children: [_jsx("button", { ref: menuButtonRef, type: "button", className: "wildo-api-ref__icon-button", "aria-label": words.openNavigation, "aria-controls": RAIL_ID, "aria-expanded": isRailOpen, onClick: () => setIsRailOpen(true), children: _jsx(MenuIcon, {}) }), _jsx("span", { className: "wildo-api-ref__subbar-title", children: subbarTitle }), currentResource !== null && currentResource.families.length > 0
|
|
104
|
+
? _jsx(OperationSwitcher, { resource: currentResource, activeFragment: activeFragment })
|
|
105
|
+
: null] }), children] })] }) });
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* The left rail: the other sections of the portal, a resource filter, the overview, and every
|
|
109
|
+
* resource by category. The resource being read lists its operation families beneath it.
|
|
110
|
+
*/
|
|
111
|
+
function NavigationRail({ navigation, currentResource, activeFragment, filterRef, onClose, onNavigate, }) {
|
|
112
|
+
const words = useReferenceWords();
|
|
12
113
|
const [query, setQuery] = useState('');
|
|
13
|
-
const
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
114
|
+
const normalizedQuery = query.trim().toLocaleLowerCase();
|
|
115
|
+
const categories = useMemo(() => navigation.categories
|
|
116
|
+
.map((category) => ({
|
|
117
|
+
...category,
|
|
118
|
+
resources: normalizedQuery.length === 0
|
|
119
|
+
? category.resources
|
|
120
|
+
: category.resources.filter((resource) => resource.label.toLocaleLowerCase().includes(normalizedQuery)
|
|
121
|
+
|| (category.label ?? '').toLocaleLowerCase().includes(normalizedQuery)),
|
|
122
|
+
}))
|
|
123
|
+
.filter((category) => category.resources.length > 0), [navigation, normalizedQuery]);
|
|
124
|
+
const matchCount = categories.reduce((count, category) => count + category.resources.length, 0);
|
|
125
|
+
return _jsxs("aside", { id: RAIL_ID, className: "wildo-api-ref__rail", "aria-label": words.apiNavigation, onClick: (event) => {
|
|
126
|
+
if (event.target.closest('a') !== null)
|
|
127
|
+
onNavigate();
|
|
128
|
+
}, children: [_jsxs("div", { className: "wildo-api-ref__rail-head", children: [_jsx("strong", { children: navigation.title }), _jsx("button", { type: "button", className: "wildo-api-ref__icon-button", "aria-label": words.closeNavigation, onClick: onClose, children: _jsx(CloseIcon, {}) })] }), navigation.sections.length > 1 ? _jsx("nav", { "aria-label": words.apiSections, className: "wildo-api-ref__sections", children: navigation.sections.map((section) => _jsx("a", { href: section.href, "aria-current": section.href === navigation.sectionRootPath ? 'page' : undefined, children: section.label }, section.href)) }) : null, _jsxs("div", { children: [_jsx("label", { className: "wildo-api-ref__label", htmlFor: `${RAIL_ID}-filter`, children: words.filterResources }), _jsx("input", { ref: filterRef, id: `${RAIL_ID}-filter`, className: "wildo-api-ref__input", type: "search", value: query, placeholder: words.resourceCount(navigation.resourceCount), onChange: (event) => setQuery(event.target.value) })] }), _jsxs("nav", { "aria-label": words.resourcesNavigation, children: [_jsx("ul", { className: "wildo-api-ref__rail-list", children: _jsx("li", { children: _jsx("a", { className: "wildo-api-ref__rail-link", href: navigation.sectionRootPath, "aria-current": currentResource === null ? 'page' : undefined, children: words.overview }) }) }), _jsxs("div", { className: "wildo-api-ref__rail-categories", children: [categories.length === 0 ? _jsx("p", { className: "wildo-api-ref__rail-empty", role: "status", children: words.noResourceMatches(query) }) : null, categories.map((category) => {
|
|
129
|
+
const holdsCurrent = currentResource !== null && category.resources.some((resource) => resource.resourceRef === currentResource.resourceRef);
|
|
130
|
+
return _jsxs("details", { className: "wildo-api-ref__rail-category", open: normalizedQuery.length > 0 || holdsCurrent, children: [_jsxs("summary", { children: [_jsx("span", { children: category.label ?? words.otherResources }), _jsx("span", { className: "wildo-api-ref__rail-count", children: category.resources.length })] }), _jsx("ul", { className: "wildo-api-ref__rail-list", children: category.resources.map((resource) => {
|
|
131
|
+
const isCurrent = resource.resourceRef === currentResource?.resourceRef;
|
|
132
|
+
return _jsxs("li", { children: [_jsxs("a", { className: "wildo-api-ref__rail-link", href: resource.href, "aria-current": isCurrent ? 'page' : undefined, children: [_jsx("span", { children: resource.label }), _jsx("span", { className: "wildo-api-ref__rail-count", "aria-hidden": "true", title: words.operationCount(resource.operationCount), children: resource.operationCount })] }), isCurrent ? _jsx(FamilyLinks, { resource: currentResource, activeFragment: activeFragment, className: "wildo-api-ref__rail-families" }) : null] }, resource.resourceRef);
|
|
133
|
+
}) })] }, `${category.id}:${normalizedQuery.length > 0 ? 'filtered' : 'all'}`);
|
|
134
|
+
})] }), _jsx("p", { className: "wildo-api-ref__visually-hidden", role: "status", children: normalizedQuery.length > 0 ? words.matchingResources(matchCount) : '' })] })] });
|
|
135
|
+
}
|
|
136
|
+
/** The method a family is known by: its first canonical variant's. */
|
|
137
|
+
function familyMethod(family) {
|
|
138
|
+
return (family.variants.find((variant) => variant.aliasOfPath === null) ?? family.variants[0])?.method ?? '';
|
|
139
|
+
}
|
|
140
|
+
/** The family that holds the fragment the reader is on, if any. */
|
|
141
|
+
function familyForFragment(resource, fragment) {
|
|
142
|
+
if (fragment.length === 0)
|
|
143
|
+
return null;
|
|
144
|
+
return resource.families.find((family) => family.anchorId === fragment || family.variants.some((variant) => variantOwnsFragment(variant, fragment))) ?? null;
|
|
145
|
+
}
|
|
146
|
+
/** Links to each operation family of `resource`, with its method, marking the one being read. */
|
|
147
|
+
function FamilyLinks({ resource, activeFragment, className, onSelect, }) {
|
|
148
|
+
const current = familyForFragment(resource, activeFragment);
|
|
149
|
+
return _jsx("ul", { className: className, children: resource.families.map((family) => _jsx("li", { children: _jsxs("a", { className: "wildo-api-ref__rail-link", href: `#${family.anchorId}`, "aria-current": family === current ? 'location' : undefined, onClick: onSelect, children: [_jsx(MethodBadge, { method: familyMethod(family), small: true }), _jsx("span", { children: family.label })] }) }, family.familyRef)) });
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* The sub-bar's operation switcher: a button naming the operation being read, opening a list of the
|
|
153
|
+
* resource's operation families. It closes on a choice, on Escape and on a click outside it.
|
|
154
|
+
*/
|
|
155
|
+
function OperationSwitcher({ resource, activeFragment }) {
|
|
156
|
+
const words = useReferenceWords();
|
|
157
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
158
|
+
const [query, setQuery] = useState('');
|
|
159
|
+
const containerRef = useRef(null);
|
|
160
|
+
const buttonRef = useRef(null);
|
|
161
|
+
const current = familyForFragment(resource, activeFragment);
|
|
162
|
+
const panelId = `${resource.anchorId}--operations`;
|
|
17
163
|
useEffect(() => {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
164
|
+
if (!isOpen)
|
|
165
|
+
return;
|
|
166
|
+
const onPointerDown = (event) => {
|
|
167
|
+
if (containerRef.current !== null && !containerRef.current.contains(event.target))
|
|
168
|
+
setIsOpen(false);
|
|
22
169
|
};
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
170
|
+
const onKeyDown = (event) => {
|
|
171
|
+
if (event.key !== 'Escape')
|
|
172
|
+
return;
|
|
173
|
+
setIsOpen(false);
|
|
174
|
+
buttonRef.current?.focus();
|
|
175
|
+
};
|
|
176
|
+
document.addEventListener('pointerdown', onPointerDown);
|
|
177
|
+
document.addEventListener('keydown', onKeyDown);
|
|
178
|
+
return () => {
|
|
179
|
+
document.removeEventListener('pointerdown', onPointerDown);
|
|
180
|
+
document.removeEventListener('keydown', onKeyDown);
|
|
181
|
+
};
|
|
182
|
+
}, [isOpen]);
|
|
183
|
+
const normalizedQuery = query.trim().toLocaleLowerCase();
|
|
184
|
+
const filtered = normalizedQuery.length === 0
|
|
185
|
+
? resource
|
|
186
|
+
: { ...resource, families: resource.families.filter((family) => family.label.toLocaleLowerCase().includes(normalizedQuery)
|
|
187
|
+
|| family.variants.some((variant) => variant.summary.toLocaleLowerCase().includes(normalizedQuery) || variant.path.toLocaleLowerCase().includes(normalizedQuery))) };
|
|
188
|
+
return _jsxs("div", { ref: containerRef, className: "wildo-api-ref__switcher", children: [_jsxs("button", { ref: buttonRef, type: "button", className: "wildo-api-ref__button", "aria-expanded": isOpen, "aria-controls": panelId, onClick: () => setIsOpen((open) => !open), children: [current !== null ? _jsxs(_Fragment, { children: [_jsx(MethodBadge, { method: familyMethod(current), small: true }), current.label] }) : _jsx(_Fragment, { children: words.operationsSwitcher(resource.families.length) }), _jsx(ChevronIcon, {})] }), isOpen ? _jsxs("div", { id: panelId, className: "wildo-api-ref__switcher-panel", role: "group", "aria-label": words.resourceOperations(resource.label), children: [resource.families.length > 8 ? _jsxs("div", { className: "wildo-api-ref__switcher-filter", children: [_jsx("label", { className: "wildo-api-ref__label", htmlFor: `${panelId}-filter`, children: words.filterOperations(resource.families.length) }), _jsx("input", { id: `${panelId}-filter`, className: "wildo-api-ref__input", type: "search", value: query, onChange: (event) => setQuery(event.target.value), autoFocus: true })] }) : null, _jsx(FamilyLinks, { resource: filtered, activeFragment: activeFragment, className: "wildo-api-ref__switcher-list", onSelect: () => setIsOpen(false) })] }) : null] });
|
|
189
|
+
}
|
|
190
|
+
/* Index ---------------------------------------------------------------------------------------- */
|
|
191
|
+
/**
|
|
192
|
+
* The section's overview, connection details, search and category grid. `resourceHref` decides where a
|
|
193
|
+
* resource card leads: an anchor on the single-page reference, or the resource's own page.
|
|
194
|
+
*/
|
|
195
|
+
function ResourceIndex({ model, query, onQueryChange, resourceHref, isOpen, collapsible, }) {
|
|
196
|
+
const words = useReferenceWords();
|
|
197
|
+
const resources = useMemo(() => filterResources(model, query), [model, query]);
|
|
198
|
+
const grid = _jsx("nav", { "aria-label": words.apiResources, children: resourceCategoryGroups(resources).map((group) => _jsxs("section", { className: "wildo-api-ref__category", children: [group.category !== null ? _jsxs(_Fragment, { children: [_jsx("h2", { id: group.anchorId, children: group.category.label }), group.category.description !== null ? _jsx("p", { children: _jsx(OpenApiInlineCommonMark, { source: group.category.description }) }) : null] }) : null, _jsx("ul", { className: "wildo-api-ref__resource-grid", children: group.resources.map((resource) => _jsx(ResourceCard, { resource: resource, href: resourceHref(resource) }, resource.resourceRef)) })] }, group.id)) });
|
|
199
|
+
return _jsxs(_Fragment, { children: [model.description !== null ? _jsx(OpenApiDocumentDescription, { description: model.description }) : null, (model.servers.length > 0 || model.securitySchemes.length > 0)
|
|
200
|
+
? _jsx(ApiConnectionDetails, { servers: model.servers, securitySchemes: model.securitySchemes })
|
|
201
|
+
: null, _jsxs("ul", { className: "wildo-api-ref__stats", "aria-label": words.referenceSize, children: [_jsx("li", { children: words.resourceCount(model.resources.length) }), _jsx("li", { children: words.operationCount(model.operationCount) })] }), _jsx("p", { className: "wildo-api-ref__visually-hidden", children: words.referenceSizeSummary(model.operationCount, model.resources.length) }), _jsxs("div", { className: "wildo-api-ref__search", children: [_jsxs("div", { children: [_jsx("label", { className: "wildo-api-ref__label", htmlFor: "api-reference-search", children: words.searchReference }), _jsx("input", { id: "api-reference-search", className: "wildo-api-ref__input", type: "search", "aria-describedby": "api-reference-search-status", value: query, onChange: (event) => onQueryChange(event.target.value), placeholder: words.searchPlaceholder })] }), query.length > 0 ? _jsx("button", { className: "wildo-api-ref__button", type: "button", onClick: () => onQueryChange(''), children: words.clearSearch }) : null] }), _jsx("p", { id: "api-reference-search-status", className: "wildo-api-ref__search-status", role: "status", "aria-live": "polite", children: query.length > 0 ? words.matchingResources(resources.length) : '' }), collapsible
|
|
202
|
+
? _jsxs("details", { open: isOpen, className: "wildo-api-ref__disclosure", children: [_jsx("summary", { children: words.browseResources(resources.length) }), _jsx("div", { children: grid })] })
|
|
203
|
+
: grid, resources.length === 0 ? _jsx("p", { role: "status", children: words.noSearchMatches }) : null] });
|
|
204
|
+
}
|
|
205
|
+
/** The fragment the reader arrived on, tracked across in-page navigation, with the jump repeated once mounted. */
|
|
206
|
+
function useActiveFragment(deps) {
|
|
207
|
+
const [activeFragment, setActiveFragment] = useState(() => currentFragment());
|
|
208
|
+
useEffect(() => {
|
|
209
|
+
const synchronize = () => setActiveFragment(currentFragment());
|
|
210
|
+
window.addEventListener('hashchange', synchronize);
|
|
211
|
+
return () => window.removeEventListener('hashchange', synchronize);
|
|
212
|
+
}, []);
|
|
32
213
|
useEffect(() => {
|
|
33
214
|
const target = activeFragment.length > 0 ? document.getElementById(activeFragment) : null;
|
|
34
215
|
if (target !== null && typeof target.scrollIntoView === 'function')
|
|
35
216
|
target.scrollIntoView({ block: 'start' });
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
217
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
218
|
+
}, [activeFragment, ...deps]);
|
|
219
|
+
return activeFragment;
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* The section root when each resource has its own page (#1623): overview, search and the category
|
|
223
|
+
* grid, with each card leading to `<sectionRootPath>/<slug>`.
|
|
224
|
+
*
|
|
225
|
+
* A link written for the single-page reference (`/api#operation-create-task`) still arrives here; the
|
|
226
|
+
* page plan's fragment map says which page now carries that fragment, and the reader is sent there.
|
|
227
|
+
*/
|
|
228
|
+
export function OpenApiReferenceIndexView({ model, words, sectionRootPath, pageSlugByFragment, navigation = null, title = null, navigate = (href) => window.location.replace(href), }) {
|
|
229
|
+
const [query, setQuery] = useState('');
|
|
230
|
+
useEffect(() => {
|
|
231
|
+
const fragment = currentFragment();
|
|
232
|
+
if (fragment.length > 0 && apiReferencePageSlugForFragment(fragment, pageSlugByFragment) !== undefined) {
|
|
233
|
+
navigate(apiReferencePageHref(sectionRootPath, fragment, { pageSlugByFragment }));
|
|
234
|
+
}
|
|
235
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
236
|
+
}, [pageSlugByFragment, sectionRootPath]);
|
|
237
|
+
return _jsx(ReferenceProviders, { model: model, words: words, children: _jsx(ReferenceShell, { navigation: navigation, subbarTitle: title ?? navigation?.title ?? model.title, currentResource: null, activeFragment: "", children: _jsxs("div", { className: "wildo-api-ref__page", children: [title !== null ? _jsxs("header", { children: [_jsx("p", { className: "wildo-api-ref__eyebrow", children: words.contractEyebrow }), _jsx("h1", { className: "wildo-api-ref__title", children: title }), _jsx("p", { className: "wildo-api-ref__lead", children: words.indexLead })] }) : null, _jsx(ResourceIndex, { model: model, query: query, onQueryChange: setQuery, resourceHref: (resource) => `${sectionRootPath}/${apiReferenceResourceSlug(resource)}`, isOpen: true, collapsible: false })] }) }) });
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* One resource's own page (#1623): the resource, its object, its operation families and the reusable
|
|
241
|
+
* schemas its operations reach — everything the page's document slice carries, and nothing else.
|
|
242
|
+
*/
|
|
243
|
+
export function OpenApiResourcePageView({ model, words, resourceName, sectionRootPath, navigation = null, }) {
|
|
244
|
+
const resource = model.resources.find((candidate) => candidate.resourceRef === `technical-documentation:resource/${resourceName}`) ?? null;
|
|
245
|
+
const activeFragment = useActiveFragment([resource]);
|
|
246
|
+
return _jsx(ReferenceProviders, { model: model, words: words, children: _jsxs(ReferenceShell, { navigation: navigation, subbarTitle: resource?.label ?? resourceName, currentResource: resource, activeFragment: activeFragment, children: [resource === null
|
|
247
|
+
? _jsxs("div", { className: "wildo-api-ref__page", children: [_jsx("p", { children: _jsx("a", { className: "wildo-api-ref__button", href: sectionRootPath, children: words.allResources }) }), _jsx("p", { role: "alert", children: words.resourceNotInDocument(resourceName) })] })
|
|
248
|
+
: _jsx(ResourceReference, { resource: resource, activeFragment: activeFragment, level: 1, breadcrumb: _jsx("nav", { "aria-label": words.breadcrumb, className: "wildo-api-ref__breadcrumb", children: _jsxs("ol", { children: [_jsx("li", { children: _jsx("a", { href: sectionRootPath, children: navigation?.title ?? words.allResources }) }), resource.category !== null ? _jsx("li", { children: resource.category.label }) : null] }) }) }), _jsx(ComponentSchemaReference, { schemas: model.componentSchemas, activeFragment: activeFragment })] }) });
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* The whole reference on one page, a resource selected by the URL fragment. Used where no per-resource
|
|
252
|
+
* routes exist; a portal with the page plan renders `OpenApiReferenceIndexView` and
|
|
253
|
+
* `OpenApiResourcePageView` instead.
|
|
254
|
+
*/
|
|
255
|
+
export function OpenApiReferenceView({ model, words }) {
|
|
256
|
+
const [query, setQuery] = useState('');
|
|
257
|
+
const [selectedResourceRef, setSelectedResourceRef] = useState(null);
|
|
258
|
+
const selectedResource = model.resources.find((resource) => resource.resourceRef === selectedResourceRef) ?? null;
|
|
259
|
+
const activeFragment = useActiveFragment([selectedResource]);
|
|
260
|
+
useEffect(() => {
|
|
261
|
+
// Following a data-model link keeps the operation the reader came from: a schema belongs to
|
|
262
|
+
// no resource, and deselecting on it used to throw the reader back to the category grid.
|
|
263
|
+
const isComponentSchemaTarget = model.componentSchemas.some((component) => component.anchorId === activeFragment);
|
|
264
|
+
const owner = findResourceForFragment(model.resources, activeFragment);
|
|
265
|
+
setSelectedResourceRef((previous) => owner?.resourceRef ?? (isComponentSchemaTarget ? previous : null));
|
|
266
|
+
}, [model, activeFragment]);
|
|
267
|
+
return _jsx(ReferenceProviders, { model: model, words: words, children: _jsxs(ReferenceShell, { navigation: null, subbarTitle: model.title, currentResource: selectedResource, activeFragment: activeFragment, children: [_jsxs("div", { className: "wildo-api-ref__page", children: [_jsx(ResourceIndex, { model: model, query: query, onQueryChange: (value) => {
|
|
268
|
+
setQuery(value);
|
|
269
|
+
clearResourceSelection();
|
|
270
|
+
}, resourceHref: (resource) => `#${resource.anchorId}`, isOpen: selectedResource === null, collapsible: true }), selectedResource === null && filterResources(model, query).length > 0 ? _jsx("p", { role: "status", children: words.selectResourcePrompt }) : null, selectedResource !== null ? _jsx("p", { children: _jsx("button", { className: "wildo-api-ref__button", type: "button", onClick: clearResourceSelection, children: words.allResources }) }) : null] }), selectedResource !== null ? _jsx("section", { children: _jsx(ResourceReference, { resource: selectedResource, activeFragment: activeFragment, level: 2, breadcrumb: null }) }, selectedResource.resourceRef) : null, _jsx(ComponentSchemaReference, { schemas: model.componentSchemas, activeFragment: activeFragment })] }) });
|
|
43
271
|
}
|
|
44
272
|
function OpenApiDocumentDescription({ description }) {
|
|
273
|
+
const words = useReferenceWords();
|
|
274
|
+
// The generator's own section marker in the document text, not a word shown as chrome.
|
|
45
275
|
const conventionsMarker = '\n\n## API conventions';
|
|
46
276
|
const markerIndex = description.indexOf(conventionsMarker);
|
|
47
277
|
const overview = markerIndex === -1 ? description : description.slice(0, markerIndex);
|
|
48
278
|
const conventions = markerIndex === -1 ? null : description.slice(markerIndex + conventionsMarker.length).trim();
|
|
49
|
-
return _jsxs("section", { className: "
|
|
279
|
+
return _jsxs("section", { className: "wildo-api-ref__lead", "aria-label": words.apiOverview, children: [_jsx(OpenApiCommonMark, { source: overview }), conventions !== null && conventions.length > 0 ? _jsxs("details", { className: "wildo-api-ref__disclosure", children: [_jsx("summary", { children: _jsx("h2", { children: words.apiConventions }) }), _jsx("div", { children: _jsx(OpenApiCommonMark, { source: conventions }) })] }) : null] });
|
|
50
280
|
}
|
|
51
281
|
/**
|
|
52
282
|
* Safe, deliberately small CommonMark presenter for the generator-owned
|
|
@@ -104,11 +334,13 @@ function OpenApiInlineCommonMark({ source }) {
|
|
|
104
334
|
}) });
|
|
105
335
|
}
|
|
106
336
|
function ApiConnectionDetails({ servers, securitySchemes, }) {
|
|
107
|
-
|
|
337
|
+
const words = useReferenceWords();
|
|
338
|
+
return _jsxs("section", { className: "wildo-api-ref__panel", "aria-labelledby": "api-connection-details", children: [_jsx("h2", { id: "api-connection-details", children: words.connectionDetails }), servers.length > 0 ? _jsxs(_Fragment, { children: [_jsx("h3", { children: words.baseUrls }), _jsx("ul", { children: servers.map((server) => _jsxs("li", { children: [_jsx("code", { children: server.url }), server.description !== null ? _jsxs(_Fragment, { children: [" \u2014 ", server.description] }) : null] }, server.url)) })] }) : null, securitySchemes.length > 0 ? _jsxs(_Fragment, { children: [_jsx("h3", { children: words.authenticationMechanisms }), _jsx("dl", { children: securitySchemes.map((securityScheme) => _jsxs(Fragment, { children: [_jsx("dt", { children: _jsx("code", { children: securityScheme.name }) }), _jsxs("dd", { children: [_jsxs("p", { className: "margin-bottom--sm", children: [words.schemeType, " ", _jsx("code", { children: securityScheme.type }), securityScheme.scheme !== null ? _jsxs(_Fragment, { children: [" \u00B7 ", words.schemeScheme, " ", _jsx("code", { children: securityScheme.scheme })] }) : null, securityScheme.bearerFormat !== null ? _jsxs(_Fragment, { children: [" \u00B7 ", words.schemeFormat, " ", _jsx("code", { children: securityScheme.bearerFormat })] }) : null, securityScheme.location !== null ? _jsxs(_Fragment, { children: [" \u00B7 ", words.schemeLocation, " ", _jsx("code", { children: securityScheme.location })] }) : null, securityScheme.parameterName !== null ? _jsxs(_Fragment, { children: [" \u00B7 ", words.schemeName, " ", _jsx("code", { children: securityScheme.parameterName })] }) : null] }), securityScheme.description !== null ? _jsx("p", { children: _jsx(OpenApiInlineCommonMark, { source: securityScheme.description }) }) : null] })] }, securityScheme.name)) })] }) : null] });
|
|
108
339
|
}
|
|
109
|
-
function ResourceCard({ resource }) {
|
|
340
|
+
function ResourceCard({ resource, href }) {
|
|
341
|
+
const words = useReferenceWords();
|
|
110
342
|
const variantCount = resource.families.reduce((count, family) => count + family.variants.length, 0);
|
|
111
|
-
return _jsxs("li", {
|
|
343
|
+
return _jsxs("li", { className: "wildo-api-ref__resource-card", children: [_jsx("a", { href: href, children: resource.label }), resource.description !== null ? _jsx("p", { children: _jsx(OpenApiInlineCommonMark, { source: resource.description }) }) : null, _jsx("p", { className: "wildo-api-ref__card-meta", children: words.resourceCardMeta(resource.families.length, variantCount) })] });
|
|
112
344
|
}
|
|
113
345
|
/**
|
|
114
346
|
* Groups only by source-projected category metadata. A document without
|
|
@@ -133,14 +365,49 @@ function resourceCategoryGroups(resources) {
|
|
|
133
365
|
category: group.category,
|
|
134
366
|
resources: group.resources,
|
|
135
367
|
}))
|
|
136
|
-
|
|
368
|
+
// Uncategorized last, the order the navigation rail uses (`buildApiReferenceNavigation`).
|
|
369
|
+
.sort((left, right) => (left.category === null ? 1 : right.category === null ? -1 : left.category.label.localeCompare(right.category.label)));
|
|
137
370
|
}
|
|
138
|
-
|
|
139
|
-
|
|
371
|
+
/* Resource ------------------------------------------------------------------------------------- */
|
|
372
|
+
/**
|
|
373
|
+
* A resource: its introduction (description, lifecycle, relationships, object) above, then one
|
|
374
|
+
* section per operation family, each with its documentation beside its samples.
|
|
375
|
+
*
|
|
376
|
+
* `level` is the resource heading's level: 1 on the resource's own page, 2 on the single-page
|
|
377
|
+
* reference. Families, operations and their parts take the next levels down.
|
|
378
|
+
*/
|
|
379
|
+
function ResourceReference({ resource, activeFragment, level, breadcrumb, }) {
|
|
380
|
+
const words = useReferenceWords();
|
|
381
|
+
return _jsxs(_Fragment, { children: [_jsxs("header", { className: "wildo-api-ref__intro", children: [breadcrumb, _jsx(Heading, { level: level, id: resource.anchorId, className: "wildo-api-ref__title", children: resource.label }), resource.description !== null ? _jsx("div", { className: "wildo-api-ref__lead", children: _jsx(OpenApiCommonMark, { source: resource.description }) }) : null, resource.lifecycleRole !== null ? _jsxs(_Fragment, { children: [_jsx(Heading, { level: level + 1, className: "wildo-api-ref__subheading", children: words.lifecycle }), _jsx("div", { className: "wildo-api-ref__lead", children: _jsx(OpenApiCommonMark, { source: resource.lifecycleRole }) })] }) : null, resource.relationships.length > 0 ? _jsx(RelationshipSemantics, { relationships: resource.relationships }) : null, resource.objectSchemaName !== null ? _jsx(ResourceObject, { resource: resource, objectSchemaName: resource.objectSchemaName, level: level + 1 }) : null] }), resource.families.length > 0 ? _jsx("div", { className: "wildo-api-ref__operations", children: resource.families.map((family) => _jsx(OperationFamily, { family: family, activeFragment: activeFragment, level: level + 1 }, family.familyRef)) }) : null] });
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* "The Tasks object": the resource's representation, described once on its page (#1621). The
|
|
385
|
+
* operations that return it reference the same schema, so their response sections and this one are
|
|
386
|
+
* the same fields by construction.
|
|
387
|
+
*/
|
|
388
|
+
function ResourceObject({ resource, objectSchemaName, level }) {
|
|
389
|
+
const words = useReferenceWords();
|
|
390
|
+
const resolution = useContext(SchemaResolutionContext);
|
|
391
|
+
const schema = resolution.componentsByName.get(objectSchemaName);
|
|
392
|
+
if (schema === undefined)
|
|
393
|
+
return null;
|
|
394
|
+
return _jsxs("section", { "aria-labelledby": `${resource.anchorId}--object`, children: [_jsx(Heading, { level: level, id: `${resource.anchorId}--object`, className: "wildo-api-ref__subheading", children: words.resourceObject(resource.label) }), _jsx(SchemaResolutionContext.Provider, { value: { ...resolution, expanding: new Set([...resolution.expanding, objectSchemaName]) }, children: _jsx(SchemaContract, { schema: schema, collapseLongLists: true }) })] });
|
|
140
395
|
}
|
|
141
396
|
function RelationshipSemantics({ relationships }) {
|
|
142
|
-
|
|
143
|
-
|
|
397
|
+
const words = useReferenceWords();
|
|
398
|
+
return _jsxs("details", { className: "wildo-api-ref__disclosure", children: [_jsx("summary", { children: words.dataRelationships(relationships.length) }), _jsx("ul", { children: relationships.map((relationship, index) => _jsxs("li", { children: [relationship.foreignKeyField !== null
|
|
399
|
+
? renderSentence(words.relationshipVia, { related: _jsx("strong", { children: relationship.relatedResourceLabel }), field: _jsx("code", { children: relationship.foreignKeyField }) })
|
|
400
|
+
: _jsx("strong", { children: relationship.relatedResourceLabel }), ": ", words.relationshipSummary({
|
|
401
|
+
nature: relationship.nature,
|
|
402
|
+
cardinality: relationship.resourceCardinality,
|
|
403
|
+
relatedCardinality: relationship.relatedResourceCardinality,
|
|
404
|
+
lifecycleModel: relationship.lifecycleModel,
|
|
405
|
+
}), relationship.onParentDelete !== null
|
|
406
|
+
? _jsxs(_Fragment, { children: [" ", words.parentDeletePolicy(!relationship.onParentDelete.enabled
|
|
407
|
+
? words.parentDeleteDoesNotCascade
|
|
408
|
+
: relationship.onParentDelete.refusesWhileChildrenExist
|
|
409
|
+
? relationship.onParentDelete.strategy
|
|
410
|
+
: words.parentDeleteCascade({ strategy: relationship.onParentDelete.strategy, mode: relationship.onParentDelete.mode, maxDepth: relationship.onParentDelete.maxDepth }))] })
|
|
144
411
|
: null] }, `${relationship.relatedResourceName}:${relationship.foreignKeyField ?? 'none'}:${relationship.nature}:${index}`)) })] });
|
|
145
412
|
}
|
|
146
413
|
function clearResourceSelection() {
|
|
@@ -152,9 +419,7 @@ function findResourceForFragment(resources, hash) {
|
|
|
152
419
|
if (fragment.length === 0)
|
|
153
420
|
return null;
|
|
154
421
|
return resources.find((resource) => resource.anchorId === fragment
|
|
155
|
-
|| resource.families.some((family) => family.anchorId === fragment || family.variants.some((variant) => variant
|
|
156
|
-
|| variant.requestBody?.anchorId === fragment
|
|
157
|
-
|| variant.responses.some((response) => response.anchorId === fragment)))) ?? null;
|
|
422
|
+
|| resource.families.some((family) => family.anchorId === fragment || family.variants.some((variant) => variantOwnsFragment(variant, fragment)))) ?? null;
|
|
158
423
|
}
|
|
159
424
|
function filterResources(model, query) {
|
|
160
425
|
const normalizedQuery = query.trim().toLocaleLowerCase();
|
|
@@ -192,40 +457,411 @@ function filterResources(model, query) {
|
|
|
192
457
|
})).filter((family) => family.variants.length > 0),
|
|
193
458
|
})).filter((resource) => resource.families.length > 0);
|
|
194
459
|
}
|
|
195
|
-
|
|
196
|
-
|
|
460
|
+
/**
|
|
461
|
+
* A route that reaches an operation through its parent is the SAME operation (`aliasOfPath`); showing
|
|
462
|
+
* it as a second full panel made a third of the Wonder Todos reference repeat itself (315 of 960). Each
|
|
463
|
+
* alias is listed under its canonical operation instead. An alias whose canonical is not among these
|
|
464
|
+
* variants — it lives in the other section, or a search matched only the alias — keeps its own panel,
|
|
465
|
+
* so every variant is still presented exactly once.
|
|
466
|
+
*/
|
|
467
|
+
function groupAliasesUnderCanonical(variants) {
|
|
468
|
+
const canonicalKey = (method, path) => `${method} ${path}`;
|
|
469
|
+
const groups = variants
|
|
470
|
+
.filter((variant) => variant.aliasOfPath === null)
|
|
471
|
+
.map((variant) => ({ variant, aliases: [] }));
|
|
472
|
+
const groupByKey = new Map(groups.map((group) => [canonicalKey(group.variant.method, group.variant.path), group]));
|
|
473
|
+
const orphans = [];
|
|
474
|
+
for (const variant of variants) {
|
|
475
|
+
if (variant.aliasOfPath === null)
|
|
476
|
+
continue;
|
|
477
|
+
const group = groupByKey.get(canonicalKey(variant.method, variant.aliasOfPath));
|
|
478
|
+
if (group === undefined)
|
|
479
|
+
orphans.push({ variant, aliases: [] });
|
|
480
|
+
else
|
|
481
|
+
group.aliases.push(variant);
|
|
482
|
+
}
|
|
483
|
+
return [...groups, ...orphans];
|
|
484
|
+
}
|
|
485
|
+
/** The path parameters an alias route takes that its canonical route does not — what makes it a different address. */
|
|
486
|
+
function additionalPathParameters(alias, canonical) {
|
|
487
|
+
const canonicalNames = new Set(canonical.parameters.filter((parameter) => parameter.location === 'path').map((parameter) => parameter.name));
|
|
488
|
+
return alias.parameters.filter((parameter) => parameter.location === 'path' && !canonicalNames.has(parameter.name)).map((parameter) => parameter.name);
|
|
489
|
+
}
|
|
490
|
+
/**
|
|
491
|
+
* Moves the selection with the arrow, Home and End keys inside a tab list, as the WAI-ARIA tabs pattern
|
|
492
|
+
* asks: only the selected tab is in the Tab order, the arrows move between tabs.
|
|
493
|
+
*/
|
|
494
|
+
function onTabListKeyDown(event) {
|
|
495
|
+
const tabs = [...event.currentTarget.querySelectorAll('[role="tab"]')];
|
|
496
|
+
const current = tabs.indexOf(event.target);
|
|
497
|
+
if (current === -1)
|
|
498
|
+
return;
|
|
499
|
+
const next = event.key === 'ArrowRight' || event.key === 'ArrowDown' ? (current + 1) % tabs.length
|
|
500
|
+
: event.key === 'ArrowLeft' || event.key === 'ArrowUp' ? (current - 1 + tabs.length) % tabs.length
|
|
501
|
+
: event.key === 'Home' ? 0
|
|
502
|
+
: event.key === 'End' ? tabs.length - 1
|
|
503
|
+
: null;
|
|
504
|
+
if (next === null)
|
|
505
|
+
return;
|
|
506
|
+
event.preventDefault();
|
|
507
|
+
tabs[next]?.focus();
|
|
508
|
+
tabs[next]?.click();
|
|
509
|
+
}
|
|
510
|
+
/**
|
|
511
|
+
* One operation family. A family with several routes (a variant per scope, say) shows them as tabs;
|
|
512
|
+
* a deep link to any part of a variant — its operation, request, a response or an alias — selects
|
|
513
|
+
* the tab that holds it, so the target exists before the page scrolls to it.
|
|
514
|
+
*/
|
|
515
|
+
function OperationFamily({ family, activeFragment, level }) {
|
|
516
|
+
const words = useReferenceWords();
|
|
517
|
+
const groups = useMemo(() => groupAliasesUnderCanonical(family.variants), [family]);
|
|
518
|
+
const owningIndex = (fragment) => groups.findIndex((group) => variantOwnsFragment(group.variant, fragment)
|
|
519
|
+
|| group.aliases.some((alias) => alias.anchorId === fragment));
|
|
520
|
+
const [selectedIndex, setSelectedIndex] = useState(() => Math.max(owningIndex(activeFragment), 0));
|
|
197
521
|
useEffect(() => {
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
522
|
+
const index = owningIndex(activeFragment);
|
|
523
|
+
if (index !== -1)
|
|
524
|
+
setSelectedIndex(index);
|
|
525
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
526
|
+
}, [activeFragment, groups]);
|
|
527
|
+
const selected = groups[selectedIndex] ?? groups[0];
|
|
528
|
+
if (selected === undefined)
|
|
529
|
+
return null;
|
|
530
|
+
const tabId = (index) => `${family.anchorId}--variant-${index}`;
|
|
531
|
+
const header = _jsxs(_Fragment, { children: [_jsx(Heading, { level: level, id: family.anchorId, className: "wildo-api-ref__family-title", children: family.label }), groups.length > 1 ? _jsx("div", { role: "tablist", "aria-label": words.familyVariants(family.label), className: "wildo-api-ref__segmented", onKeyDown: onTabListKeyDown, children: groups.map((group, index) => _jsxs("button", { type: "button", role: "tab", id: tabId(index), className: "wildo-api-ref__segment", "aria-selected": index === selectedIndex, "aria-controls": `${family.anchorId}--variant-panel`, tabIndex: index === selectedIndex ? 0 : -1, onClick: () => setSelectedIndex(index), children: [_jsx(MethodBadge, { method: group.variant.method, small: true }), group.variant.summary] }, group.variant.operationId)) }) : null] });
|
|
532
|
+
return _jsx("section", { className: "wildo-api-ref__family", children: _jsx(OperationVariant, { variant: selected.variant, aliases: selected.aliases, activeFragment: activeFragment, level: level + 1, header: header, panelId: groups.length > 1 ? `${family.anchorId}--variant-panel` : null, labelledBy: groups.length > 1 ? tabId(selectedIndex) : null }) });
|
|
202
533
|
}
|
|
203
|
-
/**
|
|
204
|
-
|
|
205
|
-
|
|
534
|
+
/**
|
|
535
|
+
* The class of an HTTP status, which decides the colour of its dot: a success or redirect (2xx, 3xx)
|
|
536
|
+
* is what the reader builds for, a client error (4xx) is theirs to fix, a server error (5xx) is not.
|
|
537
|
+
* The values are the dot's CSS modifier names.
|
|
538
|
+
*/
|
|
539
|
+
var ResponseStatusClass;
|
|
540
|
+
(function (ResponseStatusClass) {
|
|
541
|
+
ResponseStatusClass["SUCCESS"] = "success";
|
|
542
|
+
ResponseStatusClass["CLIENT_ERROR"] = "client-error";
|
|
543
|
+
ResponseStatusClass["SERVER_ERROR"] = "server-error";
|
|
544
|
+
ResponseStatusClass["OTHER"] = "other";
|
|
545
|
+
})(ResponseStatusClass || (ResponseStatusClass = {}));
|
|
546
|
+
function responseStatusClass(status) {
|
|
547
|
+
if (/^[23]/.test(status))
|
|
548
|
+
return ResponseStatusClass.SUCCESS;
|
|
549
|
+
if (/^4/.test(status))
|
|
550
|
+
return ResponseStatusClass.CLIENT_ERROR;
|
|
551
|
+
if (/^5/.test(status))
|
|
552
|
+
return ResponseStatusClass.SERVER_ERROR;
|
|
553
|
+
return ResponseStatusClass.OTHER;
|
|
554
|
+
}
|
|
555
|
+
function StatusDot({ status }) {
|
|
556
|
+
return _jsx("span", { className: `wildo-api-ref__status-dot wildo-api-ref__status-dot--${responseStatusClass(status)}`, "aria-hidden": "true" });
|
|
557
|
+
}
|
|
558
|
+
/**
|
|
559
|
+
* One operation: its documentation (title, endpoint, access, parameters, body, responses, errors) and,
|
|
560
|
+
* beside it, its samples. The response the reader selects is the same on both sides.
|
|
561
|
+
*/
|
|
562
|
+
function OperationVariant({ variant, aliases = [], activeFragment, level, header, panelId, labelledBy, }) {
|
|
563
|
+
const linkedResponse = variant.responses.find((response) => response.anchorId === activeFragment) ?? null;
|
|
564
|
+
/**
|
|
565
|
+
* The response the reader picked, remembered with the variant it was picked on: this component is
|
|
566
|
+
* not remounted when the family's tab changes, so a choice made on one variant must not carry over.
|
|
567
|
+
*/
|
|
568
|
+
const [statusChoice, setStatusChoice] = useState(null);
|
|
569
|
+
const selectedStatus = statusChoice?.operationId === variant.operationId ? statusChoice.status : linkedResponse?.status ?? null;
|
|
570
|
+
const setSelectedStatus = (status) => setStatusChoice({ operationId: variant.operationId, status });
|
|
571
|
+
useEffect(() => {
|
|
572
|
+
if (linkedResponse !== null)
|
|
573
|
+
setStatusChoice({ operationId: variant.operationId, status: linkedResponse.status });
|
|
574
|
+
}, [linkedResponse, variant.operationId]);
|
|
575
|
+
/**
|
|
576
|
+
* A variant behind a tab mounts one render after the view's own fragment jump has already looked for
|
|
577
|
+
* it, so the jump is repeated once the target exists — once per fragment and variant. Picking a
|
|
578
|
+
* response status afterwards must not scroll the reader back to the linked anchor.
|
|
579
|
+
*/
|
|
580
|
+
const scrolledFor = useRef(null);
|
|
581
|
+
useEffect(() => {
|
|
582
|
+
if (!variantOwnsFragment(variant, activeFragment) && !aliases.some((alias) => alias.anchorId === activeFragment))
|
|
583
|
+
return;
|
|
584
|
+
const scrollKey = `${activeFragment}|${variant.operationId}`;
|
|
585
|
+
if (scrolledFor.current === scrollKey)
|
|
586
|
+
return;
|
|
587
|
+
scrolledFor.current = scrollKey;
|
|
588
|
+
const target = document.getElementById(activeFragment);
|
|
589
|
+
if (target !== null && typeof target.scrollIntoView === 'function')
|
|
590
|
+
target.scrollIntoView({ block: 'start' });
|
|
591
|
+
}, [activeFragment, variant, aliases]);
|
|
592
|
+
const words = useReferenceWords();
|
|
593
|
+
const subLevel = level + 1;
|
|
594
|
+
const roleLabels = variant.access.roleRequirements;
|
|
595
|
+
return _jsxs("article", { className: "wildo-api-ref__operation", children: [_jsxs("div", { className: "wildo-api-ref__operation-doc", children: [header, _jsxs("div", { id: panelId ?? undefined, role: panelId !== null ? 'tabpanel' : undefined, "aria-labelledby": labelledBy ?? undefined, children: [_jsx(Heading, { level: level, id: variant.anchorId, className: "wildo-api-ref__operation-title", children: variant.summary }), _jsxs("div", { className: "wildo-api-ref__endpoint", children: [_jsx(MethodBadge, { method: variant.method }), _jsx("code", { className: "wildo-api-ref__path", children: _jsx(EndpointPath, { path: variant.path }) }), _jsx(CopyButton, { text: variant.path, label: words.copyPathOf(variant.summary) })] }), variant.description !== null ? _jsx("div", { className: "wildo-api-ref__lead", children: _jsx(OpenApiCommonMark, { source: variant.description }) }) : null, _jsxs("dl", { className: "wildo-api-ref__facts", children: [_jsxs("div", { className: "wildo-api-ref__fact", children: [_jsx("dt", { children: words.access }), _jsx("dd", { children: variant.access.authenticationSummary })] }), roleLabels.length > 0 ? _jsxs("div", { className: "wildo-api-ref__fact", children: [_jsx("dt", { children: words.requiredRole }), _jsx("dd", { children: renderSentence(words.requiredRoles, {
|
|
596
|
+
roles: renderList(words, roleLabels.map((requirement) => renderSentence(words.roleWithKey, {
|
|
597
|
+
label: _jsx("strong", { children: requirement.label }),
|
|
598
|
+
key: _jsx("code", { children: requirement.role }),
|
|
599
|
+
})), DocumentationListKind.DISJUNCTION),
|
|
600
|
+
}) })] }) : null, variant.securitySchemes.length > 0 ? _jsxs("div", { className: "wildo-api-ref__fact", children: [_jsx("dt", { children: words.authentication }), _jsx("dd", { children: renderList(words, variant.securitySchemes.map((scheme) => _jsx("code", { children: scheme })), DocumentationListKind.DISJUNCTION) })] }) : null, variant.idempotent !== null ? _jsxs("div", { className: "wildo-api-ref__fact", children: [_jsx("dt", { children: words.idempotent }), _jsx("dd", { children: variant.idempotent ? words.yes : words.no })] }) : null] }), roleLabels.some((requirement) => requirement.businessRole !== null || requirement.authorityBoundary !== null) ? _jsxs("details", { className: "wildo-api-ref__inline-disclosure", children: [_jsx("summary", { children: words.roleAuthorityDetails }), _jsx("ul", { children: roleLabels.map((requirement) => _jsxs("li", { children: [_jsx("strong", { children: requirement.label }), requirement.businessRole !== null ? _jsxs(_Fragment, { children: [" \u2014 ", _jsx(OpenApiInlineCommonMark, { source: requirement.businessRole })] }) : null, requirement.authorityBoundary !== null ? _jsxs("p", { children: [words.boundary, " ", _jsx(OpenApiInlineCommonMark, { source: requirement.authorityBoundary })] }) : null] }, requirement.role)) })] }) : null, aliases.length > 0 ? _jsxs("div", { className: "wildo-api-ref__note", children: [_jsx("span", { children: renderSentence(words.aliasNote, { heading: _jsx("strong", { children: words.alsoReachableAt }) }) }), _jsx("ul", { children: aliases.map((alias) => {
|
|
601
|
+
const extra = additionalPathParameters(alias, variant);
|
|
602
|
+
const address = _jsxs(_Fragment, { children: [_jsx("code", { children: alias.method }), " ", _jsx("code", { children: alias.path })] });
|
|
603
|
+
return _jsx("li", { id: alias.anchorId, children: extra.length > 0
|
|
604
|
+
? renderSentence(words.aliasWithParameters, {
|
|
605
|
+
address,
|
|
606
|
+
parameters: renderList(words, extra.map((name) => _jsx("code", { children: name })), DocumentationListKind.CONJUNCTION),
|
|
607
|
+
})
|
|
608
|
+
: address }, alias.operationId);
|
|
609
|
+
}) })] }) : null, _jsx(SamplesSheet, { variant: variant, selectedStatus: selectedStatus, onSelectStatus: setSelectedStatus }), _jsx(ParameterSections, { parameters: variant.parameters, level: subLevel }), variant.requestBody !== null ? _jsxs("section", { children: [_jsxs(Heading, { level: subLevel, id: variant.requestBody.anchorId, className: "wildo-api-ref__subheading", children: [words.requestBody, " ", _jsx("span", { className: "wildo-api-ref__subheading-meta", children: variant.requestBody.required ? words.required : words.optional })] }), _jsx(RepresentationList, { mediaTypes: variant.requestBody.mediaTypes })] }) : null, _jsxs("section", { children: [_jsx(Heading, { level: subLevel, className: "wildo-api-ref__subheading", children: words.responses }), variant.responses.length === 0
|
|
610
|
+
? _jsx("p", { children: words.noResponseContract })
|
|
611
|
+
: _jsx(ResponseTabs, { variant: variant, selectedStatus: selectedStatus, onSelectStatus: setSelectedStatus })] }), variant.errorScenarios.length > 0 ? _jsxs("section", { children: [_jsx(Heading, { level: subLevel, className: "wildo-api-ref__subheading", children: words.errors }), _jsx(ErrorScenarioTable, { scenarios: variant.errorScenarios })] }) : null] }, variant.operationId)] }), _jsx("div", { className: "wildo-api-ref__operation-code", children: _jsx(OperationSamples, { variant: variant, selectedStatus: selectedStatus, onSelectStatus: setSelectedStatus }) }, variant.operationId)] });
|
|
612
|
+
}
|
|
613
|
+
/** A path with its `{parameters}` set apart, so the parts a reader fills in stand out. */
|
|
614
|
+
function EndpointPath({ path }) {
|
|
615
|
+
return _jsx(_Fragment, { children: path.split(/(\{[^}]+\})/g).filter(Boolean).map((part, index) => (part.startsWith('{')
|
|
616
|
+
? _jsx("span", { className: "wildo-api-ref__path-param", children: part }, index)
|
|
617
|
+
: _jsx(Fragment, { children: part }, index))) });
|
|
618
|
+
}
|
|
619
|
+
/** The HTTP method, coloured by verb so a reader can scan a family for its writes. */
|
|
620
|
+
function MethodBadge({ method, small = false }) {
|
|
621
|
+
return _jsx("span", { className: `wildo-api-ref__method wildo-api-ref__method--${method.toLowerCase()}${small ? ' wildo-api-ref__method--small' : ''}`, children: method });
|
|
622
|
+
}
|
|
623
|
+
/** Copies `text` to the clipboard and says so for a moment. A browser without the Clipboard API shows no change. */
|
|
624
|
+
function CopyButton({ text, label }) {
|
|
625
|
+
const words = useReferenceWords();
|
|
626
|
+
const [isCopied, setIsCopied] = useState(false);
|
|
627
|
+
useEffect(() => {
|
|
628
|
+
if (!isCopied)
|
|
629
|
+
return;
|
|
630
|
+
const timer = setTimeout(() => setIsCopied(false), 1500);
|
|
631
|
+
return () => clearTimeout(timer);
|
|
632
|
+
}, [isCopied]);
|
|
633
|
+
return _jsxs(_Fragment, { children: [_jsx("button", { type: "button", className: "wildo-api-ref__copy", "aria-label": label, onClick: () => {
|
|
634
|
+
if (typeof navigator === 'undefined' || navigator.clipboard === undefined)
|
|
635
|
+
return;
|
|
636
|
+
navigator.clipboard.writeText(text).then(() => setIsCopied(true), () => undefined);
|
|
637
|
+
}, children: isCopied ? words.copied : words.copy }), _jsx("span", { role: "status", className: "wildo-api-ref__visually-hidden", children: isCopied ? words.copied : '' })] });
|
|
638
|
+
}
|
|
639
|
+
const SAMPLE_LANGUAGE_LABELS = {
|
|
640
|
+
[ApiReferenceSampleLanguage.CURL]: 'cURL',
|
|
641
|
+
[ApiReferenceSampleLanguage.JAVASCRIPT]: 'JavaScript',
|
|
642
|
+
[ApiReferenceSampleLanguage.PYTHON]: 'Python',
|
|
643
|
+
};
|
|
644
|
+
/**
|
|
645
|
+
* The code panel of an operation: the request in three languages, then one response example per
|
|
646
|
+
* status in tabs, successes first. Both are derived from the same verified document as the
|
|
647
|
+
* documentation beside them (see `openapi-reference-samples.ts`), so the panel can only restate it.
|
|
648
|
+
*/
|
|
649
|
+
function OperationSamples({ variant, selectedStatus, onSelectStatus, idScope = variant.anchorId, }) {
|
|
650
|
+
const words = useReferenceWords();
|
|
651
|
+
const connection = useContext(ApiDocumentConnectionContext);
|
|
652
|
+
const requestSamples = useMemo(() => buildRequestSamples(variant, connection.serverUrl, connection.securitySchemes), [variant, connection]);
|
|
653
|
+
const samples = useMemo(() => responseSamples(variant.responses), [variant]);
|
|
654
|
+
const [language, setLanguage] = useState(ApiReferenceSampleLanguage.CURL);
|
|
655
|
+
const selected = samples.find((sample) => sample.status === selectedStatus) ?? samples[0] ?? null;
|
|
656
|
+
const tabId = (kind, value) => `${idScope}--sample-${kind}-${value}`;
|
|
657
|
+
const serverSelectId = `${idScope}--server`;
|
|
658
|
+
return _jsxs("aside", { className: "wildo-api-ref__samples", "aria-label": words.samplesFor(variant.summary), children: [_jsxs("div", { role: "group", className: "wildo-api-ref__code-card", "aria-label": words.requestSample, children: [_jsxs("div", { className: "wildo-api-ref__code-head", children: [_jsx("span", { className: "wildo-api-ref__code-title", children: words.request }), _jsx("div", { role: "tablist", "aria-label": words.language, className: "wildo-api-ref__code-tabs", onKeyDown: onTabListKeyDown, children: Object.values(ApiReferenceSampleLanguage).map((candidate) => _jsx("button", { type: "button", role: "tab", id: tabId('language', candidate), className: "wildo-api-ref__code-tab", "aria-selected": candidate === language, "aria-controls": `${tabId('language', 'panel')}`, tabIndex: candidate === language ? 0 : -1, onClick: () => setLanguage(candidate), children: SAMPLE_LANGUAGE_LABELS[candidate] }, candidate)) }), _jsx(CopyButton, { text: requestSamples[language], label: words.copyRequestSample })] }), connection.servers.length > 1 ? _jsxs("div", { className: "wildo-api-ref__code-server", children: [_jsx("label", { htmlFor: serverSelectId, children: words.server }), _jsx("select", { id: serverSelectId, value: connection.serverUrl ?? '', onChange: (event) => connection.selectServer(event.target.value), children: connection.servers.map((server) => _jsx("option", { value: server.url, children: server.description ?? server.url }, server.url)) })] }) : connection.servers.length === 1 && connection.servers[0]?.description !== null ? _jsxs("div", { className: "wildo-api-ref__code-server", children: [_jsx("span", { children: words.server }), _jsx("span", { children: connection.servers[0]?.description })] }) : null, _jsx("div", { role: "tabpanel", id: tabId('language', 'panel'), "aria-labelledby": tabId('language', language), children: _jsx("pre", { tabIndex: 0, children: _jsx("code", { children: highlightCode(requestSamples[language]) }) }) })] }), _jsxs("div", { role: "group", className: "wildo-api-ref__code-card", "aria-label": words.responseExample, children: [_jsxs("div", { className: "wildo-api-ref__code-head", children: [_jsx("span", { className: "wildo-api-ref__code-title", children: words.response }), selected !== null ? _jsx("div", { role: "tablist", "aria-label": words.responseStatus, className: "wildo-api-ref__code-tabs", onKeyDown: onTabListKeyDown, children: samples.map((sample) => _jsxs("button", { type: "button", role: "tab", id: tabId('status', sample.status), className: "wildo-api-ref__code-tab", "aria-selected": sample.status === selected.status, "aria-controls": sample.status === selected.status ? `${tabId('status', sample.status)}-panel` : undefined, tabIndex: sample.status === selected.status ? 0 : -1, title: sample.description ?? undefined, onClick: () => onSelectStatus(sample.status), children: [_jsx(StatusDot, { status: sample.status }), sample.status] }, sample.status)) }) : null, selected !== null ? _jsx(CopyButton, { text: selected.jsonValue, label: words.copyResponseExample }) : null] }), selected !== null
|
|
659
|
+
? _jsxs("div", { role: "tabpanel", id: `${tabId('status', selected.status)}-panel`, "aria-labelledby": tabId('status', selected.status), children: [selectedStatus !== null && selectedStatus !== selected.status ? _jsx("p", { className: "wildo-api-ref__code-caption", children: words.noExampleForStatus(selectedStatus, selected.status) }) : null, _jsx("pre", { tabIndex: 0, children: _jsx("code", { children: highlightCode(selected.jsonValue) }) })] })
|
|
660
|
+
: _jsx("p", { className: "wildo-api-ref__code-empty", children: words.noResponseExample })] })] });
|
|
661
|
+
}
|
|
662
|
+
/**
|
|
663
|
+
* On a phone the samples do not fit beside the documentation, so each operation offers them in a
|
|
664
|
+
* bottom sheet (a modal `<dialog>`). The sheet's content is built only while it is open. From 960 px
|
|
665
|
+
* up the button is hidden and the samples sit in the right-hand column instead.
|
|
666
|
+
*/
|
|
667
|
+
function SamplesSheet({ variant, selectedStatus, onSelectStatus, }) {
|
|
668
|
+
const words = useReferenceWords();
|
|
669
|
+
const dialogRef = useRef(null);
|
|
670
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
671
|
+
const open = () => {
|
|
672
|
+
const dialog = dialogRef.current;
|
|
673
|
+
if (dialog === null)
|
|
674
|
+
return;
|
|
675
|
+
setIsOpen(true);
|
|
676
|
+
if (typeof dialog.showModal === 'function')
|
|
677
|
+
dialog.showModal();
|
|
678
|
+
else
|
|
679
|
+
dialog.setAttribute('open', '');
|
|
680
|
+
};
|
|
681
|
+
const close = () => {
|
|
682
|
+
const dialog = dialogRef.current;
|
|
683
|
+
if (dialog !== null && typeof dialog.close === 'function')
|
|
684
|
+
dialog.close();
|
|
685
|
+
else
|
|
686
|
+
dialog?.removeAttribute('open');
|
|
687
|
+
setIsOpen(false);
|
|
688
|
+
};
|
|
689
|
+
return _jsxs(_Fragment, { children: [_jsx("button", { type: "button", className: "wildo-api-ref__button wildo-api-ref__sheet-open", onClick: open, children: words.requestAndResponse }), _jsx("dialog", { ref: dialogRef, className: "wildo-api-ref__sheet", "aria-label": words.requestAndResponseFor(variant.summary), onClose: () => setIsOpen(false), onClick: (event) => {
|
|
690
|
+
if (event.target === event.currentTarget)
|
|
691
|
+
close();
|
|
692
|
+
}, children: isOpen ? _jsxs(_Fragment, { children: [_jsxs("div", { className: "wildo-api-ref__sheet-head", children: [_jsx("strong", { children: variant.summary }), _jsx("button", { type: "button", className: "wildo-api-ref__icon-button", "aria-label": words.close, onClick: close, children: _jsx(CloseIcon, {}) })] }), _jsx(OperationSamples, { variant: variant, selectedStatus: selectedStatus, onSelectStatus: onSelectStatus, idScope: `${variant.anchorId}--sheet` })] }) : null })] });
|
|
693
|
+
}
|
|
694
|
+
/**
|
|
695
|
+
* Colours a sample for reading: object keys, strings, numbers and literals, and the few keywords the
|
|
696
|
+
* three request languages open with. Display only — the text is unchanged, so copying it copies the
|
|
697
|
+
* sample exactly.
|
|
698
|
+
*/
|
|
699
|
+
const CODE_TOKEN_PATTERN = /("(?:[^"\\\n]|\\.)*"|'(?:[^'\\\n]|\\.)*')(\s*:)?|\b(true|false|null|True|False|None)\b|(-?\b\d+(?:\.\d+)?(?:[eE][+-]?\d+)?\b)|\b(curl|const|await|import|print)\b/g;
|
|
700
|
+
function highlightCode(source) {
|
|
701
|
+
const nodes = [];
|
|
702
|
+
let last = 0;
|
|
703
|
+
for (const match of source.matchAll(CODE_TOKEN_PATTERN)) {
|
|
704
|
+
const index = match.index ?? 0;
|
|
705
|
+
if (index > last)
|
|
706
|
+
nodes.push(source.slice(last, index));
|
|
707
|
+
const [text, quoted, colon, literal, number] = match;
|
|
708
|
+
if (quoted !== undefined) {
|
|
709
|
+
nodes.push(_jsx("span", { className: `wildo-api-ref__tok--${colon !== undefined ? 'key' : 'string'}`, children: quoted }, index));
|
|
710
|
+
if (colon !== undefined)
|
|
711
|
+
nodes.push(colon);
|
|
712
|
+
}
|
|
713
|
+
else {
|
|
714
|
+
const kind = literal !== undefined ? 'literal' : number !== undefined ? 'number' : 'keyword';
|
|
715
|
+
nodes.push(_jsx("span", { className: `wildo-api-ref__tok--${kind}`, children: text }, index));
|
|
716
|
+
}
|
|
717
|
+
last = index + text.length;
|
|
718
|
+
}
|
|
719
|
+
if (last < source.length)
|
|
720
|
+
nodes.push(source.slice(last));
|
|
721
|
+
return nodes;
|
|
722
|
+
}
|
|
723
|
+
/** Whether `fragment` names this variant, its request body or one of its responses. */
|
|
724
|
+
function variantOwnsFragment(variant, fragment) {
|
|
725
|
+
return fragment.length > 0 && (variant.anchorId === fragment
|
|
726
|
+
|| variant.requestBody?.anchorId === fragment
|
|
727
|
+
|| variant.responses.some((response) => response.anchorId === fragment));
|
|
206
728
|
}
|
|
207
729
|
function currentFragment() {
|
|
208
730
|
if (typeof window === 'undefined')
|
|
209
731
|
return '';
|
|
210
732
|
return window.location.hash.startsWith('#') ? window.location.hash.slice(1) : window.location.hash;
|
|
211
733
|
}
|
|
212
|
-
|
|
213
|
-
|
|
734
|
+
/**
|
|
735
|
+
* The operation's documented failure modes, each with what a client branches on. The third column
|
|
736
|
+
* names the FIELD as well as the value, because `error.type`, `error.customMessageReference` and
|
|
737
|
+
* `error.code` are read from three different places in the error body.
|
|
738
|
+
*/
|
|
739
|
+
/** The error-body fields a client branches on — wire field names, shown as code, not words. */
|
|
740
|
+
const ERROR_BRANCH_FIELDS = { TYPE: 'error.type', MESSAGE_REFERENCE: 'error.customMessageReference', CODE: 'error.code' };
|
|
741
|
+
function ErrorScenarioTable({ scenarios }) {
|
|
742
|
+
const words = useReferenceWords();
|
|
743
|
+
return _jsx("div", { className: "wildo-api-ref__table-scroll", children: _jsxs("table", { children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { scope: "col", children: words.errorStatus }), _jsx("th", { scope: "col", children: words.errorWhen }), _jsx("th", { scope: "col", children: words.errorBranchOn })] }) }), _jsx("tbody", { children: scenarios.map((scenario, index) => {
|
|
744
|
+
const identifiers = [
|
|
745
|
+
scenario.errorType === null ? null : [ERROR_BRANCH_FIELDS.TYPE, scenario.errorType],
|
|
746
|
+
scenario.customMessageReference === null ? null : [ERROR_BRANCH_FIELDS.MESSAGE_REFERENCE, scenario.customMessageReference],
|
|
747
|
+
scenario.errorCode === null ? null : [ERROR_BRANCH_FIELDS.CODE, scenario.errorCode],
|
|
748
|
+
].filter((identifier) => identifier !== null);
|
|
749
|
+
return _jsxs("tr", { children: [_jsx("td", { children: _jsx("code", { children: scenario.status }) }), _jsx("td", { children: _jsx(OpenApiInlineCommonMark, { source: scenario.when }) }), _jsx("td", { children: identifiers.length === 0 ? '—' : identifiers.map(([field, value], identifierIndex) => _jsxs(Fragment, { children: [identifierIndex > 0 ? _jsx("br", {}) : null, _jsx("code", { children: field }), " = ", _jsx("code", { children: value })] }, field)) })] }, `${scenario.status}:${index}`);
|
|
750
|
+
}) })] }) });
|
|
751
|
+
}
|
|
752
|
+
/** Where a parameter travels (the OpenAPI `in` values), in reading order, with the word that heads each group. */
|
|
753
|
+
const PARAMETER_LOCATION_HEADINGS = [
|
|
754
|
+
['path', (words) => words.pathParameters],
|
|
755
|
+
['query', (words) => words.queryParameters],
|
|
756
|
+
['header', (words) => words.headerParameters],
|
|
757
|
+
['cookie', (words) => words.cookieParameters],
|
|
758
|
+
];
|
|
759
|
+
/** The operation's parameters as field rows, grouped by where they travel: path, query, header, cookie. */
|
|
760
|
+
function ParameterSections({ parameters, level }) {
|
|
761
|
+
const words = useReferenceWords();
|
|
762
|
+
if (parameters.length === 0)
|
|
763
|
+
return null;
|
|
764
|
+
const known = new Set(PARAMETER_LOCATION_HEADINGS.map(([location]) => location));
|
|
765
|
+
const sections = [
|
|
766
|
+
...PARAMETER_LOCATION_HEADINGS.map(([location, heading]) => ({ heading: heading(words), parameters: parameters.filter((parameter) => parameter.location === location) })),
|
|
767
|
+
{ heading: words.otherParameters, parameters: parameters.filter((parameter) => !known.has(parameter.location)) },
|
|
768
|
+
].filter((section) => section.parameters.length > 0);
|
|
769
|
+
return _jsx(_Fragment, { children: sections.map((section) => _jsxs("section", { children: [_jsx(Heading, { level: level, className: "wildo-api-ref__subheading", children: section.heading }), _jsx("ul", { className: "wildo-api-ref__fields", "aria-label": section.heading, children: section.parameters.map((parameter) => _jsxs("li", { className: "wildo-api-ref__field", children: [_jsxs("div", { className: "wildo-api-ref__field-head", children: [_jsx("code", { className: "wildo-api-ref__field-name", children: parameter.name }), _jsxs("span", { className: "wildo-api-ref__field-meta", children: [parameter.schema === null ? null : _jsxs(_Fragment, { children: [_jsx(SchemaType, { schema: parameter.schema }), " \u00B7 "] }), parameter.required ? _jsx("span", { className: "wildo-api-ref__required", children: words.required }) : words.optional] })] }), _jsxs("div", { className: "wildo-api-ref__field-body", children: [_jsx(ParameterDescription, { parameter: parameter }), parameter.examples.length > 0 ? _jsx(Examples, { examples: parameter.examples }) : null] })] }, `${parameter.location}:${parameter.name}`)) })] }, section.heading)) });
|
|
214
770
|
}
|
|
771
|
+
/**
|
|
772
|
+
* A parameter's own description, then the contract its schema carries: allowed values with their
|
|
773
|
+
* meanings, default, format and limits. The row's head shows only the identity (`string`), so
|
|
774
|
+
* without this an enum filter such as `status` read as a free string although the document lists
|
|
775
|
+
* its four values.
|
|
776
|
+
*
|
|
777
|
+
* The generator copies a filter field's description onto both the parameter and its schema, so the
|
|
778
|
+
* schema's copy is dropped when the parameter's text already begins with it (a range filter appends
|
|
779
|
+
* its wire hint after the same meaning). A description carried ONLY by the schema is kept.
|
|
780
|
+
*/
|
|
781
|
+
function ParameterDescription({ parameter }) {
|
|
782
|
+
const schemaDescription = parameter.schema?.description ?? null;
|
|
783
|
+
const restatesSchemaDescription = schemaDescription !== null && parameter.description !== null && parameter.description.startsWith(schemaDescription);
|
|
784
|
+
const contract = parameter.schema === null ? null : { ...parameter.schema, description: restatesSchemaDescription ? null : schemaDescription };
|
|
785
|
+
const resolution = useContext(SchemaResolutionContext);
|
|
786
|
+
const hasContract = contract !== null && schemaHasContractDetails(contract, resolution);
|
|
787
|
+
if (parameter.description === null && !hasContract)
|
|
788
|
+
return '—';
|
|
789
|
+
return _jsxs(_Fragment, { children: [parameter.description !== null
|
|
790
|
+
? _jsx(OpenApiCommonMark, { source: contract === null ? parameter.description : withoutRestatedEnumLegend(parameter.description, contract) })
|
|
791
|
+
: null, hasContract ? _jsx(SchemaContract, { schema: contract }) : null] });
|
|
792
|
+
}
|
|
793
|
+
/**
|
|
794
|
+
* The generator appends a markdown `Values:` legend to an enum field's description for readers
|
|
795
|
+
* that ignore `x-enum-descriptions` (spec-to-operation-doc `composeFieldDescription`). Where this
|
|
796
|
+
* view renders those same values as their own "Allowed values" list, the legend is a second copy
|
|
797
|
+
* of it, so it is dropped — only when EVERY value carries its own description, so nothing the
|
|
798
|
+
* legend says can be lost.
|
|
799
|
+
*/
|
|
800
|
+
function withoutRestatedEnumLegend(description, schema) {
|
|
801
|
+
const everyValueDescribed = schema.enumValues.length > 0 && schema.enumValues.every((enumValue) => enumValue.description !== null);
|
|
802
|
+
return everyValueDescribed ? description.replace(/(?:^|\n\n)Values:\n(?:- [^\n]*(?:\n|$))+$/, '').trimEnd() : description;
|
|
803
|
+
}
|
|
804
|
+
/**
|
|
805
|
+
* The documented responses as status tabs, successes first, with the selected one's description and
|
|
806
|
+
* body below. The selection is shared with the code panel's response example.
|
|
807
|
+
*/
|
|
808
|
+
function ResponseTabs({ variant, selectedStatus, onSelectStatus, }) {
|
|
809
|
+
const words = useReferenceWords();
|
|
810
|
+
const ordered = useMemo(() => {
|
|
811
|
+
const isSuccess = (status) => responseStatusClass(status) === ResponseStatusClass.SUCCESS;
|
|
812
|
+
return [...variant.responses.filter((response) => isSuccess(response.status)), ...variant.responses.filter((response) => !isSuccess(response.status))];
|
|
813
|
+
}, [variant]);
|
|
814
|
+
const selected = ordered.find((response) => response.status === selectedStatus) ?? ordered[0];
|
|
815
|
+
if (selected === undefined)
|
|
816
|
+
return null;
|
|
817
|
+
const tabId = (status) => `${variant.anchorId}--status-${status}`;
|
|
818
|
+
return _jsxs(_Fragment, { children: [_jsx("div", { role: "tablist", "aria-label": words.documentedResponses, className: "wildo-api-ref__status-tabs", onKeyDown: onTabListKeyDown, children: ordered.map((response) => _jsxs("button", { type: "button", role: "tab", id: tabId(response.status), className: "wildo-api-ref__status-tab", "aria-selected": response === selected, "aria-controls": response === selected ? `${tabId(response.status)}-panel` : undefined, tabIndex: response === selected ? 0 : -1, onClick: () => onSelectStatus(response.status), children: [_jsx(StatusDot, { status: response.status }), response.status] }, response.status)) }), _jsx("div", { role: "tabpanel", id: `${tabId(selected.status)}-panel`, "aria-labelledby": tabId(selected.status), children: _jsx(ResponseContract, { response: selected }, selected.status) })] });
|
|
819
|
+
}
|
|
820
|
+
/**
|
|
821
|
+
* An error status carries the one shared error envelope on almost every operation, so its fields are
|
|
822
|
+
* behind a disclosure rendered only when opened; a success or request body is what the reader came
|
|
823
|
+
* for, so its fields are shown in place.
|
|
824
|
+
*/
|
|
215
825
|
function ResponseContract({ response }) {
|
|
216
|
-
|
|
826
|
+
const statusClass = responseStatusClass(response.status);
|
|
827
|
+
const isErrorStatus = statusClass === ResponseStatusClass.CLIENT_ERROR || statusClass === ResponseStatusClass.SERVER_ERROR;
|
|
828
|
+
return _jsxs("section", { className: "wildo-api-ref__response", id: response.anchorId, children: [_jsxs("p", { children: [_jsx("strong", { children: _jsx("code", { children: response.status }) }), response.description !== null ? _jsxs(_Fragment, { children: [" \u2014 ", _jsx(OpenApiInlineCommonMark, { source: response.description })] }) : null] }), _jsx(RepresentationList, { mediaTypes: response.mediaTypes, fieldsCollapsed: isErrorStatus })] });
|
|
217
829
|
}
|
|
218
|
-
function RepresentationList({ mediaTypes }) {
|
|
830
|
+
function RepresentationList({ mediaTypes, fieldsCollapsed = false }) {
|
|
831
|
+
const words = useReferenceWords();
|
|
832
|
+
const resolution = useContext(SchemaResolutionContext);
|
|
219
833
|
if (mediaTypes.length === 0)
|
|
220
|
-
return _jsx("p", { children:
|
|
221
|
-
return _jsx("ul", {
|
|
834
|
+
return _jsx("p", { children: words.noStructuredRepresentation });
|
|
835
|
+
return _jsx("ul", { className: "wildo-api-ref__representations", children: mediaTypes.map((representation) => {
|
|
836
|
+
const schema = representation.schema;
|
|
837
|
+
const hasFields = schema !== null && schemaHasContractDetails(displayedSchema(schema, resolution), resolution);
|
|
838
|
+
return _jsxs("li", { children: [_jsx("span", { className: "wildo-api-ref__media", children: representation.mediaType }), schema !== null ? _jsxs(_Fragment, { children: [" \u2014 ", _jsx(SchemaType, { schema: schema })] }) : null, schema !== null && hasFields
|
|
839
|
+
? fieldsCollapsed
|
|
840
|
+
? _jsx(LazyDisclosure, { summary: words.bodyFields, render: () => _jsx(SchemaContract, { schema: schema }) })
|
|
841
|
+
: _jsx("div", { children: _jsx(SchemaContract, { schema: schema, openCollections: true, collapseLongLists: true }) })
|
|
842
|
+
: null, representation.examples.length > 0 ? _jsx(Examples, { examples: representation.examples }) : null] }, representation.mediaType);
|
|
843
|
+
}) });
|
|
844
|
+
}
|
|
845
|
+
/**
|
|
846
|
+
* A disclosure whose content is built only once it is opened. Nested fields are the bulk of a resource
|
|
847
|
+
* page: building every closed "Child fields" disclosure put Wonder Todos' Todos page at 33,043 nodes
|
|
848
|
+
* during the #1616 redesign, so what is closed is not built.
|
|
849
|
+
*/
|
|
850
|
+
function LazyDisclosure({ summary, render, initiallyOpen = false }) {
|
|
851
|
+
const [isOpen, setIsOpen] = useState(initiallyOpen);
|
|
852
|
+
return _jsxs("details", { className: "wildo-api-ref__children", open: initiallyOpen, onToggle: (event) => setIsOpen(event.currentTarget.open), children: [_jsx("summary", { children: summary }), isOpen ? _jsx("div", { className: "wildo-api-ref__nested", children: render() }) : null] });
|
|
222
853
|
}
|
|
223
|
-
/**
|
|
224
|
-
|
|
854
|
+
/**
|
|
855
|
+
* Presents a schema identity; a reference stays a link to the reusable model it names. `linked={false}`
|
|
856
|
+
* renders the name without the link, for a place that is itself interactive (a disclosure's summary),
|
|
857
|
+
* where a nested link is unreachable by keyboard and confusing to a screen reader.
|
|
858
|
+
*/
|
|
859
|
+
function SchemaType({ schema, linked = true }) {
|
|
860
|
+
const words = useReferenceWords();
|
|
225
861
|
if (schema.booleanSchema !== null)
|
|
226
|
-
return _jsx("code", { children: schema.booleanSchema ?
|
|
862
|
+
return _jsx("code", { children: schema.booleanSchema ? words.anyValue : words.noValue });
|
|
227
863
|
if (schema.referenceName !== null) {
|
|
228
|
-
return schema.referenceAnchorId === null
|
|
864
|
+
return schema.referenceAnchorId === null || !linked
|
|
229
865
|
? _jsx("code", { children: schema.referenceName })
|
|
230
866
|
: _jsx("a", { href: `#${schema.referenceAnchorId}`, children: _jsx("code", { children: schema.referenceName }) });
|
|
231
867
|
}
|
|
@@ -233,16 +869,17 @@ function SchemaType({ schema }) {
|
|
|
233
869
|
return _jsx("code", { children: schema.types.join(' | ') });
|
|
234
870
|
if (schema.compositions.length > 0)
|
|
235
871
|
return _jsx("code", { children: schema.compositions.map((composition) => composition.kind).join(' + ') });
|
|
236
|
-
return _jsx("code", { children:
|
|
872
|
+
return _jsx("code", { children: words.unnamedSchema });
|
|
237
873
|
}
|
|
238
|
-
function schemaHasNestedContract(schema) {
|
|
239
|
-
return schema
|
|
874
|
+
function schemaHasNestedContract(schema, resolution) {
|
|
875
|
+
return expandableReference(schema, resolution) !== null
|
|
876
|
+
|| schema.properties.length > 0
|
|
240
877
|
|| schema.items !== null
|
|
241
878
|
|| schema.compositions.length > 0
|
|
242
879
|
|| (schema.additionalProperties !== null && typeof schema.additionalProperties === 'object');
|
|
243
880
|
}
|
|
244
|
-
function schemaHasContractDetails(schema) {
|
|
245
|
-
return schemaHasNestedContract(schema)
|
|
881
|
+
function schemaHasContractDetails(schema, resolution) {
|
|
882
|
+
return schemaHasNestedContract(schema, resolution)
|
|
246
883
|
|| schema.description !== null
|
|
247
884
|
|| schema.format !== null
|
|
248
885
|
|| schema.defaultValue !== null
|
|
@@ -251,34 +888,143 @@ function schemaHasContractDetails(schema) {
|
|
|
251
888
|
|| schema.constraints.length > 0
|
|
252
889
|
|| schema.additionalProperties !== null;
|
|
253
890
|
}
|
|
254
|
-
/**
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
function
|
|
263
|
-
const
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
}
|
|
268
|
-
|
|
891
|
+
/**
|
|
892
|
+
* Recursively renders only the contract fields carried by the OpenAPI Schema Object: first what the
|
|
893
|
+
* value itself must be (`SchemaFacts`), then what it contains (`SchemaChildren`).
|
|
894
|
+
*
|
|
895
|
+
* A reference to a reusable component is shown in place (see `SchemaResolution`). `openCollections`
|
|
896
|
+
* opens an array property and its items at this level — a paginated body's `data[]` rows are what a
|
|
897
|
+
* list response is for — and is not passed further down.
|
|
898
|
+
*/
|
|
899
|
+
function SchemaContract({ schema, openCollections = false, collapseLongLists = false }) {
|
|
900
|
+
const resolution = useContext(SchemaResolutionContext);
|
|
901
|
+
const reference = expandableReference(schema, resolution);
|
|
902
|
+
if (reference !== null) {
|
|
903
|
+
return _jsx(SchemaResolutionContext.Provider, { value: { ...resolution, expanding: new Set([...resolution.expanding, reference.name]) }, children: _jsx(SchemaContract, { schema: reference.schema, openCollections: openCollections, collapseLongLists: collapseLongLists }) });
|
|
904
|
+
}
|
|
905
|
+
return _jsxs(_Fragment, { children: [_jsx(SchemaFacts, { schema: schema }), _jsx(SchemaChildren, { schema: schema, openCollections: openCollections, collapseLongLists: collapseLongLists })] });
|
|
906
|
+
}
|
|
907
|
+
/** What a value must be: its description, format, default, constant, limits and allowed values. */
|
|
908
|
+
function SchemaFacts({ schema }) {
|
|
909
|
+
const words = useReferenceWords();
|
|
910
|
+
const everyValueUndescribed = schema.enumValues.every((enumValue) => enumValue.description === null);
|
|
911
|
+
return _jsxs(_Fragment, { children: [schema.description !== null ? _jsx(OpenApiCommonMark, { source: withoutRestatedEnumLegend(schema.description, schema) }) : null, schema.format !== null ? _jsxs("p", { children: [_jsx("strong", { children: words.format }), " ", _jsx("code", { children: schema.format })] }) : null, schema.defaultValue !== null ? _jsxs("p", { children: [_jsx("strong", { children: words.defaultValue }), " ", _jsx("code", { children: schema.defaultValue })] }) : null, schema.constValue !== null ? _jsxs("p", { children: [_jsx("strong", { children: words.constantValue }), " ", _jsx("code", { children: schema.constValue })] }) : null, schema.constraints.length > 0 ? _jsx("p", { className: "wildo-api-ref__chips", children: schema.constraints.map((constraint) => _jsxs("code", { className: "wildo-api-ref__chip wildo-api-ref__chip--outline", children: [constraint.kind, ": ", constraint.jsonValue] }, constraint.kind)) }) : null, schema.enumValues.length > 0 ? _jsxs(_Fragment, { children: [_jsx("p", { className: "wildo-api-ref__nested-label", children: words.allowedValues }), everyValueUndescribed
|
|
912
|
+
? _jsx("ul", { className: "wildo-api-ref__chips", children: schema.enumValues.map((enumValue) => _jsx("li", { children: _jsx("code", { className: "wildo-api-ref__chip", children: enumValue.jsonValue }) }, enumValue.jsonValue)) })
|
|
913
|
+
: _jsx("ul", { className: "wildo-api-ref__enum", children: schema.enumValues.map((enumValue) => _jsxs("li", { children: [_jsx("code", { className: "wildo-api-ref__chip", children: enumValue.jsonValue }), enumValue.description !== null ? _jsxs(_Fragment, { children: [" \u2014 ", _jsx(OpenApiInlineCommonMark, { source: enumValue.description })] }) : null] }, enumValue.jsonValue)) })] }) : null] });
|
|
914
|
+
}
|
|
915
|
+
/** Past this many properties, a list shows the first few and offers the rest behind a button. */
|
|
916
|
+
const PROPERTY_LIST_COLLAPSE_THRESHOLD = 14;
|
|
917
|
+
const PROPERTY_LIST_COLLAPSED_COUNT = 10;
|
|
918
|
+
/** What a value contains: its properties, array items, composition branches and extra keys. */
|
|
919
|
+
function SchemaChildren({ schema, openCollections, collapseLongLists }) {
|
|
920
|
+
const words = useReferenceWords();
|
|
921
|
+
const resolution = useContext(SchemaResolutionContext);
|
|
922
|
+
const [showsAll, setShowsAll] = useState(false);
|
|
923
|
+
const ordered = propertiesInReadingOrder(schema.properties);
|
|
924
|
+
const isCollapsed = collapseLongLists && !showsAll && ordered.length > PROPERTY_LIST_COLLAPSE_THRESHOLD;
|
|
925
|
+
const shown = isCollapsed ? ordered.slice(0, PROPERTY_LIST_COLLAPSED_COUNT) : ordered;
|
|
926
|
+
const hidden = ordered.slice(shown.length);
|
|
927
|
+
return _jsxs(_Fragment, { children: [ordered.length > 0 ? _jsxs(_Fragment, { children: [_jsx("p", { className: "wildo-api-ref__nested-label", children: words.properties }), _jsx("ul", { className: "wildo-api-ref__fields", children: shown.map((property) => _jsx(SchemaProperty, { property: property, openCollections: openCollections }, property.name)) }), isCollapsed ? _jsx("button", { type: "button", className: "wildo-api-ref__more", onClick: () => setShowsAll(true), children: words.showMoreFields(hidden.length, hidden.slice(0, 4).map((property) => (property.required ? words.requiredFieldMarker(property.name) : property.name)), hidden.length > 4) }) : null] }) : null, schema.items !== null ? _jsx(LazyDisclosure, { summary: renderSentence(words.arrayItemsOf, { type: _jsx(SchemaType, { schema: schema.items, linked: false }) }), initiallyOpen: openCollections, render: () => _jsxs(_Fragment, { children: [schema.items.referenceName !== null && schema.items.referenceAnchorId !== null
|
|
928
|
+
? _jsxs("p", { className: "wildo-api-ref__media", children: [words.model, " ", _jsx(SchemaType, { schema: schema.items })] })
|
|
929
|
+
: null, _jsx(SchemaContract, { schema: schema.items })] }) }) : null, schema.compositions.map((composition) => (composition.kind !== ApiReferenceSchemaCompositionKind.ALL_OF && compositionBranchesAreObjects(composition.branches, resolution)
|
|
930
|
+
? _jsx(CompositionSwitch, { kind: composition.kind, branches: composition.branches, collapseLongLists: collapseLongLists }, composition.kind)
|
|
931
|
+
: _jsxs("section", { children: [_jsx("p", { className: "wildo-api-ref__nested-label", children: schemaCompositionLabel(composition.kind, words) }), _jsx("ol", { children: composition.branches.map((branch, index) => _jsxs("li", { children: [_jsx(SchemaType, { schema: branch }), schemaHasContractDetails(branch, resolution)
|
|
932
|
+
? _jsx("div", { className: "wildo-api-ref__nested", children: _jsx(SchemaContract, { schema: branch }) })
|
|
933
|
+
: null] }, index)) })] }, composition.kind))), schema.additionalProperties !== null ? _jsxs("div", { children: [_jsx("strong", { children: words.additionalProperties }), additionalPropertiesIsFlag(schema.additionalProperties)
|
|
934
|
+
? _jsxs(_Fragment, { children: [" ", schema.additionalProperties ? words.allowed : words.notAllowed] })
|
|
935
|
+
: _jsxs(_Fragment, { children: [_jsx("span", { children: " " }), _jsx(SchemaType, { schema: schema.additionalProperties }), _jsx("div", { className: "wildo-api-ref__nested", children: _jsx(SchemaContract, { schema: schema.additionalProperties }) })] })] }) : null] });
|
|
936
|
+
}
|
|
937
|
+
/** Whether `additionalProperties` is the boolean form (allowed / not allowed) rather than a schema. */
|
|
938
|
+
function additionalPropertiesIsFlag(value) {
|
|
939
|
+
return typeof value === 'boolean';
|
|
940
|
+
}
|
|
941
|
+
/**
|
|
942
|
+
* Properties in the order a reader wants them: required first, then optional, each in the order the
|
|
943
|
+
* contract declares them, and engine metadata (`_id`, `_version`, `_field_meta`) last. Alphabetical
|
|
944
|
+
* order put `_field_meta` first and separated `title` from `status`.
|
|
945
|
+
*/
|
|
946
|
+
function propertiesInReadingOrder(properties) {
|
|
947
|
+
const rank = (property) => (property.name.startsWith('_') ? 2 : property.required ? 0 : 1);
|
|
948
|
+
return properties.map((property, index) => ({ property, index }))
|
|
949
|
+
.sort((left, right) => rank(left.property) - rank(right.property) || left.index - right.index)
|
|
950
|
+
.map(({ property }) => property);
|
|
951
|
+
}
|
|
952
|
+
/**
|
|
953
|
+
* One field row: its name, type and whether it is required on the left; on the right what it must be,
|
|
954
|
+
* then, behind a disclosure, the fields it contains.
|
|
955
|
+
*/
|
|
956
|
+
function SchemaProperty({ property, openCollections = false }) {
|
|
957
|
+
const words = useReferenceWords();
|
|
958
|
+
const resolution = useContext(SchemaResolutionContext);
|
|
959
|
+
const reference = expandableReference(property.schema, resolution);
|
|
960
|
+
const shown = reference?.schema ?? property.schema;
|
|
961
|
+
const innerResolution = reference === null ? resolution : { ...resolution, expanding: new Set([...resolution.expanding, reference.name]) };
|
|
962
|
+
const hasChildren = schemaHasNestedContract(shown, innerResolution);
|
|
963
|
+
const opensAsCollection = openCollections && shown.items !== null;
|
|
964
|
+
return _jsxs("li", { className: "wildo-api-ref__field", children: [_jsxs("div", { className: "wildo-api-ref__field-head", children: [_jsx("code", { className: "wildo-api-ref__field-name", children: property.name }), _jsxs("span", { className: "wildo-api-ref__field-meta", children: [_jsx(SchemaType, { schema: property.schema }), " \u00B7 ", property.required ? _jsx("span", { className: "wildo-api-ref__required", children: words.required }) : words.optional] })] }), _jsx("div", { className: "wildo-api-ref__field-body", children: _jsxs(SchemaResolutionContext.Provider, { value: innerResolution, children: [_jsx(SchemaFacts, { schema: shown }), hasChildren ? _jsx(LazyDisclosure, { summary: _jsx(_Fragment, { children: words.childFieldsOf(property.name) }), initiallyOpen: opensAsCollection, render: () => _jsx(SchemaChildren, { schema: shown, openCollections: opensAsCollection, collapseLongLists: false }) }) : null] }) })] });
|
|
965
|
+
}
|
|
966
|
+
/** Whether every branch of a composition is an object with fields, so the branches read as alternative shapes. */
|
|
967
|
+
function compositionBranchesAreObjects(branches, resolution) {
|
|
968
|
+
return branches.length > 1 && branches.every((branch) => displayedSchema(branch, resolution).properties.length > 0);
|
|
969
|
+
}
|
|
970
|
+
/**
|
|
971
|
+
* What tells the branches of a composition apart: a property every branch declares with a single
|
|
972
|
+
* value of its own (`recurringType: "one_time"` against `"recurring"`), or else the branch's model name.
|
|
973
|
+
*/
|
|
974
|
+
function compositionBranchLabels(branches, resolution, words) {
|
|
975
|
+
const shown = branches.map((branch) => displayedSchema(branch, resolution));
|
|
976
|
+
const fixedValue = (schema, name) => {
|
|
977
|
+
const property = schema.properties.find((candidate) => candidate.name === name);
|
|
978
|
+
if (property === undefined)
|
|
979
|
+
return null;
|
|
980
|
+
const value = property.schema.constValue ?? (property.schema.enumValues.length === 1 ? property.schema.enumValues[0].jsonValue : null);
|
|
981
|
+
if (value === null)
|
|
982
|
+
return null;
|
|
983
|
+
try {
|
|
984
|
+
const parsed = JSON.parse(value);
|
|
985
|
+
return typeof parsed === 'string' ? parsed : value;
|
|
986
|
+
}
|
|
987
|
+
catch {
|
|
988
|
+
return value;
|
|
989
|
+
}
|
|
990
|
+
};
|
|
991
|
+
const discriminator = (shown[0]?.properties ?? []).map((property) => property.name).find((name) => {
|
|
992
|
+
const values = shown.map((schema) => fixedValue(schema, name));
|
|
993
|
+
return values.every((value) => value !== null) && new Set(values).size === values.length;
|
|
994
|
+
});
|
|
995
|
+
return branches.map((branch, index) => (discriminator !== undefined ? words.discriminatorValue(discriminator, fixedValue(shown[index], discriminator) ?? '') : branch.referenceName ?? words.compositionOption(index + 1)));
|
|
996
|
+
}
|
|
997
|
+
/**
|
|
998
|
+
* A `oneOf`/`anyOf` whose branches are objects (never an `allOf`, whose branches all apply at once) is a choice between shapes of one body — a one-time or a
|
|
999
|
+
* recurring todo. It is shown as a switch between the shapes, one built at a time, rather than every
|
|
1000
|
+
* shape's fields one after another.
|
|
1001
|
+
*/
|
|
1002
|
+
function CompositionSwitch({ kind, branches, collapseLongLists }) {
|
|
1003
|
+
const words = useReferenceWords();
|
|
1004
|
+
const resolution = useContext(SchemaResolutionContext);
|
|
1005
|
+
const labels = useMemo(() => compositionBranchLabels(branches, resolution, words), [branches, resolution, words]);
|
|
1006
|
+
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
1007
|
+
const idBase = useId();
|
|
1008
|
+
const tabId = (index) => `${idBase}-shape-${index}`;
|
|
1009
|
+
const selected = branches[selectedIndex] ?? branches[0];
|
|
1010
|
+
return _jsxs("section", { children: [_jsx("p", { className: "wildo-api-ref__nested-label", children: words.compositionShapes(schemaCompositionLabel(kind, words), branches.length) }), _jsx("div", { role: "tablist", "aria-label": words.compositionShapes(schemaCompositionLabel(kind, words), branches.length), className: "wildo-api-ref__segmented", onKeyDown: onTabListKeyDown, children: labels.map((label, index) => _jsx("button", { type: "button", role: "tab", id: tabId(index), className: "wildo-api-ref__segment", "aria-selected": index === selectedIndex, "aria-controls": index === selectedIndex ? `${idBase}-shape-panel` : undefined, tabIndex: index === selectedIndex ? 0 : -1, onClick: () => setSelectedIndex(index), children: label }, index)) }), _jsxs("div", { role: "tabpanel", id: `${idBase}-shape-panel`, "aria-labelledby": tabId(selectedIndex), children: [selected.referenceName !== null ? _jsxs("p", { className: "wildo-api-ref__media", children: [words.model, " ", _jsx(SchemaType, { schema: selected })] }) : null, _jsx(SchemaContract, { schema: selected, collapseLongLists: collapseLongLists }, selectedIndex)] })] });
|
|
1011
|
+
}
|
|
1012
|
+
function schemaCompositionLabel(kind, words) {
|
|
269
1013
|
switch (kind) {
|
|
270
|
-
case ApiReferenceSchemaCompositionKind.ONE_OF: return
|
|
271
|
-
case ApiReferenceSchemaCompositionKind.ANY_OF: return
|
|
272
|
-
case ApiReferenceSchemaCompositionKind.ALL_OF: return
|
|
1014
|
+
case ApiReferenceSchemaCompositionKind.ONE_OF: return words.oneOf;
|
|
1015
|
+
case ApiReferenceSchemaCompositionKind.ANY_OF: return words.anyOf;
|
|
1016
|
+
case ApiReferenceSchemaCompositionKind.ALL_OF: return words.allOf;
|
|
273
1017
|
}
|
|
274
1018
|
}
|
|
275
1019
|
/** Renders OpenAPI reusable schemas only when a reader explicitly selects one. */
|
|
276
1020
|
function ComponentSchemaReference({ schemas, activeFragment, }) {
|
|
1021
|
+
const words = useReferenceWords();
|
|
277
1022
|
const selectedSchema = schemas.find((schema) => schema.anchorId === activeFragment) ?? null;
|
|
278
|
-
return _jsxs("details", { className: "
|
|
1023
|
+
return _jsx("div", { className: "wildo-api-ref__models", children: _jsxs("details", { className: "wildo-api-ref__disclosure", open: selectedSchema !== null, children: [_jsx("summary", { children: words.reusableDataModels(schemas.length) }), selectedSchema === null ? _jsx("p", { children: words.selectModelPrompt }) : _jsxs("section", { children: [_jsx("h2", { id: selectedSchema.anchorId, children: selectedSchema.name }), _jsxs("p", { children: [_jsx("strong", { children: words.schemaType }), " ", _jsx("span", { children: " " }), _jsx(SchemaType, { schema: selectedSchema.schema })] }), _jsx(SchemaContract, { schema: selectedSchema.schema })] })] }) });
|
|
279
1024
|
}
|
|
280
1025
|
/** Renders only OpenAPI-declared examples; this component never invents sample payloads. */
|
|
281
1026
|
function Examples({ examples }) {
|
|
282
|
-
|
|
1027
|
+
const words = useReferenceWords();
|
|
1028
|
+
return _jsxs("details", { className: "wildo-api-ref__inline-disclosure", children: [_jsx("summary", { children: words.examples(examples.length) }), examples.map((example) => _jsxs("div", { children: [_jsxs("p", { children: [_jsx("strong", { children: example.name }), example.summary !== null ? ` — ${example.summary}` : ''] }), example.description !== null ? _jsx("p", { children: _jsx(OpenApiInlineCommonMark, { source: example.description }) }) : null, example.jsonValue !== null ? _jsx("pre", { tabIndex: 0, children: _jsx("code", { children: example.jsonValue }) }) : null, example.externalValue !== null ? _jsxs("p", { children: [words.externalExample, " ", _jsx("a", { href: example.externalValue, children: example.externalValue })] }) : null, example.jsonValue === null && example.externalValue === null ? _jsx("p", { children: words.noInlineExample }) : null] }, example.name))] });
|
|
283
1029
|
}
|
|
284
1030
|
//# sourceMappingURL=openapi-reference-view.js.map
|