@scalar/workspace-store 0.59.0 → 0.61.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 (198) hide show
  1. package/CHANGELOG.md +61 -0
  2. package/README.md +6 -0
  3. package/dist/channel-example/build-connection-url.d.ts.map +1 -1
  4. package/dist/channel-example/build-connection-url.js +3 -3
  5. package/dist/channel-example/dedupe-requirements.d.ts +1 -1
  6. package/dist/channel-example/get-async-api-protocols.js +2 -2
  7. package/dist/channel-example/get-asyncapi-security-requirements.d.ts +1 -1
  8. package/dist/channel-example/get-channel-connection-security.d.ts +1 -1
  9. package/dist/channel-example/get-channel-operations.d.ts.map +1 -1
  10. package/dist/channel-example/get-channel-operations.js +4 -1
  11. package/dist/channel-example/get-channel-parameters.d.ts.map +1 -1
  12. package/dist/channel-example/get-channel-parameters.js +3 -1
  13. package/dist/channel-example/resolve-channel.d.ts.map +1 -1
  14. package/dist/channel-example/resolve-channel.js +2 -0
  15. package/dist/channel-example/resolve-operation-channel.d.ts.map +1 -1
  16. package/dist/channel-example/resolve-operation-channel.js +4 -0
  17. package/dist/channel-example/resolve-operation-with-traits.js +2 -2
  18. package/dist/channel-example/servers.d.ts.map +1 -1
  19. package/dist/channel-example/servers.js +3 -1
  20. package/dist/channel-example/types.d.ts +1 -1
  21. package/dist/client.d.ts +1 -1
  22. package/dist/client.js +3 -3
  23. package/dist/entities/auth/schema.d.ts +470 -20
  24. package/dist/entities/auth/schema.d.ts.map +1 -1
  25. package/dist/entities/auth/schema.js +2 -2
  26. package/dist/events/definitions/auth.d.ts +2 -2
  27. package/dist/events/definitions/document.d.ts +1 -1
  28. package/dist/events/definitions/operation.d.ts +3 -2
  29. package/dist/events/definitions/operation.d.ts.map +1 -1
  30. package/dist/events/definitions/server.d.ts +1 -1
  31. package/dist/events/old-definitions.d.ts +1 -1
  32. package/dist/helpers/create-chunk-writer.d.ts +6 -0
  33. package/dist/helpers/create-chunk-writer.d.ts.map +1 -0
  34. package/dist/helpers/create-chunk-writer.js +44 -0
  35. package/dist/helpers/dynamic-ref.d.ts +13 -2
  36. package/dist/helpers/dynamic-ref.d.ts.map +1 -1
  37. package/dist/helpers/dynamic-ref.js +20 -7
  38. package/dist/helpers/encode-chunk-name.d.ts +3 -0
  39. package/dist/helpers/encode-chunk-name.d.ts.map +1 -0
  40. package/dist/helpers/encode-chunk-name.js +9 -0
  41. package/dist/helpers/for-each-path-item-operation.d.ts +2 -2
  42. package/dist/helpers/get-first-server.d.ts +1 -1
  43. package/dist/helpers/get-resolved-ref.d.ts +6 -1
  44. package/dist/helpers/get-resolved-ref.d.ts.map +1 -1
  45. package/dist/helpers/get-resolved-ref.js +3 -10
  46. package/dist/helpers/is-non-optional-security-requirement.d.ts +1 -1
  47. package/dist/helpers/merge-object.d.ts +2 -2
  48. package/dist/helpers/merge-object.d.ts.map +1 -1
  49. package/dist/helpers/merge-object.js +2 -5
  50. package/dist/mutators/auth.d.ts +1 -1
  51. package/dist/mutators/auth.d.ts.map +1 -1
  52. package/dist/mutators/auth.js +15 -6
  53. package/dist/mutators/index.d.ts +10 -10
  54. package/dist/mutators/operation/body.js +2 -2
  55. package/dist/mutators/operation/helpers/fetch-request-to-har.d.ts.map +1 -1
  56. package/dist/mutators/operation/helpers/fetch-request-to-har.js +12 -2
  57. package/dist/mutators/operation/helpers/har-to-operation.d.ts +1 -1
  58. package/dist/mutators/operation/helpers/har-to-operation.d.ts.map +1 -1
  59. package/dist/mutators/operation/helpers/har-to-operation.js +9 -5
  60. package/dist/mutators/operation/helpers/sync-path-parameters.d.ts +2 -2
  61. package/dist/mutators/operation/history.js +2 -2
  62. package/dist/mutators/operation/operation.d.ts.map +1 -1
  63. package/dist/mutators/operation/operation.js +4 -0
  64. package/dist/mutators/operation/parameters.js +1 -1
  65. package/dist/mutators/server.d.ts +3 -3
  66. package/dist/mutators/server.js +1 -1
  67. package/dist/mutators/tag.d.ts.map +1 -1
  68. package/dist/mutators/tag.js +4 -0
  69. package/dist/navigation/helpers/get-openapi-object.d.ts +1 -1
  70. package/dist/navigation/helpers/traverse-asyncapi-document.d.ts.map +1 -1
  71. package/dist/navigation/helpers/traverse-asyncapi-document.js +2 -2
  72. package/dist/navigation/helpers/traverse-description.d.ts +1 -1
  73. package/dist/navigation/helpers/traverse-document.d.ts +1 -1
  74. package/dist/navigation/helpers/traverse-examples.d.ts +12 -1
  75. package/dist/navigation/helpers/traverse-examples.d.ts.map +1 -1
  76. package/dist/navigation/helpers/traverse-examples.js +22 -1
  77. package/dist/navigation/helpers/traverse-paths.d.ts +1 -1
  78. package/dist/navigation/helpers/traverse-schemas.d.ts +1 -1
  79. package/dist/navigation/helpers/traverse-tags.d.ts +1 -1
  80. package/dist/navigation/helpers/traverse-webhooks.d.ts +1 -1
  81. package/dist/navigation/helpers/update-order-ids.d.ts +2 -2
  82. package/dist/navigation/index.d.ts +1 -0
  83. package/dist/navigation/index.d.ts.map +1 -1
  84. package/dist/navigation/index.js +1 -0
  85. package/dist/navigation/types.d.ts +1 -1
  86. package/dist/persistence/index.d.ts.map +1 -1
  87. package/dist/persistence/index.js +10 -10
  88. package/dist/persistence/indexdb.d.ts.map +1 -1
  89. package/dist/persistence/indexdb.js +7 -5
  90. package/dist/persistence/migrations/v2-team-to-local.d.ts.map +1 -1
  91. package/dist/persistence/migrations/v2-team-to-local.js +3 -4
  92. package/dist/plugins/bundler/index.d.ts +5 -2
  93. package/dist/plugins/bundler/index.d.ts.map +1 -1
  94. package/dist/plugins/bundler/index.js +24 -26
  95. package/dist/request-example/builder/body/build-request-body.d.ts +2 -1
  96. package/dist/request-example/builder/body/build-request-body.d.ts.map +1 -1
  97. package/dist/request-example/builder/body/build-request-body.js +65 -4
  98. package/dist/request-example/builder/body/encode-multipart-body.d.ts +9 -0
  99. package/dist/request-example/builder/body/encode-multipart-body.d.ts.map +1 -0
  100. package/dist/request-example/builder/body/encode-multipart-body.js +29 -0
  101. package/dist/request-example/builder/body/get-request-body-example.d.ts +1 -1
  102. package/dist/request-example/builder/body/get-selected-body-content-type.d.ts +1 -1
  103. package/dist/request-example/builder/body/schema-value-coercion.d.ts +1 -1
  104. package/dist/request-example/builder/body/schema-value-coercion.js +1 -1
  105. package/dist/request-example/builder/body/serialize-form-property.d.ts +1 -1
  106. package/dist/request-example/builder/body/serialize-multipart-array.d.ts +16 -0
  107. package/dist/request-example/builder/body/serialize-multipart-array.d.ts.map +1 -0
  108. package/dist/request-example/builder/body/serialize-multipart-array.js +37 -0
  109. package/dist/request-example/builder/build-request.d.ts.map +1 -1
  110. package/dist/request-example/builder/build-request.js +12 -7
  111. package/dist/request-example/builder/header/build-request-parameters.d.ts +1 -1
  112. package/dist/request-example/builder/header/build-request-parameters.d.ts.map +1 -1
  113. package/dist/request-example/builder/header/build-request-parameters.js +7 -1
  114. package/dist/request-example/builder/header/de-serialize-parameter.d.ts +1 -1
  115. package/dist/request-example/builder/header/de-serialize-parameter.d.ts.map +1 -1
  116. package/dist/request-example/builder/header/de-serialize-parameter.js +17 -6
  117. package/dist/request-example/builder/header/is-param-disabled.d.ts +1 -1
  118. package/dist/request-example/builder/header/serialize-parameter.d.ts.map +1 -1
  119. package/dist/request-example/builder/header/serialize-parameter.js +13 -14
  120. package/dist/request-example/builder/helpers/get-example-from-schema.d.ts +9 -1
  121. package/dist/request-example/builder/helpers/get-example-from-schema.d.ts.map +1 -1
  122. package/dist/request-example/builder/helpers/get-example-from-schema.js +255 -20
  123. package/dist/request-example/builder/helpers/get-example.d.ts +1 -1
  124. package/dist/request-example/builder/helpers/get-example.js +2 -2
  125. package/dist/request-example/builder/helpers/get-resolved-url.d.ts +1 -1
  126. package/dist/request-example/builder/helpers/get-server-variables.d.ts +1 -1
  127. package/dist/request-example/builder/index.d.ts +1 -0
  128. package/dist/request-example/builder/index.d.ts.map +1 -1
  129. package/dist/request-example/builder/index.js +1 -0
  130. package/dist/request-example/builder/request-factory.d.ts +2 -2
  131. package/dist/request-example/builder/security/broker-scheme-types.d.ts.map +1 -1
  132. package/dist/request-example/builder/security/broker-scheme-types.js +2 -2
  133. package/dist/request-example/builder/security/build-request-security.d.ts.map +1 -1
  134. package/dist/request-example/builder/security/build-request-security.js +3 -4
  135. package/dist/request-example/builder/security/secret-types.d.ts +13 -4
  136. package/dist/request-example/builder/security/secret-types.d.ts.map +1 -1
  137. package/dist/request-example/context/get-request-example-context.d.ts +3 -3
  138. package/dist/request-example/context/get-request-example-context.js +1 -1
  139. package/dist/request-example/context/headers.d.ts +1 -1
  140. package/dist/request-example/context/headers.js +1 -1
  141. package/dist/request-example/context/helpers/combine-params.d.ts +1 -1
  142. package/dist/request-example/context/security/extract-security-scheme-secrets.d.ts +16 -3
  143. package/dist/request-example/context/security/extract-security-scheme-secrets.d.ts.map +1 -1
  144. package/dist/request-example/context/security/extract-security-scheme-secrets.js +21 -19
  145. package/dist/request-example/context/security/get-security-requirements.d.ts +2 -2
  146. package/dist/request-example/context/security/get-security-schemes.d.ts +1 -1
  147. package/dist/request-example/context/security/get-selected-security.d.ts +1 -1
  148. package/dist/request-example/context/security/is-auth-optional.d.ts +1 -1
  149. package/dist/request-example/context/security/merge-security.d.ts +3 -3
  150. package/dist/request-example/context/security/merge-security.d.ts.map +1 -1
  151. package/dist/request-example/context/security/merge-security.js +40 -11
  152. package/dist/request-example/context/servers.d.ts +2 -2
  153. package/dist/request-example/functions.d.ts.map +1 -1
  154. package/dist/request-example/index.d.ts +1 -1
  155. package/dist/request-example/index.d.ts.map +1 -1
  156. package/dist/request-example/index.js +1 -1
  157. package/dist/resolve.d.ts +1 -1
  158. package/dist/resolve.js +1 -1
  159. package/dist/schemas/extensions/document/x-scalar-default-request-body-view.d.ts +23 -0
  160. package/dist/schemas/extensions/document/x-scalar-default-request-body-view.d.ts.map +1 -0
  161. package/dist/schemas/extensions/document/x-scalar-default-request-body-view.js +23 -0
  162. package/dist/schemas/navigation.d.ts +4 -4
  163. package/dist/schemas/navigation.js +1 -1
  164. package/dist/schemas/reference-config/index.d.ts +94 -4
  165. package/dist/schemas/reference-config/index.d.ts.map +1 -1
  166. package/dist/schemas/reference-config/settings.d.ts +95 -5
  167. package/dist/schemas/reference-config/settings.d.ts.map +1 -1
  168. package/dist/schemas/reference-config/settings.js +1 -1
  169. package/dist/schemas/type-guards.d.ts +1 -1
  170. package/dist/schemas/v3.1/openapi/index.d.ts +2 -0
  171. package/dist/schemas/v3.1/openapi/index.d.ts.map +1 -1
  172. package/dist/schemas/v3.1/openapi/index.js +13 -1
  173. package/dist/schemas/v3.1/strict/openapi-document.d.ts +284 -1
  174. package/dist/schemas/v3.1/strict/openapi-document.d.ts.map +1 -1
  175. package/dist/schemas/v3.1/strict/openapi-document.js +2 -1
  176. package/dist/schemas/v3.1/strict/security-scheme.d.ts +13 -1
  177. package/dist/schemas/v3.1/strict/security-scheme.d.ts.map +1 -1
  178. package/dist/schemas/v3.1/strict/security-scheme.js +11 -1
  179. package/dist/schemas/v3.2/openapi/index.d.ts +2 -0
  180. package/dist/schemas/v3.2/openapi/index.d.ts.map +1 -1
  181. package/dist/schemas/v3.2/openapi/index.js +13 -1
  182. package/dist/schemas/v3.2/strict/oauth-flow.d.ts +23 -1
  183. package/dist/schemas/v3.2/strict/oauth-flow.d.ts.map +1 -1
  184. package/dist/schemas/v3.2/strict/oauth-flow.js +6 -1
  185. package/dist/schemas/v3.2/strict/oauthflows.d.ts +20 -0
  186. package/dist/schemas/v3.2/strict/oauthflows.d.ts.map +1 -1
  187. package/dist/schemas/v3.2/strict/openapi-document.d.ts +1439 -141
  188. package/dist/schemas/v3.2/strict/openapi-document.d.ts.map +1 -1
  189. package/dist/schemas/v3.2/strict/openapi-document.js +2 -1
  190. package/dist/schemas/v3.2/strict/security-scheme.d.ts +29 -6
  191. package/dist/schemas/v3.2/strict/security-scheme.d.ts.map +1 -1
  192. package/dist/schemas/v3.2/strict/security-scheme.js +16 -3
  193. package/dist/schemas/workspace-specification/index.d.ts +1 -1
  194. package/dist/schemas/workspace.d.ts +1 -1
  195. package/dist/server.d.ts +1 -1
  196. package/dist/server.d.ts.map +1 -1
  197. package/dist/server.js +23 -20
  198. package/package.json +9 -9
