@gitbook/react-openapi 1.1.6 → 1.1.7

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 (50) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/dist/InteractiveSection.d.ts +0 -2
  3. package/dist/InteractiveSection.jsx +3 -4
  4. package/dist/OpenAPICodeSample.jsx +4 -4
  5. package/dist/OpenAPICodeSampleInteractive.d.ts +4 -3
  6. package/dist/OpenAPICodeSampleInteractive.jsx +22 -15
  7. package/dist/OpenAPICopyButton.d.ts +7 -0
  8. package/dist/OpenAPICopyButton.jsx +6 -6
  9. package/dist/OpenAPIOperation.jsx +21 -1
  10. package/dist/OpenAPIPath.jsx +2 -2
  11. package/dist/OpenAPIRequestBody.jsx +1 -1
  12. package/dist/OpenAPIResponse.jsx +1 -1
  13. package/dist/OpenAPIResponses.jsx +2 -2
  14. package/dist/OpenAPISchema.d.ts +5 -14
  15. package/dist/OpenAPISchema.jsx +79 -28
  16. package/dist/OpenAPISchemaName.jsx +1 -0
  17. package/dist/OpenAPISchemaServer.d.ts +12 -0
  18. package/dist/OpenAPISchemaServer.jsx +8 -0
  19. package/dist/OpenAPISpec.d.ts +0 -6
  20. package/dist/OpenAPISpec.jsx +5 -11
  21. package/dist/OpenAPITabs.jsx +3 -11
  22. package/dist/code-samples.js +44 -9
  23. package/dist/decycle.d.ts +2 -0
  24. package/dist/decycle.js +70 -0
  25. package/dist/schemas/OpenAPISchemas.jsx +1 -1
  26. package/dist/schemas/resolveOpenAPISchemas.d.ts +2 -6
  27. package/dist/schemas/resolveOpenAPISchemas.js +1 -21
  28. package/dist/tsconfig.build.tsbuildinfo +1 -1
  29. package/dist/types.d.ts +2 -5
  30. package/package.json +1 -1
  31. package/src/InteractiveSection.tsx +2 -6
  32. package/src/OpenAPICodeSample.tsx +16 -5
  33. package/src/OpenAPICodeSampleInteractive.tsx +53 -28
  34. package/src/OpenAPICopyButton.tsx +17 -4
  35. package/src/OpenAPIOperation.tsx +39 -2
  36. package/src/OpenAPIPath.tsx +2 -2
  37. package/src/OpenAPIRequestBody.tsx +1 -1
  38. package/src/OpenAPIResponse.tsx +4 -4
  39. package/src/OpenAPIResponses.tsx +1 -5
  40. package/src/OpenAPISchema.tsx +152 -58
  41. package/src/OpenAPISchemaName.tsx +3 -0
  42. package/src/OpenAPISchemaServer.tsx +34 -0
  43. package/src/OpenAPISpec.tsx +13 -11
  44. package/src/OpenAPITabs.tsx +3 -13
  45. package/src/code-samples.test.ts +69 -1
  46. package/src/code-samples.ts +45 -9
  47. package/src/decycle.ts +68 -0
  48. package/src/schemas/OpenAPISchemas.tsx +1 -1
  49. package/src/schemas/resolveOpenAPISchemas.ts +3 -31
  50. package/src/types.ts +6 -6
package/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # @gitbook/react-openapi
2
2
 
3
+ ## 1.1.7
4
+
5
+ ### Patch Changes
6
+
7
+ - bd35348: Fix missing alternative schemas
8
+ - ae78fc5: Fix XML in code sample
9
+ - 7bb37c7: Move filterSelectedOpenAPISchemas to @gitbook/openapi-parser
10
+ - 373183a: Safe parse OpenAPI JSON schema
11
+ - 1505ddb: Fix multiple request examples selector not showing
12
+ - 61db166: Add OpenAPI write-only indicator
13
+ - 5b1e01c: Support for x-stability property
14
+ - cd99ed5: Fix spec properties rendering and missing keys
15
+ - 813b2af: Support for x-enumDescriptions and x-gitbook-enum
16
+ - a25fded: Replace $ref with $reference in json-decycle
17
+ - Updated dependencies [7bb37c7]
18
+ - Updated dependencies [5b1e01c]
19
+ - Updated dependencies [813b2af]
20
+ - @gitbook/openapi-parser@2.1.2
21
+
3
22
  ## 1.1.6
4
23
 
5
24
  ### Patch Changes
@@ -23,8 +23,6 @@ export declare function InteractiveSection(props: {
23
23
  defaultTab?: string;
24
24
  /** Content of the header */
25
25
  header?: React.ReactNode;
26
- /** Body of the section */
27
- children?: React.ReactNode;
28
26
  /** Children to display within the container */
29
27
  overlay?: React.ReactNode;
30
28
  }): import("react").JSX.Element;
@@ -10,7 +10,7 @@ import { Section, SectionBody, SectionHeader, SectionHeaderContent } from './Sta
10
10
  */
