@gitbook/react-openapi 1.0.0 → 1.0.2

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 (49) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/InteractiveSection.d.ts +0 -2
  3. package/dist/InteractiveSection.jsx +8 -44
  4. package/dist/OpenAPICodeSample.jsx +12 -12
  5. package/dist/OpenAPIOperation.jsx +8 -5
  6. package/dist/OpenAPIRequestBody.d.ts +2 -2
  7. package/dist/OpenAPIRequestBody.jsx +5 -2
  8. package/dist/OpenAPIResponse.jsx +17 -25
  9. package/dist/OpenAPIResponseExample.jsx +157 -48
  10. package/dist/OpenAPIResponses.jsx +2 -3
  11. package/dist/OpenAPISchema.d.ts +2 -2
  12. package/dist/OpenAPISchema.jsx +28 -29
  13. package/dist/OpenAPISpec.jsx +4 -28
  14. package/dist/OpenAPITabs.d.ts +1 -0
  15. package/dist/OpenAPITabs.jsx +52 -16
  16. package/dist/generateSchemaExample.d.ts +5 -6
  17. package/dist/generateSchemaExample.js +15 -11
  18. package/dist/json2xml.d.ts +4 -0
  19. package/dist/json2xml.js +7 -0
  20. package/dist/resolveOpenAPIOperation.js +4 -4
  21. package/dist/tsconfig.build.tsbuildinfo +1 -1
  22. package/dist/types.d.ts +4 -0
  23. package/dist/useSyncedTabsGlobalState.d.ts +1 -0
  24. package/dist/useSyncedTabsGlobalState.js +16 -0
  25. package/dist/utils.d.ts +26 -3
  26. package/dist/utils.js +72 -9
  27. package/package.json +4 -2
  28. package/src/InteractiveSection.tsx +4 -41
  29. package/src/OpenAPICodeSample.tsx +12 -12
  30. package/src/OpenAPIOperation.tsx +8 -5
  31. package/src/OpenAPIRequestBody.tsx +7 -3
  32. package/src/OpenAPIResponse.tsx +7 -27
  33. package/src/OpenAPIResponseExample.tsx +237 -70
  34. package/src/OpenAPIResponses.tsx +2 -7
  35. package/src/OpenAPISchema.tsx +34 -33
  36. package/src/OpenAPISpec.tsx +4 -22
  37. package/src/OpenAPITabs.tsx +63 -23
  38. package/src/__snapshots__/json2xml.test.ts.snap +18 -0
  39. package/src/generateSchemaExample.ts +11 -10
  40. package/src/json2xml.test.ts +46 -0
  41. package/src/json2xml.ts +8 -0
  42. package/src/resolveOpenAPIOperation.test.ts +1 -1
  43. package/src/resolveOpenAPIOperation.ts +8 -7
  44. package/src/types.ts +1 -0
  45. package/src/useSyncedTabsGlobalState.ts +23 -0
  46. package/src/utils.ts +81 -13
  47. package/dist/fetchOpenAPIOperation.d.ts +0 -27
  48. package/dist/fetchOpenAPIOperation.js +0 -195
  49. package/dist/tsconfig.tsbuildinfo +0 -1
@@ -1,8 +1,10 @@
1
1
  'use client';
2
2
 
3
- import { createContext, useContext, useMemo, useState } from 'react';
3
+ import { createContext, useContext, useEffect, useMemo, useState } from 'react';
4
4
  import { Key, Tab, TabList, TabPanel, Tabs, TabsProps } from 'react-aria-components';
5
5
  import { Markdown } from './Markdown';
6
+ import { useSyncedTabsGlobalState } from './useSyncedTabsGlobalState';
7
+ import { useIntersectionObserver } from 'usehooks-ts';
6
8
 