@@ -5,6 +5,8 @@
5
5
  */
6
6
  import { HTTP_METHODS } from '@scalar/helpers/http/http-methods';
7
7
  import { isObject } from '@scalar/helpers/object/is-object';
8
+ import { isSchemaPath } from '@scalar/helpers/openapi/is-schema-path';
9
+ import { resolveReferencePath } from '@scalar/json-magic/bundle';
8
10
  import { isLocalRef } from '../../helpers/general.js';
9
11
  import { syncParametersForPathChange, } from '../../mutators/operation/helpers/sync-path-parameters.js';
10
12
  import { getResolvedRef } from '../../plugins/bundler/helpers.js';
@@ -49,15 +51,18 @@ export const externalValueResolver = () => {
49
51
  if (typeof externalValue !== 'string') {
50
52
  return;
51
53
  }
52
- const loader = context.loaders.find((it) => it.validate(externalValue));
54
+ // `externalValue` may be relative (for example `/examples/pet.json`). Resolve it against the
55
+ // origin of the document it lives in so it becomes an absolute URL a loader can fetch.
56
+ const resolvedValue = resolveReferencePath(context.origin, externalValue);
57
+ const loader = context.loaders.find((it) => it.validate(resolvedValue));
53
58
  // We can not process the external value
54
59
  if (!loader) {
55
60
  return;
56
61
  }
57
- if (!cache.has(externalValue)) {
58
- cache.set(externalValue, loader.exec(externalValue));
62
+ if (!cache.has(resolvedValue)) {
63
+ cache.set(resolvedValue, loader.exec(resolvedValue));
59
64
  }
60
- const result = await cache.get(externalValue);
65
+ const result = await cache.get(resolvedValue);
61
66
  // If fetch is successful, assign the data to the node's 'value' property
62
67
  if (result?.ok) {
63
68
  node['value'] = result.data;
@@ -199,35 +204,28 @@ export const normalizeAuthSchemes = () => {
199
204
  };
200
205
  /**
201
206
  * Lifecycle plugin to normalize $ref nodes:
202
- * Ensures that for any non-schema object containing a $ref, only $ref,
207
+ * Ensures that for any OpenAPI Reference Object containing a $ref, only $ref,
203
208
  * summary, description, and $status properties are preserved.
204
209
  * This keeps $ref references clean and predictable for downstream consumers.
210
+ *
211
+ * Schema Objects are deliberately skipped: in JSON Schema 2020-12 a $ref may
212
+ * carry sibling keywords, so their siblings must not be stripped.
205
213
  */
206
214
  export const normalizeRefs = () => {
207
215
  return {
208
216
  type: 'lifecycle',
209
217
  onBeforeNodeProcess: (node, context) => {
210
- const { path } = context;
211
- // If the node is a $ref and we are not on the schema object, we need to normalize the $ref
212
- if (typeof node['$ref'] === 'string' && !(path[0] === 'components' && path[1] === 'schemas')) {
213
- // Remove any other properties from the node and only keep the '$ref', 'summary', 'description' and '$status'.
214
- // The JSON Schema 2020-12 reference keywords are also kept: a schema-position `$ref` may carry a
215
- // `$defs`/`$dynamicAnchor` binding as a sibling to specialize a generic template (the `Paginated<T>`
216
- // pattern). Such a schema can appear inline anywhere a schema is allowed — for example a response's
217
- // `content.<media>.schema` — not only under `components/schemas`. Dropping these siblings here would
218
- // discard the item-type binding, leaving `$dynamicRef` to resolve to the template's empty fallback and
219
- // rendering an empty array. See https://github.com/scalar/scalar/issues/9414.
220
- const keepProperties = new Set([
221
- '$ref',
222
- 'summary',
223
- 'description',
224
- '$status',
225
- '$id',
226
- '$anchor',
227
- '$dynamicAnchor',
228
- '$dynamicRef',
229
- '$defs',
230
- ]);
218
+ const { path, referencedFromPath } = context;
219
+ const isSchema = isSchemaPath(path) || isSchemaPath(referencedFromPath);
220
+ // Normalization only applies to OpenAPI Reference Objects, where a `$ref` may sit next to nothing but
221
+ // `summary` and `description`. Schema Objects are left untouched: in JSON Schema 2020-12 a `$ref` may
222
+ // legally carry sibling keywords — for example a `$defs`/`$dynamicAnchor` binding that specializes a
223
+ // generic template (the `Paginated<T>` pattern) — and such schemas appear inline, in reusable schemas,
224
+ // and in raw external schema documents. Stripping those siblings would discard the binding and leave
225
+ // `$dynamicRef` resolving to the template's empty fallback. See https://github.com/scalar/scalar/issues/9414.
226
+ if (typeof node['$ref'] === 'string' && !isSchema) {
227
+ // Remove any other properties from the node and only keep the '$ref', 'summary', 'description' and '$status'
228
+ const keepProperties = new Set(['$ref', 'summary', 'description', '$status']);
231
229
  Object.keys(node).forEach((key) => {
232
230
  if (!keepProperties.has(key)) {
233
231
  delete node[key];
@@ -1,10 +1,11 @@
1
- import type { RequestBodyObject } from '@scalar/workspace-store/schemas/v3.1/strict/request-body';
1
+ import type { RequestBodyObject } from '@scalar/workspace-store/schemas/v3.2/strict/request-body';
2
2
  type FormData = {
3
3
  mode: 'formdata';
4
4
  value: ({
5
5
  type: 'text';
6
6
  key: string;
7
7
  value: string;
8
+ contentType?: string;
8
9
  } | {
9
10
  type: 'file';
10
11
  key: string;
@@ -1 +1 @@
1
- {"version":3,"file":"build-request-body.d.ts","sourceRoot":"","sources":["../../../../src/request-example/builder/body/build-request-body.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0DAA0D,CAAA;AAOjG,KAAK,QAAQ,GAAG;IACd,IAAI,EAAE,UAAU,CAAA;IAChB,KAAK,EAAE,CACH;QACE,IAAI,EAAE,MAAM,CAAA;QACZ,GAAG,EAAE,MAAM,CAAA;QACX,KAAK,EAAE,MAAM,CAAA;KACd,GACD;QACE,IAAI,EAAE,MAAM,CAAA;QACZ,GAAG,EAAE,MAAM,CAAA;QACX,KAAK,EAAE,IAAI,CAAA;QACX,WAAW,CAAC,EAAE,MAAM,CAAA;KACrB,GACD;QACE,IAAI,EAAE,MAAM,CAAA;QACZ,GAAG,EAAE,MAAM,CAAA;QACX,KAAK,EAAE,IAAI,CAAA;QACX,WAAW,CAAC,EAAE,MAAM,CAAA;KACrB,CACJ,EAAE,CAAA;CACJ,CAAA;AAED,KAAK,UAAU,GAAG;IAChB,IAAI,EAAE,YAAY,CAAA;IAClB,KAAK,EAAE;QACL,GAAG,EAAE,MAAM,CAAA;QACX,KAAK,EAAE,MAAM,CAAA;KACd,EAAE,CAAA;CACJ,CAAA;AAED,KAAK,GAAG,GAAG;IACT,IAAI,EAAE,KAAK,CAAA;IACX,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAAA;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,CAAA;AAED,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,UAAU,GAAG,GAAG,CAAA;AAKrD;;GAEG;AACH,eAAO,MAAM,gBAAgB,GAC3B,aAAa,iBAAiB,GAAG,SAAS;AAC1C,qCAAqC;AACrC,oBAAuB;AACvB,sEAAsE;AACtE,kCAAkC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KACvD,WAAW,GAAG,IA2RhB,CAAA"}
1
+ {"version":3,"file":"build-request-body.d.ts","sourceRoot":"","sources":["../../../../src/request-example/builder/body/build-request-body.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0DAA0D,CAAA;AASjG,KAAK,QAAQ,GAAG;IACd,IAAI,EAAE,UAAU,CAAA;IAChB,KAAK,EAAE,CACH;QACE,IAAI,EAAE,MAAM,CAAA;QACZ,GAAG,EAAE,MAAM,CAAA;QACX,KAAK,EAAE,MAAM,CAAA;QACb,WAAW,CAAC,EAAE,MAAM,CAAA;KACrB,GACD;QACE,IAAI,EAAE,MAAM,CAAA;QACZ,GAAG,EAAE,MAAM,CAAA;QACX,KAAK,EAAE,IAAI,CAAA;QACX,WAAW,CAAC,EAAE,MAAM,CAAA;KACrB,GACD;QACE,IAAI,EAAE,MAAM,CAAA;QACZ,GAAG,EAAE,MAAM,CAAA;QACX,KAAK,EAAE,IAAI,CAAA;QACX,WAAW,CAAC,EAAE,MAAM,CAAA;KACrB,CACJ,EAAE,CAAA;CACJ,CAAA;AAwBD,KAAK,UAAU,GAAG;IAChB,IAAI,EAAE,YAAY,CAAA;IAClB,KAAK,EAAE;QACL,GAAG,EAAE,MAAM,CAAA;QACX,KAAK,EAAE,MAAM,CAAA;KACd,EAAE,CAAA;CACJ,CAAA;AAED,KAAK,GAAG,GAAG;IACT,IAAI,EAAE,KAAK,CAAA;IACX,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAAA;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,CAAA;AAED,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,UAAU,GAAG,GAAG,CAAA;AAKrD;;GAEG;AACH,eAAO,MAAM,gBAAgB,GAC3B,aAAa,iBAAiB,GAAG,SAAS;AAC1C,qCAAqC;AACrC,oBAAuB;AACvB,sEAAsE;AACtE,kCAAkC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KACvD,WAAW,GAAG,IAuUhB,CAAA"}
@@ -3,10 +3,32 @@ import { isObject } from '@scalar/helpers/object/is-object';
3
3
  import { setValueAtPath } from '@scalar/helpers/object/set-value-at-path';
4
4
  import { getResolvedRef, mergeSiblingReferences } from '@scalar/workspace-store/helpers/get-resolved-ref';
5
5
  import { unpackProxyObject } from '@scalar/workspace-store/helpers/unpack-proxy';
6
+ import { isObjectSchema } from '@scalar/workspace-store/schemas/v3.2/strict/type-guards';
6
7
  import { getExampleFromBody } from './get-request-body-example.js';
7
8
  import { getSelectedBodyContentType } from './get-selected-body-content-type.js';
8
9
  import { buildDottedNestedRowPredicate, coerceLeafValueToSchemaType, resolveLeafSchema } from './schema-value-coercion.js';
9
10
  import { serializeFormPropertyWithEncoding } from './serialize-form-property.js';
11
+ import { serializeMultipartArray } from './serialize-multipart-array.js';
12
+ /** Preserve per-item media types while keeping uploaded file bytes and names intact. */
13
+ const toMultipartPart = (part) => {
14
+ if (part.value instanceof File) {
15
+ const file = part.value;
16
+ return {
17
+ type: 'file',
18
+ key: part.key,
19
+ value: part.contentType && part.contentType !== file.type
20
+ ? new File([file], file.name, { type: part.contentType, lastModified: file.lastModified })
21
+ : file,
22
+ contentType: part.contentType,
23
+ };
24
+ }
25
+ return {
26
+ type: 'text',
27
+ key: part.key,
28
+ value: part.value,
29
+ ...(part.contentType ? { contentType: part.contentType } : {}),
30
+ };
31
+ };
10
32
  const getMultipartEncodingContentType = (requestBody, bodyContentType, fieldName) => requestBody.content[bodyContentType]?.encoding?.[fieldName]?.contentType;
11
33
  /**
12
34
  * Create the fetch request body
@@ -29,6 +51,22 @@ requestBodyCompositionSelection) => {
29
51
  if (!example) {
30
52
  return null;
31
53
  }
54
+ // Optional body properties default to "not sent", matching how optional parameters are
55
+ // dropped by `isParamDisabled`. We only know a property is optional when the body has an
56
+ // object schema that declares it outside `required`, so undeclared and required keys are
57
+ // always kept. This mirrors the unchecked-by-default checkbox in the Test Request panel.
58
+ // The array (edited) form path is unaffected: it carries its own per-row `isDisabled`.
59
+ const resolvedBodySchema = getResolvedRef(requestBody.content[bodyContentType]?.schema, mergeSiblingReferences);
60
+ const objectBodySchema = resolvedBodySchema && isObjectSchema(resolvedBodySchema) ? resolvedBodySchema : undefined;
61
+ // Composition (allOf/oneOf/anyOf) can mark a property required inside a subschema we do not
62
+ // merge here, so skip dropping entirely for composed schemas rather than risk removing an
63
+ // effectively-required field.
64
+ const isComposed = Boolean(objectBodySchema?.allOf || objectBodySchema?.oneOf || objectBodySchema?.anyOf);
65
+ const bodyProperties = objectBodySchema && !isComposed ? objectBodySchema.properties : undefined;
66
+ const requiredBodyProperties = new Set(objectBodySchema && !isComposed ? (objectBodySchema.required ?? []) : []);
67
+ // `Object.hasOwn` (not `in`) so an undeclared key named like an `Object.prototype` member
68
+ // (`toString`, `constructor`, …) is not misread as declared-and-optional and dropped.
69
+ const isOptionalBodyProperty = (key) => Boolean(bodyProperties && Object.hasOwn(bodyProperties, key) && !requiredBodyProperties.has(key));
32
70
  // Form data - array format (from UI editor)
33
71
  if ((bodyContentType === 'multipart/form-data' || bodyContentType === 'application/x-www-form-urlencoded') &&
34
72
  Array.isArray(example.value)) {
@@ -86,6 +124,16 @@ requestBodyCompositionSelection) => {
86
124
  return;
87
125
  }
88
126
  const partEncoding = requestBody.content[bodyContentType]?.encoding?.[name];
127
+ if (result.mode === 'formdata') {
128
+ // Older saved form rows store JSON text. Only restore arrays declared by the schema.
129
+ const schema = resolveLeafSchema(multipartSchema, [name]);
130
+ const restored = coerceLeafValueToSchemaType(value, schema);
131
+ const arrayParts = serializeMultipartArray(name, Array.isArray(restored) ? restored : value, partEncoding);
132
+ if (arrayParts) {
133
+ result.value.push(...arrayParts.map(toMultipartPart));
134
+ return;
135
+ }
136
+ }
89
137
  // When the encoding sets style/explode, serialize objects/arrays RFC6570-style
90
138
  // (bracket or exploded notation) instead of JSON, so the wire request matches the
91
139
  // generated code snippet.
@@ -122,9 +170,9 @@ requestBodyCompositionSelection) => {
122
170
  const serializedValue = typeof value === 'object' && value !== null ? JSON.stringify(unpackProxyObject(value)) : String(value);
123
171
  if (result.mode === 'formdata' && partContentType) {
124
172
  return result.value.push({
125
- type: 'blob',
173
+ type: 'text',
126
174
  key: name,
127
- value: new Blob([serializedValue], { type: partContentType }),
175
+ value: serializedValue,
128
176
  contentType: partContentType,
129
177
  });
130
178
  }
@@ -148,6 +196,10 @@ requestBodyCompositionSelection) => {
148
196
  };
149
197
  // Convert object properties to form fields
150
198
  for (const [key, value] of Object.entries(example.value)) {
199
+ // Optional properties are left out unless the user enabled them (edited form path).
200
+ if (isOptionalBodyProperty(key)) {
201
+ continue;
202
+ }
151
203
  if (key && value !== undefined && value !== null) {
152
204
  const partEncoding = requestBody.content[bodyContentType]?.encoding?.[key];
153
205
  // Encoding style/explode turns objects into bracket or exploded notation.
@@ -177,7 +229,16 @@ requestBodyCompositionSelection) => {
177
229
  if (!key || value === undefined || value === null) {
178
230
  continue;
179
231
  }
232
+ // Optional properties are left out unless the user enabled them (edited form path).
233
+ if (isOptionalBodyProperty(key)) {
234
+ continue;
235
+ }
180
236
  const partEncoding = requestBody.content[bodyContentType]?.encoding?.[key];
237
+ const arrayParts = serializeMultipartArray(key, value, partEncoding);
238
+ if (arrayParts) {
239
+ result.value.push(...arrayParts.map(toMultipartPart));
240
+ continue;
241
+ }
181
242
  // Encoding style/explode turns objects into bracket or exploded notation instead of
182
243
  // the default single JSON part.
183
244
  const styleParts = serializeFormPropertyWithEncoding(key, value, partEncoding);
@@ -207,9 +268,9 @@ requestBodyCompositionSelection) => {
207
268
  const serializedValue = typeof value === 'object' && value !== null ? JSON.stringify(unpackProxyObject(value)) : String(value);
208
269
  if (partContentType) {
209
270
  result.value.push({
210
- type: 'blob',
271
+ type: 'text',
211
272
  key,
212
- value: new Blob([serializedValue], { type: partContentType }),
273
+ value: serializedValue,
213
274
  contentType: partContentType,
214
275
  });
215
276
  continue;
@@ -0,0 +1,9 @@
1
+ import type { RequestBody } from './build-request-body.js';
2
+ /**
3
+ * Encode typed text fields without turning them into file uploads.
4
+ * Blob parts retain binary bytes and keep request construction synchronous.
5
+ */
6
+ export declare const encodeMultipartBody: (parts: Extract<RequestBody, {
7
+ mode: "formdata";
8
+ }>["value"]) => Blob;
9
+ //# sourceMappingURL=encode-multipart-body.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"encode-multipart-body.d.ts","sourceRoot":"","sources":["../../../../src/request-example/builder/body/encode-multipart-body.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAA;AAUvD;;;GAGG;AACH,eAAO,MAAM,mBAAmB,GAAI,OAAO,OAAO,CAAC,WAAW,EAAE;IAAE,IAAI,EAAE,UAAU,CAAA;CAAE,CAAC,CAAC,OAAO,CAAC,KAAG,IAqBhG,CAAA"}
@@ -0,0 +1,29 @@
1
+ /** Escape disposition parameters using the same percent escapes as browser form submissions. */
2
+ const escapeParameter = (value) => value
3
+ .replace(/\r\n|\r|\n/g, '\r\n')
4
+ .replace(/\r/g, '%0D')
5
+ .replace(/\n/g, '%0A')
6
+ .replace(/"/g, '%22');
7
+ /**
8
+ * Encode typed text fields without turning them into file uploads.
9
+ * Blob parts retain binary bytes and keep request construction synchronous.
10
+ */
11
+ export const encodeMultipartBody = (parts) => {
12
+ // getRandomValues also works on HTTP playgrounds, where randomUUID is unavailable.
13
+ const random = crypto.getRandomValues(new Uint8Array(24));
14
+ const boundary = `----scalar-${Array.from(random, (byte) => byte.toString(16).padStart(2, '0')).join('')}`;
15
+ const chunks = parts.flatMap((part) => {
16
+ const filename = part.type === 'file' ? part.value.name : part.type === 'blob' ? 'blob' : undefined;
17
+ const contentType = part.contentType ?? (part.type === 'text' ? undefined : part.value.type || 'application/octet-stream');
18
+ // A media type comes from the API description and must never inject part headers.
19
+ if (contentType && /[\r\n\0]/.test(contentType)) {
20
+ throw new Error('Invalid multipart content type');
21
+ }
22
+ const disposition = `Content-Disposition: form-data; name="${escapeParameter(part.key)}"${filename === undefined ? '' : `; filename="${escapeParameter(filename)}"`}`;
23
+ const headers = `--${boundary}\r\n${disposition}\r\n${contentType ? `Content-Type: ${contentType}\r\n` : ''}\r\n`;
24
+ // Match native FormData line endings for ordinary text; explicitly typed data keeps its bytes.
25
+ const value = part.type === 'text' && !part.contentType ? part.value.replace(/\r\n|\r|\n/g, '\r\n') : part.value;
26
+ return [headers, value, '\r\n'];
27
+ });
28
+ return new Blob([...chunks, `--${boundary}--\r\n`], { type: `multipart/form-data; boundary=${boundary}` });
29
+ };
@@ -1,4 +1,4 @@
1
- import type { ExampleObject, RequestBodyObject } from '@scalar/workspace-store/schemas/v3.1/strict/openapi-document';
1
+ import type { ExampleObject, RequestBodyObject } from '@scalar/workspace-store/schemas/v3.2/strict/openapi-document';
2
2
  /**
3
3
  * Generate a write-mode example directly from a request body's schema, ignoring any stored example.
4
4
  *
@@ -1,4 +1,4 @@
1
- import type { RequestBodyObject } from '@scalar/workspace-store/schemas/v3.1/strict/request-body';
1
+ import type { RequestBodyObject } from '@scalar/workspace-store/schemas/v3.2/strict/request-body';
2
2
  /**
3
3
  * Returns the selected body content type for the given request body and exampleKey.
4
4
  * Priority:
@@ -1,4 +1,4 @@
1
- import type { SchemaObject } from '@scalar/workspace-store/schemas/v3.1/strict/openapi-document';
1
+ import type { SchemaObject } from '@scalar/workspace-store/schemas/v3.2/strict/openapi-document';
2
2
  /**
3
3
  * Walk an object schema along a dotted-row path and return the resolved leaf schema,
4
4
  * or undefined when any segment is not a declared object property.
@@ -1,5 +1,5 @@
1
1
  import { getResolvedRef, mergeSiblingReferences } from '@scalar/workspace-store/helpers/get-resolved-ref';
2
- import { isObjectSchema } from '@scalar/workspace-store/schemas/v3.1/strict/type-guards';
2
+ import { isObjectSchema } from '@scalar/workspace-store/schemas/v3.2/strict/type-guards';
3
3
  /** Normalize a schema's `type` (string | string[] | absent) into a plain string array. */
4
4
  const normalizeSchemaTypes = (schema) => {
5
5
  const type = 'type' in schema ? schema.type : undefined;
@@ -1,4 +1,4 @@
1
- import type { EncodingObject } from '@scalar/workspace-store/schemas/v3.1/strict/openapi-document';
1
+ import type { EncodingObject } from '@scalar/workspace-store/schemas/v3.2/strict/openapi-document';
2
2
  /** A single serialized form field, ready to become a multipart part or a urlencoded pair. */
3
3
  export type SerializedFormProperty = {
4
4
  key: string;
@@ -0,0 +1,16 @@
1
+ import type { EncodingObject } from '@scalar/workspace-store/schemas/v3.1/strict/openapi-document';
2
+ /** A multipart array item shared by the request builder and code snippets. */
3
+ export type MultipartArrayPart = {
4
+ key: string;
5
+ value: string | File;
6
+ contentType?: string;
7
+ };
8
+ /**
9
+ * Named multipart arrays produce one part per item, using the same property name.
10
+ * Encoding applies to each item, including its content type. Without an explicit
11
+ * style, nested arrays remain individual JSON values instead of expanding again.
12
+ *
13
+ * @see https://spec.openapis.org/oas/v3.2.0.html#encoding-by-name
14
+ */
15
+ export declare const serializeMultipartArray: (key: string, value: unknown, encoding?: EncodingObject) => MultipartArrayPart[] | null;
16
+ //# sourceMappingURL=serialize-multipart-array.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serialize-multipart-array.d.ts","sourceRoot":"","sources":["../../../../src/request-example/builder/body/serialize-multipart-array.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8DAA8D,CAAA;AAIlG,8EAA8E;AAC9E,MAAM,MAAM,kBAAkB,GAAG;IAC/B,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,uBAAuB,GAClC,KAAK,MAAM,EACX,OAAO,OAAO,EACd,WAAW,cAAc,KACxB,kBAAkB,EAAE,GAAG,IA+BzB,CAAA"}
@@ -0,0 +1,37 @@
1
+ import { parseMimeType } from '@scalar/helpers/http/mime-type';
2
+ import { unpackProxyObject } from '@scalar/workspace-store/helpers/unpack-proxy';
3
+ import { serializeFormPropertyWithEncoding } from './serialize-form-property.js';
4
+ /**
5
+ * Named multipart arrays produce one part per item, using the same property name.
6
+ * Encoding applies to each item, including its content type. Without an explicit
7
+ * style, nested arrays remain individual JSON values instead of expanding again.
8
+ *
9
+ * @see https://spec.openapis.org/oas/v3.2.0.html#encoding-by-name
10
+ */
11
+ export const serializeMultipartArray = (key, value, encoding) => {
12
+ if (!Array.isArray(value)) {
13
+ return null;
14
+ }
15
+ const hasStyle = encoding?.style !== undefined || encoding?.explode !== undefined || encoding?.allowReserved !== undefined;
16
+ const contentType = hasStyle ? undefined : encoding?.contentType;
17
+ return value.flatMap((item) => {
18
+ const parts = serializeFormPropertyWithEncoding(key, item, encoding);
19
+ if (parts) {
20
+ return parts;
21
+ }
22
+ if (item instanceof File) {
23
+ return [{ key, value: unpackProxyObject(item), ...(contentType ? { contentType } : {}) }];
24
+ }
25
+ const structured = typeof item === 'object' && item !== null;
26
+ const itemContentType = contentType ?? (!hasStyle && structured ? 'application/json' : undefined);
27
+ const subtype = itemContentType ? parseMimeType(itemContentType).subtype : undefined;
28
+ const isJson = subtype === 'json' || subtype?.endsWith('+json');
29
+ return [
30
+ {
31
+ key,
32
+ value: structured || isJson ? JSON.stringify(unpackProxyObject(item)) : String(item),
33
+ ...(itemContentType ? { contentType: itemContentType } : {}),
34
+ },
35
+ ];
36
+ });
37
+ };
@@ -1 +1 @@
1
- {"version":3,"file":"build-request.d.ts","sourceRoot":"","sources":["../../../src/request-example/builder/build-request.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,MAAM,EAAW,MAAM,8BAA8B,CAAA;AAOnE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2CAA2C,CAAA;AAC/E,OAAO,EACL,KAAK,6BAA6B,EAEnC,MAAM,uDAAuD,CAAA;AAK9D;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAAA;AAuClD;;;;;;;GAOG;AACH,eAAO,MAAM,2BAA2B,GACtC,SAAS,cAAc,EACvB,cAAc,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EACpC,iBAAiB;IAAE,6BAA6B,CAAC,EAAE,OAAO,CAAA;CAAE,KAC3D,MAyBF,CAAA;AAED;;;;;KAKK;AACL,MAAM,MAAM,gBAAgB,GAAG;IAC7B,kGAAkG;IAClG,cAAc,EAAE,cAAc,CAAA;IAC9B,2BAA2B;IAC3B,UAAU,EAAE,eAAe,CAAA;IAC3B,0DAA0D;IAC1D,YAAY,EAAE,OAAO,CAAA;CACtB,CAAA;AAED,wGAAwG;AACxG,eAAO,MAAM,oBAAoB,EAAG,sBAA+B,CAAA;AAEnE,MAAM,MAAM,uBAAuB,GAAG,6BAA6B,GAAG,OAAO,oBAAoB,CAAA;AAEjG,MAAM,MAAM,kBAAkB,GAAG,MAAM,CAAC,gBAAgB,EAAE,uBAAuB,CAAC,CAAA;AAElF,eAAO,MAAM,YAAY,GACvB,SAAS,cAAc,EACvB,SAAS;IACP,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACpC;;;OAGG;IACH,6BAA6B,CAAC,EAAE,OAAO,CAAA;CACxC,KACA,kBAMF,CAAA"}
1
+ {"version":3,"file":"build-request.d.ts","sourceRoot":"","sources":["../../../src/request-example/builder/build-request.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,MAAM,EAAW,MAAM,8BAA8B,CAAA;AAQnE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2CAA2C,CAAA;AAC/E,OAAO,EACL,KAAK,6BAA6B,EAEnC,MAAM,uDAAuD,CAAA;AAK9D;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAAA;AAuClD;;;;;;;GAOG;AACH,eAAO,MAAM,2BAA2B,GACtC,SAAS,cAAc,EACvB,cAAc,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EACpC,iBAAiB;IAAE,6BAA6B,CAAC,EAAE,OAAO,CAAA;CAAE,KAC3D,MAyBF,CAAA;AAED;;;;;KAKK;AACL,MAAM,MAAM,gBAAgB,GAAG;IAC7B,kGAAkG;IAClG,cAAc,EAAE,cAAc,CAAA;IAC9B,2BAA2B;IAC3B,UAAU,EAAE,eAAe,CAAA;IAC3B,0DAA0D;IAC1D,YAAY,EAAE,OAAO,CAAA;CACtB,CAAA;AAED,wGAAwG;AACxG,eAAO,MAAM,oBAAoB,EAAG,sBAA+B,CAAA;AAEnE,MAAM,MAAM,uBAAuB,GAAG,6BAA6B,GAAG,OAAO,oBAAoB,CAAA;AAEjG,MAAM,MAAM,kBAAkB,GAAG,MAAM,CAAC,gBAAgB,EAAE,uBAAuB,CAAC,CAAA;AAElF,eAAO,MAAM,YAAY,GACvB,SAAS,cAAc,EACvB,SAAS;IACP,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACpC;;;OAGG;IACH,6BAA6B,CAAC,EAAE,OAAO,CAAA;CACxC,KACA,kBAMF,CAAA"}
@@ -4,6 +4,7 @@ import { err, ok } from '@scalar/helpers/types/result';
4
4
  import { safeRun } from '@scalar/helpers/types/safe-run';
5
5
  import { redirectToProxy, shouldUseProxy } from '@scalar/helpers/url/redirect-to-proxy';
6
6
  import { encode as encodeBase64 } from 'js-base64';
7
+ import { encodeMultipartBody } from '../../request-example/builder/body/encode-multipart-body.js';
7
8
  import { buildRequestCookieHeader } from '../../request-example/builder/header/build-request-cookie-header.js';
8
9
  import { applyAllowReservedToUrl } from '../../request-example/builder/helpers/apply-allow-reserved-to-url.js';
9
10
  import { resolveRequestFactoryUrl, } from '../../request-example/builder/resolve-request-factory-url.js';
@@ -93,14 +94,18 @@ const buildRequestInner = (request, options) => {
93
94
  return request.body.value;
94
95
  }
95
96
  if (request.body?.mode === 'formdata') {
97
+ const parts = request.body.value.map((item) => ({
98
+ ...item,
99
+ key: replaceEnvVariables(item.key, replace),
100
+ }));
101
+ const resolvedParts = parts.map((item) => item.type === 'text' ? { ...item, value: replaceEnvVariables(item.value, replace) } : item);
102
+ if (request.body.value.some((item) => item.type === 'text' && item.contentType)) {
103
+ const encoded = encodeMultipartBody(resolvedParts);
104
+ headers.set('content-type', encoded.type);
105
+ return encoded;
106
+ }
96
107
  const form = new FormData();
97
- request.body.value.forEach((item) => {
98
- if (item.type === 'text') {
99
- form.append(replaceEnvVariables(item.key, replace), replaceEnvVariables(item.value, replace));
100
- return;
101
- }
102
- form.append(replaceEnvVariables(item.key, replace), item.value);
103
- });
108
+ resolvedParts.forEach((item) => form.append(item.key, item.value));
104
109
  return form;
105
110
  }
106
111
  if (request.body?.mode === 'urlencoded') {
@@ -1,5 +1,5 @@
1
1
  import { type XScalarCookie } from '@scalar/workspace-store/schemas/extensions/general/x-scalar-cookies';
2
- import type { ParameterObject, ReferenceType } from '@scalar/workspace-store/schemas/v3.1/strict/openapi-document';
2
+ import type { ParameterObject, ReferenceType } from '@scalar/workspace-store/schemas/v3.2/strict/openapi-document';
3
3
  /**
4
4
  * Converts the parameters into a set of headers, cookies and url params while
5
5
  * replacing environment variables and extracting example values. Also builds up a record of the path
@@ -1 +1 @@
1
- {"version":3,"file":"build-request-parameters.d.ts","sourceRoot":"","sources":["../../../../src/request-example/builder/header/build-request-parameters.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,KAAK,aAAa,EAEnB,MAAM,qEAAqE,CAAA;AAE5E,OAAO,KAAK,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,8DAA8D,CAAA;AAoBlH;;;;;;;;;GASG;AACH,eAAO,MAAM,sBAAsB;AACjC,qBAAqB;AACrB,aAAY,aAAa,CAAC,eAAe,CAAC,EAAO;AACjD,qCAAqC;AACrC,cAAa,MAAkB,KAC9B;IACD,OAAO,EAAE,aAAa,EAAE,CAAA;IACxB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC/B,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACrC,4BAA4B,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;IACzC,SAAS,EAAE,eAAe,CAAA;CAwF3B,CAAA"}
1
+ {"version":3,"file":"build-request-parameters.d.ts","sourceRoot":"","sources":["../../../../src/request-example/builder/header/build-request-parameters.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,KAAK,aAAa,EAEnB,MAAM,qEAAqE,CAAA;AAE5E,OAAO,KAAK,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,8DAA8D,CAAA;AAoBlH;;;;;;;;;GASG;AACH,eAAO,MAAM,sBAAsB;AACjC,qBAAqB;AACrB,aAAY,aAAa,CAAC,eAAe,CAAC,EAAO;AACjD,qCAAqC;AACrC,cAAa,MAAkB,KAC9B;IACD,OAAO,EAAE,aAAa,EAAE,CAAA;IACxB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC/B,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACrC,4BAA4B,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;IACzC,SAAS,EAAE,eAAe,CAAA;CA6F3B,CAAA"}
@@ -38,6 +38,8 @@ exampleName = 'default') => {
38
38
  // Second pass: process all parameters
39
39
  for (const referencedParam of parameters) {
40
40
  const param = getResolvedRef(referencedParam);
41
+ if (!param)
42
+ continue;
41
43
  const example = getExample(param, exampleName, undefined);
42
44
  // Skip disabled examples
43
45
  if (!example || isParamDisabled(param, example)) {
@@ -80,7 +82,11 @@ exampleName = 'default') => {
80
82
  result.pathVariables[paramName] = String(serialized);
81
83
  break;
82
84
  }
83
- case 'query': {
85
+ // The 3.2 `querystring` location represents the whole query string. Handle it like a
86
+ // regular query parameter so schema-based values still expand into the query string
87
+ // instead of being silently dropped.
88
+ case 'query':
89
+ case 'querystring': {
84
90
  processQueryParameter(param, paramName, deSerializedValue, result.urlParams, result.allowReservedQueryParameters);
85
91
  break;
86
92
  }
@@ -1,4 +1,4 @@
1
- import type { ParameterObject, ParameterWithSchemaObject } from '@scalar/workspace-store/schemas/v3.1/strict/openapi-document';
1
+ import type { ParameterObject, ParameterWithSchemaObject } from '@scalar/workspace-store/schemas/v3.2/strict/openapi-document';
2
2
  /**
3
3
  * Coerces a parameter example from the UI (always a string from CodeInput) into a value
4
4
  * request building can serialize. Only structured types are parsed; primitives stay as strings.
@@ -1 +1 @@
1
- {"version":3,"file":"de-serialize-parameter.d.ts","sourceRoot":"","sources":["../../../../src/request-example/builder/header/de-serialize-parameter.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,eAAe,EACf,yBAAyB,EAC1B,MAAM,8DAA8D,CAAA;AAErE;;;GAGG;AACH,eAAO,MAAM,oBAAoB,GAAI,SAAS,OAAO,EAAE,OAAO,eAAe,QAS5E,CAAA;AA0DD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,sBAAsB,GAAI,SAAS,OAAO,EAAE,QAAQ,yBAAyB,CAAC,QAAQ,CAAC,QAiBnG,CAAA"}
1
+ {"version":3,"file":"de-serialize-parameter.d.ts","sourceRoot":"","sources":["../../../../src/request-example/builder/header/de-serialize-parameter.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,eAAe,EACf,yBAAyB,EAC1B,MAAM,8DAA8D,CAAA;AAErE;;;GAGG;AACH,eAAO,MAAM,oBAAoB,GAAI,SAAS,OAAO,EAAE,OAAO,eAAe,QAS5E,CAAA;AA0DD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,sBAAsB,GAAI,SAAS,OAAO,EAAE,QAAQ,yBAAyB,CAAC,QAAQ,CAAC,QA6BnG,CAAA"}
@@ -1,3 +1,4 @@
1
+ import { isObjectLike } from '@scalar/helpers/object/is-object';
1
2
  import { getResolvedRef } from '@scalar/workspace-store/helpers/get-resolved-ref';
2
3
  /**
3
4
  * Coerces a parameter example from the UI (always a string from CodeInput) into a value
@@ -39,7 +40,7 @@ const structuredSchemaTypes = new Set(['array', 'object']);
39
40
  */
40
41
  const getStructuredType = (schema) => {
41
42
  const resolved = getResolvedRef(schema);
42
- if (!resolved || typeof resolved !== 'object') {
43
+ if (!isObjectLike(resolved)) {
43
44
  return undefined;
44
45
  }
45
46
  if ('type' in resolved && resolved.type) {
@@ -80,13 +81,23 @@ export const deSerializeSchemaValue = (example, schema) => {
80
81
  const type = getStructuredType(schema);
81
82
  if (type) {
82
83
  try {
83
- return JSON.parse(example);
84
+ const parsed = JSON.parse(example);
85
+ if (type !== 'array' || Array.isArray(parsed)) {
86
+ return parsed;
87
+ }
88
+ // A bare numeric string is valid JSON too (e.g. a 20-digit reference number), but parsing it
89
+ // would turn it into a precision-losing JS number instead of the array the schema expects. A
90
+ // quoted string is a deliberate escape hatch, so keep it as a single-item array.
91
+ if (typeof parsed === 'string') {
92
+ return [parsed];
93
+ }
84
94
  }
85
95
  catch {
86
- // Arrays: users often type `foo,bar` instead of JSON — split to match default form+explode query style.
87
- if (type === 'array') {
88
- return example.split(/,\s?/).filter((v) => v !== '');
89
- }
96
+ // Not JSON at all: fall through to the comma-split handling below.
97
+ }
98
+ // Arrays: users often type `foo,bar` instead of JSON — split to match default form+explode query style.
99
+ if (type === 'array') {
100
+ return example.split(/,\s?/).filter((v) => v !== '');
90
101
  }
91
102
  }
92
103
  }
@@ -1,4 +1,4 @@
1
- import type { ExampleObject, ParameterObject } from '@scalar/workspace-store/schemas/v3.1/strict/openapi-document';
1
+ import type { ExampleObject, ParameterObject } from '@scalar/workspace-store/schemas/v3.2/strict/openapi-document';
2
2
  /**
3
3
  * Determines if a parameter is disabled
4
4
  *
@@ -1 +1 @@
1
- {"version":3,"file":"serialize-parameter.d.ts","sourceRoot":"","sources":["../../../../src/request-example/builder/header/serialize-parameter.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;;;;;;GAQG;AACH,eAAO,MAAM,qBAAqB,GAAI,OAAO,OAAO,EAAE,aAAa,MAAM,KAAG,MAa3E,CAAA;AAED;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,oBAAoB,GAAI,OAAO,OAAO,EAAE,SAAS,OAAO,KAAG,OAqBvE,CAAA;AAED;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,kBAAkB,GAC7B,OAAO,OAAO,EACd,SAAS,OAAO,KACf,OAAO,GAAG,KAAK,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,CA4BjD,CAAA;AAED;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,4BAA4B,GACvC,OAAO,OAAO,EACd,SAAS,OAAO,KACf,OAAO,GAAG,KAAK,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,CA2CjD,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,4BAA4B,GAAI,OAAO,OAAO,KAAG,MAe7D,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,2BAA2B,GAAI,OAAO,OAAO,KAAG,MAe5D,CAAA;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,wBAAwB,GAAI,WAAW,MAAM,EAAE,OAAO,OAAO,KAAG,KAAK,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAoChH,CAAA"}
1
+ {"version":3,"file":"serialize-parameter.d.ts","sourceRoot":"","sources":["../../../../src/request-example/builder/header/serialize-parameter.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AAEH;;;;;;;;GAQG;AACH,eAAO,MAAM,qBAAqB,GAAI,OAAO,OAAO,EAAE,aAAa,MAAM,KAAG,MAa3E,CAAA;AAED;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,oBAAoB,GAAI,OAAO,OAAO,EAAE,SAAS,OAAO,KAAG,OAqBvE,CAAA;AAED;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,kBAAkB,GAC7B,OAAO,OAAO,EACd,SAAS,OAAO,KACf,OAAO,GAAG,KAAK,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,CA4BjD,CAAA;AAED;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,4BAA4B,GACvC,OAAO,OAAO,EACd,SAAS,OAAO,KACf,OAAO,GAAG,KAAK,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,CA2CjD,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,4BAA4B,GAAI,OAAO,OAAO,KAAG,MAe7D,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,2BAA2B,GAAI,OAAO,OAAO,KAAG,MAa5D,CAAA;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,wBAAwB,GAAI,WAAW,MAAM,EAAE,OAAO,OAAO,KAAG,KAAK,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAoChH,CAAA"}