@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.
Files changed (205) hide show
  1. package/dist/esm/build/csp-emit.d.ts.map +1 -1
  2. package/dist/esm/build/load-materialized-frontend-providers.d.ts.map +1 -1
  3. package/dist/esm/companion/application-documentation/application-administration-documentation.d.ts.map +1 -1
  4. package/dist/esm/companion/application-documentation/application-administration-documentation.js.map +1 -1
  5. package/dist/esm/companion/application-documentation/application-authentication-documentation.d.ts.map +1 -1
  6. package/dist/esm/companion/application-documentation/application-connection-documentation.d.ts +37 -0
  7. package/dist/esm/companion/application-documentation/application-connection-documentation.d.ts.map +1 -1
  8. package/dist/esm/companion/application-documentation/application-connection-documentation.js +46 -5
  9. package/dist/esm/companion/application-documentation/application-connection-documentation.js.map +1 -1
  10. package/dist/esm/companion/application-documentation/application-documentation-chapters.d.ts +101 -0
  11. package/dist/esm/companion/application-documentation/application-documentation-chapters.d.ts.map +1 -0
  12. package/dist/esm/companion/application-documentation/application-documentation-chapters.js +205 -0
  13. package/dist/esm/companion/application-documentation/application-documentation-chapters.js.map +1 -0
  14. package/dist/esm/companion/application-documentation/application-domain-documentation.d.ts.map +1 -1
  15. package/dist/esm/companion/application-documentation/application-domain-documentation.js.map +1 -1
  16. package/dist/esm/companion/application-documentation/application-integration-documentation.d.ts.map +1 -1
  17. package/dist/esm/companion/application-documentation/application-integration-documentation.js.map +1 -1
  18. package/dist/esm/companion/application-documentation/application-organization-role-documentation.d.ts.map +1 -1
  19. package/dist/esm/companion/application-documentation/application-organization-role-documentation.js.map +1 -1
  20. package/dist/esm/companion/application-documentation/application-organization-unit-resource-documentation.d.ts.map +1 -1
  21. package/dist/esm/companion/application-documentation/application-organization-unit-resource-documentation.js +3 -0
  22. package/dist/esm/companion/application-documentation/application-organization-unit-resource-documentation.js.map +1 -1
  23. package/dist/esm/companion/application-documentation/docs-api-origin-substitution.d.ts +34 -0
  24. package/dist/esm/companion/application-documentation/docs-api-origin-substitution.d.ts.map +1 -0
  25. package/dist/esm/companion/application-documentation/docs-api-origin-substitution.js +45 -0
  26. package/dist/esm/companion/application-documentation/docs-api-origin-substitution.js.map +1 -0
  27. package/dist/esm/companion/application-documentation/technical-documentation-engine-content-bundle.d.ts +24 -10
  28. package/dist/esm/companion/application-documentation/technical-documentation-engine-content-bundle.d.ts.map +1 -1
  29. package/dist/esm/companion/application-documentation/technical-documentation-engine-content-bundle.js +25 -15
  30. package/dist/esm/companion/application-documentation/technical-documentation-engine-content-bundle.js.map +1 -1
  31. package/dist/esm/companion/application-documentation/technical-documentation-operational-evidence.d.ts.map +1 -1
  32. package/dist/esm/companion/application-documentation/technical-documentation-private-derivation.d.ts +11 -3
  33. package/dist/esm/companion/application-documentation/technical-documentation-private-derivation.d.ts.map +1 -1
  34. package/dist/esm/companion/application-documentation/technical-documentation-private-derivation.js +9 -2
  35. package/dist/esm/companion/application-documentation/technical-documentation-private-derivation.js.map +1 -1
  36. package/dist/esm/companion/application-documentation/technical-documentation-publication-policy.d.ts.map +1 -1
  37. package/dist/esm/companion/application-documentation/technical-documentation-publication-policy.js +10 -4
  38. package/dist/esm/companion/application-documentation/technical-documentation-publication-policy.js.map +1 -1
  39. package/dist/esm/companion/content/application-consumer-documentation-content-loader.d.ts.map +1 -1
  40. package/dist/esm/companion/index.d.ts +2 -0
  41. package/dist/esm/companion/index.d.ts.map +1 -1
  42. package/dist/esm/companion/index.js +2 -0
  43. package/dist/esm/companion/index.js.map +1 -1
  44. package/dist/esm/companion/manual-controller-route-projection.d.ts.map +1 -1
  45. package/dist/esm/companion/manual-controller-route-projection.js.map +1 -1
  46. package/dist/esm/companion/openapi-example-derivation.d.ts +53 -0
  47. package/dist/esm/companion/openapi-example-derivation.d.ts.map +1 -0
  48. package/dist/esm/companion/openapi-example-derivation.js +229 -0
  49. package/dist/esm/companion/openapi-example-derivation.js.map +1 -0
  50. package/dist/esm/companion/openapi-generator.d.ts +8 -4
  51. package/dist/esm/companion/openapi-generator.d.ts.map +1 -1
  52. package/dist/esm/companion/openapi-generator.js +323 -33
  53. package/dist/esm/companion/openapi-generator.js.map +1 -1
  54. package/dist/esm/companion/operation-projection.schemas.d.ts +57 -1
  55. package/dist/esm/companion/operation-projection.schemas.d.ts.map +1 -1
  56. package/dist/esm/companion/operation-projection.schemas.js +29 -0
  57. package/dist/esm/companion/operation-projection.schemas.js.map +1 -1
  58. package/dist/esm/companion/publish-result.types.d.ts.map +1 -1
  59. package/dist/esm/companion/publish-result.types.js.map +1 -1
  60. package/dist/esm/companion/rendering/technical-documentation-authorized-bundle-reader.d.ts.map +1 -1
  61. package/dist/esm/companion/rendering/technical-documentation-docusaurus-renderer.d.ts.map +1 -1
  62. package/dist/esm/companion/rendering/technical-documentation-docusaurus-renderer.js +8 -0
  63. package/dist/esm/companion/rendering/technical-documentation-docusaurus-renderer.js.map +1 -1
  64. package/dist/esm/companion/rendering/technical-documentation-managed-tree-validator.d.ts.map +1 -1
  65. package/dist/esm/companion/rendering/technical-documentation-managed-tree-validator.js +20 -0
  66. package/dist/esm/companion/rendering/technical-documentation-managed-tree-validator.js.map +1 -1
  67. package/dist/esm/companion/rendering/technical-documentation-openapi-renderer.d.ts +2 -0
  68. package/dist/esm/companion/rendering/technical-documentation-openapi-renderer.d.ts.map +1 -1
  69. package/dist/esm/companion/rendering/technical-documentation-openapi-renderer.js +48 -31
  70. package/dist/esm/companion/rendering/technical-documentation-openapi-renderer.js.map +1 -1
  71. package/dist/esm/companion/rendering/technical-documentation-render-model.d.ts.map +1 -1
  72. package/dist/esm/companion/rendering/technical-documentation-render-model.js +26 -10
  73. package/dist/esm/companion/rendering/technical-documentation-render-model.js.map +1 -1
  74. package/dist/esm/companion/rendering/technical-documentation-search-index-renderer.d.ts.map +1 -1
  75. package/dist/esm/companion/rendering/technical-documentation-search-index-renderer.js.map +1 -1
  76. package/dist/esm/companion/spec-to-operation-doc.d.ts +52 -10
  77. package/dist/esm/companion/spec-to-operation-doc.d.ts.map +1 -1
  78. package/dist/esm/companion/spec-to-operation-doc.js +125 -7
  79. package/dist/esm/companion/spec-to-operation-doc.js.map +1 -1
  80. package/dist/esm/companion/technical-documentation-asset-path.d.ts.map +1 -1
  81. package/dist/esm/companion/technical-documentation-capture-execution-port.d.ts.map +1 -1
  82. package/dist/esm/companion/technical-documentation-capture-execution-port.js.map +1 -1
  83. package/dist/esm/companion/technical-documentation-diagram-definitions.d.ts.map +1 -1
  84. package/dist/esm/companion/technical-documentation-diagram-definitions.js.map +1 -1
  85. package/dist/esm/companion/technical-documentation-diagram-materializer.d.ts.map +1 -1
  86. package/dist/esm/companion/technical-documentation-diagram-materializer.js.map +1 -1
  87. package/dist/esm/companion/technical-documentation-placeholder-materializer.d.ts.map +1 -1
  88. package/dist/esm/companion/zod-to-openapi.d.ts.map +1 -1
  89. package/dist/esm/companion-exports.d.ts.map +1 -1
  90. package/dist/esm/config/define-tech-doc-config.d.ts.map +1 -1
  91. package/dist/esm/config/index.d.ts.map +1 -1
  92. package/dist/esm/config/wildo-tech-doc-config.schemas.d.ts.map +1 -1
  93. package/dist/esm/config/wildo-tech-doc-config.schemas.js.map +1 -1
  94. package/dist/esm/content/application-consumer-documentation-content-manifest.schemas.d.ts.map +1 -1
  95. package/dist/esm/content/application-consumer-documentation-content.techdoc.d.ts +27 -24
  96. package/dist/esm/content/application-consumer-documentation-content.techdoc.d.ts.map +1 -1
  97. package/dist/esm/content/application-consumer-documentation-content.techdoc.js +205 -82
  98. package/dist/esm/content/application-consumer-documentation-content.techdoc.js.map +1 -1
  99. package/dist/esm/content.exports.d.ts.map +1 -1
  100. package/dist/esm/index.d.ts.map +1 -1
  101. package/dist/esm/openapi/api-reference-link-index.d.ts +3 -0
  102. package/dist/esm/openapi/api-reference-link-index.d.ts.map +1 -1
  103. package/dist/esm/openapi/api-reference-link-index.js +26 -15
  104. package/dist/esm/openapi/api-reference-link-index.js.map +1 -1
  105. package/dist/esm/openapi/api-reference-pages.d.ts +55 -0
  106. package/dist/esm/openapi/api-reference-pages.d.ts.map +1 -0
  107. package/dist/esm/openapi/api-reference-pages.js +229 -0
  108. package/dist/esm/openapi/api-reference-pages.js.map +1 -0
  109. package/dist/esm/openapi/api-reference-search.d.ts +53 -0
  110. package/dist/esm/openapi/api-reference-search.d.ts.map +1 -0
  111. package/dist/esm/openapi/api-reference-search.js +100 -0
  112. package/dist/esm/openapi/api-reference-search.js.map +1 -0
  113. package/dist/esm/openapi/api-reference-targets.d.ts +11 -0
  114. package/dist/esm/openapi/api-reference-targets.d.ts.map +1 -1
  115. package/dist/esm/openapi/api-reference-targets.js +8 -0
  116. package/dist/esm/openapi/api-reference-targets.js.map +1 -1
  117. package/dist/esm/openapi/index.d.ts +2 -0
  118. package/dist/esm/openapi/index.d.ts.map +1 -1
  119. package/dist/esm/openapi/index.js +2 -0
  120. package/dist/esm/openapi/index.js.map +1 -1
  121. package/dist/esm/openapi/openapi-generation-output.schemas.d.ts.map +1 -1
  122. package/dist/esm/openapi-reference-model.exports.d.ts +2 -0
  123. package/dist/esm/openapi-reference-model.exports.d.ts.map +1 -1
  124. package/dist/esm/openapi-reference-model.exports.js +2 -0
  125. package/dist/esm/openapi-reference-model.exports.js.map +1 -1
  126. package/dist/esm/runtime/AuthExchangePage.d.ts +45 -4
  127. package/dist/esm/runtime/AuthExchangePage.d.ts.map +1 -1
  128. package/dist/esm/runtime/AuthExchangePage.js +45 -12
  129. package/dist/esm/runtime/AuthExchangePage.js.map +1 -1
  130. package/dist/esm/runtime/DocsAuthContext.d.ts +2 -2
  131. package/dist/esm/runtime/DocsAuthContext.d.ts.map +1 -1
  132. package/dist/esm/runtime/DocsAuthContext.js +8 -4
  133. package/dist/esm/runtime/DocsAuthContext.js.map +1 -1
  134. package/dist/esm/runtime/DocsFrontendProviders.d.ts +30 -0
  135. package/dist/esm/runtime/DocsFrontendProviders.d.ts.map +1 -0
  136. package/dist/esm/runtime/DocsFrontendProviders.js +39 -0
  137. package/dist/esm/runtime/DocsFrontendProviders.js.map +1 -0
  138. package/dist/esm/runtime/DocsProviderComponent.d.ts +41 -0
  139. package/dist/esm/runtime/DocsProviderComponent.d.ts.map +1 -0
  140. package/dist/esm/runtime/DocsProviderComponent.js +17 -0
  141. package/dist/esm/runtime/DocsProviderComponent.js.map +1 -0
  142. package/dist/esm/runtime/decode-jwt-claims.d.ts.map +1 -1
  143. package/dist/esm/runtime/docs-auth-client.d.ts.map +1 -1
  144. package/dist/esm/runtime/docs-auth-session.schemas.d.ts.map +1 -1
  145. package/dist/esm/runtime/documentation-site-translator.d.ts +53 -0
  146. package/dist/esm/runtime/documentation-site-translator.d.ts.map +1 -0
  147. package/dist/esm/runtime/documentation-site-translator.js +51 -0
  148. package/dist/esm/runtime/documentation-site-translator.js.map +1 -0
  149. package/dist/esm/runtime/frontend-provider-registry.techdoc.d.ts +1 -2
  150. package/dist/esm/runtime/frontend-provider-registry.techdoc.d.ts.map +1 -1
  151. package/dist/esm/runtime/frontend-provider-registry.techdoc.js.map +1 -1
  152. package/dist/esm/runtime/index.d.ts +8 -0
  153. package/dist/esm/runtime/index.d.ts.map +1 -1
  154. package/dist/esm/runtime/index.js +8 -0
  155. package/dist/esm/runtime/index.js.map +1 -1
  156. package/dist/esm/runtime/openapi-reference-conservation.d.ts.map +1 -1
  157. package/dist/esm/runtime/openapi-reference-model.d.ts +30 -0
  158. package/dist/esm/runtime/openapi-reference-model.d.ts.map +1 -1
  159. package/dist/esm/runtime/openapi-reference-model.js +85 -11
  160. package/dist/esm/runtime/openapi-reference-model.js.map +1 -1
  161. package/dist/esm/runtime/openapi-reference-navigation.d.ts +50 -0
  162. package/dist/esm/runtime/openapi-reference-navigation.d.ts.map +1 -0
  163. package/dist/esm/runtime/openapi-reference-navigation.js +46 -0
  164. package/dist/esm/runtime/openapi-reference-navigation.js.map +1 -0
  165. package/dist/esm/runtime/openapi-reference-samples.d.ts +40 -0
  166. package/dist/esm/runtime/openapi-reference-samples.d.ts.map +1 -0
  167. package/dist/esm/runtime/openapi-reference-samples.js +169 -0
  168. package/dist/esm/runtime/openapi-reference-samples.js.map +1 -0
  169. package/dist/esm/runtime/openapi-reference-styles.d.ts +28 -0
  170. package/dist/esm/runtime/openapi-reference-styles.d.ts.map +1 -0
  171. package/dist/esm/runtime/openapi-reference-styles.js +292 -0
  172. package/dist/esm/runtime/openapi-reference-styles.js.map +1 -0
  173. package/dist/esm/runtime/openapi-reference-view.d.ts +40 -5
  174. package/dist/esm/runtime/openapi-reference-view.d.ts.map +1 -1
  175. package/dist/esm/runtime/openapi-reference-view.js +828 -82
  176. package/dist/esm/runtime/openapi-reference-view.js.map +1 -1
  177. package/dist/esm/runtime/openapi-reference-words-context.d.ts +12 -0
  178. package/dist/esm/runtime/openapi-reference-words-context.d.ts.map +1 -0
  179. package/dist/esm/runtime/openapi-reference-words-context.js +30 -0
  180. package/dist/esm/runtime/openapi-reference-words-context.js.map +1 -0
  181. package/dist/esm/runtime/openapi-reference-words.d.ts +205 -0
  182. package/dist/esm/runtime/openapi-reference-words.d.ts.map +1 -0
  183. package/dist/esm/runtime/openapi-reference-words.js +162 -0
  184. package/dist/esm/runtime/openapi-reference-words.js.map +1 -0
  185. package/dist/esm/runtime/provider-component-registry.techdoc.d.ts +31 -0
  186. package/dist/esm/runtime/provider-component-registry.techdoc.d.ts.map +1 -0
  187. package/dist/esm/runtime/provider-component-registry.techdoc.js +35 -0
  188. package/dist/esm/runtime/provider-component-registry.techdoc.js.map +1 -0
  189. package/dist/esm/runtime/use-docs-auth-session.d.ts.map +1 -1
  190. package/dist/esm/runtime/use-docs-frontend-provider-registry.d.ts +1 -0
  191. package/dist/esm/runtime/use-docs-frontend-provider-registry.d.ts.map +1 -1
  192. package/dist/esm/runtime/use-docs-frontend-provider-registry.js +10 -2
  193. package/dist/esm/runtime/use-docs-frontend-provider-registry.js.map +1 -1
  194. package/dist/esm/runtime/use-docs-provider-component.d.ts +29 -0
  195. package/dist/esm/runtime/use-docs-provider-component.d.ts.map +1 -0
  196. package/dist/esm/runtime/use-docs-provider-component.js +42 -0
  197. package/dist/esm/runtime/use-docs-provider-component.js.map +1 -0
  198. package/dist/esm/runtime/use-docs-provider-scripts.d.ts +37 -0
  199. package/dist/esm/runtime/use-docs-provider-scripts.d.ts.map +1 -0
  200. package/dist/esm/runtime/use-docs-provider-scripts.js +47 -0
  201. package/dist/esm/runtime/use-docs-provider-scripts.js.map +1 -0
  202. package/dist/esm/runtime/use-docs-provider-sdks.d.ts.map +1 -1
  203. package/dist/esm/runtime/use-docs-provider-sdks.js.map +1 -1
  204. package/dist/tsconfig.build.tsbuildinfo +1 -1
  205. 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
