@scalar/oas-utils 0.10.7 → 0.10.9

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 (129) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/entities/cookie/cookie.js +11 -15
  3. package/dist/entities/cookie/index.js +1 -5
  4. package/dist/entities/environment/environment.js +7 -11
  5. package/dist/entities/environment/index.js +1 -5
  6. package/dist/entities/hotkeys/hotkeys.js +116 -111
  7. package/dist/entities/hotkeys/index.js +1 -6
  8. package/dist/entities/shared/index.js +1 -7
  9. package/dist/entities/shared/utility.js +9 -9
  10. package/dist/entities/spec/collection.js +91 -89
  11. package/dist/entities/spec/index.js +10 -59
  12. package/dist/entities/spec/operation.js +6 -6
  13. package/dist/entities/spec/parameters.js +38 -38
  14. package/dist/entities/spec/request-examples.js +421 -331
  15. package/dist/entities/spec/requests.js +102 -84
  16. package/dist/entities/spec/server.js +61 -46
  17. package/dist/entities/spec/spec-objects.js +121 -76
  18. package/dist/entities/spec/x-scalar-environments.js +18 -20
  19. package/dist/entities/spec/x-scalar-secrets.js +6 -8
  20. package/dist/entities/workspace/index.js +1 -7
  21. package/dist/entities/workspace/workspace.js +47 -46
  22. package/dist/helpers/client-plugins.js +13 -13
  23. package/dist/helpers/fetch-document.js +30 -25
  24. package/dist/helpers/fetch-with-proxy-fallback.js +26 -21
  25. package/dist/helpers/index.d.ts +1 -2
  26. package/dist/helpers/index.d.ts.map +1 -1
  27. package/dist/helpers/index.js +80 -119
  28. package/dist/helpers/normalize-mime-type-object.js +19 -18
  29. package/dist/helpers/normalize-mime-type.js +11 -9
  30. package/dist/helpers/operation-stability.js +25 -20
  31. package/dist/helpers/parse.d.ts +0 -4
  32. package/dist/helpers/parse.d.ts.map +1 -1
  33. package/dist/helpers/parse.js +77 -77
  34. package/dist/helpers/schema-model.js +13 -16
  35. package/dist/helpers/security/get-schemes.js +7 -8
  36. package/dist/helpers/security/has-token.js +18 -19
  37. package/dist/helpers/security/index.js +2 -7
  38. package/dist/helpers/servers.js +128 -79
  39. package/dist/helpers/should-ignore-entity.js +4 -5
  40. package/dist/migrations/data-version.js +15 -7
  41. package/dist/migrations/generate-types.js +34 -37
  42. package/dist/migrations/index.js +4 -18
  43. package/dist/migrations/local-storage.js +31 -29
  44. package/dist/migrations/migrate-to-indexdb.js +706 -529
  45. package/dist/migrations/migrator.js +58 -54
  46. package/dist/migrations/semver.js +24 -24
  47. package/dist/migrations/v-0.0.0/types.generated.js +1 -1
  48. package/dist/migrations/v-2.1.0/migration.js +272 -258
  49. package/dist/migrations/v-2.1.0/types.generated.js +1 -1
  50. package/dist/migrations/v-2.2.0/migration.js +99 -96
  51. package/dist/migrations/v-2.2.0/types.generated.js +1 -1
  52. package/dist/migrations/v-2.3.0/migration.js +45 -48
  53. package/dist/migrations/v-2.3.0/types.generated.js +1 -1
  54. package/dist/migrations/v-2.4.0/migration.js +25 -25
  55. package/dist/migrations/v-2.4.0/types.generated.js +1 -1
  56. package/dist/migrations/v-2.5.0/migration.js +122 -139
  57. package/dist/migrations/v-2.5.0/types.generated.js +1 -1
  58. package/dist/spec-getters/get-example-from-schema.js +489 -385
  59. package/dist/spec-getters/get-parameters-from-operation.js +39 -23
  60. package/dist/spec-getters/get-request-body-from-operation.d.ts.map +1 -1
  61. package/dist/spec-getters/get-request-body-from-operation.js +168 -126
  62. package/dist/spec-getters/get-server-variable-examples.js +10 -13
  63. package/dist/spec-getters/index.js +4 -11
  64. package/dist/transforms/import-spec.js +381 -291
  65. package/dist/transforms/index.js +1 -11
  66. package/package.json +15 -19
  67. package/dist/entities/cookie/cookie.js.map +0 -7
  68. package/dist/entities/cookie/index.js.map +0 -7
  69. package/dist/entities/environment/environment.js.map +0 -7
  70. package/dist/entities/environment/index.js.map +0 -7
  71. package/dist/entities/hotkeys/hotkeys.js.map +0 -7
  72. package/dist/entities/hotkeys/index.js.map +0 -7
  73. package/dist/entities/shared/index.js.map +0 -7
  74. package/dist/entities/shared/utility.js.map +0 -7
  75. package/dist/entities/spec/collection.js.map +0 -7
  76. package/dist/entities/spec/index.js.map +0 -7
  77. package/dist/entities/spec/operation.js.map +0 -7
  78. package/dist/entities/spec/parameters.js.map +0 -7
  79. package/dist/entities/spec/request-examples.js.map +0 -7
  80. package/dist/entities/spec/requests.js.map +0 -7
  81. package/dist/entities/spec/server.js.map +0 -7
  82. package/dist/entities/spec/spec-objects.js.map +0 -7
  83. package/dist/entities/spec/x-scalar-environments.js.map +0 -7
  84. package/dist/entities/spec/x-scalar-secrets.js.map +0 -7
  85. package/dist/entities/workspace/index.js.map +0 -7
  86. package/dist/entities/workspace/workspace.js.map +0 -7
  87. package/dist/helpers/client-plugins.js.map +0 -7
  88. package/dist/helpers/fetch-document.js.map +0 -7
  89. package/dist/helpers/fetch-with-proxy-fallback.js.map +0 -7
  90. package/dist/helpers/index.js.map +0 -7
  91. package/dist/helpers/normalize-mime-type-object.js.map +0 -7
  92. package/dist/helpers/normalize-mime-type.js.map +0 -7
  93. package/dist/helpers/operation-stability.js.map +0 -7
  94. package/dist/helpers/parse.js.map +0 -7
  95. package/dist/helpers/pretty-print-json.d.ts +0 -9
  96. package/dist/helpers/pretty-print-json.d.ts.map +0 -1
  97. package/dist/helpers/pretty-print-json.js +0 -38
  98. package/dist/helpers/pretty-print-json.js.map +0 -7
  99. package/dist/helpers/schema-model.js.map +0 -7
  100. package/dist/helpers/security/get-schemes.js.map +0 -7
  101. package/dist/helpers/security/has-token.js.map +0 -7
  102. package/dist/helpers/security/index.js.map +0 -7
  103. package/dist/helpers/servers.js.map +0 -7
  104. package/dist/helpers/should-ignore-entity.js.map +0 -7
  105. package/dist/migrations/data-version.js.map +0 -7
  106. package/dist/migrations/generate-types.js.map +0 -7
  107. package/dist/migrations/index.js.map +0 -7
  108. package/dist/migrations/local-storage.js.map +0 -7
  109. package/dist/migrations/migrate-to-indexdb.js.map +0 -7
  110. package/dist/migrations/migrator.js.map +0 -7
  111. package/dist/migrations/semver.js.map +0 -7
  112. package/dist/migrations/v-0.0.0/types.generated.js.map +0 -7
  113. package/dist/migrations/v-2.1.0/migration.js.map +0 -7
  114. package/dist/migrations/v-2.1.0/types.generated.js.map +0 -7
  115. package/dist/migrations/v-2.2.0/migration.js.map +0 -7
  116. package/dist/migrations/v-2.2.0/types.generated.js.map +0 -7
  117. package/dist/migrations/v-2.3.0/migration.js.map +0 -7
  118. package/dist/migrations/v-2.3.0/types.generated.js.map +0 -7
  119. package/dist/migrations/v-2.4.0/migration.js.map +0 -7
  120. package/dist/migrations/v-2.4.0/types.generated.js.map +0 -7
  121. package/dist/migrations/v-2.5.0/migration.js.map +0 -7
  122. package/dist/migrations/v-2.5.0/types.generated.js.map +0 -7
  123. package/dist/spec-getters/get-example-from-schema.js.map +0 -7
  124. package/dist/spec-getters/get-parameters-from-operation.js.map +0 -7
  125. package/dist/spec-getters/get-request-body-from-operation.js.map +0 -7
  126. package/dist/spec-getters/get-server-variable-examples.js.map +0 -7
  127. package/dist/spec-getters/index.js.map +0 -7
  128. package/dist/transforms/import-spec.js.map +0 -7
  129. package/dist/transforms/index.js.map +0 -7