7
9
  export type Tab = {
8
10
  key: Key;
@@ -13,8 +15,7 @@ export type Tab = {
13
15
 
14
16
  type OpenAPITabsContextData = {
15
17
  items: Tab[];
16
- selectedKey: Key;
17
- setSelectedKey: (key: Key) => void;
18
+ selectedTab: Tab;
18
19
  };
19
20
 
20
21
  const OpenAPITabsContext = createContext<OpenAPITabsContextData | null>(null);
@@ -30,25 +31,66 @@ function useOpenAPITabsContext() {
30
31
  /**
31
32
  * The OpenAPI Tabs wrapper component.
32
33
  */
33
- export function OpenAPITabs(props: React.PropsWithChildren<TabsProps & { items: Tab[] }>) {
34
- const { children, items } = props;
35
-
36
- const [selectedKey, setSelectedKey] = useState(() => {
37
- const firstItem = items[0];
38
- if (!firstItem) {
39
- throw new Error('OpenAPITabs: at least one tab is required');
34
+ export function OpenAPITabs(
35
+ props: React.PropsWithChildren<TabsProps & { items: Tab[]; stateKey?: string }>,
36
+ ) {
37
+ const { children, items, stateKey } = props;
38
+ const isVisible = stateKey
39
+ ? useIntersectionObserver({
40
+ threshold: 0.1,
41
+ rootMargin: '200px',
42
+ })
43
+ : true;
44
+ const defaultTab = items[0] as Tab;
45
+ const [syncedTabs, setSyncedTabs] = useSyncedTabsGlobalState<Tab>();
46
+ const [selectedTabKey, setSelectedTabKey] = useState(() => {
47
+ if (isVisible && stateKey && syncedTabs && syncedTabs.has(stateKey)) {
48
+ const tabFromState = syncedTabs.get(stateKey);
49
+ return tabFromState?.key ?? items[0]?.key;
40
50
  }
41
- return firstItem.key;
51
+ return items[0]?.key;
42
52
  });
53
+ const [selectedTab, setSelectedTab] = useState<Tab>(defaultTab);
54
+
55
+ const handleSelectionChange = (key: Key) => {
56
+ setSelectedTabKey(key);
57
+ if (stateKey) {
58
+ const tab = items.find((item) => item.key === key);
59
+
60
+ if (!tab) {
61
+ return;
62
+ }
63
+
64
+ setSyncedTabs((state) => {
65
+ const newState = new Map(state);
66
+ newState.set(stateKey, tab);
67
+ return newState;
68
+ });
69
+ }
70
+ };
43
71
 
44
- const contextValue = { items, selectedKey, setSelectedKey };
72
+ useEffect(() => {
73
+ if (isVisible && stateKey && syncedTabs && syncedTabs.has(stateKey)) {
74
+ const tabFromState = syncedTabs.get(stateKey);
75
+
76
+ if (!items.some((item) => item.key === tabFromState?.key)) {
77
+ return;
78
+ }
79
+
80
+ if (tabFromState && tabFromState?.key !== selectedTab?.key) {
81
+ setSelectedTab(tabFromState);
82
+ }
83
+ }
84
+ }, [isVisible, stateKey, syncedTabs, selectedTabKey]);
85
+
86
+ const contextValue = useMemo(() => ({ items, selectedTab }), [items, selectedTab]);
45
87
 
46
88
  return (
47
89
  <OpenAPITabsContext.Provider value={contextValue}>
48
90
  <Tabs
49
91
  className="openapi-tabs"
50
- onSelectionChange={setSelectedKey}
51
- selectedKey={selectedKey}
92
+ onSelectionChange={handleSelectionChange}
93
+ selectedKey={selectedTab?.key}
52
94
  >
53
95
  {children}
54
96
  </Tabs>
@@ -90,23 +132,21 @@ export function OpenAPITabsList() {
90
132
  * It renders the content of the selected tab.
91
133
  */
92
134
  export function OpenAPITabsPanels() {
93
- const { selectedKey, items } = useOpenAPITabsContext();
94
-
95
- const tab = useMemo(() => items.find((tab) => tab.key === selectedKey), [items, selectedKey]);
135
+ const { selectedTab } = useOpenAPITabsContext();
96
136
 
97
- if (!tab) {
137
+ if (!selectedTab) {
98
138
  return null;
99
139
  }
100
140
 
101
141
  return (
102
142
  <TabPanel
103
- key={`TabPanel-${tab.key}`}
104
- id={tab.key.toString()}
143
+ key={`TabPanel-${selectedTab.key}`}
144
+ id={selectedTab.key.toString()}
105
145
  className="openapi-tabs-panel"
106
146
  >
107
- {tab.body}
108
- {tab.description ? (
109
- <Markdown source={tab.description} className="openapi-tabs-footer" />
147
+ {selectedTab.body}
148
+ {selectedTab.description ? (
149
+ <Markdown source={selectedTab.description} className="openapi-tabs-footer" />
110
150
  ) : null}
111
151
  </TabPanel>
112
152
  );
@@ -0,0 +1,18 @@
1
+ // Bun Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`getUrlFromServerState indents correctly 1`] = `
4
+ "<?xml version="1.0"?>
5
+ <id>10</id>
6
+ <name>doggie</name>
7
+ <category>
8
+ <id>1</id>
9
+ <name>Dogs</name>
10
+ </category>
11
+ <photoUrls>string</photoUrls>
12
+ <tags>
13
+ <id>0</id>
14
+ <name>string</name>
15
+ </tags>
16
+ <status>available</status>
17
+ "
18
+ `;
@@ -1,21 +1,23 @@
1
1
  import type { OpenAPIV3 } from '@gitbook/openapi-parser';
2
- import { noReference } from './utils';
3
2
  import { getExampleFromSchema } from '@scalar/oas-utils/spec-getters';
4
3
 
5
4
  type JSONValue = string | number | boolean | null | JSONValue[] | { [key: string]: JSONValue };
6
5
 
6
+ type ScalarGetExampleFromSchemaOptions = NonNullable<Parameters<typeof getExampleFromSchema>[1]>;
7
+ type GenerateSchemaExampleOptions = Pick<
8
+ ScalarGetExampleFromSchemaOptions,
9
+ 'xml' | 'omitEmptyAndOptionalProperties' | 'mode'
10
+ >;
11
+
7
12
  /**
8
13
  * Generate a JSON example from a schema
9
14
  */
10
15
  export function generateSchemaExample(
11
16
  schema: OpenAPIV3.SchemaObject,
12
- options: {
13
- onlyRequired?: boolean;
14
- } = {},
17
+ options?: GenerateSchemaExampleOptions,
15
18
  ): JSONValue | undefined {
16
19
  return getExampleFromSchema(schema, {
17
20
  emptyString: 'text',
18
- omitEmptyAndOptionalProperties: options.onlyRequired,
19
21
  variables: {
20
22
  'date-time': new Date().toISOString(),
21
23
  date: new Date().toISOString().split('T')[0],
@@ -29,6 +31,7 @@ export function generateSchemaExample(
29
31
  byte: 'Ynl0ZXM=',
30
32
  password: 'password',
31
33
  },
34
+ ...options,
32
35
  });
33
36
  }
34
37
 
@@ -37,9 +40,7 @@ export function generateSchemaExample(
37
40
  */
38
41
  export function generateMediaTypeExample(
39
42
  mediaType: OpenAPIV3.MediaTypeObject,
40
- options: {
41
- onlyRequired?: boolean;
42
- } = {},
43
+ options?: GenerateSchemaExampleOptions,
43
44
  ): JSONValue | undefined {
44
45
  if (mediaType.example) {
45
46
  return mediaType.example;
@@ -50,13 +51,13 @@ export function generateMediaTypeExample(
50
51
  if (key) {
51
52
  const example = mediaType.examples[key];
52
53
  if (example) {
53
- return noReference(example).value;
54
+ return example.value;
54
55
  }
55
56
  }
56
57
  }
57
58
 
58
59
  if (mediaType.schema) {
59
- return generateSchemaExample(noReference(mediaType.schema), options);
60
+ return generateSchemaExample(mediaType.schema, options);
60
61
  }
61
62
 
62
63
  return undefined;
@@ -0,0 +1,46 @@
1
+ import { describe, expect, it } from 'bun:test';
2
+
3
+ import { json2xml } from './json2xml';
4
+
5
+ describe('getUrlFromServerState', () => {
6
+ it('transforms JSON to xml', () => {
7
+ const xml = json2xml({
8
+ foo: 'bar',
9
+ });
10
+
11
+ expect(xml).toBe('<?xml version="1.0"?>\n<foo>bar</foo>\n');
12
+ });
13
+
14
+ it('wraps array items', () => {
15
+ const xml = json2xml({
16
+ urls: {
17
+ url: ['https://example.com', 'https://example.com'],
18
+ },
19
+ });
20
+
21
+ expect(xml).toBe(
22
+ '<?xml version="1.0"?>\n<urls>\n\t<url>https://example.com</url>\n\t<url>https://example.com</url>\n</urls>\n',
23
+ );
24
+ });
25
+
26
+ it('indents correctly', () => {
27
+ const xml = json2xml({
28
+ id: 10,
29
+ name: 'doggie',
30
+ category: {
31
+ id: 1,
32
+ name: 'Dogs',
33
+ },
34
+ photoUrls: ['string'],
35
+ tags: [
36
+ {
37
+ id: 0,
38
+ name: 'string',
39
+ },
40
+ ],
41
+ status: 'available',
42
+ });
43
+
44
+ expect(xml).toMatchSnapshot();
45
+ });
46
+ });
@@ -0,0 +1,8 @@
1
+ import { jsXml } from 'json-xml-parse';
2
+
3
+ /**
4
+ * This function converts an object to XML.
5
+ */
6
+ export function json2xml(data: Record<string, any>) {
7
+ return jsXml.toXmlString(data, { beautify: true });
8
+ }
@@ -9,7 +9,7 @@ async function fetchFilesystem(url: string) {
9
9
  const filesystem = await parseOpenAPI({ value: text, rootURL: url });
10
10
  const transformedFs = await traverse(filesystem, async (node) => {
11
11
  if ('description' in node && typeof node.description === 'string' && node.description) {
12
- node['x-description-html'] = node.description;
12
+ node['x-gitbook-description-html'] = node.description;
13
13
  }
14
14
  return node;
15
15
  });
@@ -7,8 +7,8 @@ import {
7
7
  type OpenAPIV3_1,
8
8
  dereference,
9
9
  } from '@gitbook/openapi-parser';
10
- import { noReference } from './utils';
11
10
  import { OpenAPIOperationData } from './types';
11
+ import { checkIsReference } from './utils';
12
12
 
13
13
  export { toJSON, fromJSON };
14
14
 
@@ -48,8 +48,8 @@ export async function resolveOpenAPIOperation(
48
48
  const securityKey = Object.keys(entry)[0];
49
49
  if (securityKey) {
50
50
  const securityScheme = schema.components?.securitySchemes?.[securityKey];
51
- if (securityScheme) {
52
- securities.push([securityKey, noReference(securityScheme)]);
51
+ if (securityScheme && !checkIsReference(securityScheme)) {
52
+ securities.push([securityKey, securityScheme]);
53
53
  }
54
54
  }
55
55
  }
@@ -116,12 +116,13 @@ function getPathObject(
116
116
  function getPathObjectParameter(
117
117
  schema: OpenAPIV3.Document | OpenAPIV3_1.Document,
118
118
  path: string,
119
- ): OpenAPIV3.ParameterObject[] | OpenAPIV3_1.ParameterObject[] | null {
119
+ ):
120
+ | (OpenAPIV3.ReferenceObject | OpenAPIV3.ParameterObject)[]
121
+ | (OpenAPIV3.ParameterObject | OpenAPIV3_1.ReferenceObject)[]
122
+ | null {
120
123
  const pathObject = getPathObject(schema, path);
121
124
  if (pathObject?.parameters) {
122
- return pathObject.parameters.map(noReference) as
123
- | OpenAPIV3.ParameterObject[]
124
- | OpenAPIV3_1.ParameterObject[];
125
+ return pathObject.parameters;
125
126
  }
126
127
  return null;
127
128
  }
package/src/types.ts CHANGED
@@ -6,6 +6,7 @@ import type {
6
6
 
7
7
  export interface OpenAPIContextProps extends OpenAPIClientContext {
8
8
  CodeBlock: React.ComponentType<{ code: string; syntax: string }>;
9
+ renderHeading: (props: { deprecated: boolean; title: string }) => React.ReactNode;
9
10
 
10
11
  /** Spec url for the Scalar Api Client */
11
12
  specUrl: string;
@@ -0,0 +1,23 @@
1
+ 'use client';
2
+
3
+ import { create } from 'zustand';
4
+
5
+ interface SyncedTabsState<T> {
6
+ tabs: Map<string, T>;
7
+ setTabs: (updater: (tabs: Map<string, T>) => Map<string, T>) => void;
8
+ }
9
+
10
+ const useSyncedTabsStore = create<SyncedTabsState<any>>()((set) => ({
11
+ tabs: new Map<string, any>(),
12
+ setTabs: (updater) =>
13
+ set((state) => ({
14
+ tabs: updater(new Map(state.tabs)), // Ensure a new Map is created for reactivity
15
+ })),
16
+ }));
17
+
18
+ // Selector for better performance - only re-renders when tabs change
19
+ export function useSyncedTabsGlobalState<T>() {
20
+ const tabs = useSyncedTabsStore((state) => state.tabs as Map<string, T>);
21
+ const setTabs = useSyncedTabsStore((state) => state.setTabs as SyncedTabsState<T>['setTabs']);
22
+ return [tabs, setTabs] as const;
23
+ }
package/src/utils.ts CHANGED
@@ -1,14 +1,8 @@
1
- import type { AnyObject, OpenAPIV3 } from '@gitbook/openapi-parser';
1
+ import type { AnyObject, OpenAPIV3, OpenAPIV3_1 } from '@gitbook/openapi-parser';
2
2
 
3
- export function noReference<T>(input: T | OpenAPIV3.ReferenceObject): T {
4
- if (checkIsReference(input)) {
5
- throw new Error('Reference found');
6
- }
7
-
8
- return input;
9
- }
10
-
11
- export function checkIsReference(input: unknown): input is OpenAPIV3.ReferenceObject {
3
+ export function checkIsReference(
4
+ input: unknown,
5
+ ): input is OpenAPIV3.ReferenceObject | OpenAPIV3_1.ReferenceObject {
12
6
  return typeof input === 'object' && !!input && '$ref' in input;
13
7
  }
14
8
 
@@ -20,9 +14,83 @@ export function createStateKey(key: string, scope?: string) {
20
14
  * Resolve the description of an object.
21
15
  */
22
16
  export function resolveDescription(object: AnyObject) {
23
- return 'x-description-html' in object && typeof object['x-description-html'] === 'string'
24
- ? object['x-description-html']
17
+ return 'x-gitbook-description-html' in object &&
18
+ typeof object['x-gitbook-description-html'] === 'string'
19
+ ? object['x-gitbook-description-html'].trim()
25
20
  : typeof object.description === 'string'
26
- ? object.description
21
+ ? object.description.trim()
27
22
  : undefined;
28
23
  }
24
+
25
+ /**
26
+ * Extract descriptions from an object.
27
+ */
28
+ export function extractDescriptions(object: AnyObject) {
29
+ return {
30
+ description: object.description,
31
+ ['x-gitbook-description-html']:
32
+ 'x-gitbook-description-html' in object
33
+ ? object['x-gitbook-description-html']
34
+ : undefined,
35
+ };
36
+ }
37
+
38
+ /**
39
+ * Resolve the first example from an object.
40
+ */
41
+ export function resolveFirstExample(object: AnyObject) {
42
+ if ('examples' in object && typeof object.examples === 'object' && object.examples) {
43
+ const keys = Object.keys(object.examples);
44
+ const firstKey = keys[0];
45
+ if (firstKey && object.examples[firstKey]) {
46
+ return object.examples[firstKey];
47
+ }
48
+ }
49
+ if ('example' in object && object.example !== undefined) {
50
+ return object.example;
51
+ }
52
+ return undefined;
53
+ }
54
+
55
+ /**
56
+ * Resolve the schema of a parameter.
57
+ * Extract the description, example and deprecated from parameter.
58
+ */
59
+ export function resolveParameterSchema(
60
+ parameter: OpenAPIV3.ParameterBaseObject,
61
+ ): OpenAPIV3.SchemaObject {
62
+ const schema = checkIsReference(parameter.schema) ? undefined : parameter.schema;
63
+ return {
64
+ // Description of the parameter is defined at the parameter level
65
+ // we use display it if the schema doesn't override it
66
+ ...extractDescriptions(parameter),
67
+ example: resolveFirstExample(parameter),
68
+ // Deprecated can be defined at the parameter level
69
+ deprecated: parameter.deprecated,
70
+ ...schema,
71
+ };
72
+ }
73
+
74
+ /**
75
+ * Transform a parameter object to a property object.
76
+ */
77
+ export function parameterToProperty(
78
+ parameter: OpenAPIV3.ParameterObject | OpenAPIV3.ReferenceObject | OpenAPIV3_1.ReferenceObject,
79
+ ): {
80
+ propertyName: string | undefined;
81
+ schema: OpenAPIV3.SchemaObject;
82
+ required: boolean | undefined;
83
+ } {
84
+ if (checkIsReference(parameter)) {
85
+ return {
86
+ propertyName: parameter.$ref ?? 'Unknown ref',
87
+ schema: {},
88
+ required: undefined,
89
+ };
90
+ }
91
+ return {
92
+ propertyName: parameter.name,
93
+ schema: resolveParameterSchema(parameter),
94
+ required: parameter.required,
95
+ };
96
+ }
@@ -1,27 +0,0 @@
1
- import { toJSON, fromJSON } from 'flatted';
2
- import { type OpenAPICustomOperationProperties, type OpenAPICustomSpecProperties, type OpenAPIV3xDocument, type Filesystem, type OpenAPIV3 } from '@gitbook/openapi-parser';
3
- export interface OpenAPIFetcher {
4
- /**
5
- * Fetch an OpenAPI file by its URL. It should return a fully parsed OpenAPI v3 document.
6
- */
7
- fetch: (url: string) => Promise<Filesystem<OpenAPIV3xDocument>>;
8
- }
9
- export interface OpenAPIOperationData extends OpenAPICustomSpecProperties {
10
- path: string;
11
- method: string;
12
- /** Servers to be used for this operation */
13
- servers: OpenAPIV3.ServerObject[];
14
- /** Spec of the operation */
15
- operation: OpenAPIV3.OperationObject<OpenAPICustomOperationProperties>;
16
- /** Securities that should be used for this operation */
17
- securities: [string, OpenAPIV3.SecuritySchemeObject][];
18
- }
19
- export { toJSON, fromJSON };
20
- /**
21
- * Resolve an OpenAPI operation in a file and compile it to a more usable format.
22
- */
23
- export declare function fetchOpenAPIOperation(input: {
24
- url: string;
25
- path: string;
26
- method: string;
27
- }, fetcher: OpenAPIFetcher): Promise<OpenAPIOperationData | null>;
@@ -1,195 +0,0 @@
1
- var __assign = (this && this.__assign) || function () {
2
- __assign = Object.assign || function(t) {
3
- for (var s, i = 1, n = arguments.length; i < n; i++) {
4
- s = arguments[i];
5
- for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
6
- t[p] = s[p];
7
- }
8
- return t;
9
- };
10
- return __assign.apply(this, arguments);
11
- };
12
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
13
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
14
- return new (P || (P = Promise))(function (resolve, reject) {
15
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
16
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
17
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
18
- step((generator = generator.apply(thisArg, _arguments || [])).next());
19
- });
20
- };
21
- var __generator = (this && this.__generator) || function (thisArg, body) {
22
- var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
23
- return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
24
- function verb(n) { return function (v) { return step([n, v]); }; }
25
- function step(op) {
26
- if (f) throw new TypeError("Generator is already executing.");
27
- while (g && (g = 0, op[0] && (_ = 0)), _) try {
28
- if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
29
- if (y = 0, t) op = [op[0] & 2, t.value];
30
- switch (op[0]) {
31
- case 0: case 1: t = op; break;
32
- case 4: _.label++; return { value: op[1], done: false };
33
- case 5: _.label++; y = op[1]; op = [0]; continue;
34
- case 7: op = _.ops.pop(); _.trys.pop(); continue;
35
- default:
36
- if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
37
- if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
38
- if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
39
- if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
40
- if (t[2]) _.ops.pop();
41
- _.trys.pop(); continue;
42
- }
43
- op = body.call(thisArg, _);
44
- } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
45
- if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
46
- }
47
- };
48
- var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
49
- if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
50
- if (ar || !(i in from)) {
51
- if (!ar) ar = Array.prototype.slice.call(from, 0, i);
52
- ar[i] = from[i];
53
- }
54
- }
55
- return to.concat(ar || Array.prototype.slice.call(from));
56
- };
57
- import { toJSON, fromJSON } from 'flatted';
58
- import { OpenAPIParseError, dereference, } from '@gitbook/openapi-parser';
59
- import { noReference } from './utils';
60
- export { toJSON, fromJSON };
61
- /**
62
- * Resolve an OpenAPI operation in a file and compile it to a more usable format.
63
- */
64
- export function fetchOpenAPIOperation(input, fetcher) {
65
- return __awaiter(this, void 0, void 0, function () {
66
- var filesystem, schema, operation, commonParameters, servers, security, securities, _i, security_1, entry, securityKey, securityScheme;
67
- var _a, _b, _c, _d, _e, _f;
68
- return __generator(this, function (_g) {
69
- switch (_g.label) {
70
- case 0: return [4 /*yield*/, fetcher.fetch(input.url)];
71
- case 1:
72
- filesystem = _g.sent();
73
- return [4 /*yield*/, memoDereferenceFilesystem(filesystem, input.url)];
74
- case 2:
75
- schema = _g.sent();
76
- operation = getOperationByPathAndMethod(schema, input.path, input.method);
77
- if (!operation) {
78
- return [2 /*return*/, null];
79
- }
80
- commonParameters = getPathObjectParameter(schema, input.path);
81
- if (commonParameters) {
82
- operation = __assign(__assign({}, operation), { parameters: __spreadArray(__spreadArray([], commonParameters, true), ((_a = operation.parameters) !== null && _a !== void 0 ? _a : []), true) });
83
- }
84
- servers = 'servers' in schema ? ((_b = schema.servers) !== null && _b !== void 0 ? _b : []) : [];
85
- security = flattenSecurities((_d = (_c = operation.security) !== null && _c !== void 0 ? _c : schema.security) !== null && _d !== void 0 ? _d : []);
86
- securities = [];
87
- for (_i = 0, security_1 = security; _i < security_1.length; _i++) {
88
- entry = security_1[_i];
89
- securityKey = Object.keys(entry)[0];
90
- if (securityKey) {
91
- securityScheme = (_f = (_e = schema.components) === null || _e === void 0 ? void 0 : _e.securitySchemes) === null || _f === void 0 ? void 0 : _f[securityKey];
92
- if (securityScheme) {
93
- securities.push([securityKey, noReference(securityScheme)]);
94
- }
95
- }
96
- }
97
- return [2 /*return*/, {
98
- servers: servers,
99
- operation: operation,
100
- method: input.method,
101
- path: input.path,
102
- securities: securities,
103
- 'x-codeSamples': typeof schema['x-codeSamples'] === 'boolean' ? schema['x-codeSamples'] : undefined,
104
- 'x-hideTryItPanel': typeof schema['x-hideTryItPanel'] === 'boolean'
105
- ? schema['x-hideTryItPanel']
106
- : undefined,
107
- }];
108
- }
109
- });
110
- });
111
- }
112
- var dereferenceCache = new WeakMap();
113
- /**
114
- * Memoized version of `dereferenceSchema`.
115
- */
116
- function memoDereferenceFilesystem(filesystem, url) {
117
- if (dereferenceCache.has(filesystem)) {
118
- return dereferenceCache.get(filesystem);
119
- }
120
- var promise = dereferenceFilesystem(filesystem, url);
121
- dereferenceCache.set(filesystem, promise);
122
- return promise;
123
- }
124
- /**
125
- * Dereference an OpenAPI schema.
126
- */
127
- function dereferenceFilesystem(filesystem, url) {
128
- return __awaiter(this, void 0, void 0, function () {
129
- var result;
130
- return __generator(this, function (_a) {
131
- switch (_a.label) {
132
- case 0: return [4 /*yield*/, dereference(filesystem)];
133
- case 1:
134
- result = _a.sent();
135
- if (!result.schema) {
136
- throw new OpenAPIParseError('Failed to dereference OpenAPI document', url, 'failed-dereference');
137
- }
138
- return [2 /*return*/, result.schema];
139
- }
140
- });
141
- });
142
- }
143
- /**
144
- * Get a path object from its path.
145
- */
146
- function getPathObject(schema, path) {
147
- var _a;
148
- if ((_a = schema.paths) === null || _a === void 0 ? void 0 : _a[path]) {
149
- return schema.paths[path];
150
- }
151
- return null;
152
- }
153
- /**
154
- * Resolve parameters from a path in an OpenAPI schema.
155
- */
156
- function getPathObjectParameter(schema, path) {
157
- var pathObject = getPathObject(schema, path);
158
- if (pathObject === null || pathObject === void 0 ? void 0 : pathObject.parameters) {
159
- return pathObject.parameters.map(noReference);
160
- }
161
- return null;
162
- }
163
- /**
164
- * Get an operation by its path and method.
165
- */
166
- function getOperationByPathAndMethod(schema, path, method) {
167
- // Types are buffy for OpenAPIV3_1.OperationObject, so we use v3
168
- var pathObject = getPathObject(schema, path);
169
- if (!pathObject) {
170
- return null;
171
- }
172
- var normalizedMethod = method.toLowerCase();
173
- if (!pathObject[normalizedMethod]) {
174
- return null;
175
- }
176
- return pathObject[normalizedMethod];
177
- }
178
- /**
179
- * Flatten security objects in case they are nested.
180
- * @example [{bearerAuth:[], basicAuth:[]}] => [{ bearerAuth: [] }, { basicAuth: [] }]
181
- */
182
- function flattenSecurities(security) {
183
- if (!Array.isArray(security) || security.length === 0) {
184
- return [];
185
- }
186
- return security.flatMap(function (securityObject) {
187
- return Object.entries(securityObject).map(function (_a) {
188
- var _b;
189
- var authType = _a[0], config = _a[1];
190
- return (_b = {},
191
- _b[authType] = config,
192
- _b);
193
- });
194
- });
195
- }