@jpmorganchase/elemental 1.0.0 → 3.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. package/__fixtures__/api-descriptions/Instagram.d.ts +1547 -0
  2. package/__fixtures__/api-descriptions/badgesForSchema.d.ts +1 -0
  3. package/__fixtures__/api-descriptions/simpleApiWithInternalOperations.d.ts +224 -0
  4. package/__fixtures__/api-descriptions/simpleApiWithoutDescription.d.ts +212 -0
  5. package/__fixtures__/api-descriptions/todosApiBundled.d.ts +1 -0
  6. package/__fixtures__/api-descriptions/zoomApiYaml.d.ts +1 -0
  7. package/components/API/APIWithSidebarLayout.d.ts +17 -0
  8. package/components/API/APIWithStackedLayout.d.ts +15 -0
  9. package/components/API/utils.d.ts +18 -0
  10. package/containers/API.d.ts +23 -0
  11. package/containers/API.spec.d.ts +3 -0
  12. package/containers/API.stories.d.ts +57 -0
  13. package/hooks/useExportDocumentProps.d.ts +11 -0
  14. package/hooks/useExportDocumentProps.spec.d.ts +1 -0
  15. package/index.esm.js +516 -0
  16. package/index.js +549 -0
  17. package/index.mjs +516 -0
  18. package/package.json +17 -76
  19. package/styles.min.css +1 -0
  20. package/utils/oas/index.d.ts +3 -0
  21. package/utils/oas/oas2.d.ts +2 -0
  22. package/utils/oas/oas3.d.ts +2 -0
  23. package/utils/oas/types.d.ts +30 -0
  24. package/web-components/components.d.ts +1 -0
  25. package/web-components/index.d.ts +1 -0
  26. package/web-components.min.js +2 -0
  27. package/web-components.min.js.LICENSE.txt +189 -0
  28. package/.storybook/main.js +0 -1
  29. package/.storybook/manager.js +0 -1
  30. package/.storybook/preview.jsx +0 -3
  31. package/jest.config.js +0 -7
  32. package/src/__fixtures__/api-descriptions/Instagram.ts +0 -1859
  33. package/src/__fixtures__/api-descriptions/badgesForSchema.ts +0 -36
  34. package/src/__fixtures__/api-descriptions/simpleApiWithInternalOperations.ts +0 -253
  35. package/src/__fixtures__/api-descriptions/simpleApiWithoutDescription.ts +0 -243
  36. package/src/__fixtures__/api-descriptions/todosApiBundled.ts +0 -430
  37. package/src/__fixtures__/api-descriptions/zoomApiYaml.ts +0 -6083
  38. package/src/components/API/APIWithSidebarLayout.tsx +0 -111
  39. package/src/components/API/APIWithStackedLayout.tsx +0 -220
  40. package/src/components/API/__tests__/utils.test.ts +0 -848
  41. package/src/components/API/utils.ts +0 -174
  42. package/src/containers/API.spec.tsx +0 -131
  43. package/src/containers/API.stories.tsx +0 -99
  44. package/src/containers/API.tsx +0 -200
  45. package/src/hooks/useExportDocumentProps.spec.tsx +0 -68
  46. package/src/hooks/useExportDocumentProps.tsx +0 -48
  47. package/src/styles.css +0 -1
  48. package/src/utils/oas/__tests__/oas.spec.ts +0 -272
  49. package/src/utils/oas/index.ts +0 -150
  50. package/src/utils/oas/oas2.ts +0 -31
  51. package/src/utils/oas/oas3.ts +0 -37
  52. package/src/utils/oas/types.ts +0 -31
  53. package/src/web-components/__stories__/Api.stories.tsx +0 -63
  54. package/src/web-components/components.ts +0 -20
  55. package/src/web-components/index.ts +0 -3
  56. package/tsconfig.build.json +0 -18
  57. package/tsconfig.json +0 -7
  58. package/web-components.config.js +0 -1
  59. /package/{src/index.ts → index.d.ts} +0 -0