11
11
  export function InteractiveSection(props) {
12
12
  var _a, _b, _c;
13
- var id = props.id, className = props.className, _d = props.toggeable, toggeable = _d === void 0 ? false : _d, _e = props.defaultOpened, defaultOpened = _e === void 0 ? true : _e, _f = props.tabs, tabs = _f === void 0 ? [] : _f, _g = props.defaultTab, defaultTab = _g === void 0 ? (_a = tabs[0]) === null || _a === void 0 ? void 0 : _a.key : _g, header = props.header, children = props.children, overlay = props.overlay, _h = props.toggleIcon, toggleIcon = _h === void 0 ? '▶' : _h;
13
+ var id = props.id, className = props.className, _d = props.toggeable, toggeable = _d === void 0 ? false : _d, _e = props.defaultOpened, defaultOpened = _e === void 0 ? true : _e, _f = props.tabs, tabs = _f === void 0 ? [] : _f, _g = props.defaultTab, defaultTab = _g === void 0 ? (_a = tabs[0]) === null || _a === void 0 ? void 0 : _a.key : _g, header = props.header, overlay = props.overlay, _h = props.toggleIcon, toggleIcon = _h === void 0 ? '▶' : _h;
14
14
  var _j = useState(defaultTab), selectedTabKey = _j[0], setSelectedTab = _j[1];
15
15
  var selectedTab = (_b = tabs.find(function (tab) { return tab.key === selectedTabKey; })) !== null && _b !== void 0 ? _b : tabs[0];
16
16
  var state = useDisclosureState({
@@ -28,7 +28,7 @@ export function InteractiveSection(props) {
28
28
  }
29
29
  }} className={className}>
30
30
  <SectionHeaderContent className={className}>
31
- {(children || (selectedTab === null || selectedTab === void 0 ? void 0 : selectedTab.body)) && toggeable ? (<button {...mergeProps(buttonProps, focusProps)} ref={triggerRef} className={clsx('openapi-section-toggle', "".concat(className, "-toggle"))} style={{
31
+ {(selectedTab === null || selectedTab === void 0 ? void 0 : selectedTab.body) && toggeable ? (<button {...mergeProps(buttonProps, focusProps)} ref={triggerRef} className={clsx('openapi-section-toggle', "".concat(className, "-toggle"))} style={{
32
32
  outline: isFocusVisible
33
33
  ? '2px solid rgb(var(--primary-color-500) / 0.4)'
34
34
  : 'none',
@@ -50,8 +50,7 @@ export function InteractiveSection(props) {
50
50
  </select>) : null}
51
51
  </div>
52
52
  </SectionHeader>) : null}
53
- {(!toggeable || state.isExpanded) && (children || (selectedTab === null || selectedTab === void 0 ? void 0 : selectedTab.body)) ? (<SectionBody ref={panelRef} {...panelProps} className={className}>
54
- {children}
53
+ {(!toggeable || state.isExpanded) && (selectedTab === null || selectedTab === void 0 ? void 0 : selectedTab.body) ? (<SectionBody ref={panelRef} {...panelProps} className={className}>
55
54
  {selectedTab === null || selectedTab === void 0 ? void 0 : selectedTab.body}
56
55
  </SectionBody>) : null}
57
56
  {overlay ? (<div className={clsx('openapi-section-overlay', "".concat(className, "-overlay"))}>
@@ -120,7 +120,7 @@ function generateCodeSamples(props) {
120
120
  return {
121
121
  key: "default-".concat(generator.id),
122
122
  label: generator.label,
123
- body: <OpenAPIMediaTypeExamplesBody data={data} renderers={renderers}/>,
123
+ body: (<OpenAPIMediaTypeExamplesBody method={data.method} path={data.path} renderers={renderers}/>),
124
124
  footer: (<OpenAPICodeSampleFooter renderers={renderers} data={data} context={context}/>),
125
125
  };
126
126
  }
@@ -145,15 +145,15 @@ function OpenAPICodeSampleFooter(props) {
145
145
  var method = data.method, path = data.path;
146
146
  var specUrl = context.specUrl;
147
147
  var hideTryItPanel = data['x-hideTryItPanel'] || data.operation['x-hideTryItPanel'];
148
- var hasMediaTypes = renderers.length > 0;
149
- if (hideTryItPanel && !hasMediaTypes) {
148
+ var hasMultipleMediaTypes = renderers.length > 1 || renderers.some(function (renderer) { return renderer.examples.length > 0; });
149
+ if (hideTryItPanel && !hasMultipleMediaTypes) {
150
150
  return null;
151
151
  }
152
152
  if (!validateHttpMethod(method)) {
153
153
  return null;
154
154
  }
155
155
  return (<div className="openapi-codesample-footer">
156
- {hasMediaTypes ? (<OpenAPIMediaTypeExamplesSelector data={data} renderers={renderers}/>) : (<span />)}
156
+ {hasMultipleMediaTypes ? (<OpenAPIMediaTypeExamplesSelector method={data.method} path={data.path} renderers={renderers}/>) : (<span />)}
157
157
  {!hideTryItPanel && <ScalarApiButton method={method} path={path} specUrl={specUrl}/>}
158
158
  </div>);
159
159
  }
@@ -1,10 +1,11 @@
1
1
  import type { MediaTypeRenderer } from './OpenAPICodeSample';
2
- import type { OpenAPIOperationData } from './types';
3
2
  export declare function OpenAPIMediaTypeExamplesSelector(props: {
4
- data: OpenAPIOperationData;
3
+ method: string;
4
+ path: string;
5
5
  renderers: MediaTypeRenderer[];
6
6
  }): import("react").JSX.Element;
7
7
  export declare function OpenAPIMediaTypeExamplesBody(props: {
8
- data: OpenAPIOperationData;
8
+ method: string;
9
+ path: string;
9
10
  renderers: MediaTypeRenderer[];
10
11
  }): string | number | boolean | Iterable<import("react").ReactNode> | import("react").JSX.Element | null | undefined;
@@ -26,24 +26,31 @@ function useMediaTypeSampleIndexState(data, mediaType) {
26
26
  };
27
27
  }
28
28
  export function OpenAPIMediaTypeExamplesSelector(props) {
29
- var data = props.data, renderers = props.renderers;
29
+ var method = props.method, path = props.path, renderers = props.renderers;
30
30
  if (!renderers[0]) {
31
31
  throw new Error('No renderers provided');
32
32
  }
33
- var state = useMediaTypeState(data, renderers[0].mediaType);
33
+ var state = useMediaTypeState({ method: method, path: path }, renderers[0].mediaType);
34
34
  var selected = renderers.find(function (r) { return r.mediaType === state.mediaType; }) || renderers[0];
35
35
  return (<div className="openapi-codesample-selectors">
36
- <select className={clsx('openapi-select')} value={state.mediaType} onChange={function (e) { return state.setMediaType(e.target.value); }}>
37
- {renderers.map(function (renderer) { return (<option key={renderer.mediaType} value={renderer.mediaType}>
38
- {renderer.mediaType}
39
- </option>); })}
40
- </select>
41
- <ExamplesSelector data={data} renderer={selected}/>
36
+ <MediaTypeSelector state={state} renderers={renderers}/>
37
+ <ExamplesSelector method={method} path={path} renderer={selected}/>
42
38
  </div>);
43
39
  }
40
+ function MediaTypeSelector(props) {
41
+ var renderers = props.renderers, state = props.state;
42
+ if (renderers.length < 2) {
43
+ return null;
44
+ }
45
+ return (<select className={clsx('openapi-select')} value={state.mediaType} onChange={function (e) { return state.setMediaType(e.target.value); }}>
46
+ {renderers.map(function (renderer) { return (<option key={renderer.mediaType} value={renderer.mediaType}>
47
+ {renderer.mediaType}
48
+ </option>); })}
49
+ </select>);
50
+ }
44
51
  function ExamplesSelector(props) {
45
- var data = props.data, renderer = props.renderer;
46
- var state = useMediaTypeSampleIndexState(data, renderer.mediaType);
52
+ var method = props.method, path = props.path, renderer = props.renderer;
53
+ var state = useMediaTypeSampleIndexState({ method: method, path: path }, renderer.mediaType);
47
54
  if (renderer.examples.length < 2) {
48
55
  return null;
49
56
  }
@@ -55,21 +62,21 @@ function ExamplesSelector(props) {
55
62
  }
56
63
  export function OpenAPIMediaTypeExamplesBody(props) {
57
64
  var _a;
58
- var renderers = props.renderers, data = props.data;
65
+ var renderers = props.renderers, method = props.method, path = props.path;
59
66
  if (!renderers[0]) {
60
67
  throw new Error('No renderers provided');
61
68
  }
62
- var mediaTypeState = useMediaTypeState(data, renderers[0].mediaType);
69
+ var mediaTypeState = useMediaTypeState({ method: method, path: path }, renderers[0].mediaType);
63
70
  var selected = (_a = renderers.find(function (r) { return r.mediaType === mediaTypeState.mediaType; })) !== null && _a !== void 0 ? _a : renderers[0];
64
71
  if (selected.examples.length === 0) {
65
72
  return selected.element;
66
73
  }
67
- return <ExamplesBody data={data} renderer={selected}/>;
74
+ return <ExamplesBody method={method} path={path} renderer={selected}/>;
68
75
  }
69
76
  function ExamplesBody(props) {
70
77
  var _a;
71
- var data = props.data, renderer = props.renderer;
72
- var exampleState = useMediaTypeSampleIndexState(data, renderer.mediaType);
78
+ var method = props.method, path = props.path, renderer = props.renderer;
79
+ var exampleState = useMediaTypeSampleIndexState({ method: method, path: path }, renderer.mediaType);
73
80
  var example = (_a = renderer.examples[exampleState.index]) !== null && _a !== void 0 ? _a : renderer.examples[0];
74
81
  if (!example) {
75
82
  throw new Error("No example found for index ".concat(exampleState.index));
@@ -1,4 +1,11 @@
1
1
  import { type ButtonProps } from 'react-aria-components';
2
2
  export declare function OpenAPICopyButton(props: ButtonProps & {
3
3
  value: string;
4
+ children: React.ReactNode;
5
+ label?: string;
6
+ /**
7
+ * Whether to show a tooltip.
8
+ * @default true
9
+ */
10
+ withTooltip?: boolean;
4
11
  }): import("react").JSX.Element;
@@ -2,10 +2,9 @@
2
2
  import { useState } from 'react';
3
3
  import { Button, Tooltip, TooltipTrigger } from 'react-aria-components';
4
4
  export function OpenAPICopyButton(props) {
5
- var value = props.value;
6
- var children = props.children, onPress = props.onPress, className = props.className;
7
- var _a = useState(false), copied = _a[0], setCopied = _a[1];
8
- var _b = useState(false), isOpen = _b[0], setIsOpen = _b[1];
5
+ var value = props.value, label = props.label, children = props.children, onPress = props.onPress, className = props.className, _a = props.withTooltip, withTooltip = _a === void 0 ? true : _a;
6
+ var _b = useState(false), copied = _b[0], setCopied = _b[1];
7
+ var _c = useState(false), isOpen = _c[0], setIsOpen = _c[1];
9
8
  var handleCopy = function () {
10
9
  if (!value)
11
10
  return;
@@ -14,10 +13,11 @@ export function OpenAPICopyButton(props) {
14
13
  setCopied(true);
15
14
  setTimeout(function () {
16
15
  setCopied(false);
16
+ setIsOpen(false);
17
17
  }, 2000);
18
18
  });
19
19
  };
20
- return (<TooltipTrigger isOpen={isOpen} onOpenChange={setIsOpen} closeDelay={200} delay={200}>
20
+ return (<TooltipTrigger isOpen={isOpen} onOpenChange={setIsOpen} isDisabled={!withTooltip} closeDelay={200} delay={200}>
21
21
  <Button type="button" preventFocusOnPress onPress={function (e) {
22
22
  handleCopy();
23
23
  onPress === null || onPress === void 0 ? void 0 : onPress(e);
@@ -26,7 +26,7 @@ export function OpenAPICopyButton(props) {
26
26
  </Button>
27
27
 
28
28
  <Tooltip isOpen={isOpen} onOpenChange={setIsOpen} placement="top" offset={4} className="openapi-tooltip">
29
- {copied ? 'Copied' : 'Copy to clipboard'}{' '}
29
+ {copied ? 'Copied' : label || 'Copy to clipboard'}
30
30
  </Tooltip>
31
31
  </TooltipTrigger>);
32
32
  }
@@ -19,14 +19,18 @@ export function OpenAPIOperation(props) {
19
19
  };
20
20
  return (<div className={clsx('openapi-operation', className)}>
21
21
  <div className="openapi-summary" id={operation.summary ? undefined : context.id}>
22
+ {(operation.deprecated || operation['x-stability']) && (<div className="openapi-summary-tags">
23
+ {operation.deprecated && (<div className="openapi-deprecated">Deprecated</div>)}
24
+ {operation['x-stability'] && (<OpenAPIOperationStability stability={operation['x-stability']}/>)}
25
+ </div>)}
22
26
  {operation.summary
23
27
  ? context.renderHeading({
24
28
  deprecated: (_a = operation.deprecated) !== null && _a !== void 0 ? _a : false,
29
+ stability: operation['x-stability'],
25
30
  title: operation.summary,
26
31
  })
27
32
  : null}
28
33
  <OpenAPIPath data={data} context={context}/>
29
- {operation.deprecated && <div className="openapi-deprecated">Deprecated</div>}
30
34
  </div>
31
35
  <div className="openapi-columns">
32
36
  <div className="openapi-column-spec">
@@ -66,3 +70,19 @@ function OpenAPIOperationDescription(props) {
66
70
  <Markdown className="openapi-description" source={description}/>
67
71
  </div>);
68
72
  }
73
+ var stabilityEnum = {
74
+ experimental: 'Experimental',
75
+ alpha: 'Alpha',
76
+ beta: 'Beta',
77
+ stable: 'Stable',
78
+ };
79
+ function OpenAPIOperationStability(props) {
80
+ var stability = props.stability;
81
+ var foundStability = stabilityEnum[stability];
82
+ if (!foundStability) {
83
+ return null;
84
+ }
85
+ return (<div className={"openapi-stability openapi-stability-".concat(foundStability.toLowerCase())}>
86
+ {foundStability}
87
+ </div>);
88
+ }
@@ -30,7 +30,7 @@ function formatPath(path) {
30
30
  if (offset > lastIndex) {
31
31
  parts.push(path.slice(lastIndex, offset));
32
32
  }
33
- parts.push(<span key={offset} className="openapi-path-variable">
33
+ parts.push(<span key={"offset-".concat(offset)} className="openapi-path-variable">
34
34
  {match}
35
35
  </span>);
36
36
  lastIndex = offset + match.length;
@@ -41,7 +41,7 @@ function formatPath(path) {
41
41
  }
42
42
  var formattedPath = parts.map(function (part, index) {
43
43
  if (typeof part === 'string') {
44
- return <span key={index}>{part}</span>;
44
+ return <span key={"part-".concat(index)}>{part}</span>;
45
45
  }
46
46
  return part;
47
47
  });
@@ -1,5 +1,5 @@
1
1
  import { InteractiveSection } from './InteractiveSection';
2
- import { OpenAPIRootSchema } from './OpenAPISchema';
2
+ import { OpenAPIRootSchema } from './OpenAPISchemaServer';
3
3
  import { checkIsReference } from './utils';
4
4
  /**
5
5
  * Display an interactive request body.
@@ -10,7 +10,7 @@ var __assign = (this && this.__assign) || function () {
10
10
  return __assign.apply(this, arguments);
11
11
  };
12
12
  import { OpenAPIDisclosure } from './OpenAPIDisclosure';
13
- import { OpenAPISchemaProperties } from './OpenAPISchema';
13
+ import { OpenAPISchemaProperties } from './OpenAPISchemaServer';
14
14
  import { parameterToProperty, resolveDescription } from './utils';
15
15
  /**
16
16
  * Display an interactive response body.
@@ -15,7 +15,7 @@ export function OpenAPIResponses(props) {
15
15
  var description = response.description;
16
16
  return {
17
17
  id: statusCode,
18
- label: (<div className="openapi-response-tab-content" key={"response-".concat(statusCode)}>
18
+ label: (<div className="openapi-response-tab-content">
19
19
  <span className="openapi-response-statuscode">
20
20
  {statusCode}
21
21
  </span>
@@ -26,7 +26,7 @@ export function OpenAPIResponses(props) {
26
26
  return ({
27
27
  id: contentType,
28
28
  label: contentType,
29
- body: (<OpenAPIResponse key={"$response-".concat(statusCode, "-").concat(contentType)} response={response} mediaType={mediaType} context={context}/>),
29
+ body: (<OpenAPIResponse response={response} mediaType={mediaType} context={context}/>),
30
30
  });
31
31
  }),
32
32
  };
@@ -1,29 +1,20 @@
1
1
  import type { OpenAPIV3 } from '@gitbook/openapi-parser';
2
2
  import type { OpenAPIClientContext } from './types';
3
- type CircularRefsIds = Map<OpenAPIV3.SchemaObject, string>;
4
- interface OpenAPISchemaPropertyEntry {
3
+ export interface OpenAPISchemaPropertyEntry {
5
4
  propertyName?: string | undefined;
6
5
  required?: boolean | undefined;
7
6
  schema: OpenAPIV3.SchemaObject;
8
7
  }
9
- /**
10
- * Render a set of properties of an OpenAPI schema.
11
- */
12
- export declare function OpenAPISchemaProperties(props: {
8
+ export declare function OpenAPISchemaPropertiesFromServer(props: {
13
9
  id?: string;
14
- properties: OpenAPISchemaPropertyEntry[];
15
- circularRefs?: CircularRefsIds;
10
+ properties: string;
16
11
  context: OpenAPIClientContext;
17
12
  }): import("react").JSX.Element;
18
- /**
19
- * Render a root schema (such as the request body or response body).
20
- */
21
- export declare function OpenAPIRootSchema(props: {
22
- schema: OpenAPIV3.SchemaObject;
13
+ export declare function OpenAPIRootSchemaFromServer(props: {
14
+ schema: string;
23
15
  context: OpenAPIClientContext;
24
16
  }): import("react").JSX.Element;
25
17
  /**
26
18
  * Get the alternatives to display for a schema.
27
19
  */
28
20
  export declare function getSchemaAlternatives(schema: OpenAPIV3.SchemaObject, ancestors?: Set<OpenAPIV3.SchemaObject>): OpenAPIV3.SchemaObject[] | null;
29
- export {};
@@ -1,55 +1,74 @@
1
+ 'use client';
1
2
  import { useId } from 'react';
2
3
  import clsx from 'clsx';
3
4
  import { Markdown } from './Markdown';
5
+ import { OpenAPICopyButton } from './OpenAPICopyButton';
4
6
  import { OpenAPIDisclosure } from './OpenAPIDisclosure';
5
7
  import { OpenAPISchemaName } from './OpenAPISchemaName';
8
+ import { retrocycle } from './decycle';
6
9
  import { checkIsReference, resolveDescription, resolveFirstExample } from './utils';
7
10
  /**
8
11
  * Render a property of an OpenAPI schema.
9
12
  */
10
13
  function OpenAPISchemaProperty(props) {
11
- var property = props.property, _a = props.circularRefs, parentCircularRefs = _a === void 0 ? new Map() : _a, context = props.context, className = props.className;
14
+ var parentCircularRefs = props.circularRefs, context = props.context, className = props.className, property = props.property;
12
15
  var schema = property.schema;
13
16
  var id = useId();
14
17
  return (<div id={id} className={clsx('openapi-schema', className)}>
15
18
  <OpenAPISchemaPresentation property={property}/>
16
19
  {(function () {
17
- var parentCircularRef = parentCircularRefs.get(schema);
20
+ var circularRefId = parentCircularRefs.get(schema);
18
21
  // Avoid recursing infinitely, and instead render a link to the parent schema
19
- if (parentCircularRef) {
20
- return <OpenAPISchemaCircularRef id={parentCircularRef} schema={schema}/>;
22
+ if (circularRefId) {
23
+ return <OpenAPISchemaCircularRef id={circularRefId} schema={schema}/>;
21
24
  }
22
- var circularRefs = parentCircularRefs.set(schema, id);
25
+ var circularRefs = new Map(parentCircularRefs);
26
+ circularRefs.set(schema, id);
23
27
  var properties = getSchemaProperties(schema);
24
- var alternatives = getSchemaAlternatives(schema, new Set(circularRefs.keys()));
25
- return (<>
26
- {alternatives === null || alternatives === void 0 ? void 0 : alternatives.map(function (schema, index) { return (<OpenAPISchemaAlternative key={index} schema={schema} circularRefs={circularRefs} context={context}/>); })}
27
- {(properties === null || properties === void 0 ? void 0 : properties.length) ? (<OpenAPIDisclosure context={context} label={getDisclosureLabel(schema)}>
28
- <OpenAPISchemaProperties properties={properties} circularRefs={circularRefs} context={context}/>
29
- </OpenAPIDisclosure>) : null}
30
- </>);
28
+ if (properties === null || properties === void 0 ? void 0 : properties.length) {
29
+ return (<OpenAPIDisclosure context={context} label={getDisclosureLabel(schema)}>
30
+ <OpenAPISchemaProperties properties={properties} circularRefs={circularRefs} context={context}/>
31
+ </OpenAPIDisclosure>);
32
+ }
33
+ var ancestors = new Set(circularRefs.keys());
34
+ var alternatives = getSchemaAlternatives(schema, ancestors);
35
+ if (alternatives) {
36
+ return alternatives.map(function (schema, index) { return (<OpenAPISchemaAlternative key={index} schema={schema} circularRefs={circularRefs} context={context}/>); });
37
+ }
38
+ return null;
31
39
  })()}
32
40
  </div>);
33
41
  }
34
42
  /**
35
43
  * Render a set of properties of an OpenAPI schema.
36
44
  */
37
- export function OpenAPISchemaProperties(props) {
38
- var id = props.id, properties = props.properties, circularRefs = props.circularRefs, context = props.context;
45
+ function OpenAPISchemaProperties(props) {
46
+ var id = props.id, properties = props.properties, _a = props.circularRefs, circularRefs = _a === void 0 ? new Map() : _a, context = props.context;
39
47
  return (<div id={id} className="openapi-schema-properties">
40
- {properties.map(function (property, index) { return (<OpenAPISchemaProperty key={index} circularRefs={circularRefs} property={property} context={context}/>); })}
48
+ {properties.map(function (property, index) {
49
+ return (<OpenAPISchemaProperty key={index} circularRefs={circularRefs} property={property} context={context}/>);
50
+ })}
41
51
  </div>);
42
52
  }
53
+ export function OpenAPISchemaPropertiesFromServer(props) {
54
+ return (<OpenAPISchemaProperties id={props.id} properties={JSON.parse(props.properties, retrocycle())} context={props.context}/>);
55
+ }
43
56
  /**
44
57
  * Render a root schema (such as the request body or response body).
45
58
  */
46
- export function OpenAPIRootSchema(props) {
47
- var schema = props.schema, context = props.context;
59
+ function OpenAPIRootSchema(props) {
60
+ var schema = props.schema, context = props.context, _a = props.circularRefs, parentCircularRefs = _a === void 0 ? new Map() : _a;
61
+ var id = useId();
48
62
  var properties = getSchemaProperties(schema);
49
63
  if (properties === null || properties === void 0 ? void 0 : properties.length) {
50
- return <OpenAPISchemaProperties properties={properties} context={context}/>;
64
+ var circularRefs = new Map(parentCircularRefs);
65
+ circularRefs.set(schema, id);
66
+ return (<OpenAPISchemaProperties properties={properties} circularRefs={circularRefs} context={context}/>);
51
67
  }
52
- return (<OpenAPISchemaProperty className="openapi-schema-root" property={{ schema: schema }} context={context}/>);
68
+ return (<OpenAPISchemaProperty className="openapi-schema-root" property={{ schema: schema }} context={context} circularRefs={parentCircularRefs}/>);
69
+ }
70
+ export function OpenAPIRootSchemaFromServer(props) {
71
+ return (<OpenAPIRootSchema schema={JSON.parse(props.schema, retrocycle())} context={props.context}/>);
53
72
  }
54
73
  /**
55
74
  * Render a tab for an alternative schema.
@@ -81,15 +100,47 @@ function OpenAPISchemaCircularRef(props) {
81
100
  * Render the enum value for a schema.
82
101
  */
83
102
  function OpenAPISchemaEnum(props) {
84
- var enumValues = props.enumValues;
103
+ var schema = props.schema;
104
+ var enumValues = (function () {
105
+ var _a;
106
+ // Render x-gitbook-enum first, as it has a different format
107
+ if (schema['x-gitbook-enum']) {
108
+ return Object.entries(schema['x-gitbook-enum']).map(function (_a) {
109
+ var name = _a[0], description = _a[1].description;
110
+ return {
111
+ value: name,
112
+ description: description,
113
+ };
114
+ });
115
+ }
116
+ if (schema['x-enumDescriptions']) {
117
+ return Object.entries(schema['x-enumDescriptions']).map(function (_a) {
118
+ var value = _a[0], description = _a[1];
119
+ return {
120
+ value: value,
121
+ description: description,
122
+ };
123
+ });
124
+ }
125
+ return (_a = schema.enum) === null || _a === void 0 ? void 0 : _a.map(function (value) {
126
+ return {
127
+ value: value,
128
+ description: undefined,
129
+ };
130
+ });
131
+ })();
132
+ if (!(enumValues === null || enumValues === void 0 ? void 0 : enumValues.length)) {
133
+ return null;
134
+ }
85
135
  return (<div className="openapi-schema-enum">
86
- <span>
87
- Options:{' '}
88
- {enumValues.map(function (value, index) { return (<span key={index} className="openapi-schema-enum-value">
89
- <code>{"".concat(value)}</code>
90
- {index < enumValues.length - 1 ? ', ' : ''}
136
+ <span>Available options:</span>
137
+ <div className="openapi-schema-enum-list">
138
+ {enumValues.map(function (item, index) { return (<span key={index} className="openapi-schema-enum-value">
139
+ <OpenAPICopyButton value={item.value} label={item.description} withTooltip={!!item.description}>
140
+ <code>{"".concat(item.value)}</code>
141
+ </OpenAPICopyButton>
91
142
  </span>); })}
92
- </span>
143
+ </div>
93
144
  </div>);
94
145
  }
95
146
  /**
@@ -114,7 +165,7 @@ function OpenAPISchemaPresentation(props) {
114
165
  {schema.pattern ? (<div className="openapi-schema-pattern">
115
166
  Pattern: <code>{schema.pattern}</code>
116
167
  </div>) : null}
117
- {schema.enum && schema.enum.length > 0 ? (<OpenAPISchemaEnum enumValues={schema.enum}/>) : null}
168
+ <OpenAPISchemaEnum schema={schema}/>
118
169
  </div>);
119
170
  }
120
171
  /**
@@ -208,7 +259,7 @@ function getSchemaTitle(schema) {
208
259
  var _a;
209
260
  // Otherwise try to infer a nice title
210
261
  var type = 'any';
211
- if (schema.enum) {
262
+ if (schema.enum || schema['x-enumDescriptions'] || schema['x-gitbook-enum']) {
212
263
  type = "".concat(schema.type, " \u00B7 enum");
213
264
  // check array AND schema.items as this is sometimes null despite what the type indicates
214
265
  }
@@ -15,6 +15,7 @@ export function OpenAPISchemaName(props) {
15
15
  {additionalItems ? (<span className="openapi-schema-type">{additionalItems}</span>) : null}
16
16
  </span>
17
17
  {(schema === null || schema === void 0 ? void 0 : schema.readOnly) ? <span className="openapi-schema-readonly">read-only</span> : null}
18
+ {(schema === null || schema === void 0 ? void 0 : schema.writeOnly) ? (<span className="openapi-schema-writeonly">write-only</span>) : null}
18
19
  {required ? (<span className="openapi-schema-required">required</span>) : (<span className="openapi-schema-optional">optional</span>)}
19
20
  {(schema === null || schema === void 0 ? void 0 : schema.deprecated) ? <span className="openapi-deprecated">Deprecated</span> : null}
20
21
  </div>);
@@ -0,0 +1,12 @@
1
+ import type { OpenAPIV3 } from '@gitbook/openapi-parser';
2
+ import { type OpenAPISchemaPropertyEntry } from './OpenAPISchema';
3
+ import type { OpenAPIClientContext } from './types';
4
+ export declare function OpenAPISchemaProperties(props: {
5
+ id?: string;
6
+ properties: OpenAPISchemaPropertyEntry[];
7
+ context: OpenAPIClientContext;
8
+ }): import("react").JSX.Element;
9
+ export declare function OpenAPIRootSchema(props: {
10
+ schema: OpenAPIV3.SchemaObject;
11
+ context: OpenAPIClientContext;
12
+ }): import("react").JSX.Element;
@@ -0,0 +1,8 @@
1
+ import { OpenAPIRootSchemaFromServer, OpenAPISchemaPropertiesFromServer, } from './OpenAPISchema';
2
+ import { decycle } from './decycle';
3
+ export function OpenAPISchemaProperties(props) {
4
+ return (<OpenAPISchemaPropertiesFromServer id={props.id} properties={JSON.stringify(props.properties, decycle())} context={props.context}/>);
5
+ }
6
+ export function OpenAPIRootSchema(props) {
7
+ return (<OpenAPIRootSchemaFromServer schema={JSON.stringify(props.schema, decycle())} context={props.context}/>);
8
+ }
@@ -1,10 +1,4 @@
1
1
  import type { OpenAPIClientContext, OpenAPIOperationData } from './types';
2
- /**
3
- * Client component to render the spec for the request and response.
4
- *
5
- * We use a client component as rendering recursive JSON schema in the server is expensive
6
- * (the entire schema is rendered at once, while the client component only renders the visible part)
7
- */
8
2
  export declare function OpenAPISpec(props: {
9
3
  data: OpenAPIOperationData;
10
4
  context: OpenAPIClientContext;
@@ -1,15 +1,9 @@
1
1
  import { OpenAPIRequestBody } from './OpenAPIRequestBody';
2
2
  import { OpenAPIResponses } from './OpenAPIResponses';
3
- import { OpenAPISchemaProperties } from './OpenAPISchema';
3
+ import { OpenAPISchemaProperties } from './OpenAPISchemaServer';
4
4
  import { OpenAPISecurities } from './OpenAPISecurities';
5
5
  import { StaticSection } from './StaticSection';
6
6
  import { parameterToProperty } from './utils';
7
- /**
8
- * Client component to render the spec for the request and response.
9
- *
10
- * We use a client component as rendering recursive JSON schema in the server is expensive
11
- * (the entire schema is rendered at once, while the client component only renders the visible part)
12
- */
13
7
  export function OpenAPISpec(props) {
14
8
  var _a;
15
9
  var data = props.data, context = props.context;
@@ -17,16 +11,16 @@ export function OpenAPISpec(props) {
17
11
  var parameters = (_a = operation.parameters) !== null && _a !== void 0 ? _a : [];
18
12
  var parameterGroups = groupParameters(parameters);
19
13
  return (<>
20
- {securities.length > 0 ? (<OpenAPISecurities securities={securities} context={context}/>) : null}
14
+ {securities.length > 0 ? (<OpenAPISecurities key="securities" securities={securities} context={context}/>) : null}
21
15
 
22
16
  {parameterGroups.map(function (group) {
23
- return (<StaticSection key={group.key} className="openapi-parameters" header={group.label}>
17
+ return (<StaticSection key={"parameter-".concat(group.key)} className="openapi-parameters" header={group.label}>
24
18
  <OpenAPISchemaProperties properties={group.parameters.map(parameterToProperty)} context={context}/>
25
19
  </StaticSection>);
26
20
  })}
27
21
 
28
- {operation.requestBody ? (<OpenAPIRequestBody requestBody={operation.requestBody} context={context}/>) : null}
29
- {operation.responses ? (<OpenAPIResponses responses={operation.responses} context={context}/>) : null}
22
+ {operation.requestBody ? (<OpenAPIRequestBody key="body" requestBody={operation.requestBody} context={context}/>) : null}
23
+ {operation.responses ? (<OpenAPIResponses key="responses" responses={operation.responses} context={context}/>) : null}
30
24
  </>);
31
25
  }
32
26
  function groupParameters(parameters) {
@@ -102,16 +102,8 @@ export function OpenAPITabsPanels() {
102
102
  return null;
103
103
  }
104
104
  var key = selectedTab.key.toString();
105
- return (<TabPanel key={key} id={key} className="openapi-tabs-panel">
106
- {selectedTab.body}
107
- {selectedTab.footer ? (<OpenAPITabsPanelFooter>{selectedTab.footer}</OpenAPITabsPanelFooter>) : null}
105
+ return (<TabPanel id={key} className="openapi-tabs-panel">
106
+ <div className="openapi-tabs-body">{selectedTab.body}</div>
107
+ {selectedTab.footer ? (<div className="openapi-tabs-footer">{selectedTab.footer}</div>) : null}
108
108
  </TabPanel>);
109
109
  }
110
- /**
111
- * The OpenAPI Tabs panel footer component.
112
- * This component should be used as a child of the OpenAPITabs component.
113
- */
114
- function OpenAPITabsPanelFooter(props) {
115
- var children = props.children;
116
- return <div className="openapi-tabs-footer">{children}</div>;
117
- }