rapidoc 9.3.8 → 10.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.
Files changed (69) hide show
  1. package/README.md +193 -61
  2. package/dist/rapidoc-min.js +279 -3914
  3. package/package.json +23 -46
  4. package/.babelrc.json +0 -42
  5. package/.eslintrc +0 -53
  6. package/.github/FUNDING.yml +0 -13
  7. package/.github/workflow/words.yaml +0 -22
  8. package/CONTRIBUTING.md +0 -35
  9. package/dist/index.html +0 -1010
  10. package/dist/index.html.gz +0 -0
  11. package/dist/rapidoc-min.js.LICENSE.txt +0 -64
  12. package/dist/rapidoc-min.js.LICENSE.txt.gz +0 -0
  13. package/dist/rapidoc-min.js.gz +0 -0
  14. package/dist/rapidoc-min.js.map +0 -1
  15. package/dist/rapidoc-min.js.map.gz +0 -0
  16. package/dist/rapidoc.js +0 -21603
  17. package/dist/report.html +0 -39
  18. package/index-hashroute.html +0 -65
  19. package/index.html +0 -1045
  20. package/index_bak.html +0 -251
  21. package/jsconfig.json +0 -11
  22. package/logo.png +0 -0
  23. package/performance.html +0 -31
  24. package/src/components/api-request.js +0 -1793
  25. package/src/components/api-response.js +0 -335
  26. package/src/components/dialog-box.js +0 -73
  27. package/src/components/json-tree.js +0 -145
  28. package/src/components/schema-table.js +0 -367
  29. package/src/components/schema-tree.js +0 -381
  30. package/src/components/tag-input.js +0 -135
  31. package/src/index.js +0 -7
  32. package/src/json-schema-viewer.js +0 -346
  33. package/src/oauth-receiver.js +0 -56
  34. package/src/rapidoc-mini.js +0 -382
  35. package/src/rapidoc.js +0 -1083
  36. package/src/styles/border-styles.js +0 -24
  37. package/src/styles/custom-styles.js +0 -10
  38. package/src/styles/dialog-box-styles.js +0 -75
  39. package/src/styles/endpoint-styles.js +0 -202
  40. package/src/styles/flex-styles.js +0 -15
  41. package/src/styles/font-styles.js +0 -268
  42. package/src/styles/info-styles.js +0 -21
  43. package/src/styles/input-styles.js +0 -207
  44. package/src/styles/nav-styles.js +0 -198
  45. package/src/styles/prism-styles.js +0 -105
  46. package/src/styles/schema-styles.js +0 -121
  47. package/src/styles/tab-styles.js +0 -47
  48. package/src/styles/table-styles.js +0 -58
  49. package/src/templates/advance-search-template.js +0 -72
  50. package/src/templates/callback-template.js +0 -75
  51. package/src/templates/code-samples-template.js +0 -34
  52. package/src/templates/components-template.js +0 -76
  53. package/src/templates/endpoint-template.js +0 -251
  54. package/src/templates/expanded-endpoint-template.js +0 -199
  55. package/src/templates/focused-endpoint-template.js +0 -110
  56. package/src/templates/header-template.js +0 -66
  57. package/src/templates/json-schema-viewer-template.js +0 -185
  58. package/src/templates/logo-template.js +0 -18
  59. package/src/templates/main-body-template.js +0 -110
  60. package/src/templates/navbar-template.js +0 -272
  61. package/src/templates/overview-template.js +0 -79
  62. package/src/templates/security-scheme-template.js +0 -612
  63. package/src/templates/server-template.js +0 -115
  64. package/src/utils/color-utils.js +0 -59
  65. package/src/utils/common-utils.js +0 -168
  66. package/src/utils/schema-utils.js +0 -1086
  67. package/src/utils/spec-parser.js +0 -462
  68. package/src/utils/theme.js +0 -288
  69. package/webpack.config.js +0 -142