package/index.js ADDED
@@ -0,0 +1,549 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var elementalCore = require('@jpmorganchase/elemental-core');
6
+ var mosaic = require('@stoplight/mosaic');
7
+ var flow = require('lodash/flow.js');
8
+ var React = require('react');
9
+ var reactQuery = require('react-query');
10
+ var types = require('@stoplight/types');
11
+ var reactRouterDom = require('react-router-dom');
12
+ var defaults = require('lodash/defaults.js');
13
+ var cn = require('classnames');
14
+ var yaml = require('@stoplight/yaml');
15
+ var saver = require('file-saver');
16
+ var oas2 = require('@stoplight/http-spec/oas2');
17
+ var oas3 = require('@stoplight/http-spec/oas3');
18
+ var json = require('@stoplight/json');
19
+ var get = require('lodash/get.js');
20
+ var isObject = require('lodash/isObject.js');
21
+ var last = require('lodash/last.js');
22
+
23
+ function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
24
+
25
+ function _interopNamespace(e) {
26
+ if (e && e.__esModule) return e;
27
+ var n = Object.create(null);
28
+ if (e) {
29
+ Object.keys(e).forEach(function (k) {
30
+ if (k !== 'default') {
31
+ var d = Object.getOwnPropertyDescriptor(e, k);
32
+ Object.defineProperty(n, k, d.get ? d : {
33
+ enumerable: true,
34
+ get: function () { return e[k]; }
35
+ });
36
+ }
37
+ });
38
+ }
39
+ n["default"] = e;
40
+ return Object.freeze(n);
41
+ }
42
+
43
+ var flow__default = /*#__PURE__*/_interopDefaultLegacy(flow);
44
+ var React__namespace = /*#__PURE__*/_interopNamespace(React);
45
+ var defaults__default = /*#__PURE__*/_interopDefaultLegacy(defaults);
46
+ var cn__default = /*#__PURE__*/_interopDefaultLegacy(cn);
47
+ var saver__default = /*#__PURE__*/_interopDefaultLegacy(saver);
48
+ var get__default = /*#__PURE__*/_interopDefaultLegacy(get);
49
+ var isObject__default = /*#__PURE__*/_interopDefaultLegacy(isObject);
50
+ var last__default = /*#__PURE__*/_interopDefaultLegacy(last);
51
+
52
+ const computeTagGroups = (serviceNode) => {
53
+ const groupsByTagId = {};
54
+ const ungrouped = [];
55
+ const lowerCaseServiceTags = serviceNode.tags.map(tn => tn.toLowerCase());
56
+ for (const node of serviceNode.children) {
57
+ if (node.type !== types.NodeType.HttpOperation)
58
+ continue;
59
+ const tagName = node.tags[0];
60
+ if (tagName) {
61
+ const tagId = tagName.toLowerCase();
62
+ if (groupsByTagId[tagId]) {
63
+ groupsByTagId[tagId].items.push(node);
64
+ }
65
+ else {
66
+ const serviceTagIndex = lowerCaseServiceTags.findIndex(tn => tn === tagId);
67
+ const serviceTagName = serviceNode.tags[serviceTagIndex];
68
+ groupsByTagId[tagId] = {
69
+ title: serviceTagName || tagName,
70
+ items: [node],
71
+ };
72
+ }
73
+ }
74
+ else {
75
+ ungrouped.push(node);
76
+ }
77
+ }
78
+ const orderedTagGroups = Object.entries(groupsByTagId)
79
+ .sort(([g1], [g2]) => {
80
+ const g1LC = g1.toLowerCase();
81
+ const g2LC = g2.toLowerCase();
82
+ const g1Idx = lowerCaseServiceTags.findIndex(tn => tn === g1LC);
83
+ const g2Idx = lowerCaseServiceTags.findIndex(tn => tn === g2LC);
84
+ if (g1Idx < 0 && g2Idx < 0)
85
+ return 0;
86
+ if (g1Idx < 0)
87
+ return 1;
88
+ if (g2Idx < 0)
89
+ return -1;
90
+ return g1Idx - g2Idx;
91
+ })
92
+ .map(([, tagGroup]) => tagGroup);
93
+ return { groups: orderedTagGroups, ungrouped };
94
+ };
95
+ const defaultComputerAPITreeConfig = {
96
+ hideSchemas: false,
97
+ hideInternal: false,
98
+ };
99
+ const computeAPITree = (serviceNode, config = {}) => {
100
+ const mergedConfig = defaults__default["default"](config, defaultComputerAPITreeConfig);
101
+ const tree = [];
102
+ tree.push({
103
+ id: '/',
104
+ slug: '/',
105
+ title: 'Overview',
106
+ type: 'overview',
107
+ meta: '',
108
+ });
109
+ const operationNodes = serviceNode.children.filter(node => node.type === types.NodeType.HttpOperation);
110
+ if (operationNodes.length) {
111
+ tree.push({
112
+ title: 'Endpoints',
113
+ });
114
+ const { groups, ungrouped } = computeTagGroups(serviceNode);
115
+ ungrouped.forEach(operationNode => {
116
+ if (mergedConfig.hideInternal && operationNode.data.internal) {
117
+ return;
118
+ }
119
+ tree.push({
120
+ id: operationNode.uri,
121
+ slug: operationNode.uri,
122
+ title: operationNode.name,
123
+ type: operationNode.type,
124
+ meta: operationNode.data.method,
125
+ });
126
+ });
127
+ groups.forEach(group => {
128
+ const items = group.items.flatMap(operationNode => {
129
+ if (mergedConfig.hideInternal && operationNode.data.internal) {
130
+ return [];
131
+ }
132
+ return {
133
+ id: operationNode.uri,
134
+ slug: operationNode.uri,
135
+ title: operationNode.name,
136
+ type: operationNode.type,
137
+ meta: operationNode.data.method,
138
+ };
139
+ });
140
+ if (items.length > 0) {
141
+ tree.push({
142
+ title: group.title,
143
+ items,
144
+ });
145
+ }
146
+ });
147
+ }
148
+ let schemaNodes = serviceNode.children.filter(node => node.type === types.NodeType.Model);
149
+ if (mergedConfig.hideInternal) {
150
+ schemaNodes = schemaNodes.filter(node => !node.data['x-internal']);
151
+ }
152
+ if (!mergedConfig.hideSchemas && schemaNodes.length) {
153
+ tree.push({
154
+ title: 'Schemas',
155
+ });
156
+ schemaNodes.forEach(node => {
157
+ tree.push({
158
+ id: node.uri,
159
+ slug: node.uri,
160
+ title: node.name,
161
+ type: node.type,
162
+ meta: '',
163
+ });
164
+ });
165
+ }
166
+ return tree;
167
+ };
168
+ const findFirstNodeSlug = (tree) => {
169
+ for (const item of tree) {
170
+ if ('slug' in item) {
171
+ return item.slug;
172
+ }
173
+ if ('items' in item) {
174
+ const slug = findFirstNodeSlug(item.items);
175
+ if (slug) {
176
+ return slug;
177
+ }
178
+ }
179
+ }
180
+ return;
181
+ };
182
+ const isInternal = (node) => {
183
+ const data = node.data;
184
+ if (elementalCore.isHttpOperation(data)) {
185
+ return !!data.internal;
186
+ }
187
+ if (elementalCore.isHttpService(data)) {
188
+ return false;
189
+ }
190
+ return !!data['x-internal'];
191
+ };
192
+
193
+ const APIWithSidebarLayout = ({ serviceNode, logo, hideTryIt, hideSchemas, hideInternal, hideExport, exportProps, tryItCredentialsPolicy, tryItCorsProxy, tryItOutDefaultServer, }) => {
194
+ const container = React__namespace.useRef(null);
195
+ const tree = React__namespace.useMemo(() => computeAPITree(serviceNode, { hideSchemas, hideInternal }), [serviceNode, hideSchemas, hideInternal]);
196
+ const location = reactRouterDom.useLocation();
197
+ const { pathname } = location;
198
+ const isRootPath = !pathname || pathname === '/';
199
+ const node = isRootPath ? serviceNode : serviceNode.children.find(child => child.uri === pathname);
200
+ const layoutOptions = React__namespace.useMemo(() => ({ hideTryIt: hideTryIt, hideExport: hideExport || (node === null || node === void 0 ? void 0 : node.type) !== types.NodeType.HttpService }), [hideTryIt, hideExport, node]);
201
+ if (!node) {
202
+ const firstSlug = findFirstNodeSlug(tree);
203
+ if (firstSlug) {
204
+ return React__namespace.createElement(reactRouterDom.Redirect, { to: firstSlug });
205
+ }
206
+ }
207
+ if (hideInternal && node && isInternal(node)) {
208
+ return React__namespace.createElement(reactRouterDom.Redirect, { to: "/" });
209
+ }
210
+ const handleTocClick = () => {
211
+ if (container.current) {
212
+ container.current.scrollIntoView();
213
+ }
214
+ };
215
+ const sidebar = (React__namespace.createElement(React__namespace.Fragment, null,
216
+ React__namespace.createElement(mosaic.Flex, { ml: 4, mb: 5, alignItems: "center" },
217
+ logo ? (React__namespace.createElement(elementalCore.Logo, { logo: { url: logo, altText: 'logo' } })) : (serviceNode.data.logo && React__namespace.createElement(elementalCore.Logo, { logo: serviceNode.data.logo })),
218
+ React__namespace.createElement(mosaic.Heading, { size: 4 }, serviceNode.name)),
219
+ React__namespace.createElement(mosaic.Flex, { flexGrow: true, flexShrink: true, overflowY: "auto", direction: "col" },
220
+ React__namespace.createElement(elementalCore.TableOfContents, { tree: tree, activeId: pathname, Link: reactRouterDom.Link, onLinkClick: handleTocClick })),
221
+ React__namespace.createElement(elementalCore.PoweredByLink, { source: serviceNode.name, pathname: pathname, packageType: "elements" })));
222
+ return (React__namespace.createElement(elementalCore.SidebarLayout, { ref: container, sidebar: sidebar }, node && (React__namespace.createElement(elementalCore.ParsedDocs, { key: pathname, uri: pathname, node: node, nodeTitle: node.name, layoutOptions: layoutOptions, location: location, exportProps: exportProps, tryItCredentialsPolicy: tryItCredentialsPolicy, tryItCorsProxy: tryItCorsProxy, tryItOutDefaultServer: tryItOutDefaultServer }))));
223
+ };
224
+
225
+ const itemMatchesHash = (hash, item) => {
226
+ return hash.substr(1) === `${item.name}-${item.data.method}`;
227
+ };
228
+ const TryItContext = React__namespace.createContext({
229
+ hideTryIt: false,
230
+ tryItCredentialsPolicy: 'omit',
231
+ });
232
+ TryItContext.displayName = 'TryItContext';
233
+ const APIWithStackedLayout = ({ serviceNode, hideTryIt, hideExport, exportProps, tryItCredentialsPolicy, tryItCorsProxy, tryItOutDefaultServer, }) => {
234
+ const location = reactRouterDom.useLocation();
235
+ const { groups } = computeTagGroups(serviceNode);
236
+ return (React__namespace.createElement(TryItContext.Provider, { value: { hideTryIt, tryItCredentialsPolicy, corsProxy: tryItCorsProxy } },
237
+ React__namespace.createElement(mosaic.Flex, { w: "full", flexDirection: "col", m: "auto", className: "sl-max-w-4xl" },
238
+ React__namespace.createElement(mosaic.Box, { w: "full", borderB: true },
239
+ React__namespace.createElement(elementalCore.Docs, { className: "sl-mx-auto", nodeData: serviceNode.data, nodeTitle: serviceNode.name, nodeType: types.NodeType.HttpService, location: location, layoutOptions: { showPoweredByLink: true, hideExport }, exportProps: exportProps, tryItCredentialsPolicy: tryItCredentialsPolicy, tryItOutDefaultServer: tryItOutDefaultServer })),
240
+ groups.map(group => (React__namespace.createElement(Group, { key: group.title, group: group }))))));
241
+ };
242
+ const Group = React__namespace.memo(({ group }) => {
243
+ const [isExpanded, setIsExpanded] = React__namespace.useState(false);
244
+ const { hash } = reactRouterDom.useLocation();
245
+ const scrollRef = React__namespace.useRef(null);
246
+ const urlHashMatches = hash.substr(1) === group.title;
247
+ const onClick = React__namespace.useCallback(() => setIsExpanded(!isExpanded), [isExpanded]);
248
+ const shouldExpand = React__namespace.useMemo(() => {
249
+ return urlHashMatches || group.items.some(item => itemMatchesHash(hash, item));
250
+ }, [group, hash, urlHashMatches]);
251
+ React__namespace.useEffect(() => {
252
+ var _a;
253
+ if (shouldExpand) {
254
+ setIsExpanded(true);
255
+ if (urlHashMatches && ((_a = scrollRef === null || scrollRef === void 0 ? void 0 : scrollRef.current) === null || _a === void 0 ? void 0 : _a.offsetTop)) {
256
+ window.scrollTo(0, scrollRef.current.offsetTop);
257
+ }
258
+ }
259
+ }, [shouldExpand, urlHashMatches, group, hash]);
260
+ return (React__namespace.createElement(mosaic.Box, null,
261
+ React__namespace.createElement(mosaic.Flex, { ref: scrollRef, onClick: onClick, mx: "auto", justifyContent: "between", alignItems: "center", borderB: true, px: 2, py: 4, cursor: "pointer", color: { default: 'current', hover: 'muted' } },
262
+ React__namespace.createElement(mosaic.Box, { fontSize: "lg", fontWeight: "medium" }, group.title),
263
+ React__namespace.createElement(mosaic.Icon, { className: "sl-mr-2", icon: isExpanded ? 'chevron-down' : 'chevron-right', size: "sm" })),
264
+ React__namespace.createElement(Collapse, { isOpen: isExpanded }, group.items.map(item => {
265
+ return React__namespace.createElement(Item, { key: item.uri, item: item });
266
+ }))));
267
+ });
268
+ const Item = React__namespace.memo(({ item }) => {
269
+ const location = reactRouterDom.useLocation();
270
+ const { hash } = location;
271
+ const [isExpanded, setIsExpanded] = React__namespace.useState(false);
272
+ const scrollRef = React__namespace.useRef(null);
273
+ const color = elementalCore.HttpMethodColors[item.data.method] || 'gray';
274
+ const isDeprecated = !!item.data.deprecated;
275
+ const { hideTryIt, tryItCredentialsPolicy, corsProxy } = React__namespace.useContext(TryItContext);
276
+ const onClick = React__namespace.useCallback(() => setIsExpanded(!isExpanded), [isExpanded]);
277
+ React__namespace.useEffect(() => {
278
+ var _a;
279
+ if (itemMatchesHash(hash, item)) {
280
+ setIsExpanded(true);
281
+ if ((_a = scrollRef === null || scrollRef === void 0 ? void 0 : scrollRef.current) === null || _a === void 0 ? void 0 : _a.offsetTop) {
282
+ window.scrollTo(0, scrollRef.current.offsetTop);
283
+ }
284
+ }
285
+ }, [hash, item]);
286
+ return (React__namespace.createElement(mosaic.Box, { ref: scrollRef, w: "full", my: 2, border: true, borderColor: { default: isExpanded ? 'light' : 'transparent', hover: 'light' }, bg: { default: isExpanded ? 'code' : 'transparent', hover: 'code' } },
287
+ React__namespace.createElement(mosaic.Flex, { mx: "auto", alignItems: "center", cursor: "pointer", fontSize: "lg", p: 2, onClick: onClick, color: "current" },
288
+ React__namespace.createElement(mosaic.Box, { w: 24, textTransform: "uppercase", textAlign: "center", fontWeight: "semibold", border: true, rounded: true, px: 2, bg: "canvas", className: cn__default["default"](`sl-mr-5 sl-text-base`, `sl-text-${color}`, `sl-border-${color}`) }, item.data.method || 'UNKNOWN'),
289
+ React__namespace.createElement(mosaic.Box, { flex: 1, fontWeight: "medium", wordBreak: "all" }, item.name),
290
+ isDeprecated && React__namespace.createElement(elementalCore.DeprecatedBadge, null)),
291
+ React__namespace.createElement(Collapse, { isOpen: isExpanded }, hideTryIt ? (React__namespace.createElement(mosaic.Box, { as: elementalCore.ParsedDocs, layoutOptions: { noHeading: true, hideTryItPanel: true }, node: item, p: 4 })) : (React__namespace.createElement(mosaic.Tabs, { appearance: "line" },
292
+ React__namespace.createElement(mosaic.TabList, null,
293
+ React__namespace.createElement(mosaic.Tab, null, "Docs"),
294
+ React__namespace.createElement(mosaic.Tab, null, "TryIt")),
295
+ React__namespace.createElement(mosaic.TabPanels, null,
296
+ React__namespace.createElement(mosaic.TabPanel, null,
297
+ React__namespace.createElement(elementalCore.ParsedDocs, { className: "sl-px-4", node: item, location: location, layoutOptions: { noHeading: true, hideTryItPanel: true } })),
298
+ React__namespace.createElement(mosaic.TabPanel, null,
299
+ React__namespace.createElement(elementalCore.TryItWithRequestSamples, { httpOperation: item.data, tryItCredentialsPolicy: tryItCredentialsPolicy, corsProxy: corsProxy }))))))));
300
+ });
301
+ const Collapse = ({ isOpen, children }) => {
302
+ if (!isOpen)
303
+ return null;
304
+ return React__namespace.createElement(mosaic.Box, null, children);
305
+ };
306
+
307
+ var NodeTypes;
308
+ (function (NodeTypes) {
309
+ NodeTypes["Paths"] = "paths";
310
+ NodeTypes["Path"] = "path";
311
+ NodeTypes["Operation"] = "operation";
312
+ NodeTypes["Components"] = "components";
313
+ NodeTypes["Models"] = "models";
314
+ NodeTypes["Model"] = "model";
315
+ })(NodeTypes || (NodeTypes = {}));
316
+
317
+ const oas2SourceMap = [
318
+ {
319
+ match: 'paths',
320
+ type: NodeTypes.Paths,
321
+ children: [
322
+ {
323
+ notMatch: '^x-',
324
+ type: NodeTypes.Path,
325
+ children: [
326
+ {
327
+ match: 'get|post|put|delete|options|head|patch|trace',
328
+ type: NodeTypes.Operation,
329
+ },
330
+ ],
331
+ },
332
+ ],
333
+ },
334
+ {
335
+ match: 'definitions',
336
+ type: NodeTypes.Models,
337
+ children: [
338
+ {
339
+ notMatch: '^x-',
340
+ type: NodeTypes.Model,
341
+ },
342
+ ],
343
+ },
344
+ ];
345
+
346
+ const oas3SourceMap = [
347
+ {
348
+ match: 'paths',
349
+ type: NodeTypes.Paths,
350
+ children: [
351
+ {
352
+ notMatch: '^x-',
353
+ type: NodeTypes.Path,
354
+ children: [
355
+ {
356
+ match: 'get|post|put|delete|options|head|patch|trace',
357
+ type: NodeTypes.Operation,
358
+ },
359
+ ],
360
+ },
361
+ ],
362
+ },
363
+ {
364
+ match: 'components',
365
+ type: NodeTypes.Components,
366
+ children: [
367
+ {
368
+ match: 'schemas',
369
+ type: NodeTypes.Models,
370
+ children: [
371
+ {
372
+ notMatch: '^x-',
373
+ type: NodeTypes.Model,
374
+ },
375
+ ],
376
+ },
377
+ ],
378
+ },
379
+ ];
380
+
381
+ const isOas2 = (parsed) => isObject__default["default"](parsed) &&
382
+ 'swagger' in parsed &&
383
+ Number.parseInt(String(parsed.swagger)) === 2;
384
+ const isOas3 = (parsed) => isObject__default["default"](parsed) &&
385
+ 'openapi' in parsed &&
386
+ Number.parseFloat(String(parsed.openapi)) >= 3;
387
+ const isOas31 = (parsed) => isObject__default["default"](parsed) &&
388
+ 'openapi' in parsed &&
389
+ Number.parseFloat(String(parsed.openapi)) === 3.1;
390
+ const OAS_MODEL_REGEXP = /((definitions|components)\/?(schemas)?)\//;
391
+ function transformOasToServiceNode(apiDescriptionDocument) {
392
+ if (isOas31(apiDescriptionDocument)) {
393
+ return computeServiceNode(Object.assign(Object.assign({}, apiDescriptionDocument), { jsonSchemaDialect: 'http://json-schema.org/draft-07/schema#' }), oas3SourceMap, oas3.transformOas3Service, oas3.transformOas3Operation);
394
+ }
395
+ if (isOas3(apiDescriptionDocument)) {
396
+ return computeServiceNode(apiDescriptionDocument, oas3SourceMap, oas3.transformOas3Service, oas3.transformOas3Operation);
397
+ }
398
+ else if (isOas2(apiDescriptionDocument)) {
399
+ return computeServiceNode(apiDescriptionDocument, oas2SourceMap, oas2.transformOas2Service, oas2.transformOas2Operation);
400
+ }
401
+ return null;
402
+ }
403
+ function computeServiceNode(document, map, transformService, transformOperation) {
404
+ var _a;
405
+ const serviceDocument = transformService({ document });
406
+ const serviceNode = {
407
+ type: types.NodeType.HttpService,
408
+ uri: '/',
409
+ name: serviceDocument.name,
410
+ data: serviceDocument,
411
+ tags: ((_a = serviceDocument.tags) === null || _a === void 0 ? void 0 : _a.map(tag => tag.name)) || [],
412
+ children: computeChildNodes(document, document, map, transformOperation),
413
+ };
414
+ return serviceNode;
415
+ }
416
+ function computeChildNodes(document, data, map, transformer, parentUri = '') {
417
+ var _a;
418
+ const nodes = [];
419
+ if (!isObject__default["default"](data))
420
+ return nodes;
421
+ for (const key of Object.keys(data)) {
422
+ const sanitizedKey = json.encodePointerFragment(key);
423
+ const match = findMapMatch(sanitizedKey, map);
424
+ if (match) {
425
+ const uri = `${parentUri}/${sanitizedKey}`;
426
+ const jsonPath = json.pointerToPath(`#${uri}`);
427
+ if (match.type === NodeTypes.Operation && jsonPath.length === 3) {
428
+ const path = String(jsonPath[1]);
429
+ const method = String(jsonPath[2]);
430
+ const operationDocument = transformer({ document, path, method });
431
+ let parsedUri;
432
+ const encodedPath = String(json.encodePointerFragment(path));
433
+ if (operationDocument.iid) {
434
+ parsedUri = `/operations/${operationDocument.iid}`;
435
+ }
436
+ else {
437
+ parsedUri = uri.replace(encodedPath, elementalCore.slugify(path));
438
+ }
439
+ nodes.push({
440
+ type: types.NodeType.HttpOperation,
441
+ uri: parsedUri,
442
+ data: operationDocument,
443
+ name: operationDocument.summary || operationDocument.iid || operationDocument.path,
444
+ tags: ((_a = operationDocument.tags) === null || _a === void 0 ? void 0 : _a.map(tag => tag.name)) || [],
445
+ });
446
+ }
447
+ else if (match.type === NodeTypes.Model) {
448
+ const schemaDocument = get__default["default"](document, jsonPath);
449
+ const parsedUri = uri.replace(OAS_MODEL_REGEXP, 'schemas/');
450
+ nodes.push({
451
+ type: types.NodeType.Model,
452
+ uri: parsedUri,
453
+ data: schemaDocument,
454
+ name: schemaDocument.title || last__default["default"](uri.split('/')) || '',
455
+ tags: schemaDocument['x-tags'] || [],
456
+ });
457
+ }
458
+ if (match.children) {
459
+ nodes.push(...computeChildNodes(document, data[key], match.children, transformer, uri));
460
+ }
461
+ }
462
+ }
463
+ return nodes;
464
+ }
465
+ function findMapMatch(key, map) {
466
+ var _a;
467
+ if (typeof key === 'number')
468
+ return;
469
+ for (const entry of map) {
470
+ if (!!((_a = entry.match) === null || _a === void 0 ? void 0 : _a.match(key)) || (entry.notMatch !== void 0 && !entry.notMatch.match(key))) {
471
+ return entry;
472
+ }
473
+ }
474
+ }
475
+ function isJson(value) {
476
+ try {
477
+ JSON.parse(value);
478
+ }
479
+ catch (e) {
480
+ return false;
481
+ }
482
+ return true;
483
+ }
484
+
485
+ function useExportDocumentProps({ originalDocument, bundledDocument, }) {
486
+ const isJsonDocument = typeof originalDocument === 'object' || (!!originalDocument && isJson(originalDocument));
487
+ const exportDocument = React__namespace.useCallback((document) => {
488
+ const type = isJsonDocument ? 'json' : 'yaml';
489
+ const blob = new Blob([document], {
490
+ type: `application/${type}`,
491
+ });
492
+ saver__default["default"].saveAs(blob, `document.${type}`);
493
+ }, [isJsonDocument]);
494
+ const exportOriginalDocument = React__namespace.useCallback(() => {
495
+ const stringifiedDocument = typeof originalDocument === 'object' ? JSON.stringify(originalDocument, null, 2) : originalDocument || '';
496
+ exportDocument(stringifiedDocument);
497
+ }, [originalDocument, exportDocument]);
498
+ const exportBundledDocument = React__namespace.useCallback(() => {
499
+ const stringifiedDocument = isJsonDocument
500
+ ? JSON.stringify(bundledDocument, null, 2)
501
+ : yaml.safeStringify(bundledDocument);
502
+ exportDocument(stringifiedDocument);
503
+ }, [bundledDocument, isJsonDocument, exportDocument]);
504
+ return {
505
+ original: {
506
+ onPress: exportOriginalDocument,
507
+ },
508
+ bundled: {
509
+ onPress: exportBundledDocument,
510
+ },
511
+ };
512
+ }
513
+
514
+ const propsAreWithDocument = (props) => {
515
+ return props.hasOwnProperty('apiDescriptionDocument');
516
+ };
517
+ const APIImpl = props => {
518
+ const { layout, apiDescriptionUrl = '', logo, hideTryIt, hideSchemas, hideInternal, hideExport, tryItCredentialsPolicy, tryItCorsProxy, tryItOutDefaultServer, } = props;
519
+ const apiDescriptionDocument = propsAreWithDocument(props) ? props.apiDescriptionDocument : undefined;
520
+ const { data: fetchedDocument, error } = reactQuery.useQuery([apiDescriptionUrl], () => fetch(apiDescriptionUrl).then(res => {
521
+ if (res.ok) {
522
+ return res.text();
523
+ }
524
+ throw new Error(`Unable to load description document, status code: ${res.status}`);
525
+ }), {
526
+ enabled: apiDescriptionUrl !== '' && !apiDescriptionDocument,
527
+ });
528
+ const document = apiDescriptionDocument || fetchedDocument || '';
529
+ const parsedDocument = elementalCore.useParsedValue(document);
530
+ const bundledDocument = elementalCore.useBundleRefsIntoDocument(parsedDocument, { baseUrl: apiDescriptionUrl });
531
+ const serviceNode = React__namespace.useMemo(() => transformOasToServiceNode(bundledDocument), [bundledDocument]);
532
+ const exportProps = useExportDocumentProps({ originalDocument: document, bundledDocument });
533
+ if (error) {
534
+ return (React__namespace.createElement(mosaic.Flex, { justify: "center", alignItems: "center", w: "full", minH: "screen" },
535
+ React__namespace.createElement(elementalCore.NonIdealState, { title: "Document could not be loaded", description: "The API description document could not be fetched. This could indicate connectivity problems, or issues with the server hosting the spec.", icon: "exclamation-triangle" })));
536
+ }
537
+ if (!bundledDocument) {
538
+ return (React__namespace.createElement(mosaic.Flex, { justify: "center", alignItems: "center", w: "full", minH: "screen", color: "light" },
539
+ React__namespace.createElement(mosaic.Box, { as: mosaic.Icon, icon: ['fal', 'circle-notch'], size: "3x", spin: true })));
540
+ }
541
+ if (!serviceNode) {
542
+ return (React__namespace.createElement(mosaic.Flex, { justify: "center", alignItems: "center", w: "full", minH: "screen" },
543
+ React__namespace.createElement(elementalCore.NonIdealState, { title: "Failed to parse OpenAPI file", description: "Please make sure your OpenAPI file is valid and try again" })));
544
+ }
545
+ return (React__namespace.createElement(elementalCore.InlineRefResolverProvider, { document: parsedDocument }, layout === 'stacked' ? (React__namespace.createElement(APIWithStackedLayout, { serviceNode: serviceNode, hideTryIt: hideTryIt, hideExport: hideExport, exportProps: exportProps, tryItCredentialsPolicy: tryItCredentialsPolicy, tryItCorsProxy: tryItCorsProxy, tryItOutDefaultServer: tryItOutDefaultServer })) : (React__namespace.createElement(APIWithSidebarLayout, { logo: logo, serviceNode: serviceNode, hideTryIt: hideTryIt, hideSchemas: hideSchemas, hideInternal: hideInternal, hideExport: hideExport, exportProps: exportProps, tryItCredentialsPolicy: tryItCredentialsPolicy, tryItCorsProxy: tryItCorsProxy, tryItOutDefaultServer: tryItOutDefaultServer }))));
546
+ };
547
+ const API = flow__default["default"](elementalCore.withRouter, elementalCore.withStyles, elementalCore.withPersistenceBoundary, elementalCore.withMosaicProvider, elementalCore.withQueryClientProvider)(APIImpl);
548
+
549
+ exports.API = API;