- export function OpenApiReferenceView({ model }) {
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 [selectedResourceRef, setSelectedResourceRef] = useState(null);
14
- const [activeFragment, setActiveFragment] = useState(() => currentFragment());
15
- const resources = useMemo(() => filterResources(model, query), [model, query]);
16
- const selectedResource = model.resources.find((resource) => resource.resourceRef === selectedResourceRef) ?? null;
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
- const synchronizeSelection = () => {
19
- const fragment = currentFragment();
20
- setActiveFragment(fragment);
21
- setSelectedResourceRef(findResourceForFragment(model.resources, fragment)?.resourceRef ?? null);
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
- synchronizeSelection();
24
- window.addEventListener('hashchange', synchronizeSelection);
25
- return () => window.removeEventListener('hashchange', synchronizeSelection);
26
- }, [model]);
27
- /**
28
- * The browser performs its ordinary fragment jump before React has mounted a
29
- * resource selected from a deep link. Repeat the jump after that resource is
30
- * present so resource, family and variant URLs all behave as true deep links.
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
- }, [activeFragment, selectedResource]);
37
- return _jsxs(_Fragment, { children: [model.description !== null ? _jsx(OpenApiDocumentDescription, { description: model.description }) : null, (model.servers.length > 0 || model.securitySchemes.length > 0)
38
- ? _jsx(ApiConnectionDetails, { servers: model.servers, securitySchemes: model.securitySchemes })
39
- : null, _jsxs("p", { children: [model.operationCount, " operations across ", model.resources.length, " resources."] }), _jsxs("div", { className: "margin-bottom--lg", children: [_jsx("label", { htmlFor: "api-reference-search", children: "Search this API reference" }), _jsx("input", { id: "api-reference-search", className: "margin-left--sm", type: "search", "aria-describedby": "api-reference-search-status", value: query, onChange: (event) => {
40
- setQuery(event.target.value);
41
- clearResourceSelection();
42
- }, placeholder: "Resource, operation, path, role\u2026" }), query.length > 0 ? _jsx("button", { className: "button button--secondary button--sm margin-left--sm", type: "button", onClick: () => setQuery(''), children: "Clear" }) : null] }), _jsx("p", { id: "api-reference-search-status", className: "margin-bottom--lg", role: "status", "aria-live": "polite", children: query.length > 0 ? `${resources.length} matching resource${resources.length === 1 ? '' : 's'}.` : '' }), _jsxs("details", { open: selectedResource === null, className: "margin-bottom--xl", children: [_jsxs("summary", { children: ["Browse ", resources.length, " resource", resources.length === 1 ? '' : 's'] }), _jsx("nav", { "aria-label": "API resources", children: resourceCategoryGroups(resources).map((group) => _jsxs("section", { className: "margin-top--lg", children: [group.category !== null ? _jsxs(_Fragment, { children: [_jsx("h2", { id: group.anchorId, children: group.category.label }), group.category.description !== null ? _jsx("p", { children: group.category.description }) : null] }) : null, _jsx("ul", { style: { display: 'grid', gap: '0.75rem', gridTemplateColumns: 'repeat(auto-fit, minmax(16rem, 1fr))', listStyle: 'none', margin: '1rem 0 0', padding: 0 }, children: group.resources.map((resource) => _jsx(ResourceCard, { resource: resource }, resource.resourceRef)) })] }, group.id)) })] }), resources.length === 0 ? _jsx("p", { role: "status", children: "No resources or operation variants match this search." }) : null, selectedResource === null && resources.length > 0 ? _jsx("p", { role: "status", children: "Select a resource to explore its operation families and variants." }) : null, selectedResource !== null ? _jsxs("section", { className: "margin-bottom--xl", children: [_jsx("p", { children: _jsx("button", { className: "button button--secondary button--sm", type: "button", onClick: clearResourceSelection, children: "All resources" }) }), _jsx(ResourceReference, { resource: selectedResource, activeFragment: activeFragment })] }, selectedResource.resourceRef) : null, _jsx(ComponentSchemaReference, { schemas: model.componentSchemas, activeFragment: activeFragment })] });
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: "margin-bottom--lg", "aria-label": "API overview", children: [_jsx(OpenApiCommonMark, { source: overview }), conventions !== null && conventions.length > 0 ? _jsxs("details", { className: "margin-top--md", children: [_jsx("summary", { children: _jsx("h2", { style: { display: 'inline', fontSize: 'inherit', margin: 0 }, children: "API conventions" }) }), _jsx("div", { className: "margin-top--md", children: _jsx(OpenApiCommonMark, { source: conventions }) })] }) : null] });
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
- return _jsx("section", { className: "card margin-bottom--lg", "aria-labelledby": "api-connection-details", children: _jsxs("div", { className: "card__body", children: [_jsx("h2", { id: "api-connection-details", children: "Connection details" }), servers.length > 0 ? _jsxs(_Fragment, { children: [_jsx("h3", { children: "Base URLs" }), _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: "Authentication mechanisms" }), _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: ["Type: ", _jsx("code", { children: securityScheme.type }), securityScheme.scheme !== null ? _jsxs(_Fragment, { children: [" \u00B7 Scheme: ", _jsx("code", { children: securityScheme.scheme })] }) : null, securityScheme.bearerFormat !== null ? _jsxs(_Fragment, { children: [" \u00B7 Format: ", _jsx("code", { children: securityScheme.bearerFormat })] }) : null, securityScheme.location !== null ? _jsxs(_Fragment, { children: [" \u00B7 Location: ", _jsx("code", { children: securityScheme.location })] }) : null, securityScheme.parameterName !== null ? _jsxs(_Fragment, { children: [" \u00B7 Name: ", _jsx("code", { children: securityScheme.parameterName })] }) : null] }), securityScheme.description !== null ? _jsx("p", { children: _jsx(OpenApiInlineCommonMark, { source: securityScheme.description }) }) : null] })] }, securityScheme.name)) })] }) : null] }) });
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", { style: { border: '1px solid var(--ifm-color-emphasis-300)', borderRadius: '0.5rem', padding: '1rem' }, children: [_jsx("a", { href: `#${resource.anchorId}`, children: _jsx("strong", { children: resource.label }) }), resource.description !== null ? _jsx("p", { className: "margin-top--sm margin-bottom--sm", children: resource.description }) : null, _jsxs("p", { className: "margin-bottom--none text--muted", children: [resource.families.length, " operation ", resource.families.length === 1 ? 'family' : 'families', " \u00B7 ", variantCount, " variant", variantCount === 1 ? '' : 's'] })] });
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
- .sort((left, right) => (left.category?.label ?? '').localeCompare(right.category?.label ?? ''));
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
- function ResourceReference({ resource, activeFragment }) {
139
- return _jsxs(_Fragment, { children: [_jsx("h2", { id: resource.anchorId, children: resource.label }), resource.description !== null ? _jsx("p", { children: resource.description }) : null, resource.lifecycleRole !== null ? _jsxs(_Fragment, { children: [_jsx("h3", { children: "Lifecycle" }), _jsx("p", { children: resource.lifecycleRole })] }) : null, resource.relationships.length > 0 ? _jsx(RelationshipSemantics, { relationships: resource.relationships }) : null, resource.families.map((family) => _jsx("div", { className: "card margin-bottom--md", children: _jsxs("div", { className: "card__body", children: [_jsx("h3", { id: family.anchorId, children: family.label }), _jsxs("p", { className: "text--muted", children: [family.variants.length, " documented variant", family.variants.length === 1 ? '' : 's'] }), family.variants.map((variant) => _jsx(OperationVariant, { variant: variant, activeFragment: activeFragment }, variant.operationId))] }) }, family.familyRef))] });
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
- return _jsxs("details", { className: "margin-bottom--lg", children: [_jsxs("summary", { children: ["Data relationships (", relationships.length, ")"] }), _jsx("ul", { className: "margin-top--md", children: relationships.map((relationship, index) => _jsxs("li", { children: [_jsx("strong", { children: relationship.relatedResourceLabel }), relationship.foreignKeyField !== null ? _jsxs(_Fragment, { children: [" via ", _jsx("code", { children: relationship.foreignKeyField })] }) : null, ": ", relationship.nature, " relationship (", relationship.resourceCardinality, " to ", relationship.relatedResourceCardinality, "); lifecycle: ", relationship.lifecycleModel, ".", relationship.onParentDelete !== null
143
- ? _jsxs(_Fragment, { children: [" Parent-delete policy: ", relationship.onParentDelete.enabled ? `${relationship.onParentDelete.strategy} (${relationship.onParentDelete.mode}, depth ${relationship.onParentDelete.maxDepth})` : 'does not cascade', "."] })
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.anchorId === fragment
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
- function OperationVariant({ variant, activeFragment }) {
196
- const [isContractOpen, setIsContractOpen] = useState(() => isContractTarget(variant, activeFragment));
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
- if (isContractTarget(variant, activeFragment))
199
- setIsContractOpen(true);
200
- }, [activeFragment, variant]);
201
- return _jsxs("article", { className: "margin-bottom--lg", children: [_jsx("h4", { id: variant.anchorId, children: variant.summary }), _jsxs("p", { children: [_jsx("code", { children: variant.method }), " ", _jsx("code", { children: variant.path })] }), variant.description !== null ? _jsx("p", { children: variant.description }) : null, _jsxs("p", { className: "margin-bottom--sm", children: [_jsx("strong", { children: "Access:" }), " ", variant.access.authenticationSummary, variant.access.roleRequirements.length > 0 ? _jsxs(_Fragment, { children: [" Authorization: any one of ", variant.access.roleRequirements.map((requirement, index) => _jsxs(Fragment, { children: [index > 0 ? ' or ' : '', _jsx("strong", { children: requirement.label }), " (", _jsx("code", { children: requirement.role }), ")"] }, requirement.role)), "."] }) : null, ' ', variant.securitySchemes.length > 0 ? _jsxs(_Fragment, { children: ["Authentication: ", _jsx("code", { children: variant.securitySchemes.join(' or ') }), ". "] }) : null, variant.idempotent !== null ? _jsxs(_Fragment, { children: ["Idempotent: ", _jsx("code", { children: variant.idempotent ? 'yes' : 'no' }), "."] }) : null] }), variant.access.roleRequirements.some((requirement) => requirement.businessRole !== null || requirement.authorityBoundary !== null) ? _jsxs("details", { className: "margin-bottom--sm", children: [_jsx("summary", { children: "Role authority details" }), _jsx("ul", { className: "margin-top--sm", children: variant.access.roleRequirements.map((requirement) => _jsxs("li", { children: [_jsx("strong", { children: requirement.label }), requirement.businessRole !== null ? _jsxs(_Fragment, { children: [" \u2014 ", requirement.businessRole] }) : null, requirement.authorityBoundary !== null ? _jsxs("p", { className: "margin-bottom--sm", children: ["Boundary: ", requirement.authorityBoundary] }) : null] }, requirement.role)) })] }) : null, _jsxs("details", { open: isContractOpen, onToggle: (event) => setIsContractOpen(event.currentTarget.open), children: [_jsx("summary", { children: "Request and response contract" }), _jsxs("div", { className: "margin-top--md", children: [_jsx("h5", { children: "Parameters" }), variant.parameters.length === 0 ? _jsx("p", { children: "No parameters." }) : _jsx(ParameterTable, { parameters: variant.parameters }), variant.requestBody !== null ? _jsxs(_Fragment, { children: [_jsxs("h5", { id: variant.requestBody.anchorId, children: ["Request body ", variant.requestBody.required ? '(required)' : '(optional)'] }), _jsx(RepresentationList, { mediaTypes: variant.requestBody.mediaTypes })] }) : null, _jsx("h5", { children: "Responses" }), variant.responses.length === 0 ? _jsx("p", { children: "No response contract was emitted." }) : variant.responses.map((response) => _jsx(ResponseContract, { response: response }, response.status))] })] })] });
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
- /** Opens the native contract disclosure before a request/response fragment is scrolled into view. */
204
- function isContractTarget(variant, fragment) {
205
- return variant.requestBody?.anchorId === fragment || variant.responses.some((response) => response.anchorId === fragment);
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
- function ParameterTable({ parameters }) {
213
- return _jsx("div", { style: { maxWidth: '100%', overflowX: 'auto' }, children: _jsxs("table", { children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { children: "Name" }), _jsx("th", { children: "Location" }), _jsx("th", { children: "Type" }), _jsx("th", { children: "Required" }), _jsx("th", { children: "Description" }), _jsx("th", { children: "Examples" })] }) }), _jsx("tbody", { children: parameters.map((parameter) => _jsxs("tr", { children: [_jsx("td", { children: _jsx("code", { children: parameter.name }) }), _jsx("td", { children: parameter.location }), _jsx("td", { children: parameter.schema === null ? '—' : _jsx(SchemaType, { schema: parameter.schema }) }), _jsx("td", { children: parameter.required ? 'Yes' : 'No' }), _jsx("td", { children: parameter.description ?? '—' }), _jsx("td", { children: parameter.examples.length > 0 ? _jsx(Examples, { examples: parameter.examples }) : '—' })] }, `${parameter.location}:${parameter.name}`)) })] }) });
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
- return _jsxs("section", { className: "margin-bottom--md", children: [_jsxs("p", { id: response.anchorId, className: "margin-bottom--sm", children: [_jsx("strong", { children: _jsx("code", { children: response.status }) }), response.description !== null ? ` — ${response.description}` : ''] }), _jsx(RepresentationList, { mediaTypes: response.mediaTypes })] });
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: "No structured representation." });
221
- return _jsx("ul", { children: mediaTypes.map((representation) => _jsxs("li", { children: [_jsx("code", { children: representation.mediaType }), representation.schema !== null ? _jsxs(_Fragment, { children: [" \u2014 ", _jsx(SchemaType, { schema: representation.schema })] }) : null, representation.examples.length > 0 ? _jsx(Examples, { examples: representation.examples }) : null] }, representation.mediaType)) });
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
- /** Presents a schema identity without resolving component references in React. */
224
- function SchemaType({ schema }) {
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 ? 'any value' : 'no value' });
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: "schema" });
872
+ return _jsx("code", { children: words.unnamedSchema });
237
873
  }