@@ -1,462 +0,0 @@
1
- /* eslint-disable no-use-before-define */
2
- import OpenApiParser from '@apitools/openapi-parser';
3
- import { marked } from 'marked';
4
- import { invalidCharsRegEx, rapidocApiKey, sleep } from '~/utils/common-utils';
5
-
6
- export default async function ProcessSpec(
7
- specUrl,
8
- generateMissingTags = false,
9
- sortTags = false,
10
- sortSchemas = false,
11
- sortEndpointsBy = '',
12
- attrApiKey = '',
13
- attrApiKeyLocation = '',
14
- attrApiKeyValue = '',
15
- serverUrl = '',
16
- matchPaths = '',
17
- matchType = '',
18
- removeEndpointsWithBadgeLabelAs = '',
19
- ) {
20
- let jsonParsedSpec;
21
-
22
- try {
23
- this.requestUpdate(); // important to show the initial loader
24
- let specMeta;
25
- if (typeof specUrl === 'string') {
26
- specMeta = await OpenApiParser.resolve({ url: specUrl, allowMetaPatches: false }); // Swagger(specUrl);
27
- } else {
28
- specMeta = await OpenApiParser.resolve({ spec: specUrl, allowMetaPatches: false }); // Swagger({ spec: specUrl });
29
- }
30
- await sleep(0); // important to show the initial loader (allows for rendering updates)
31
-
32
- // If JSON Schema Viewer
33
- if (specMeta.resolvedSpec?.jsonSchemaViewer && specMeta.resolvedSpec?.schemaAndExamples) {
34
- this.dispatchEvent(new CustomEvent('before-render', { detail: { spec: specMeta.resolvedSpec } }));
35
- const schemaAndExamples = Object.entries(specMeta.resolvedSpec.schemaAndExamples).map((v) => ({ show: true, expanded: true, selectedExample: null, name: v[0], elementId: v[0].replace(invalidCharsRegEx, '-'), ...v[1] }));
36
- const parsedSpec = {
37
- specLoadError: false,
38
- isSpecLoading: false,
39
- info: specMeta.resolvedSpec.info,
40
- schemaAndExamples,
41
- };
42
- return parsedSpec;
43
- }
44
-
45
- // If RapiDoc or RapiDocMini
46
- if (specMeta.spec && (specMeta.spec.components || specMeta.spec.info || specMeta.spec.servers || specMeta.spec.tags || specMeta.spec.paths)) {
47
- jsonParsedSpec = filterPaths(specMeta.spec, matchPaths, matchType, removeEndpointsWithBadgeLabelAs);
48
- this.dispatchEvent(new CustomEvent('before-render', { detail: { spec: jsonParsedSpec } }));
49
- } else {
50
- console.info('RapiDoc: %c There was an issue while parsing the spec %o ', 'color:orangered', specMeta); // eslint-disable-line no-console
51
- return {
52
- specLoadError: true,
53
- isSpecLoading: false,
54
- info: {
55
- title: 'Error loading the spec',
56
- description: specMeta.response?.url ? `${specMeta.response?.url} ┃ ${specMeta.response?.status} ${specMeta.response?.statusText}` : 'Unable to load the Spec',
57
- version: ' ',
58
- },
59
- tags: [],
60
- };
61
- }
62
- } catch (err) {
63
- console.info('RapiDoc: %c There was an issue while parsing the spec %o ', 'color:orangered', err); // eslint-disable-line no-console
64
- }
65
-
66
- // const pathGroups = groupByPaths(jsonParsedSpec);
67
-
68
- // Tags with Paths and WebHooks
69
- const tags = groupByTags(jsonParsedSpec, sortEndpointsBy, generateMissingTags, sortTags);
70
-
71
- // Components
72
- const components = getComponents(jsonParsedSpec, sortSchemas);
73
-
74
- // Info Description Headers
75
- const infoDescriptionHeaders = jsonParsedSpec.info?.description ? getHeadersFromMarkdown(jsonParsedSpec.info.description) : [];
76
-
77
- // Security Scheme
78
- const securitySchemes = [];
79
- if (jsonParsedSpec.components?.securitySchemes) {
80
- const securitySchemeSet = new Set();
81
- Object.entries(jsonParsedSpec.components.securitySchemes).forEach((kv) => {
82
- if (!securitySchemeSet.has(kv[0])) {
83
- securitySchemeSet.add(kv[0]);
84
- const securityObj = { securitySchemeId: kv[0], ...kv[1] };
85
- securityObj.value = '';
86
- securityObj.finalKeyValue = '';
87
- if (kv[1].type === 'apiKey' || kv[1].type === 'http') {
88
- securityObj.in = kv[1].in || 'header';
89
- securityObj.name = kv[1].name || 'Authorization';
90
- securityObj.user = '';
91
- securityObj.password = '';
92
- } else if (kv[1].type === 'oauth2') {
93
- securityObj.in = 'header';
94
- securityObj.name = 'Authorization';
95
- securityObj.clientId = '';
96
- securityObj.clientSecret = '';
97
- }
98
- securitySchemes.push(securityObj);
99
- }
100
- });
101
- }
102
-
103
- if (attrApiKey && attrApiKeyLocation && attrApiKeyValue) {
104
- securitySchemes.push({
105
- securitySchemeId: rapidocApiKey,
106
- description: 'api-key provided in rapidoc element attributes',
107
- type: 'apiKey',
108
- oAuthFlow: '',
109
- name: attrApiKey,
110
- in: attrApiKeyLocation,
111
- value: attrApiKeyValue,
112
- finalKeyValue: attrApiKeyValue,
113
- });
114
- }
115
-
116
- // Updated Security Type Display Text based on Type
117
- securitySchemes.forEach((v) => {
118
- if (v.type === 'http') {
119
- v.typeDisplay = v.scheme === 'basic' ? 'HTTP Basic' : 'HTTP Bearer';
120
- } else if (v.type === 'apiKey') {
121
- v.typeDisplay = `API Key (${v.name})`;
122
- } else if (v.type === 'oauth2') {
123
- v.typeDisplay = `OAuth (${v.securitySchemeId})`;
124
- } else {
125
- v.typeDisplay = v.type || 'None';
126
- }
127
- });
128
-
129
- // Servers
130
- let servers = [];
131
- if (jsonParsedSpec.servers && Array.isArray(jsonParsedSpec.servers) && jsonParsedSpec.servers.length > 0) {
132
- jsonParsedSpec.servers.forEach((v) => {
133
- let computedUrl = v.url.trim();
134
- if (!(computedUrl.startsWith('http') || computedUrl.startsWith('//') || computedUrl.startsWith('{'))) {
135
- if (window.location.origin.startsWith('http')) {
136
- v.url = window.location.origin + v.url;
137
- computedUrl = v.url;
138
- }
139
- }
140
- // Apply server-variables to generate final computed-url
141
- if (v.variables) {
142
- Object.entries(v.variables).forEach((kv) => {
143
- const regex = new RegExp(`{${kv[0]}}`, 'g');
144
- computedUrl = computedUrl.replace(regex, kv[1].default || '');
145
- kv[1].value = kv[1].default || '';
146
- });
147
- }
148
- v.computedUrl = computedUrl;
149
- });
150
- if (serverUrl) {
151
- jsonParsedSpec.servers.push({ url: serverUrl, computedUrl: serverUrl });
152
- }
153
- } else if (serverUrl) {
154
- jsonParsedSpec.servers = [{ url: serverUrl, computedUrl: serverUrl }];
155
- } else if (window.location.origin.startsWith('http')) {
156
- jsonParsedSpec.servers = [{ url: window.location.origin, computedUrl: window.location.origin }];
157
- } else {
158
- jsonParsedSpec.servers = [{ url: 'http://localhost', computedUrl: 'http://localhost' }];
159
- }
160
- servers = jsonParsedSpec.servers; // eslint-disable-line prefer-destructuring
161
- const parsedSpec = {
162
- specLoadError: false,
163
- isSpecLoading: false,
164
- info: jsonParsedSpec.info,
165
- infoDescriptionHeaders,
166
- tags,
167
- components,
168
- externalDocs: jsonParsedSpec.externalDocs,
169
- securitySchemes,
170
- servers,
171
- };
172
- return parsedSpec;
173
- }
174
-
175
- function filterPaths(openApiObject, matchPaths = '', matchType = '', removeEndpointsWithBadgeLabelAs = '') {
176
- const filteredPaths = {};
177
-
178
- // Convert the removePathsWithBadgeLabeledAs to an array if provided
179
- const labelsToRemove = removeEndpointsWithBadgeLabelAs.split(',').map((label) => label.trim().toLowerCase()).filter(Boolean);
180
-
181
- // Helper function to check if a path should be included based on matchPaths
182
- function pathMatches(pathsKey, httpMethod) {
183
- if (!matchPaths) {
184
- return true; // If no matchPaths provided, include everything
185
- }
186
- const fullPath = `${httpMethod} ${pathsKey}`.toLowerCase(); // Construct "method path" string
187
- if (matchType === 'regex') {
188
- const regex = new RegExp(matchPaths, 'i');
189
- return regex.test(matchPaths.toLowerCase());
190
- }
191
- return fullPath.includes(matchPaths.toLowerCase());
192
- }
193
-
194
- // Helper function to check if the badges contain any label that needs to be removed
195
- function containsLabelToRemove(badges) {
196
- return badges.some((badge) => labelsToRemove.includes(badge?.label.toLowerCase()));
197
- }
198
-
199
- // Loop through the paths in the openApiObject
200
- Object.entries(openApiObject.paths).forEach(([pathsKey, methods]) => {
201
- const filteredMethods = {};
202
-
203
- Object.entries(methods).forEach(([httpMethod, methodDetails]) => {
204
- const badges = methodDetails['x-badges'];
205
-
206
- // Filter by matchPaths
207
- if (pathMatches(pathsKey, httpMethod)) {
208
- if (badges && Array.isArray(badges)) {
209
- // Filter out based on removePathsWithBadgeLabeledAs
210
- if (!containsLabelToRemove(badges)) {
211
- filteredMethods[httpMethod] = methodDetails;
212
- }
213
- } else {
214
- // No badges present, include the method
215
- filteredMethods[httpMethod] = methodDetails;
216
- }
217
- }
218
- });
219
-
220
- if (Object.keys(filteredMethods).length > 0) {
221
- filteredPaths[pathsKey] = filteredMethods;
222
- }
223
- });
224
-
225
- openApiObject.paths = filteredPaths;
226
- return openApiObject;
227
- }
228
-
229
- function getHeadersFromMarkdown(markdownContent) {
230
- const tokens = marked.lexer(markdownContent);
231
- const headers = tokens.filter((v) => v.type === 'heading' && v.depth <= 2);
232
- return headers || [];
233
- }
234
-
235
- function getComponents(openApiSpec, sortSchemas = false) {
236
- if (!openApiSpec.components) {
237
- return [];
238
- }
239
- const components = [];
240
- for (const component in openApiSpec.components) {
241
- const subComponents = [];
242
- for (const sComponent in openApiSpec.components[component]) {
243
- const scmp = {
244
- show: true,
245
- id: `${component.toLowerCase()}-${sComponent.toLowerCase()}`.replace(invalidCharsRegEx, '-'),
246
- name: sComponent,
247
- component: openApiSpec.components[component][sComponent],
248
- };
249
- subComponents.push(scmp);
250
- }
251
-
252
- let cmpDescription = component;
253
- let cmpName = component;
254
-
255
- switch (component) {
256
- case 'schemas':
257
- if (sortSchemas) {
258
- subComponents.sort((c1, c2) => c1.name.localeCompare(c2.name));
259
- }
260
-
261
- cmpName = 'Schemas';
262
- cmpDescription = 'Schemas allows the definition of input and output data types. These types can be objects, but also primitives and arrays.';
263
- break;
264
- case 'responses':
265
- cmpName = 'Responses';
266
- cmpDescription = 'Describes responses from an API Operation, including design-time, static links to operations based on the response.';
267
- break;
268
- case 'parameters':
269
- cmpName = 'Parameters';
270
- cmpDescription = 'Describes operation parameters. A unique parameter is defined by a combination of a name and location.';
271
- break;
272
- case 'examples':
273
- cmpName = 'Examples';
274
- cmpDescription = 'List of Examples for operations, can be requests, responses and objects examples.';
275
- break;
276
- case 'requestBodies':
277
- cmpName = 'Request Bodies';
278
- cmpDescription = 'Describes common request bodies that are used across the API operations.';
279
- break;
280
- case 'headers':
281
- cmpName = 'Headers';
282
- cmpDescription = 'Headers follows the structure of the Parameters but they are explicitly in "header"';
283
- break;
284
- case 'securitySchemes':
285
- cmpName = 'Security Schemes';
286
- // eslint-disable-next-line max-len
287
- cmpDescription = 'Defines a security scheme that can be used by the operations. Supported schemes are HTTP authentication, an API key (either as a header, a cookie parameter or as a query parameter), OAuth2\'s common flows(implicit, password, client credentials and authorization code) as defined in RFC6749, and OpenID Connect Discovery.';
288
- break;
289
- case 'links':
290
- cmpName = 'Links';
291
- cmpDescription = 'Links represent a possible design-time link for a response. The presence of a link does not guarantee the caller\'s ability to successfully invoke it, rather it provides a known relationship and traversal mechanism between responses and other operations.';
292
- break;
293
- case 'callbacks':
294
- cmpName = 'Callbacks';
295
- // eslint-disable-next-line max-len
296
- cmpDescription = 'A map of possible out-of band callbacks related to the parent operation. Each value in the map is a Path Item Object that describes a set of requests that may be initiated by the API provider and the expected responses. The key value used to identify the path item object is an expression, evaluated at runtime, that identifies a URL to use for the callback operation.';
297
- break;
298
- default:
299
- cmpName = component;
300
- cmpDescription = component;
301
- break;
302
- }
303
-
304
- const cmp = {
305
- show: true,
306
- name: cmpName,
307
- description: cmpDescription,
308
- subComponents,
309
- };
310
- components.push(cmp);
311
- }
312
-
313
- return components || [];
314
- }
315
-
316
- function groupByTags(openApiSpec, sortEndpointsBy, generateMissingTags = false, sortTags = false) {
317
- const supportedMethods = ['get', 'put', 'post', 'delete', 'patch', 'head', 'options']; // this is also used for ordering endpoints by methods
318
- const tags = openApiSpec.tags && Array.isArray(openApiSpec.tags) && openApiSpec.tags.length > 0
319
- ? openApiSpec.tags.map((v) => ({
320
- show: true,
321
- elementId: `tag--${v.name.replace(invalidCharsRegEx, '-')}`,
322
- name: v.name,
323
- displayName: v['x-displayName'] || v.name,
324
- description: v.description || '',
325
- headers: v.description ? getHeadersFromMarkdown(v.description) : [],
326
- paths: [],
327
- expanded: v['x-tag-expanded'] !== false,
328
- }))
329
- : [];
330
-
331
- const pathsAndWebhooks = openApiSpec.paths || {};
332
- if (openApiSpec.webhooks) {
333
- for (const [key, value] of Object.entries(openApiSpec.webhooks)) {
334
- value._type = 'webhook'; // eslint-disable-line no-underscore-dangle
335
- pathsAndWebhooks[key] = value;
336
- }
337
- }
338
- // For each path find the tag and push it into the corresponding tag
339
- for (const pathOrHookName in pathsAndWebhooks) {
340
- const commonParams = pathsAndWebhooks[pathOrHookName].parameters;
341
- const commonPathProp = {
342
- servers: pathsAndWebhooks[pathOrHookName].servers || [],
343
- parameters: pathsAndWebhooks[pathOrHookName].parameters || [],
344
- };
345
- const isWebhook = pathsAndWebhooks[pathOrHookName]._type === 'webhook'; // eslint-disable-line no-underscore-dangle
346
- supportedMethods.forEach((methodName) => {
347
- if (pathsAndWebhooks[pathOrHookName][methodName]) {
348
- const pathOrHookObj = openApiSpec.paths[pathOrHookName][methodName];
349
- // If path.methods are tagged, else generate it from path
350
- const pathTags = pathOrHookObj.tags || [];
351
- if (pathTags.length === 0) {
352
- if (generateMissingTags) {
353
- const pathOrHookNameKey = pathOrHookName.replace(/^\/+|\/+$/g, '');
354
- const firstWordEndIndex = pathOrHookNameKey.indexOf('/');
355
- if (firstWordEndIndex === -1) {
356
- pathTags.push(pathOrHookNameKey);
357
- } else {
358
- // firstWordEndIndex -= 1;
359
- pathTags.push(pathOrHookNameKey.substring(0, firstWordEndIndex));
360
- }
361
- } else {
362
- pathTags.push('General ⦂');
363
- }
364
- }
365
-
366
- pathTags.forEach((tag) => {
367
- let tagObj;
368
- let specTagsItem;
369
-
370
- if (openApiSpec.tags) {
371
- specTagsItem = openApiSpec.tags.find((v) => (v.name.toLowerCase() === tag.toLowerCase()));
372
- }
373
-
374
- tagObj = tags.find((v) => v.name === tag);
375
- if (!tagObj) {
376
- tagObj = {
377
- show: true,
378
- elementId: `tag--${tag.replace(invalidCharsRegEx, '-')}`,
379
- name: tag,
380
- description: specTagsItem?.description || '',
381
- headers: specTagsItem?.description ? getHeadersFromMarkdown(specTagsItem.description) : [],
382
- paths: [],
383
- expanded: (specTagsItem ? specTagsItem['x-tag-expanded'] !== false : true),
384
- };
385
- tags.push(tagObj);
386
- }
387
-
388
- // Generate a short summary which is broken
389
- let shortSummary = (pathOrHookObj.summary || pathOrHookObj.description || `${methodName.toUpperCase()} ${pathOrHookName}`).trim();
390
- if (shortSummary.length > 100) {
391
- [shortSummary] = shortSummary.split(/[.|!|?]\s|[\r?\n]/); // take the first line (period or carriage return)
392
- }
393
- // Merge Common Parameters with This methods parameters
394
- let finalParameters = [];
395
- if (commonParams) {
396
- if (pathOrHookObj.parameters) {
397
- finalParameters = commonParams.filter((commonParam) => {
398
- if (!pathOrHookObj.parameters.some((param) => (commonParam.name === param.name && commonParam.in === param.in))) {
399
- return commonParam;
400
- }
401
- }).concat(pathOrHookObj.parameters);
402
- } else {
403
- finalParameters = commonParams.slice(0);
404
- }
405
- } else {
406
- finalParameters = pathOrHookObj.parameters ? pathOrHookObj.parameters.slice(0) : [];
407
- }
408
-
409
- // Filter callbacks to contain only objects.
410
- if (pathOrHookObj.callbacks) {
411
- for (const [callbackName, callbackConfig] of Object.entries(pathOrHookObj.callbacks)) {
412
- const filteredCallbacks = Object.entries(callbackConfig).filter((entry) => typeof entry[1] === 'object') || [];
413
- pathOrHookObj.callbacks[callbackName] = Object.fromEntries(filteredCallbacks);
414
- }
415
- }
416
-
417
- // Update Responses
418
- tagObj.paths.push({
419
- show: true,
420
- expanded: false,
421
- isWebhook,
422
- expandedAtLeastOnce: false,
423
- summary: (pathOrHookObj.summary || ''),
424
- description: (pathOrHookObj.description || ''),
425
- externalDocs: pathOrHookObj.externalDocs,
426
- shortSummary,
427
- method: methodName,
428
- path: pathOrHookName,
429
- operationId: pathOrHookObj.operationId,
430
- elementId: `${methodName}-${pathOrHookName.replace(invalidCharsRegEx, '-')}`,
431
- servers: pathOrHookObj.servers ? commonPathProp.servers.concat(pathOrHookObj.servers) : commonPathProp.servers,
432
- parameters: finalParameters,
433
- requestBody: pathOrHookObj.requestBody,
434
- responses: pathOrHookObj.responses,
435
- callbacks: pathOrHookObj.callbacks,
436
- deprecated: pathOrHookObj.deprecated,
437
- security: pathOrHookObj.security,
438
- // commonSummary: commonPathProp.summary,
439
- // commonDescription: commonPathProp.description,
440
- xBadges: pathOrHookObj['x-badges'] || undefined,
441
- xCodeSamples: pathOrHookObj['x-codeSamples'] || pathOrHookObj['x-code-samples'] || '',
442
- });
443
- });// End of tag path create
444
- }
445
- }); // End of Methods
446
- }
447
-
448
- const tagsWithSortedPaths = tags.filter((tag) => tag.paths && tag.paths.length > 0);
449
- tagsWithSortedPaths.forEach((tag) => {
450
- if (sortEndpointsBy === 'method') {
451
- tag.paths.sort((a, b) => supportedMethods.indexOf(a.method).toString().localeCompare(supportedMethods.indexOf(b.method)));
452
- } else if (sortEndpointsBy === 'summary') {
453
- tag.paths.sort((a, b) => (a.shortSummary).localeCompare(b.shortSummary));
454
- } else if (sortEndpointsBy === 'path') {
455
- tag.paths.sort((a, b) => a.path.localeCompare(b.path));
456
- } else if (sortEndpointsBy === 'none') {
457
- // don't sort if sortEndpointsBy is 'none'
458
- }
459
- tag.firstPathId = tag.paths[0].elementId;
460
- });
461
- return sortTags ? tagsWithSortedPaths.sort((a, b) => a.name.localeCompare(b.name)) : tagsWithSortedPaths;
462
- }