@stoplight/elements 7.16.5 → 8.0.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.
@@ -1,4 +1,4 @@
1
- import { ExportButtonProps } from '@stoplight/elements-core';
1
+ import { ExportButtonProps, TableOfContentsItem } from '@stoplight/elements-core';
2
2
  import * as React from 'react';
3
3
  import { ServiceNode } from '../../utils/oas/types';
4
4
  declare type SidebarLayoutProps = {
@@ -13,4 +13,12 @@ declare type SidebarLayoutProps = {
13
13
  tryItCorsProxy?: string;
14
14
  };
15
15
  export declare const APIWithSidebarLayout: React.FC<SidebarLayoutProps>;
16
+ declare type SidebarProps = {
17
+ serviceNode: ServiceNode;
18
+ logo?: string;
19
+ container: React.RefObject<HTMLElement>;
20
+ pathname: string;
21
+ tree: TableOfContentsItem[];
22
+ };
23
+ export declare const Sidebar: React.FC<SidebarProps>;
16
24
  export {};
@@ -1,12 +1,13 @@
1
1
  import { TableOfContentsItem } from '@stoplight/elements-core';
2
- import { OperationNode, ServiceChildNode, ServiceNode } from '../../utils/oas/types';
3
- export declare type TagGroup = {
2
+ import { OperationNode, ServiceChildNode, ServiceNode, WebhookNode } from '../../utils/oas/types';
3
+ declare type GroupableNode = OperationNode | WebhookNode;
4
+ export declare type TagGroup<T extends GroupableNode> = {
4
5
  title: string;
5
- items: OperationNode[];
6
+ items: T[];
6
7
  };
7
- export declare const computeTagGroups: (serviceNode: ServiceNode) => {
8
- groups: TagGroup[];
9
- ungrouped: OperationNode[];
8
+ export declare function computeTagGroups<T extends GroupableNode>(serviceNode: ServiceNode, nodeType: T['type']): {
9
+ groups: TagGroup<T>[];
10
+ ungrouped: T[];
10
11
  };
11
12
  interface ComputeAPITreeConfig {
12
13
  hideSchemas?: boolean;
package/index.esm.js CHANGED
@@ -1,4 +1,4 @@
1
- import { isHttpOperation, isHttpService, HttpMethodColors, DeprecatedBadge, ParsedDocs, TryItWithRequestSamples, Docs, Logo, TableOfContents, PoweredByLink, SidebarLayout, slugify, withRouter, withStyles, withPersistenceBoundary, withMosaicProvider, withQueryClientProvider, useParsedValue, useBundleRefsIntoDocument, NonIdealState, InlineRefResolverProvider } from '@stoplight/elements-core';
1
+ import { isHttpOperation, isHttpWebhookOperation, isHttpService, HttpMethodColors, DeprecatedBadge, ParsedDocs, TryItWithRequestSamples, Docs, SidebarLayout, Logo, TableOfContents, PoweredByLink, slugify, withRouter, withStyles, withPersistenceBoundary, withMosaicProvider, withQueryClientProvider, useParsedValue, useBundleRefsIntoDocument, NonIdealState, InlineRefResolverProvider } from '@stoplight/elements-core';
2
2
  import { Box, Flex, Icon, Tabs, TabList, Tab, TabPanels, TabPanel, Heading } from '@stoplight/mosaic';
3
3
  import { NodeType } from '@stoplight/types';
4
4
  import cn from 'classnames';
@@ -9,6 +9,7 @@ import { useQuery } from 'react-query';
9
9
  import { useLocation, Redirect, Link } from 'react-router-dom';
10
10
  import { safeStringify } from '@stoplight/yaml';
11
11
  import saver from 'file-saver';
12
+ import { OPERATION_CONFIG, WEBHOOK_CONFIG } from '@stoplight/http-spec/oas';
12
13
  import { transformOas2Service, transformOas2Operation } from '@stoplight/http-spec/oas2';
13
14
  import { transformOas3Service, transformOas3Operation } from '@stoplight/http-spec/oas3';
14
15
  import { encodePointerFragment, pointerToPath } from '@stoplight/json';
@@ -16,13 +17,12 @@ import get from 'lodash/get.js';
16
17
  import isObject from 'lodash/isObject.js';
17
18
  import last from 'lodash/last.js';
18
19
 
19
- const computeTagGroups = (serviceNode) => {
20
+ function computeTagGroups(serviceNode, nodeType) {
20
21
  const groupsByTagId = {};
21
22
  const ungrouped = [];
22
23
  const lowerCaseServiceTags = serviceNode.tags.map(tn => tn.toLowerCase());
23
- for (const node of serviceNode.children) {
24
- if (node.type !== NodeType.HttpOperation)
25
- continue;
24
+ const groupableNodes = serviceNode.children.filter(n => n.type === nodeType);
25
+ for (const node of groupableNodes) {
26
26
  const tagName = node.tags[0];
27
27
  if (tagName) {
28
28
  const tagId = tagName.toLowerCase();
@@ -58,7 +58,7 @@ const computeTagGroups = (serviceNode) => {
58
58
  })
59
59
  .map(([, tagGroup]) => tagGroup);
60
60
  return { groups: orderedTagGroups, ungrouped };
61
- };
61
+ }
62
62
  const defaultComputerAPITreeConfig = {
63
63
  hideSchemas: false,
64
64
  hideInternal: false,
@@ -73,12 +73,52 @@ const computeAPITree = (serviceNode, config = {}) => {
73
73
  type: 'overview',
74
74
  meta: '',
75
75
  });
76
- const operationNodes = serviceNode.children.filter(node => node.type === NodeType.HttpOperation);
77
- if (operationNodes.length) {
76
+ const hasOperationNodes = serviceNode.children.some(node => node.type === NodeType.HttpOperation);
77
+ if (hasOperationNodes) {
78
78
  tree.push({
79
79
  title: 'Endpoints',
80
80
  });
81
- const { groups, ungrouped } = computeTagGroups(serviceNode);
81
+ const { groups, ungrouped } = computeTagGroups(serviceNode, NodeType.HttpOperation);
82
+ ungrouped.forEach(operationNode => {
83
+ if (mergedConfig.hideInternal && operationNode.data.internal) {
84
+ return;
85
+ }
86
+ tree.push({
87
+ id: operationNode.uri,
88
+ slug: operationNode.uri,
89
+ title: operationNode.name,
90
+ type: operationNode.type,
91
+ meta: operationNode.data.method,
92
+ });
93
+ });
94
+ groups.forEach(group => {
95
+ const items = group.items.flatMap(operationNode => {
96
+ if (mergedConfig.hideInternal && operationNode.data.internal) {
97
+ return [];
98
+ }
99
+ return {
100
+ id: operationNode.uri,
101
+ slug: operationNode.uri,
102
+ title: operationNode.name,
103
+ type: operationNode.type,
104
+ meta: operationNode.data.method,
105
+ };
106
+ });
107
+ if (items.length > 0) {
108
+ tree.push({
109
+ title: group.title,
110
+ items,
111
+ itemsType: 'http_operation',
112
+ });
113
+ }
114
+ });
115
+ }
116
+ const hasWebhookNodes = serviceNode.children.some(node => node.type === NodeType.HttpWebhook);
117
+ if (hasWebhookNodes) {
118
+ tree.push({
119
+ title: 'Webhooks',
120
+ });
121
+ const { groups, ungrouped } = computeTagGroups(serviceNode, NodeType.HttpWebhook);
82
122
  ungrouped.forEach(operationNode => {
83
123
  if (mergedConfig.hideInternal && operationNode.data.internal) {
84
124
  return;
@@ -108,13 +148,14 @@ const computeAPITree = (serviceNode, config = {}) => {
108
148
  tree.push({
109
149
  title: group.title,
110
150
  items,
151
+ itemsType: 'http_webhook',
111
152
  });
112
153
  }
113
154
  });
114
155
  }
115
156
  let schemaNodes = serviceNode.children.filter(node => node.type === NodeType.Model);
116
157
  if (mergedConfig.hideInternal) {
117
- schemaNodes = schemaNodes.filter(node => !node.data['x-internal']);
158
+ schemaNodes = schemaNodes.filter(n => !isInternal(n));
118
159
  }
119
160
  if (!mergedConfig.hideSchemas && schemaNodes.length) {
120
161
  tree.push({
@@ -148,7 +189,7 @@ const findFirstNodeSlug = (tree) => {
148
189
  };
149
190
  const isInternal = (node) => {
150
191
  const data = node.data;
151
- if (isHttpOperation(data)) {
192
+ if (isHttpOperation(data) || isHttpWebhookOperation(data)) {
152
193
  return !!data.internal;
153
194
  }
154
195
  if (isHttpService(data)) {
@@ -158,7 +199,12 @@ const isInternal = (node) => {
158
199
  };
159
200
 
160
201
  const itemMatchesHash = (hash, item) => {
161
- return hash.substr(1) === `${item.data.path}-${item.data.method}`;
202
+ if (item.type === NodeType.HttpOperation) {
203
+ return hash.substr(1) === `${item.data.path}-${item.data.method}`;
204
+ }
205
+ else {
206
+ return hash.substr(1) === `${item.data.name}-${item.data.method}`;
207
+ }
162
208
  };
163
209
  const TryItContext = React.createContext({
164
210
  hideTryIt: false,
@@ -176,14 +222,19 @@ const LocationContext = React.createContext({
176
222
  });
177
223
  LocationContext.displayName = 'LocationContext';
178
224
  const APIWithStackedLayout = ({ serviceNode, hideTryIt, hideExport, exportProps, tryItCredentialsPolicy, tryItCorsProxy, showPoweredByLink = true, location, }) => {
179
- const { groups } = computeTagGroups(serviceNode);
225
+ const { groups: operationGroups } = computeTagGroups(serviceNode, NodeType.HttpOperation);
226
+ const { groups: webhookGroups } = computeTagGroups(serviceNode, NodeType.HttpWebhook);
180
227
  return (React.createElement(LocationContext.Provider, { value: { location } },
181
228
  React.createElement(TryItContext.Provider, { value: { hideTryIt, tryItCredentialsPolicy, corsProxy: tryItCorsProxy } },
182
229
  React.createElement(Flex, { w: "full", flexDirection: "col", m: "auto", className: "sl-max-w-4xl" },
183
230
  React.createElement(Box, { w: "full", borderB: true },
184
231
  React.createElement(Docs, { className: "sl-mx-auto", nodeData: serviceNode.data, nodeTitle: serviceNode.name, nodeType: NodeType.HttpService, location: location, layoutOptions: { showPoweredByLink, hideExport }, exportProps: exportProps, tryItCredentialsPolicy: tryItCredentialsPolicy })),
185
- groups.map(group => (React.createElement(Group, { key: group.title, group: group })))))));
232
+ operationGroups.length > 0 && webhookGroups.length > 0 ? React.createElement(Heading, { size: 2 }, "Endpoints") : null,
233
+ operationGroups.map(group => (React.createElement(Group, { key: group.title, group: group }))),
234
+ webhookGroups.length > 0 ? React.createElement(Heading, { size: 2 }, "Webhooks") : null,
235
+ webhookGroups.map(group => (React.createElement(Group, { key: group.title, group: group })))))));
186
236
  };
237
+ APIWithStackedLayout.displayName = 'APIWithStackedLayout';
187
238
  const Group = React.memo(({ group }) => {
188
239
  const [isExpanded, setIsExpanded] = React.useState(false);
189
240
  const scrollRef = React.useRef(null);
@@ -210,6 +261,7 @@ const Group = React.memo(({ group }) => {
210
261
  return React.createElement(Item, { key: item.uri, item: item });
211
262
  }))));
212
263
  });
264
+ Group.displayName = 'Group';
213
265
  const Item = React.memo(({ item }) => {
214
266
  const { location } = React.useContext(LocationContext);
215
267
  const { hash } = location;
@@ -231,7 +283,7 @@ const Item = React.memo(({ item }) => {
231
283
  return (React.createElement(Box, { ref: scrollRef, w: "full", my: 2, border: true, borderColor: { default: isExpanded ? 'light' : 'transparent', hover: 'light' }, bg: { default: isExpanded ? 'code' : 'transparent', hover: 'code' } },
232
284
  React.createElement(Flex, { mx: "auto", alignItems: "center", cursor: "pointer", fontSize: "lg", p: 2, onClick: onClick, color: "current" },
233
285
  React.createElement(Box, { w: 24, textTransform: "uppercase", textAlign: "center", fontWeight: "semibold", border: true, rounded: true, px: 2, bg: "canvas", className: cn(`sl-mr-5 sl-text-base`, `sl-text-${color}`, `sl-border-${color}`) }, item.data.method || 'UNKNOWN'),
234
- React.createElement(Box, { flex: 1, fontWeight: "medium", wordBreak: "all" }, item.data.path),
286
+ React.createElement(Box, { flex: 1, fontWeight: "medium", wordBreak: "all" }, item.type === NodeType.HttpOperation ? item.data.path : item.name),
235
287
  isDeprecated && React.createElement(DeprecatedBadge, null)),
236
288
  React.createElement(Collapse, { isOpen: isExpanded },
237
289
  React.createElement(Box, { flex: 1, p: 2, fontWeight: "medium", mx: "auto", fontSize: "xl" }, item.name),
@@ -245,11 +297,13 @@ const Item = React.memo(({ item }) => {
245
297
  React.createElement(TabPanel, null,
246
298
  React.createElement(TryItWithRequestSamples, { httpOperation: item.data, tryItCredentialsPolicy: tryItCredentialsPolicy, corsProxy: corsProxy }))))))));
247
299
  });
300
+ Item.displayName = 'Item';
248
301
  const Collapse = ({ isOpen, children }) => {
249
302
  if (!isOpen)
250
303
  return null;
251
304
  return React.createElement(Box, null, children);
252
- };
305
+ };
306
+ Collapse.displayName = 'Collapse';
253
307
 
254
308
  const APIWithSidebarLayout = ({ serviceNode, logo, hideTryIt, hideSchemas, hideInternal, hideExport, exportProps, tryItCredentialsPolicy, tryItCorsProxy, }) => {
255
309
  const container = React.useRef(null);
@@ -268,26 +322,32 @@ const APIWithSidebarLayout = ({ serviceNode, logo, hideTryIt, hideSchemas, hideI
268
322
  if (hideInternal && node && isInternal(node)) {
269
323
  return React.createElement(Redirect, { to: "/" });
270
324
  }
325
+ const sidebar = (React.createElement(Sidebar, { serviceNode: serviceNode, logo: logo, container: container, pathname: pathname, tree: tree }));
326
+ return (React.createElement(SidebarLayout, { ref: container, sidebar: sidebar }, node && (React.createElement(ParsedDocs, { key: pathname, uri: pathname, node: node, nodeTitle: node.name, layoutOptions: layoutOptions, location: location, exportProps: exportProps, tryItCredentialsPolicy: tryItCredentialsPolicy, tryItCorsProxy: tryItCorsProxy }))));
327
+ };
328
+ const Sidebar = ({ serviceNode, logo, container, pathname, tree }) => {
271
329
  const handleTocClick = () => {
272
330
  if (container.current) {
273
331
  container.current.scrollIntoView();
274
332
  }
275
333
  };
276
- const sidebar = (React.createElement(React.Fragment, null,
334
+ return (React.createElement(React.Fragment, null,
277
335
  React.createElement(Flex, { ml: 4, mb: 5, alignItems: "center" },
278
336
  logo ? (React.createElement(Logo, { logo: { url: logo, altText: 'logo' } })) : (serviceNode.data.logo && React.createElement(Logo, { logo: serviceNode.data.logo })),
279
337
  React.createElement(Heading, { size: 4 }, serviceNode.name)),
280
338
  React.createElement(Flex, { flexGrow: true, flexShrink: true, overflowY: "auto", direction: "col" },
281
339
  React.createElement(TableOfContents, { tree: tree, activeId: pathname, Link: Link, onLinkClick: handleTocClick })),
282
340
  React.createElement(PoweredByLink, { source: serviceNode.name, pathname: pathname, packageType: "elements" })));
283
- return (React.createElement(SidebarLayout, { ref: container, sidebar: sidebar }, node && (React.createElement(ParsedDocs, { key: pathname, uri: pathname, node: node, nodeTitle: node.name, layoutOptions: layoutOptions, location: location, exportProps: exportProps, tryItCredentialsPolicy: tryItCredentialsPolicy, tryItCorsProxy: tryItCorsProxy }))));
284
- };
341
+ };
342
+ Sidebar.displayName = 'Sidebar';
285
343
 
286
344
  var NodeTypes;
287
345
  (function (NodeTypes) {
288
346
  NodeTypes["Paths"] = "paths";
289
347
  NodeTypes["Path"] = "path";
290
348
  NodeTypes["Operation"] = "operation";
349
+ NodeTypes["Webhooks"] = "webhooks";
350
+ NodeTypes["Webhook"] = "webhook";
291
351
  NodeTypes["Components"] = "components";
292
352
  NodeTypes["Models"] = "models";
293
353
  NodeTypes["Model"] = "model";
@@ -339,6 +399,22 @@ const oas3SourceMap = [
339
399
  },
340
400
  ],
341
401
  },
402
+ {
403
+ match: 'webhooks',
404
+ type: NodeTypes.Webhooks,
405
+ children: [
406
+ {
407
+ notMatch: '^x-',
408
+ type: NodeTypes.Webhook,
409
+ children: [
410
+ {
411
+ match: 'get|post|put|delete|options|head|patch|trace',
412
+ type: NodeTypes.Webhook,
413
+ },
414
+ ],
415
+ },
416
+ ],
417
+ },
342
418
  {
343
419
  match: 'components',
344
420
  type: NodeTypes.Components,
@@ -393,7 +469,7 @@ function computeServiceNode(document, map, transformService, transformOperation)
393
469
  return serviceNode;
394
470
  }
395
471
  function computeChildNodes(document, data, map, transformer, parentUri = '') {
396
- var _a;
472
+ var _a, _b;
397
473
  const nodes = [];
398
474
  if (!isObject(data))
399
475
  return nodes;
@@ -406,7 +482,12 @@ function computeChildNodes(document, data, map, transformer, parentUri = '') {
406
482
  if (match.type === NodeTypes.Operation && jsonPath.length === 3) {
407
483
  const path = String(jsonPath[1]);
408
484
  const method = String(jsonPath[2]);
409
- const operationDocument = transformer({ document, path, method });
485
+ const operationDocument = transformer({
486
+ document,
487
+ name: path,
488
+ method,
489
+ config: OPERATION_CONFIG,
490
+ });
410
491
  let parsedUri;
411
492
  const encodedPath = String(encodePointerFragment(path));
412
493
  if (operationDocument.iid) {
@@ -423,6 +504,31 @@ function computeChildNodes(document, data, map, transformer, parentUri = '') {
423
504
  tags: ((_a = operationDocument.tags) === null || _a === void 0 ? void 0 : _a.map(tag => tag.name)) || [],
424
505
  });
425
506
  }
507
+ else if (match.type === NodeTypes.Webhook && jsonPath.length === 3) {
508
+ const name = String(jsonPath[1]);
509
+ const method = String(jsonPath[2]);
510
+ const webhookDocument = transformer({
511
+ document,
512
+ name,
513
+ method,
514
+ config: WEBHOOK_CONFIG,
515
+ });
516
+ let parsedUri;
517
+ const encodedPath = String(encodePointerFragment(name));
518
+ if (webhookDocument.iid) {
519
+ parsedUri = `/webhooks/${webhookDocument.iid}`;
520
+ }
521
+ else {
522
+ parsedUri = uri.replace(encodedPath, slugify(name));
523
+ }
524
+ nodes.push({
525
+ type: NodeType.HttpWebhook,
526
+ uri: parsedUri,
527
+ data: webhookDocument,
528
+ name: webhookDocument.summary || webhookDocument.name,
529
+ tags: ((_b = webhookDocument.tags) === null || _b === void 0 ? void 0 : _b.map(tag => tag.name)) || [],
530
+ });
531
+ }
426
532
  else if (match.type === NodeTypes.Model) {
427
533
  const schemaDocument = get(document, jsonPath);
428
534
  const parsedUri = uri.replace(OAS_MODEL_REGEXP, 'schemas/');
package/index.js CHANGED
@@ -13,6 +13,7 @@ var reactQuery = require('react-query');
13
13
  var reactRouterDom = require('react-router-dom');
14
14
  var yaml = require('@stoplight/yaml');
15
15
  var saver = require('file-saver');
16
+ var oas = require('@stoplight/http-spec/oas');
16
17
  var oas2 = require('@stoplight/http-spec/oas2');
17
18
  var oas3 = require('@stoplight/http-spec/oas3');
18
19
  var json = require('@stoplight/json');
@@ -49,13 +50,12 @@ var get__default = /*#__PURE__*/_interopDefaultLegacy(get);
49
50
  var isObject__default = /*#__PURE__*/_interopDefaultLegacy(isObject);
50
51
  var last__default = /*#__PURE__*/_interopDefaultLegacy(last);
51
52
 
52
- const computeTagGroups = (serviceNode) => {
53
+ function computeTagGroups(serviceNode, nodeType) {
53
54
  const groupsByTagId = {};
54
55
  const ungrouped = [];
55
56
  const lowerCaseServiceTags = serviceNode.tags.map(tn => tn.toLowerCase());
56
- for (const node of serviceNode.children) {
57
- if (node.type !== types.NodeType.HttpOperation)
58
- continue;
57
+ const groupableNodes = serviceNode.children.filter(n => n.type === nodeType);
58
+ for (const node of groupableNodes) {
59
59
  const tagName = node.tags[0];
60
60
  if (tagName) {
61
61
  const tagId = tagName.toLowerCase();
@@ -91,7 +91,7 @@ const computeTagGroups = (serviceNode) => {
91
91
  })
92
92
  .map(([, tagGroup]) => tagGroup);
93
93
  return { groups: orderedTagGroups, ungrouped };
94
- };
94
+ }
95
95
  const defaultComputerAPITreeConfig = {
96
96
  hideSchemas: false,
97
97
  hideInternal: false,
@@ -106,12 +106,52 @@ const computeAPITree = (serviceNode, config = {}) => {
106
106
  type: 'overview',
107
107
  meta: '',
108
108
  });
109
- const operationNodes = serviceNode.children.filter(node => node.type === types.NodeType.HttpOperation);
110
- if (operationNodes.length) {
109
+ const hasOperationNodes = serviceNode.children.some(node => node.type === types.NodeType.HttpOperation);
110
+ if (hasOperationNodes) {
111
111
  tree.push({
112
112
  title: 'Endpoints',
113
113
  });
114
- const { groups, ungrouped } = computeTagGroups(serviceNode);
114
+ const { groups, ungrouped } = computeTagGroups(serviceNode, types.NodeType.HttpOperation);
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
+ itemsType: 'http_operation',
145
+ });
146
+ }
147
+ });
148
+ }
149
+ const hasWebhookNodes = serviceNode.children.some(node => node.type === types.NodeType.HttpWebhook);
150
+ if (hasWebhookNodes) {
151
+ tree.push({
152
+ title: 'Webhooks',
153
+ });
154
+ const { groups, ungrouped } = computeTagGroups(serviceNode, types.NodeType.HttpWebhook);
115
155
  ungrouped.forEach(operationNode => {
116
156
  if (mergedConfig.hideInternal && operationNode.data.internal) {
117
157
  return;
@@ -141,13 +181,14 @@ const computeAPITree = (serviceNode, config = {}) => {
141
181
  tree.push({
142
182
  title: group.title,
143
183
  items,
184
+ itemsType: 'http_webhook',
144
185
  });
145
186
  }
146
187
  });
147
188
  }
148
189
  let schemaNodes = serviceNode.children.filter(node => node.type === types.NodeType.Model);
149
190
  if (mergedConfig.hideInternal) {
150
- schemaNodes = schemaNodes.filter(node => !node.data['x-internal']);
191
+ schemaNodes = schemaNodes.filter(n => !isInternal(n));
151
192
  }
152
193
  if (!mergedConfig.hideSchemas && schemaNodes.length) {
153
194
  tree.push({
@@ -181,7 +222,7 @@ const findFirstNodeSlug = (tree) => {
181
222
  };
182
223
  const isInternal = (node) => {
183
224
  const data = node.data;
184
- if (elementsCore.isHttpOperation(data)) {
225
+ if (elementsCore.isHttpOperation(data) || elementsCore.isHttpWebhookOperation(data)) {
185
226
  return !!data.internal;
186
227
  }
187
228
  if (elementsCore.isHttpService(data)) {
@@ -191,7 +232,12 @@ const isInternal = (node) => {
191
232
  };
192
233
 
193
234
  const itemMatchesHash = (hash, item) => {
194
- return hash.substr(1) === `${item.data.path}-${item.data.method}`;
235
+ if (item.type === types.NodeType.HttpOperation) {
236
+ return hash.substr(1) === `${item.data.path}-${item.data.method}`;
237
+ }
238
+ else {
239
+ return hash.substr(1) === `${item.data.name}-${item.data.method}`;
240
+ }
195
241
  };
196
242
  const TryItContext = React__namespace.createContext({
197
243
  hideTryIt: false,
@@ -209,14 +255,19 @@ const LocationContext = React__namespace.createContext({
209
255
  });
210
256
  LocationContext.displayName = 'LocationContext';
211
257
  const APIWithStackedLayout = ({ serviceNode, hideTryIt, hideExport, exportProps, tryItCredentialsPolicy, tryItCorsProxy, showPoweredByLink = true, location, }) => {
212
- const { groups } = computeTagGroups(serviceNode);
258
+ const { groups: operationGroups } = computeTagGroups(serviceNode, types.NodeType.HttpOperation);
259
+ const { groups: webhookGroups } = computeTagGroups(serviceNode, types.NodeType.HttpWebhook);
213
260
  return (React__namespace.createElement(LocationContext.Provider, { value: { location } },
214
261
  React__namespace.createElement(TryItContext.Provider, { value: { hideTryIt, tryItCredentialsPolicy, corsProxy: tryItCorsProxy } },
215
262
  React__namespace.createElement(mosaic.Flex, { w: "full", flexDirection: "col", m: "auto", className: "sl-max-w-4xl" },
216
263
  React__namespace.createElement(mosaic.Box, { w: "full", borderB: true },
217
264
  React__namespace.createElement(elementsCore.Docs, { className: "sl-mx-auto", nodeData: serviceNode.data, nodeTitle: serviceNode.name, nodeType: types.NodeType.HttpService, location: location, layoutOptions: { showPoweredByLink, hideExport }, exportProps: exportProps, tryItCredentialsPolicy: tryItCredentialsPolicy })),
218
- groups.map(group => (React__namespace.createElement(Group, { key: group.title, group: group })))))));
265
+ operationGroups.length > 0 && webhookGroups.length > 0 ? React__namespace.createElement(mosaic.Heading, { size: 2 }, "Endpoints") : null,
266
+ operationGroups.map(group => (React__namespace.createElement(Group, { key: group.title, group: group }))),
267
+ webhookGroups.length > 0 ? React__namespace.createElement(mosaic.Heading, { size: 2 }, "Webhooks") : null,
268
+ webhookGroups.map(group => (React__namespace.createElement(Group, { key: group.title, group: group })))))));
219
269
  };
270
+ APIWithStackedLayout.displayName = 'APIWithStackedLayout';
220
271
  const Group = React__namespace.memo(({ group }) => {
221
272
  const [isExpanded, setIsExpanded] = React__namespace.useState(false);
222
273
  const scrollRef = React__namespace.useRef(null);
@@ -243,6 +294,7 @@ const Group = React__namespace.memo(({ group }) => {
243
294
  return React__namespace.createElement(Item, { key: item.uri, item: item });
244
295
  }))));
245
296
  });
297
+ Group.displayName = 'Group';
246
298
  const Item = React__namespace.memo(({ item }) => {
247
299
  const { location } = React__namespace.useContext(LocationContext);
248
300
  const { hash } = location;
@@ -264,7 +316,7 @@ const Item = React__namespace.memo(({ item }) => {
264
316
  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' } },
265
317
  React__namespace.createElement(mosaic.Flex, { mx: "auto", alignItems: "center", cursor: "pointer", fontSize: "lg", p: 2, onClick: onClick, color: "current" },
266
318
  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'),
267
- React__namespace.createElement(mosaic.Box, { flex: 1, fontWeight: "medium", wordBreak: "all" }, item.data.path),
319
+ React__namespace.createElement(mosaic.Box, { flex: 1, fontWeight: "medium", wordBreak: "all" }, item.type === types.NodeType.HttpOperation ? item.data.path : item.name),
268
320
  isDeprecated && React__namespace.createElement(elementsCore.DeprecatedBadge, null)),
269
321
  React__namespace.createElement(Collapse, { isOpen: isExpanded },
270
322
  React__namespace.createElement(mosaic.Box, { flex: 1, p: 2, fontWeight: "medium", mx: "auto", fontSize: "xl" }, item.name),
@@ -278,11 +330,13 @@ const Item = React__namespace.memo(({ item }) => {
278
330
  React__namespace.createElement(mosaic.TabPanel, null,
279
331
  React__namespace.createElement(elementsCore.TryItWithRequestSamples, { httpOperation: item.data, tryItCredentialsPolicy: tryItCredentialsPolicy, corsProxy: corsProxy }))))))));
280
332
  });
333
+ Item.displayName = 'Item';
281
334
  const Collapse = ({ isOpen, children }) => {
282
335
  if (!isOpen)
283
336
  return null;
284
337
  return React__namespace.createElement(mosaic.Box, null, children);
285
- };
338
+ };
339
+ Collapse.displayName = 'Collapse';
286
340
 
287
341
  const APIWithSidebarLayout = ({ serviceNode, logo, hideTryIt, hideSchemas, hideInternal, hideExport, exportProps, tryItCredentialsPolicy, tryItCorsProxy, }) => {
288
342
  const container = React__namespace.useRef(null);
@@ -301,26 +355,32 @@ const APIWithSidebarLayout = ({ serviceNode, logo, hideTryIt, hideSchemas, hideI
301
355
  if (hideInternal && node && isInternal(node)) {
302
356
  return React__namespace.createElement(reactRouterDom.Redirect, { to: "/" });
303
357
  }
358
+ const sidebar = (React__namespace.createElement(Sidebar, { serviceNode: serviceNode, logo: logo, container: container, pathname: pathname, tree: tree }));
359
+ return (React__namespace.createElement(elementsCore.SidebarLayout, { ref: container, sidebar: sidebar }, node && (React__namespace.createElement(elementsCore.ParsedDocs, { key: pathname, uri: pathname, node: node, nodeTitle: node.name, layoutOptions: layoutOptions, location: location, exportProps: exportProps, tryItCredentialsPolicy: tryItCredentialsPolicy, tryItCorsProxy: tryItCorsProxy }))));
360
+ };
361
+ const Sidebar = ({ serviceNode, logo, container, pathname, tree }) => {
304
362
  const handleTocClick = () => {
305
363
  if (container.current) {
306
364
  container.current.scrollIntoView();
307
365
  }
308
366
  };
309
- const sidebar = (React__namespace.createElement(React__namespace.Fragment, null,
367
+ return (React__namespace.createElement(React__namespace.Fragment, null,
310
368
  React__namespace.createElement(mosaic.Flex, { ml: 4, mb: 5, alignItems: "center" },
311
369
  logo ? (React__namespace.createElement(elementsCore.Logo, { logo: { url: logo, altText: 'logo' } })) : (serviceNode.data.logo && React__namespace.createElement(elementsCore.Logo, { logo: serviceNode.data.logo })),
312
370
  React__namespace.createElement(mosaic.Heading, { size: 4 }, serviceNode.name)),
313
371
  React__namespace.createElement(mosaic.Flex, { flexGrow: true, flexShrink: true, overflowY: "auto", direction: "col" },
314
372
  React__namespace.createElement(elementsCore.TableOfContents, { tree: tree, activeId: pathname, Link: reactRouterDom.Link, onLinkClick: handleTocClick })),
315
373
  React__namespace.createElement(elementsCore.PoweredByLink, { source: serviceNode.name, pathname: pathname, packageType: "elements" })));
316
- return (React__namespace.createElement(elementsCore.SidebarLayout, { ref: container, sidebar: sidebar }, node && (React__namespace.createElement(elementsCore.ParsedDocs, { key: pathname, uri: pathname, node: node, nodeTitle: node.name, layoutOptions: layoutOptions, location: location, exportProps: exportProps, tryItCredentialsPolicy: tryItCredentialsPolicy, tryItCorsProxy: tryItCorsProxy }))));
317
- };
374
+ };
375
+ Sidebar.displayName = 'Sidebar';
318
376
 
319
377
  var NodeTypes;
320
378
  (function (NodeTypes) {
321
379
  NodeTypes["Paths"] = "paths";
322
380
  NodeTypes["Path"] = "path";
323
381
  NodeTypes["Operation"] = "operation";
382
+ NodeTypes["Webhooks"] = "webhooks";
383
+ NodeTypes["Webhook"] = "webhook";
324
384
  NodeTypes["Components"] = "components";
325
385
  NodeTypes["Models"] = "models";
326
386
  NodeTypes["Model"] = "model";
@@ -372,6 +432,22 @@ const oas3SourceMap = [
372
432
  },
373
433
  ],
374
434
  },
435
+ {
436
+ match: 'webhooks',
437
+ type: NodeTypes.Webhooks,
438
+ children: [
439
+ {
440
+ notMatch: '^x-',
441
+ type: NodeTypes.Webhook,
442
+ children: [
443
+ {
444
+ match: 'get|post|put|delete|options|head|patch|trace',
445
+ type: NodeTypes.Webhook,
446
+ },
447
+ ],
448
+ },
449
+ ],
450
+ },
375
451
  {
376
452
  match: 'components',
377
453
  type: NodeTypes.Components,
@@ -426,7 +502,7 @@ function computeServiceNode(document, map, transformService, transformOperation)
426
502
  return serviceNode;
427
503
  }
428
504
  function computeChildNodes(document, data, map, transformer, parentUri = '') {
429
- var _a;
505
+ var _a, _b;
430
506
  const nodes = [];
431
507
  if (!isObject__default["default"](data))
432
508
  return nodes;
@@ -439,7 +515,12 @@ function computeChildNodes(document, data, map, transformer, parentUri = '') {
439
515
  if (match.type === NodeTypes.Operation && jsonPath.length === 3) {
440
516
  const path = String(jsonPath[1]);
441
517
  const method = String(jsonPath[2]);
442
- const operationDocument = transformer({ document, path, method });
518
+ const operationDocument = transformer({
519
+ document,
520
+ name: path,
521
+ method,
522
+ config: oas.OPERATION_CONFIG,
523
+ });
443
524
  let parsedUri;
444
525
  const encodedPath = String(json.encodePointerFragment(path));
445
526
  if (operationDocument.iid) {
@@ -456,6 +537,31 @@ function computeChildNodes(document, data, map, transformer, parentUri = '') {
456
537
  tags: ((_a = operationDocument.tags) === null || _a === void 0 ? void 0 : _a.map(tag => tag.name)) || [],
457
538
  });
458
539
  }
540
+ else if (match.type === NodeTypes.Webhook && jsonPath.length === 3) {
541
+ const name = String(jsonPath[1]);
542
+ const method = String(jsonPath[2]);
543
+ const webhookDocument = transformer({
544
+ document,
545
+ name,
546
+ method,
547
+ config: oas.WEBHOOK_CONFIG,
548
+ });
549
+ let parsedUri;
550
+ const encodedPath = String(json.encodePointerFragment(name));
551
+ if (webhookDocument.iid) {
552
+ parsedUri = `/webhooks/${webhookDocument.iid}`;
553
+ }
554
+ else {
555
+ parsedUri = uri.replace(encodedPath, elementsCore.slugify(name));
556
+ }
557
+ nodes.push({
558
+ type: types.NodeType.HttpWebhook,
559
+ uri: parsedUri,
560
+ data: webhookDocument,
561
+ name: webhookDocument.summary || webhookDocument.name,
562
+ tags: ((_b = webhookDocument.tags) === null || _b === void 0 ? void 0 : _b.map(tag => tag.name)) || [],
563
+ });
564
+ }
459
565
  else if (match.type === NodeTypes.Model) {
460
566
  const schemaDocument = get__default["default"](document, jsonPath);
461
567
  const parsedUri = uri.replace(OAS_MODEL_REGEXP, 'schemas/');