@gitbook/react-openapi 1.0.5 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/dist/OpenAPIDisclosure.d.ts +5 -9
- package/dist/OpenAPIDisclosure.jsx +24 -27
- package/dist/OpenAPIDisclosureGroup.d.ts +1 -1
- package/dist/OpenAPIDisclosureGroup.jsx +5 -5
- package/dist/OpenAPIPath.jsx +5 -1
- package/dist/OpenAPISchema.d.ts +3 -26
- package/dist/OpenAPISchema.jsx +80 -131
- package/dist/ScalarApiButton.d.ts +3 -2
- package/dist/ScalarApiButton.jsx +22 -18
- package/dist/dereference.d.ts +5 -0
- package/dist/dereference.js +68 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.js +2 -1
- package/dist/models/OpenAPIModels.d.ts +9 -0
- package/dist/models/OpenAPIModels.jsx +62 -0
- package/dist/models/index.d.ts +2 -0
- package/dist/models/index.js +2 -0
- package/dist/models/resolveOpenAPIModels.d.ts +7 -0
- package/dist/models/resolveOpenAPIModels.js +73 -0
- package/dist/resolveOpenAPIOperation.d.ts +2 -2
- package/dist/resolveOpenAPIOperation.js +3 -34
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/dist/types.d.ts +8 -0
- package/dist/utils.js +42 -3
- package/package.json +3 -3
- package/src/OpenAPIDisclosure.tsx +34 -42
- package/src/OpenAPIDisclosureGroup.tsx +2 -2
- package/src/OpenAPIPath.tsx +7 -1
- package/src/OpenAPISchema.test.ts +26 -35
- package/src/OpenAPISchema.tsx +136 -225
- package/src/ScalarApiButton.tsx +26 -28
- package/src/dereference.ts +29 -0
- package/src/index.ts +3 -2
- package/src/models/OpenAPIModels.tsx +89 -0
- package/src/models/index.ts +2 -0
- package/src/models/resolveOpenAPIModels.ts +35 -0
- package/src/resolveOpenAPIOperation.ts +8 -36
- package/src/types.ts +10 -0
- package/src/utils.ts +51 -3
package/dist/types.d.ts
CHANGED
|
@@ -51,3 +51,11 @@ export interface OpenAPIOperationData extends OpenAPICustomSpecProperties {
|
|
|
51
51
|
/** Securities that should be used for this operation */
|
|
52
52
|
securities: [string, OpenAPIV3.SecuritySchemeObject][];
|
|
53
53
|
}
|
|
54
|
+
export type OpenAPIModel = {
|
|
55
|
+
name: string;
|
|
56
|
+
schema: OpenAPIV3.SchemaObject;
|
|
57
|
+
};
|
|
58
|
+
export interface OpenAPIModelsData {
|
|
59
|
+
/** Components schemas to be used for models */
|
|
60
|
+
models: OpenAPIModel[];
|
|
61
|
+
}
|
package/dist/utils.js
CHANGED
|
@@ -9,17 +9,25 @@ var __assign = (this && this.__assign) || function () {
|
|
|
9
9
|
};
|
|
10
10
|
return __assign.apply(this, arguments);
|
|
11
11
|
};
|
|
12
|
+
import { stringifyOpenAPI } from './stringifyOpenAPI';
|
|
12
13
|
export function checkIsReference(input) {
|
|
13
14
|
return typeof input === 'object' && !!input && '$ref' in input;
|
|
14
15
|
}
|
|
15
16
|
export function createStateKey(key, scope) {
|
|
16
17
|
return scope ? "".concat(scope, "_").concat(key) : key;
|
|
17
18
|
}
|
|
19
|
+
/**
|
|
20
|
+
* Check if an object has a description. Either at the root level or in items.
|
|
21
|
+
*/
|
|
22
|
+
function hasDescription(object) {
|
|
23
|
+
return 'description' in object || 'x-gitbook-description-html' in object;
|
|
24
|
+
}
|
|
18
25
|
/**
|
|
19
26
|
* Resolve the description of an object.
|
|
20
27
|
*/
|
|
21
28
|
export function resolveDescription(object) {
|
|
22
|
-
|
|
29
|
+
// If the object has items and has a description, we resolve the description from items
|
|
30
|
+
if ('items' in object && typeof object.items === 'object' && hasDescription(object.items)) {
|
|
23
31
|
return resolveDescription(object.items);
|
|
24
32
|
}
|
|
25
33
|
return 'x-gitbook-description-html' in object &&
|
|
@@ -51,8 +59,13 @@ export function resolveFirstExample(object) {
|
|
|
51
59
|
return object.examples[firstKey];
|
|
52
60
|
}
|
|
53
61
|
}
|
|
54
|
-
|
|
55
|
-
|
|
62
|
+
// Resolve top level example first
|
|
63
|
+
if (shouldDisplayExample(object)) {
|
|
64
|
+
return formatExample(object.example);
|
|
65
|
+
}
|
|
66
|
+
// Resolve example from items if it exists
|
|
67
|
+
if (object.items && typeof object.items === 'object') {
|
|
68
|
+
return formatExample(object.items.example);
|
|
56
69
|
}
|
|
57
70
|
return undefined;
|
|
58
71
|
}
|
|
@@ -84,3 +97,29 @@ export function parameterToProperty(parameter) {
|
|
|
84
97
|
required: parameter.required,
|
|
85
98
|
};
|
|
86
99
|
}
|
|
100
|
+
/**
|
|
101
|
+
* Format the example of a schema.
|
|
102
|
+
*/
|
|
103
|
+
function formatExample(example) {
|
|
104
|
+
if (typeof example === 'string') {
|
|
105
|
+
return example
|
|
106
|
+
.replace(/\n/g, ' ') // Replace newlines with spaces
|
|
107
|
+
.replace(/\s+/g, ' ') // Collapse multiple spaces/newlines into a single space
|
|
108
|
+
.replace(/([\{\}:,])\s+/g, '$1 ') // Ensure a space after {, }, :, and ,
|
|
109
|
+
.replace(/\s+([\{\}:,])/g, ' $1') // Ensure a space before {, }, :, and ,
|
|
110
|
+
.trim();
|
|
111
|
+
}
|
|
112
|
+
return stringifyOpenAPI(example);
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Check if an example should be displayed.
|
|
116
|
+
*/
|
|
117
|
+
function shouldDisplayExample(schema) {
|
|
118
|
+
return ((typeof schema.example === 'string' && !!schema.example) ||
|
|
119
|
+
typeof schema.example === 'number' ||
|
|
120
|
+
typeof schema.example === 'boolean' ||
|
|
121
|
+
(Array.isArray(schema.example) && schema.example.length > 0) ||
|
|
122
|
+
(typeof schema.example === 'object' &&
|
|
123
|
+
schema.example !== null &&
|
|
124
|
+
Object.keys(schema.example).length > 0));
|
|
125
|
+
}
|
package/package.json
CHANGED
|
@@ -8,12 +8,12 @@
|
|
|
8
8
|
"default": "./dist/index.js"
|
|
9
9
|
}
|
|
10
10
|
},
|
|
11
|
-
"version": "1.0
|
|
11
|
+
"version": "1.1.0",
|
|
12
12
|
"sideEffects": false,
|
|
13
13
|
"dependencies": {
|
|
14
14
|
"@gitbook/openapi-parser": "workspace:*",
|
|
15
|
-
"@scalar/api-client-react": "1.
|
|
16
|
-
"@scalar/oas-utils": "^0.2.
|
|
15
|
+
"@scalar/api-client-react": "^1.1.36",
|
|
16
|
+
"@scalar/oas-utils": "^0.2.110",
|
|
17
17
|
"clsx": "^2.1.1",
|
|
18
18
|
"flatted": "^3.2.9",
|
|
19
19
|
"json-xml-parse": "^1.3.0",
|
|
@@ -1,52 +1,44 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
import { mergeProps, useButton, useDisclosure, useFocusRing } from 'react-aria';
|
|
5
|
-
import { useDisclosureState } from 'react-stately';
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
import { Button, Disclosure, DisclosurePanel, Heading } from 'react-aria-components';
|
|
6
4
|
import type { OpenAPIClientContext } from './types';
|
|
7
5
|
|
|
8
|
-
interface Props {
|
|
9
|
-
context: OpenAPIClientContext;
|
|
10
|
-
children: React.ReactNode;
|
|
11
|
-
label?: string;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
6
|
/**
|
|
15
7
|
* Display an interactive OpenAPI disclosure.
|
|
16
|
-
* The label is optional and defaults to "child attributes".
|
|
17
8
|
*/
|
|
18
|
-
export function OpenAPIDisclosure(
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
const {
|
|
24
|
-
const
|
|
9
|
+
export function OpenAPIDisclosure(props: {
|
|
10
|
+
context: OpenAPIClientContext;
|
|
11
|
+
children: React.ReactNode;
|
|
12
|
+
label: string;
|
|
13
|
+
}): React.JSX.Element {
|
|
14
|
+
const { context, children, label } = props;
|
|
15
|
+
const [isExpanded, setIsExpanded] = useState(false);
|
|
25
16
|
|
|
26
17
|
return (
|
|
27
|
-
<
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
18
|
+
<Disclosure
|
|
19
|
+
className="openapi-disclosure"
|
|
20
|
+
isExpanded={isExpanded}
|
|
21
|
+
onExpandedChange={setIsExpanded}
|
|
22
|
+
>
|
|
23
|
+
<Heading>
|
|
24
|
+
<Button
|
|
25
|
+
slot="trigger"
|
|
26
|
+
className="openapi-disclosure-trigger"
|
|
27
|
+
style={({ isFocusVisible }) => ({
|
|
28
|
+
outline: isFocusVisible
|
|
29
|
+
? '2px solid rgb(var(--primary-color-500) / 0.4)'
|
|
30
|
+
: 'none',
|
|
31
|
+
})}
|
|
32
|
+
>
|
|
33
|
+
{context.icons.plus}
|
|
34
|
+
<span>
|
|
35
|
+
{isExpanded ? 'Hide' : 'Show'} {label}
|
|
36
|
+
</span>
|
|
37
|
+
</Button>
|
|
38
|
+
</Heading>
|
|
39
|
+
<DisclosurePanel className="openapi-disclosure-panel">
|
|
40
|
+
{isExpanded ? children : null}
|
|
41
|
+
</DisclosurePanel>
|
|
42
|
+
</Disclosure>
|
|
51
43
|
);
|
|
52
44
|
}
|
|
@@ -19,7 +19,7 @@ type TDisclosureGroup = {
|
|
|
19
19
|
label: string | React.ReactNode;
|
|
20
20
|
tabs?: {
|
|
21
21
|
id: string;
|
|
22
|
-
label
|
|
22
|
+
label?: string | React.ReactNode;
|
|
23
23
|
body?: React.ReactNode;
|
|
24
24
|
}[];
|
|
25
25
|
};
|
|
@@ -121,7 +121,7 @@ function DisclosureItem(props: { group: TDisclosureGroup; icon?: React.ReactNode
|
|
|
121
121
|
</option>
|
|
122
122
|
))}
|
|
123
123
|
</select>
|
|
124
|
-
) : group.tabs[0] ? (
|
|
124
|
+
) : group.tabs[0]?.label ? (
|
|
125
125
|
<span>{group.tabs[0].label}</span>
|
|
126
126
|
) : null}
|
|
127
127
|
</div>
|
package/src/OpenAPIPath.tsx
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { OpenAPIV3_1 } from '@gitbook/openapi-parser';
|
|
1
2
|
import type React from 'react';
|
|
2
3
|
import { ScalarApiButton } from './ScalarApiButton';
|
|
3
4
|
import type { OpenAPIContextProps, OpenAPIOperationData } from './types';
|
|
@@ -12,6 +13,7 @@ export function OpenAPIPath(props: {
|
|
|
12
13
|
const { data, context } = props;
|
|
13
14
|
const { method, path } = data;
|
|
14
15
|
const { specUrl } = context;
|
|
16
|
+
const hideTryItPanel = data['x-hideTryItPanel'] || data.operation['x-hideTryItPanel'];
|
|
15
17
|
|
|
16
18
|
return (
|
|
17
19
|
<div className="openapi-path">
|
|
@@ -19,13 +21,17 @@ export function OpenAPIPath(props: {
|
|
|
19
21
|
<div className="openapi-path-title" data-deprecated={data.operation.deprecated}>
|
|
20
22
|
<p>{formatPath(path)}</p>
|
|
21
23
|
</div>
|
|
22
|
-
{
|
|
24
|
+
{!hideTryItPanel && validateHttpMethod(method) && (
|
|
23
25
|
<ScalarApiButton method={method} path={path} specUrl={specUrl} />
|
|
24
26
|
)}
|
|
25
27
|
</div>
|
|
26
28
|
);
|
|
27
29
|
}
|
|
28
30
|
|
|
31
|
+
function validateHttpMethod(method: string): method is OpenAPIV3_1.HttpMethods {
|
|
32
|
+
return ['get', 'post', 'put', 'delete', 'patch', 'head', 'options', 'trace'].includes(method);
|
|
33
|
+
}
|
|
34
|
+
|
|
29
35
|
// Format the path to highlight placeholders
|
|
30
36
|
function formatPath(path: string) {
|
|
31
37
|
// Matches placeholders like {id}, {userId}, etc.
|
|
@@ -23,18 +23,15 @@ describe('getSchemaAlternatives', () => {
|
|
|
23
23
|
],
|
|
24
24
|
})
|
|
25
25
|
).toEqual([
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
},
|
|
36
|
-
],
|
|
37
|
-
undefined,
|
|
26
|
+
{
|
|
27
|
+
type: 'number',
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
type: 'boolean',
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
type: 'string',
|
|
34
|
+
},
|
|
38
35
|
]);
|
|
39
36
|
});
|
|
40
37
|
|
|
@@ -58,22 +55,19 @@ describe('getSchemaAlternatives', () => {
|
|
|
58
55
|
],
|
|
59
56
|
})
|
|
60
57
|
).toEqual([
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
},
|
|
75
|
-
],
|
|
76
|
-
undefined,
|
|
58
|
+
{
|
|
59
|
+
allOf: [
|
|
60
|
+
{
|
|
61
|
+
type: 'number',
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
type: 'boolean',
|
|
65
|
+
},
|
|
66
|
+
],
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
type: 'string',
|
|
70
|
+
},
|
|
77
71
|
]);
|
|
78
72
|
});
|
|
79
73
|
|
|
@@ -89,13 +83,10 @@ describe('getSchemaAlternatives', () => {
|
|
|
89
83
|
a.anyOf?.push(a);
|
|
90
84
|
|
|
91
85
|
expect(getSchemaAlternatives(a)).toEqual([
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
a,
|
|
97
|
-
],
|
|
98
|
-
undefined,
|
|
86
|
+
{
|
|
87
|
+
type: 'string',
|
|
88
|
+
},
|
|
89
|
+
a,
|
|
99
90
|
]);
|
|
100
91
|
});
|
|
101
92
|
});
|