@@ -1,307 +1,397 @@
1
- import { isDefined } from "@scalar/helpers/array/is-defined";
2
- import { isHttpMethod } from "@scalar/helpers/http/is-http-method";
3
- import { keysOf } from "@scalar/object-utils/arrays";
4
- import { dereference, load, upgrade } from "@scalar/openapi-parser";
5
- import {
6
- securitySchemeSchema
7
- } from "@scalar/types/entities";
8
- import { collectionSchema } from "../entities/spec/collection.js";
9
- import { createExampleFromRequest } from "../entities/spec/request-examples.js";
10
- import { requestSchema } from "../entities/spec/requests.js";
11
- import { serverSchema } from "../entities/spec/server.js";
12
- import { tagSchema } from "../entities/spec/spec-objects.js";
13
- import { schemaModel } from "../helpers/schema-model.js";
14
- import { getServersFromDocument } from "../helpers/servers.js";
1
+ import { isDefined } from '@scalar/helpers/array/is-defined';
2
+ import { isHttpMethod } from '@scalar/helpers/http/is-http-method';
3
+ import { keysOf } from '@scalar/object-utils/arrays';
4
+ import { dereference, load, upgrade } from '@scalar/openapi-parser';
5
+ import { securitySchemeSchema, } from '@scalar/types/entities';
6
+ import { collectionSchema } from '../entities/spec/collection.js';
7
+ import { createExampleFromRequest } from '../entities/spec/request-examples.js';
8
+ import { requestSchema } from '../entities/spec/requests.js';
9
+ import { serverSchema } from '../entities/spec/server.js';
10
+ import { tagSchema } from '../entities/spec/spec-objects.js';
11
+ import { schemaModel } from '../helpers/schema-model.js';
12
+ import { getServersFromDocument } from '../helpers/servers.js';
15
13
  const dereferenceDocument = async (document, { shouldLoad = true } = {}) => {
16
- if (document === null || typeof document === "string" && document.trim() === "") {
17
- console.warn("[@scalar/oas-utils] Empty OpenAPI document provided.");
14
+ if (document === null || (typeof document === 'string' && document.trim() === '')) {
15
+ console.warn('[@scalar/oas-utils] Empty OpenAPI document provided.');
16
+ return {
17
+ schema: {},
18
+ errors: [],
19
+ };
20
+ }
21
+ let filesystem = document;
22
+ let loadErrors = [];
23
+ if (shouldLoad) {
24
+ // TODO: Plugins for URLs and files with the proxy are missing here.
25
+ // @see packages/api-reference/src/helpers/parse.ts
26
+ const response = await load(document).catch((e) => ({
27
+ errors: [
28
+ {
29
+ code: e.code,
30
+ message: e.message,
31
+ },
32
+ ],
33
+ filesystem: [],
34
+ }));
35
+ filesystem = response.filesystem;
36
+ loadErrors = response.errors ?? [];
37
+ }
38
+ const { specification } = upgrade(filesystem);
39
+ const { schema, errors: derefErrors = [] } = dereference(specification);
18
40
  return {
19
- schema: {},
20
- errors: []
41
+ schema,
42
+ errors: [...loadErrors, ...derefErrors],
21
43
  };
22
- }
23
- let filesystem = document;
24
- let loadErrors = [];
25
- if (shouldLoad) {
26
- const response = await load(document).catch((e) => ({
27
- errors: [
28
- {
29
- code: e.code,
30
- message: e.message
44
+ };
45
+ /** Takes a string or object and parses it into an openapi spec compliant schema */
46
+ export const parseSchema = async (originalDocument, { shouldLoad = true,
47
+ /** If a dereferenced document is provided, we will skip the dereferencing step. */
48
+ dereferencedDocument = undefined, } = {}) => {
49
+ // Skip, if a dereferenced document is provided
50
+ const { schema, errors } = dereferencedDocument
51
+ ? {
52
+ schema: dereferencedDocument,
53
+ errors: [],
31
54
  }
32
- ],
33
- filesystem: []
34
- }));
35
- filesystem = response.filesystem;
36
- loadErrors = response.errors ?? [];
37
- }
38
- const { specification } = upgrade(filesystem);
39
- const { schema, errors: derefErrors = [] } = dereference(specification);
40
- return {
41
- schema,
42
- errors: [...loadErrors, ...derefErrors]
43
- };
55
+ : // Otherwise, dereference the original document
56
+ await dereferenceDocument(originalDocument ?? '', {
57
+ shouldLoad,
58
+ });
59
+ if (!schema) {
60
+ console.warn('[@scalar/oas-utils] OpenAPI Parser Warning: Schema is undefined');
61
+ }
62
+ return {
63
+ /**
64
+ * Temporary fix for the parser returning an empty array
65
+ * TODO: remove this once the parser is fixed
66
+ */
67
+ schema: (Array.isArray(schema) ? {} : schema),
68
+ errors,
69
+ };
44
70
  };
45
- const parseSchema = async (originalDocument, {
46
- shouldLoad = true,
47
- /** If a dereferenced document is provided, we will skip the dereferencing step. */
48
- dereferencedDocument = void 0
49
- } = {}) => {
50
- const { schema, errors } = dereferencedDocument ? {
51
- schema: dereferencedDocument,
52
- errors: []
53
- } : (
54
- // Otherwise, dereference the original document
55
- await dereferenceDocument(originalDocument ?? "", {
56
- shouldLoad
57
- })
58
- );
59
- if (!schema) {
60
- console.warn("[@scalar/oas-utils] OpenAPI Parser Warning: Schema is undefined");
61
- }
62
- return {
71
+ /** Converts selected security requirements to uids */
72
+ export const getSelectedSecuritySchemeUids = (securityRequirements, preferredSecurityNames = [], securitySchemeMap) => {
73
+ // Set the first security requirement if no preferred security schemes are set
74
+ const names = securityRequirements[0] && !preferredSecurityNames.length ? [securityRequirements[0]] : preferredSecurityNames;
75
+ // Map names to uids
76
+ const uids = names
77
+ .map((name) => Array.isArray(name) ? name.map((k) => securitySchemeMap[k]).filter(isDefined) : securitySchemeMap[name])
78
+ .filter(isDefined);
79
+ return uids;
80
+ };
81
+ /** Create a "uid" from a slug */
82
+ export const getSlugUid = (slug) => `slug-uid-${slug}`;
83
+ /**
84
+ * Imports an OpenAPI document and converts it to workspace entities (Collection, Request, Server, etc.)
85
+ *
86
+ * The imported entities maintain a close mapping to the original OpenAPI specification to enable:
87
+ * - Bi-directional translation between spec and workspace entities
88
+ * - Preservation of specification details and structure
89
+ * - Accurate representation of relationships between components
90
+ *
91
+ * Relationships between entities are maintained through unique identifiers (UIDs) which allow:
92
+ * - Flexible organization at different levels (workspace, collection, request)
93
+ * - Proper linking between related components
94
+ * - Easy lookup and reference of dependent entities
95
+ */
96
+ export async function importSpecToWorkspace(content, {
97
+ /** If a dereferenced document is provided, we will skip the dereferencing step. */
98
+ dereferencedDocument, authentication, baseServerURL, documentUrl, servers: configuredServers, useCollectionSecurity = false, slug, shouldLoad, watchMode = false, } = {}) {
99
+ const { schema, errors } = await parseSchema(content, { shouldLoad, dereferencedDocument });
100
+ const importWarnings = [...errors.map((e) => e.message)];
101
+ if (!schema) {
102
+ return { importWarnings, error: true, collection: undefined };
103
+ }
104
+ // ---------------------------------------------------------------------------
105
+ // Some entities will be broken out as individual lists for modification in the workspace
106
+ const start = performance.now();
107
+ const requests = [];
108
+ // Add the base server url to collection servers
109
+ const collectionServers = getServersFromDocument(configuredServers || schema.servers, {
110
+ baseServerURL,
111
+ documentUrl,
112
+ });
113
+ // Store operation servers
114
+ const operationServers = [];
63
115
  /**
64
- * Temporary fix for the parser returning an empty array
65
- * TODO: remove this once the parser is fixed
116
+ * List of all tag strings. For non compliant specs we may need to
117
+ * add top level tag objects for missing tag objects
66
118
  */
67
- schema: Array.isArray(schema) ? {} : schema,
68
- errors
69
- };
70
- };
71
- const getSelectedSecuritySchemeUids = (securityRequirements, preferredSecurityNames = [], securitySchemeMap) => {
72
- const names = securityRequirements[0] && !preferredSecurityNames.length ? [securityRequirements[0]] : preferredSecurityNames;
73
- const uids = names.map(
74
- (name) => Array.isArray(name) ? name.map((k) => securitySchemeMap[k]).filter(isDefined) : securitySchemeMap[name]
75
- ).filter(isDefined);
76
- return uids;
77
- };
78
- const getSlugUid = (slug) => `slug-uid-${slug}`;
79
- async function importSpecToWorkspace(content, {
80
- /** If a dereferenced document is provided, we will skip the dereferencing step. */
81
- dereferencedDocument,
82
- authentication,
83
- baseServerURL,
84
- documentUrl,
85
- servers: configuredServers,
86
- useCollectionSecurity = false,
87
- slug,
88
- shouldLoad,
89
- watchMode = false
90
- } = {}) {
91
- const { schema, errors } = await parseSchema(content, { shouldLoad, dereferencedDocument });
92
- const importWarnings = [...errors.map((e) => e.message)];
93
- if (!schema) {
94
- return { importWarnings, error: true, collection: void 0 };
95
- }
96
- const start = performance.now();
97
- const requests = [];
98
- const collectionServers = getServersFromDocument(configuredServers || schema.servers, {
99
- baseServerURL,
100
- documentUrl
101
- });
102
- const operationServers = [];
103
- const tagNames = /* @__PURE__ */ new Set();
104
- const security = schema.components?.securitySchemes ?? schema?.securityDefinitions ?? {};
105
- if (authentication?.oAuth2 || authentication?.apiKey || authentication?.http) {
106
- console.warn(
107
- `DEPRECATION WARNING: It looks like you're using legacy authentication config. Please migrate to use the updated config. See https://github.com/scalar/scalar/blob/main/documentation/configuration.md#authentication-partial This will be removed in a future version.`
108
- );
109
- }
110
- const securitySchemes = Object.entries(security).map?.(([nameKey, _scheme]) => {
111
- const payload = {
112
- ..._scheme,
113
- // Add the new auth config overrides, we keep the old code below for backwards compatibility
114
- ...authentication?.securitySchemes?.[nameKey] ?? {},
115
- nameKey
116
- };
117
- if (payload.type === "oauth2" && payload.flows) {
118
- const flowKeys = Object.keys(payload.flows);
119
- flowKeys.forEach((key) => {
120
- if (!payload.flows?.[key] || _scheme.type !== "oauth2") {
121
- return;
122
- }
123
- const authFlow = authentication?.securitySchemes?.[nameKey]?.flows?.[key] ?? {};
124
- payload.flows[key] = {
125
- ..._scheme.flows?.[key] ?? {},
126
- ...authFlow
119
+ const tagNames = new Set();
120
+ // ---------------------------------------------------------------------------
121
+ // SECURITY HANDLING
122
+ const security = schema.components?.securitySchemes ?? schema?.securityDefinitions ?? {};
123
+ // @ts-expect-error - Toss out a deprecated warning for the old authentication state
124
+ if (authentication?.oAuth2 || authentication?.apiKey || authentication?.http) {
125
+ console.warn(`DEPRECATION WARNING: It looks like you're using legacy authentication config. Please migrate to use the updated config. See https://github.com/scalar/scalar/blob/main/documentation/configuration.md#authentication-partial This will be removed in a future version.`);
126
+ }
127
+ const securitySchemes = Object.entries(security)
128
+ .map?.(([nameKey, _scheme]) => {
129
+ // Apply any transforms we need before parsing
130
+ const payload = {
131
+ ..._scheme,
132
+ // Add the new auth config overrides, we keep the old code below for backwards compatibility
133
+ ...(authentication?.securitySchemes?.[nameKey] ?? {}),
134
+ nameKey,
127
135
  };
128
- const flow = payload.flows[key];
129
- flow.type = key;
130
- if (authentication?.oAuth2) {
131
- if (authentication.oAuth2.accessToken) {
132
- flow.token = authentication.oAuth2.accessToken;
133
- }
134
- if (authentication.oAuth2.clientId) {
135
- flow["x-scalar-client-id"] = authentication.oAuth2.clientId;
136
- }
137
- if (authentication.oAuth2.scopes) {
138
- flow.selectedScopes = authentication.oAuth2.scopes;
139
- }
140
- if (flow.type === "password") {
141
- flow.username = authentication.oAuth2.username;
142
- flow.password = authentication.oAuth2.password;
143
- }
136
+ // For oauth2 we need to add the type to the flows + prefill from authentication
137
+ if (payload.type === 'oauth2' && payload.flows) {
138
+ const flowKeys = Object.keys(payload.flows);
139
+ flowKeys.forEach((key) => {
140
+ if (!payload.flows?.[key] || _scheme.type !== 'oauth2') {
141
+ return;
142
+ }
143
+ const authFlow = authentication?.securitySchemes?.[nameKey]?.flows?.[key] ?? {};
144
+ // This part handles setting of flows via the new auth config, the rest can be removed in a future version
145
+ payload.flows[key] = {
146
+ ...(_scheme.flows?.[key] ?? {}),
147
+ ...authFlow,
148
+ };
149
+ const flow = payload.flows[key];
150
+ // Set the type
151
+ flow.type = key;
152
+ // Prefill values from authorization config - old deprecated config
153
+ // @ts-expect-error - deprecated
154
+ if (authentication?.oAuth2) {
155
+ // @ts-expect-error - deprecated
156
+ if (authentication.oAuth2.accessToken) {
157
+ // @ts-expect-error - deprecated
158
+ flow.token = authentication.oAuth2.accessToken;
159
+ }
160
+ // @ts-expect-error - deprecated
161
+ if (authentication.oAuth2.clientId) {
162
+ // @ts-expect-error - deprecated
163
+ flow['x-scalar-client-id'] = authentication.oAuth2.clientId;
164
+ }
165
+ // @ts-expect-error - deprecated
166
+ if (authentication.oAuth2.scopes) {
167
+ // @ts-expect-error - deprecated
168
+ flow.selectedScopes = authentication.oAuth2.scopes;
169
+ }
170
+ if (flow.type === 'password') {
171
+ // @ts-expect-error - deprecated
172
+ flow.username = authentication.oAuth2.username;
173
+ // @ts-expect-error - deprecated
174
+ flow.password = authentication.oAuth2.password;
175
+ }
176
+ }
177
+ // Convert scopes to an object
178
+ if (Array.isArray(flow.scopes)) {
179
+ flow.scopes = flow.scopes.reduce((prev, s) => ({ ...prev, [s]: '' }), {});
180
+ }
181
+ // Handle x-defaultClientId
182
+ if (flow['x-defaultClientId']) {
183
+ flow['x-scalar-client-id'] = flow['x-defaultClientId'];
184
+ }
185
+ });
144
186
  }
145
- if (Array.isArray(flow.scopes)) {
146
- flow.scopes = flow.scopes.reduce((prev, s) => ({ ...prev, [s]: "" }), {});
187
+ // Otherwise we just prefill - old deprecated config
188
+ else if (authentication) {
189
+ // ApiKey
190
+ // @ts-expect-error - deprecated
191
+ if (payload.type === 'apiKey' && authentication.apiKey?.token) {
192
+ // @ts-expect-error - deprecated
193
+ payload.value = authentication.apiKey.token;
194
+ }
195
+ // HTTP
196
+ else if (payload.type === 'http') {
197
+ // @ts-expect-error - deprecated
198
+ if (payload.scheme === 'basic' && authentication.http?.basic) {
199
+ // @ts-expect-error - deprecated
200
+ payload.username = authentication.http.basic.username ?? '';
201
+ // @ts-expect-error - deprecated
202
+ payload.password = authentication.http.basic.password ?? '';
203
+ }
204
+ // Bearer
205
+ // @ts-expect-error - deprecated
206
+ else if (payload.scheme === 'bearer' && authentication.http?.bearer?.token) {
207
+ // @ts-expect-error - deprecated
208
+ payload.token = authentication.http.bearer.token ?? '';
209
+ }
210
+ }
147
211
  }
148
- if (flow["x-defaultClientId"]) {
149
- flow["x-scalar-client-id"] = flow["x-defaultClientId"];
212
+ const scheme = schemaModel(payload, securitySchemeSchema, false);
213
+ if (!scheme) {
214
+ importWarnings.push(`Security scheme ${nameKey} is invalid.`);
150
215
  }
151
- });
152
- } else if (authentication) {
153
- if (payload.type === "apiKey" && authentication.apiKey?.token) {
154
- payload.value = authentication.apiKey.token;
155
- } else if (payload.type === "http") {
156
- if (payload.scheme === "basic" && authentication.http?.basic) {
157
- payload.username = authentication.http.basic.username ?? "";
158
- payload.password = authentication.http.basic.password ?? "";
159
- } else if (payload.scheme === "bearer" && authentication.http?.bearer?.token) {
160
- payload.token = authentication.http.bearer.token ?? "";
216
+ return scheme;
217
+ })
218
+ .filter((v) => !!v);
219
+ // Map of security scheme names to UIDs
220
+ const securitySchemeMap = {};
221
+ securitySchemes.forEach((s) => {
222
+ securitySchemeMap[s.nameKey] = s.uid;
223
+ });
224
+ // ---------------------------------------------------------------------------
225
+ // REQUEST HANDLING
226
+ keysOf(schema.paths ?? {}).forEach((pathString) => {
227
+ const path = schema?.paths?.[pathString];
228
+ if (!path) {
229
+ return;
161
230
  }
162
- }
163
- }
164
- const scheme = schemaModel(payload, securitySchemeSchema, false);
165
- if (!scheme) {
166
- importWarnings.push(`Security scheme ${nameKey} is invalid.`);
167
- }
168
- return scheme;
169
- }).filter((v) => !!v);
170
- const securitySchemeMap = {};
171
- securitySchemes.forEach((s) => {
172
- securitySchemeMap[s.nameKey] = s.uid;
173
- });
174
- keysOf(schema.paths ?? {}).forEach((pathString) => {
175
- const path = schema?.paths?.[pathString];
176
- if (!path) {
177
- return;
178
- }
179
- const pathServers = serverSchema.array().parse(path.servers ?? []);
180
- for (const server of pathServers) {
181
- collectionServers.push(server);
182
- }
183
- const methods = Object.keys(path).filter(isHttpMethod);
184
- methods.forEach((method) => {
185
- const operation = path[method];
186
- if (!operation) {
187
- return;
188
- }
189
- const operationLevelServers = serverSchema.array().parse(operation.servers ?? []);
190
- for (const server of operationLevelServers) {
191
- operationServers.push(server);
192
- }
193
- operation.tags?.forEach((t) => tagNames.add(t));
194
- const { security: operationSecurity, ...operationWithoutSecurity } = operation;
195
- const securityRequirements2 = (operationSecurity ?? schema.security ?? []).map((s) => {
196
- const keys = Object.keys(s);
197
- return keys.length > 1 ? keys : keys[0];
198
- }).filter(isDefined);
199
- const preferredSecurityNames2 = [authentication?.preferredSecurityScheme ?? []].flat().filter((name) => {
200
- if (Array.isArray(name)) {
201
- return securityRequirements2.some(
202
- (r) => Array.isArray(r) && r.length === name.length && r.every((v, i) => v === name[i])
203
- );
231
+ // Path level servers must be saved
232
+ const pathServers = serverSchema.array().parse(path.servers ?? []);
233
+ for (const server of pathServers) {
234
+ collectionServers.push(server);
204
235
  }
205
- return securityRequirements2.includes(name);
206
- });
207
- const selectedSecuritySchemeUids2 = securityRequirements2.length && !useCollectionSecurity ? getSelectedSecuritySchemeUids(securityRequirements2, preferredSecurityNames2, securitySchemeMap) : [];
208
- const requestPayload = {
209
- ...operationWithoutSecurity,
210
- method,
211
- path: pathString,
212
- security: operationSecurity,
213
- selectedServerUid: operationLevelServers?.[0]?.uid,
214
- selectedSecuritySchemeUids: selectedSecuritySchemeUids2,
215
- // Merge path and operation level parameters
216
- parameters: [...path?.parameters ?? [], ...operation.parameters ?? []],
217
- servers: [...pathServers, ...operationLevelServers].map((s) => s.uid)
218
- };
219
- if (requestPayload.examples) {
220
- console.warn("[@scalar/api-client] operation.examples is not a valid openapi property");
221
- delete requestPayload.examples;
222
- }
223
- const request = schemaModel(requestPayload, requestSchema, false);
224
- if (!request) {
225
- importWarnings.push(`${method} Request at ${path} is invalid.`);
226
- } else {
227
- requests.push(request);
228
- }
236
+ // Creates a sorted array of methods based on the path object.
237
+ const methods = Object.keys(path).filter(isHttpMethod);
238
+ methods.forEach((method) => {
239
+ const operation = path[method];
240
+ if (!operation) {
241
+ return;
242
+ }
243
+ const operationLevelServers = serverSchema.array().parse(operation.servers ?? []);
244
+ for (const server of operationLevelServers) {
245
+ operationServers.push(server);
246
+ }
247
+ // We will save a list of all tags to ensure they exists at the top level
248
+ // TODO: make sure we add any loose requests with no tags to the collection children
249
+ operation.tags?.forEach((t) => tagNames.add(t));
250
+ // Remove security here and add it correctly below
251
+ const { security: operationSecurity, ...operationWithoutSecurity } = operation;
252
+ const securityRequirements = (operationSecurity ?? schema.security ?? [])
253
+ .map((s) => {
254
+ const keys = Object.keys(s);
255
+ return keys.length > 1 ? keys : keys[0];
256
+ })
257
+ .filter(isDefined);
258
+ // Filter the preferred security schemes to only include the ones that are in the security requirements
259
+ const preferredSecurityNames = [authentication?.preferredSecurityScheme ?? []].flat().filter((name) => {
260
+ // Match up complex security requirements, array to array
261
+ if (Array.isArray(name)) {
262
+ // We match every element in the array
263
+ return securityRequirements.some((r) => Array.isArray(r) && r.length === name.length && r.every((v, i) => v === name[i]));
264
+ }
265
+ return securityRequirements.includes(name);
266
+ });
267
+ // Set the initially selected security scheme
268
+ const selectedSecuritySchemeUids = securityRequirements.length && !useCollectionSecurity
269
+ ? getSelectedSecuritySchemeUids(securityRequirements, preferredSecurityNames, securitySchemeMap)
270
+ : [];
271
+ const requestPayload = {
272
+ ...operationWithoutSecurity,
273
+ method,
274
+ path: pathString,
275
+ security: operationSecurity,
276
+ selectedServerUid: operationLevelServers?.[0]?.uid,
277
+ selectedSecuritySchemeUids,
278
+ // Merge path and operation level parameters
279
+ parameters: [...(path?.parameters ?? []), ...(operation.parameters ?? [])],
280
+ servers: [...pathServers, ...operationLevelServers].map((s) => s.uid),
281
+ };
282
+ // Remove any examples from the request payload as they conflict with our examples property and are not valid
283
+ if (requestPayload.examples) {
284
+ console.warn('[@scalar/api-client] operation.examples is not a valid openapi property');
285
+ delete requestPayload.examples;
286
+ }
287
+ // Save parse the request
288
+ const request = schemaModel(requestPayload, requestSchema, false);
289
+ if (!request) {
290
+ importWarnings.push(`${method} Request at ${path} is invalid.`);
291
+ }
292
+ else {
293
+ requests.push(request);
294
+ }
295
+ });
229
296
  });
230
- });
231
- const tags = schemaModel(schema?.tags ?? [], tagSchema.array(), false) ?? [];
232
- tags.forEach((t) => tagNames.delete(t.name));
233
- tagNames.forEach((name) => name && tags.push(tagSchema.parse({ name })));
234
- const tagMap = {};
235
- tags.forEach((t) => {
236
- tagMap[t.name] = t;
237
- });
238
- const collectionChildren = new Set(tags.map((t) => t.uid));
239
- tags.forEach((t) => {
240
- t["x-scalar-children"]?.forEach((c) => {
241
- const nestedUid = tagMap[c.tagName]?.uid;
242
- if (nestedUid) {
243
- t.children.push(nestedUid);
244
- collectionChildren.delete(nestedUid);
245
- }
297
+ // ---------------------------------------------------------------------------
298
+ // TAG HANDLING
299
+ // TODO: We may need to handle de-duping tags
300
+ const tags = schemaModel(schema?.tags ?? [], tagSchema.array(), false) ?? [];
301
+ // Delete any tag names that already have a definition
302
+ tags.forEach((t) => tagNames.delete(t.name));
303
+ // Add an entry for any tags that are used but do not have a definition
304
+ tagNames.forEach((name) => name && tags.push(tagSchema.parse({ name })));
305
+ // Tag name to UID map
306
+ const tagMap = {};
307
+ tags.forEach((t) => {
308
+ tagMap[t.name] = t;
246
309
  });
247
- });
248
- requests.forEach((r) => {
249
- if (r.tags?.length) {
250
- r.tags.forEach((t) => {
251
- tagMap[t]?.children.push(r.uid);
252
- });
253
- } else {
254
- collectionChildren.add(r.uid);
255
- }
256
- });
257
- const examples = [];
258
- requests.forEach((request) => {
259
- const example = createExampleFromRequest(request, "Default Example");
260
- examples.push(example);
261
- request.examples.push(example.uid);
262
- });
263
- const securityRequirements = (schema.security ?? []).map((s) => {
264
- const keys = Object.keys(s);
265
- return keys.length > 1 ? keys : keys[0];
266
- }).filter(isDefined);
267
- const preferredSecurityNames = [authentication?.preferredSecurityScheme ?? []].flat();
268
- const selectedSecuritySchemeUids = (securityRequirements.length || preferredSecurityNames?.length) && useCollectionSecurity ? getSelectedSecuritySchemeUids(securityRequirements, preferredSecurityNames, securitySchemeMap) : [];
269
- const slugObj = slug?.length ? { uid: getSlugUid(slug) } : {};
270
- const collection = collectionSchema.parse({
271
- ...slugObj,
272
- ...schema,
273
- watchMode,
274
- documentUrl,
275
- useCollectionSecurity,
276
- requests: requests.map((r) => r.uid),
277
- servers: collectionServers.map((s) => s.uid),
278
- tags: tags.map((t) => t.uid),
279
- children: [...collectionChildren],
280
- security: schema.security ?? [{}],
281
- selectedServerUid: collectionServers?.[0]?.uid,
282
- selectedSecuritySchemeUids,
283
- components: {
284
- ...schema.components
285
- },
286
- securitySchemes: securitySchemes.map((s) => s.uid)
287
- });
288
- const end = performance.now();
289
- console.log(`workspace: ${Math.round(end - start)} ms`);
290
- return {
291
- error: false,
292
- servers: [...collectionServers, ...operationServers],
293
- schema,
294
- requests,
295
- examples,
296
- collection,
297
- tags,
298
- securitySchemes
299
- };
310
+ // Add all tags by default. We will remove nested ones
311
+ const collectionChildren = new Set(tags.map((t) => t.uid));
312
+ // Nested folders go before any requests
313
+ tags.forEach((t) => {
314
+ t['x-scalar-children']?.forEach((c) => {
315
+ // Add the uid to the appropriate parent.children
316
+ const nestedUid = tagMap[c.tagName]?.uid;
317
+ if (nestedUid) {
318
+ t.children.push(nestedUid);
319
+ // Remove the nested uid from the root folder
320
+ collectionChildren.delete(nestedUid);
321
+ }
322
+ });
323
+ });
324
+ // Add the request UIDs to the tag children (or collection root)
325
+ requests.forEach((r) => {
326
+ if (r.tags?.length) {
327
+ r.tags.forEach((t) => {
328
+ tagMap[t]?.children.push(r.uid);
329
+ });
330
+ }
331
+ else {
332
+ collectionChildren.add(r.uid);
333
+ }
334
+ });
335
+ // ---------------------------------------------------------------------------
336
+ const examples = [];
337
+ // Ensure each request has at least 1 example
338
+ requests.forEach((request) => {
339
+ // TODO: Need to handle parsing examples
340
+ // if (request['x-scalar-examples']) return
341
+ // Create the initial example
342
+ const example = createExampleFromRequest(request, 'Default Example');
343
+ examples.push(example);
344
+ request.examples.push(example.uid);
345
+ });
346
+ // ---------------------------------------------------------------------------
347
+ // Generate Collection
348
+ // Grab the security requirements for this operation
349
+ const securityRequirements = (schema.security ?? [])
350
+ .map((s) => {
351
+ const keys = Object.keys(s);
352
+ return keys.length > 1 ? keys : keys[0];
353
+ })
354
+ .filter(isDefined);
355
+ // Here we do not filter these as we let the preferredSecurityScheme override the requirements
356
+ const preferredSecurityNames = [authentication?.preferredSecurityScheme ?? []].flat();
357
+ // Set the initially selected security scheme
358
+ const selectedSecuritySchemeUids = (securityRequirements.length || preferredSecurityNames?.length) && useCollectionSecurity
359
+ ? getSelectedSecuritySchemeUids(securityRequirements, preferredSecurityNames, securitySchemeMap)
360
+ : [];
361
+ // Set the uid as a prefixed slug if we have one
362
+ const slugObj = slug?.length ? { uid: getSlugUid(slug) } : {};
363
+ const collection = collectionSchema.parse({
364
+ ...slugObj,
365
+ ...schema,
366
+ watchMode,
367
+ documentUrl,
368
+ useCollectionSecurity,
369
+ requests: requests.map((r) => r.uid),
370
+ servers: collectionServers.map((s) => s.uid),
371
+ tags: tags.map((t) => t.uid),
372
+ children: [...collectionChildren],
373
+ security: schema.security ?? [{}],
374
+ selectedServerUid: collectionServers?.[0]?.uid,
375
+ selectedSecuritySchemeUids,
376
+ components: {
377
+ ...schema.components,
378
+ },
379
+ securitySchemes: securitySchemes.map((s) => s.uid),
380
+ });
381
+ const end = performance.now();
382
+ console.log(`workspace: ${Math.round(end - start)} ms`);
383
+ /**
384
+ * Servers and requests will be saved in top level maps and indexed via UID to
385
+ * maintain specification relationships
386
+ */
387
+ return {
388
+ error: false,
389
+ servers: [...collectionServers, ...operationServers],
390
+ schema,
391
+ requests,
392
+ examples,
393
+ collection,
394
+ tags,
395
+ securitySchemes,
396
+ };
300
397
  }
301
- export {
302
- getSelectedSecuritySchemeUids,
303
- getSlugUid,
304
- importSpecToWorkspace,
305
- parseSchema
306
- };
307
- //# sourceMappingURL=import-spec.js.map