238
- function schemaHasNestedContract(schema) {
239
- return schema.properties.length > 0
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
- /** Recursively renders only the contract fields carried by the OpenAPI Schema Object. */
255
- function SchemaContract({ schema }) {
256
- return _jsxs("div", { children: [schema.description !== null ? _jsx("p", { children: schema.description }) : null, schema.format !== null ? _jsxs("p", { className: "margin-bottom--sm", children: [_jsx("strong", { children: "Format:" }), " ", _jsx("code", { children: schema.format })] }) : null, schema.defaultValue !== null ? _jsxs("p", { className: "margin-bottom--sm", children: [_jsx("strong", { children: "Default:" }), " ", _jsx("code", { children: schema.defaultValue })] }) : null, schema.constValue !== null ? _jsxs("p", { className: "margin-bottom--sm", children: [_jsx("strong", { children: "Constant:" }), " ", _jsx("code", { children: schema.constValue })] }) : null, schema.constraints.length > 0 ? _jsx("dl", { className: "margin-bottom--md", children: schema.constraints.map((constraint) => _jsxs(Fragment, { children: [_jsx("dt", { children: _jsx("code", { children: constraint.kind }) }), _jsx("dd", { children: _jsx("code", { children: constraint.jsonValue }) })] }, constraint.kind)) }) : null, schema.enumValues.length > 0 ? _jsxs(_Fragment, { children: [_jsx("p", { className: "margin-bottom--sm", children: _jsx("strong", { children: "Allowed values" }) }), _jsx("ul", { children: schema.enumValues.map((enumValue) => _jsxs("li", { children: [_jsx("code", { children: enumValue.jsonValue }), enumValue.description !== null ? ` — ${enumValue.description}` : ''] }, enumValue.jsonValue)) })] }) : null, schema.properties.length > 0 ? _jsxs(_Fragment, { children: [_jsx("p", { className: "margin-bottom--sm", children: _jsx("strong", { children: "Properties" }) }), _jsx("ul", { style: { listStyle: 'none', paddingInlineStart: '1rem' }, children: schema.properties.map((property) => _jsx(SchemaProperty, { property: property }, property.name)) })] }) : null, schema.items !== null ? _jsxs("details", { className: "margin-bottom--sm", children: [_jsxs("summary", { children: ["Array items \u2014 ", _jsx(SchemaType, { schema: schema.items })] }), _jsx("div", { className: "margin-left--md margin-top--sm", children: _jsx(SchemaContract, { schema: schema.items }) })] }) : null, schema.compositions.map((composition) => _jsxs("section", { className: "margin-bottom--md", children: [_jsx("p", { className: "margin-bottom--sm", children: _jsx("strong", { children: schemaCompositionLabel(composition.kind) }) }), _jsx("ol", { children: composition.branches.map((branch, index) => _jsxs("li", { className: "margin-bottom--sm", children: [_jsx(SchemaType, { schema: branch }), schemaHasContractDetails(branch)
257
- ? _jsx("div", { className: "margin-left--md margin-top--sm", children: _jsx(SchemaContract, { schema: branch }) })
258
- : null] }, index)) })] }, composition.kind)), schema.additionalProperties !== null ? _jsxs("div", { className: "margin-bottom--sm", children: [_jsx("strong", { children: "Additional properties:" }), typeof schema.additionalProperties === 'boolean'
259
- ? ` ${schema.additionalProperties ? 'allowed' : 'not allowed'}`
260
- : _jsxs(_Fragment, { children: [_jsx("span", { children: " " }), _jsx(SchemaType, { schema: schema.additionalProperties }), _jsx("div", { className: "margin-left--md", children: _jsx(SchemaContract, { schema: schema.additionalProperties }) })] })] }) : null] });
261
- }
262
- function SchemaProperty({ property }) {
263
- const identity = _jsxs(_Fragment, { children: [_jsx("code", { children: property.name }), " \u2014 ", _jsx(SchemaType, { schema: property.schema }), " \u2014 ", property.required ? 'required' : 'optional'] });
264
- return _jsx("li", { className: "margin-bottom--sm", style: { borderLeft: '2px solid var(--ifm-color-emphasis-300)', paddingInlineStart: '0.75rem' }, children: schemaHasNestedContract(property.schema)
265
- ? _jsxs("details", { children: [_jsx("summary", { children: identity }), _jsx("div", { className: "margin-top--sm", children: _jsx(SchemaContract, { schema: property.schema }) })] })
266
- : _jsxs(_Fragment, { children: [_jsx("p", { className: "margin-bottom--sm", children: identity }), _jsx(SchemaContract, { schema: property.schema })] }) });
267
- }
268
- function schemaCompositionLabel(kind) {
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 'One of';
271
- case ApiReferenceSchemaCompositionKind.ANY_OF: return 'Any of';
272
- case ApiReferenceSchemaCompositionKind.ALL_OF: return 'All of';
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: "margin-bottom--xl", open: selectedSchema !== null, children: [_jsxs("summary", { children: ["Reusable data models (", schemas.length, ")"] }), selectedSchema === null ? _jsx("p", { className: "margin-top--md", children: "Select a request or response model link to inspect its OpenAPI schema." }) : _jsxs("section", { className: "margin-top--lg", children: [_jsx("h2", { id: selectedSchema.anchorId, children: selectedSchema.name }), _jsxs("p", { children: [_jsx("strong", { children: "Type:" }), " ", _jsx("span", { children: " " }), _jsx(SchemaType, { schema: selectedSchema.schema })] }), _jsx(SchemaContract, { schema: selectedSchema.schema })] })] });
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
- return _jsxs("details", { className: "margin-top--sm", children: [_jsxs("summary", { children: ["Examples (", examples.length, ")"] }), examples.map((example) => _jsxs("div", { className: "margin-top--sm", children: [_jsxs("p", { className: "margin-bottom--sm", children: [_jsx("strong", { children: example.name }), example.summary !== null ? ` — ${example.summary}` : ''] }), example.description !== null ? _jsx("p", { className: "margin-bottom--sm", children: example.description }) : null, example.jsonValue !== null ? _jsx("pre", { children: _jsx("code", { children: example.jsonValue }) }) : null, example.externalValue !== null ? _jsxs("p", { className: "margin-bottom--sm", children: ["External example: ", _jsx("a", { href: example.externalValue, children: example.externalValue })] }) : null, example.jsonValue === null && example.externalValue === null ? _jsx("p", { className: "margin-bottom--sm", children: "No inline example value was supplied." }) : null] }, example.name))] });
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