@scalar/workspace-store 0.64.0 → 0.66.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 (168) hide show
  1. package/CHANGELOG.md +121 -0
  2. package/README.md +45 -10
  3. package/dist/client.d.ts.map +1 -1
  4. package/dist/client.js +109 -10
  5. package/dist/entities/auth/schema.d.ts +351 -20
  6. package/dist/entities/auth/schema.d.ts.map +1 -1
  7. package/dist/entities/auth/schema.js +3 -1
  8. package/dist/events/definitions/auth.d.ts +1 -2
  9. package/dist/events/definitions/auth.d.ts.map +1 -1
  10. package/dist/events/definitions/operation.d.ts +3 -4
  11. package/dist/events/definitions/operation.d.ts.map +1 -1
  12. package/dist/events/definitions/server.d.ts +1 -2
  13. package/dist/events/definitions/server.d.ts.map +1 -1
  14. package/dist/events/definitions/ui.d.ts +3 -4
  15. package/dist/events/definitions/ui.d.ts.map +1 -1
  16. package/dist/events/old-definitions.d.ts +1 -2
  17. package/dist/events/old-definitions.d.ts.map +1 -1
  18. package/dist/helpers/chunk-index.d.ts +20 -2
  19. package/dist/helpers/chunk-index.d.ts.map +1 -1
  20. package/dist/helpers/chunk-index.js +14 -1
  21. package/dist/helpers/external-examples.d.ts +2 -0
  22. package/dist/helpers/external-examples.d.ts.map +1 -1
  23. package/dist/helpers/external-examples.js +1 -0
  24. package/dist/helpers/for-each-path-item-operation.d.ts +6 -5
  25. package/dist/helpers/for-each-path-item-operation.d.ts.map +1 -1
  26. package/dist/helpers/for-each-path-item-operation.js +40 -14
  27. package/dist/helpers/get-example-value.d.ts +15 -0
  28. package/dist/helpers/get-example-value.d.ts.map +1 -0
  29. package/dist/helpers/get-example-value.js +28 -0
  30. package/dist/helpers/get-parameter-example.d.ts +14 -0
  31. package/dist/helpers/get-parameter-example.d.ts.map +1 -0
  32. package/dist/helpers/get-parameter-example.js +19 -0
  33. package/dist/helpers/get-resolved-ref.d.ts.map +1 -1
  34. package/dist/helpers/get-resolved-ref.js +12 -1
  35. package/dist/helpers/normalize-boolean-schemas.d.ts +10 -0
  36. package/dist/helpers/normalize-boolean-schemas.d.ts.map +1 -0
  37. package/dist/helpers/normalize-boolean-schemas.js +193 -0
  38. package/dist/helpers/operation-examples.d.ts.map +1 -1
  39. package/dist/helpers/operation-examples.js +5 -2
  40. package/dist/helpers/querystring-parameter.d.ts +21 -0
  41. package/dist/helpers/querystring-parameter.d.ts.map +1 -0
  42. package/dist/helpers/querystring-parameter.js +108 -0
  43. package/dist/helpers/serialize-stream-example.d.ts +8 -0
  44. package/dist/helpers/serialize-stream-example.d.ts.map +1 -0
  45. package/dist/helpers/serialize-stream-example.js +52 -0
  46. package/dist/helpers/use-external-examples.d.ts.map +1 -1
  47. package/dist/helpers/use-external-examples.js +8 -1
  48. package/dist/mutators/index.d.ts +3 -3
  49. package/dist/mutators/operation/body.d.ts.map +1 -1
  50. package/dist/mutators/operation/body.js +9 -2
  51. package/dist/mutators/operation/operation.d.ts.map +1 -1
  52. package/dist/mutators/operation/operation.js +4 -7
  53. package/dist/mutators/operation/parameters.d.ts.map +1 -1
  54. package/dist/mutators/operation/parameters.js +43 -3
  55. package/dist/navigation/get-navigation-options.d.ts.map +1 -1
  56. package/dist/navigation/get-navigation-options.js +19 -4
  57. package/dist/navigation/helpers/traverse-paths.d.ts.map +1 -1
  58. package/dist/navigation/helpers/traverse-paths.js +2 -2
  59. package/dist/navigation/helpers/traverse-webhooks.d.ts.map +1 -1
  60. package/dist/navigation/helpers/traverse-webhooks.js +2 -2
  61. package/dist/navigation/helpers/update-order-ids.d.ts +1 -2
  62. package/dist/navigation/helpers/update-order-ids.d.ts.map +1 -1
  63. package/dist/plugins/bundler/index.d.ts +1 -0
  64. package/dist/plugins/bundler/index.d.ts.map +1 -1
  65. package/dist/plugins/bundler/index.js +11 -3
  66. package/dist/plugins/bundler/openapi-document.d.ts +6 -0
  67. package/dist/plugins/bundler/openapi-document.d.ts.map +1 -0
  68. package/dist/plugins/bundler/openapi-document.js +57 -0
  69. package/dist/request-example/builder/body/build-multipart.d.ts +29 -0
  70. package/dist/request-example/builder/body/build-multipart.d.ts.map +1 -0
  71. package/dist/request-example/builder/body/build-multipart.js +191 -0
  72. package/dist/request-example/builder/body/build-request-body.d.ts +6 -1
  73. package/dist/request-example/builder/body/build-request-body.d.ts.map +1 -1
  74. package/dist/request-example/builder/body/build-request-body.js +80 -23
  75. package/dist/request-example/builder/body/encode-multipart-body.d.ts +8 -8
  76. package/dist/request-example/builder/body/encode-multipart-body.d.ts.map +1 -1
  77. package/dist/request-example/builder/body/encode-multipart-body.js +56 -18
  78. package/dist/request-example/builder/body/get-request-body-example.d.ts.map +1 -1
  79. package/dist/request-example/builder/body/get-request-body-example.js +20 -6
  80. package/dist/request-example/builder/body/multipart-limits.d.ts +3 -0
  81. package/dist/request-example/builder/body/multipart-limits.d.ts.map +1 -0
  82. package/dist/request-example/builder/body/multipart-limits.js +2 -0
  83. package/dist/request-example/builder/body/serialize-form-property.d.ts +2 -0
  84. package/dist/request-example/builder/body/serialize-form-property.d.ts.map +1 -1
  85. package/dist/request-example/builder/body/serialize-form-property.js +3 -2
  86. package/dist/request-example/builder/body/serialize-multipart-array.d.ts +1 -1
  87. package/dist/request-example/builder/build-request.d.ts.map +1 -1
  88. package/dist/request-example/builder/build-request.js +7 -1
  89. package/dist/request-example/builder/header/build-request-parameters.d.ts +2 -0
  90. package/dist/request-example/builder/header/build-request-parameters.d.ts.map +1 -1
  91. package/dist/request-example/builder/header/build-request-parameters.js +54 -11
  92. package/dist/request-example/builder/header/is-param-disabled.d.ts.map +1 -1
  93. package/dist/request-example/builder/header/is-param-disabled.js +3 -1
  94. package/dist/request-example/builder/header/serialize-parameter.d.ts +11 -0
  95. package/dist/request-example/builder/header/serialize-parameter.d.ts.map +1 -1
  96. package/dist/request-example/builder/header/serialize-parameter.js +22 -0
  97. package/dist/request-example/builder/helpers/get-example-from-schema.d.ts.map +1 -1
  98. package/dist/request-example/builder/helpers/get-example-from-schema.js +42 -3
  99. package/dist/request-example/builder/helpers/get-example.d.ts.map +1 -1
  100. package/dist/request-example/builder/helpers/get-example.js +5 -2
  101. package/dist/request-example/builder/index.d.ts +2 -2
  102. package/dist/request-example/builder/index.d.ts.map +1 -1
  103. package/dist/request-example/builder/index.js +1 -1
  104. package/dist/request-example/builder/request-factory.d.ts +7 -0
  105. package/dist/request-example/builder/request-factory.d.ts.map +1 -1
  106. package/dist/request-example/builder/request-factory.js +10 -1
  107. package/dist/request-example/builder/resolve-request-factory-url.d.ts.map +1 -1
  108. package/dist/request-example/builder/resolve-request-factory-url.js +18 -2
  109. package/dist/request-example/builder/security/secret-types.d.ts +4 -1
  110. package/dist/request-example/builder/security/secret-types.d.ts.map +1 -1
  111. package/dist/request-example/context/headers.d.ts +1 -2
  112. package/dist/request-example/context/headers.d.ts.map +1 -1
  113. package/dist/request-example/context/security/extract-security-scheme-secrets.d.ts.map +1 -1
  114. package/dist/request-example/context/security/extract-security-scheme-secrets.js +15 -0
  115. package/dist/request-example/index.d.ts +5 -2
  116. package/dist/request-example/index.d.ts.map +1 -1
  117. package/dist/request-example/index.js +4 -1
  118. package/dist/request-example/types.d.ts +1 -2
  119. package/dist/request-example/types.d.ts.map +1 -1
  120. package/dist/request-example/xml/serialize-xml-part.d.ts +9 -0
  121. package/dist/request-example/xml/serialize-xml-part.d.ts.map +1 -0
  122. package/dist/request-example/xml/serialize-xml-part.js +12 -0
  123. package/dist/schemas/extensions/operation/x-code-samples.d.ts +22 -0
  124. package/dist/schemas/extensions/operation/x-code-samples.d.ts.map +1 -1
  125. package/dist/schemas/extensions/operation/x-code-samples.js +4 -0
  126. package/dist/schemas/extensions.d.ts +9 -0
  127. package/dist/schemas/extensions.d.ts.map +1 -1
  128. package/dist/schemas/extensions.js +9 -0
  129. package/dist/schemas/navigation.d.ts +40 -34
  130. package/dist/schemas/navigation.d.ts.map +1 -1
  131. package/dist/schemas/navigation.js +2 -3
  132. package/dist/schemas/reference-config/index.d.ts +24 -4
  133. package/dist/schemas/reference-config/index.d.ts.map +1 -1
  134. package/dist/schemas/reference-config/settings.d.ts +24 -4
  135. package/dist/schemas/reference-config/settings.d.ts.map +1 -1
  136. package/dist/schemas/v3.1/strict/oauthflows.d.ts +26 -0
  137. package/dist/schemas/v3.1/strict/oauthflows.d.ts.map +1 -1
  138. package/dist/schemas/v3.1/strict/oauthflows.js +3 -0
  139. package/dist/schemas/v3.1/strict/openapi-document.d.ts +1582 -147
  140. package/dist/schemas/v3.1/strict/openapi-document.d.ts.map +1 -1
  141. package/dist/schemas/v3.1/strict/operation.d.ts +8 -0
  142. package/dist/schemas/v3.1/strict/operation.d.ts.map +1 -1
  143. package/dist/schemas/v3.1/strict/path-item.d.ts +19 -0
  144. package/dist/schemas/v3.1/strict/path-item.d.ts.map +1 -1
  145. package/dist/schemas/v3.1/strict/path-item.js +5 -0
  146. package/dist/schemas/v3.2/openapi/index.d.ts +5 -0
  147. package/dist/schemas/v3.2/openapi/index.d.ts.map +1 -1
  148. package/dist/schemas/v3.2/openapi/index.js +48 -12
  149. package/dist/schemas/v3.2/openapi/reference.d.ts.map +1 -1
  150. package/dist/schemas/v3.2/openapi/reference.js +11 -3
  151. package/dist/schemas/v3.2/strict/openapi-document.d.ts +842 -140
  152. package/dist/schemas/v3.2/strict/openapi-document.d.ts.map +1 -1
  153. package/dist/schemas/v3.2/strict/openapi-document.js +6 -14
  154. package/dist/schemas/v3.2/strict/operation.d.ts +8 -0
  155. package/dist/schemas/v3.2/strict/operation.d.ts.map +1 -1
  156. package/dist/schemas/v3.2/strict/parameter.d.ts +14 -0
  157. package/dist/schemas/v3.2/strict/parameter.d.ts.map +1 -1
  158. package/dist/schemas/v3.2/strict/parameter.js +2 -0
  159. package/dist/server.d.ts +10 -6
  160. package/dist/server.d.ts.map +1 -1
  161. package/dist/server.js +67 -45
  162. package/package.json +19 -9
  163. package/dist/schemas/v3.1/openapi/index.d.ts +0 -149
  164. package/dist/schemas/v3.1/openapi/index.d.ts.map +0 -1
  165. package/dist/schemas/v3.1/openapi/index.js +0 -732
  166. package/dist/schemas/v3.1/openapi/reference.d.ts +0 -4
  167. package/dist/schemas/v3.1/openapi/reference.d.ts.map +0 -1
  168. package/dist/schemas/v3.1/openapi/reference.js +0 -29
@@ -1,9 +1,12 @@
1
1
  // import { replaceEnvVariables } from '@scalar/helpers/regex/replace-variables'
2
+ import { parseMimeType } from '@scalar/helpers/http/mime-type';
2
3
  import { isObject } from '@scalar/helpers/object/is-object';
3
4
  import { setValueAtPath } from '@scalar/helpers/object/set-value-at-path';
4
5
  import { getResolvedRef, mergeSiblingReferences } from '@scalar/workspace-store/helpers/get-resolved-ref';
5
6
  import { unpackProxyObject } from '@scalar/workspace-store/helpers/unpack-proxy';
6
7
  import { isObjectSchema } from '@scalar/workspace-store/schemas/v3.2/strict/type-guards';
8
+ import { getExampleValue, getExplicitExampleText } from '../../../helpers/get-example-value.js';
9
+ import { buildMultipart, getMultipartItemSchema, isPositionalMultipart, needsMultipartEncoding, } from './build-multipart.js';
7
10
  import { getExampleFromBody } from './get-request-body-example.js';
8
11
  import { getSelectedBodyContentType } from './get-selected-body-content-type.js';
9
12
  import { buildDottedNestedRowPredicate, coerceLeafValueToSchemaType, resolveLeafSchema } from './schema-value-coercion.js';
@@ -30,6 +33,33 @@ const toMultipartPart = (part) => {
30
33
  };
31
34
  };
32
35
  const getMultipartEncodingContentType = (requestBody, bodyContentType, fieldName) => requestBody.content[bodyContentType]?.encoding?.[fieldName]?.contentType;
36
+ /** Preserve schema-backed nested objects and repeated flat fields from form editor rows. */
37
+ const regroupMultipartRows = (rows, multipartSchema) => {
38
+ const isDottedNestedRow = buildDottedNestedRowPredicate(multipartSchema);
39
+ const entries = [];
40
+ const regroupedByTopKey = new Map();
41
+ for (const row of rows) {
42
+ if (!isDottedNestedRow(row.name, row.value)) {
43
+ entries.push(row);
44
+ continue;
45
+ }
46
+ const segments = row.name.split('.');
47
+ const topKey = segments[0];
48
+ if (!topKey) {
49
+ continue;
50
+ }
51
+ let target = regroupedByTopKey.get(topKey);
52
+ if (!target) {
53
+ target = {};
54
+ regroupedByTopKey.set(topKey, target);
55
+ entries.push({ name: topKey, value: target });
56
+ }
57
+ // The form table stringifies leaf values; restore the schema-declared type so the
58
+ // regrouped JSON part keeps booleans/numbers/arrays instead of string-typing them.
59
+ setValueAtPath(target, segments.slice(1), coerceLeafValueToSchemaType(row.value, resolveLeafSchema(multipartSchema, segments)));
60
+ }
61
+ return entries;
62
+ };
33
63
  /**
34
64
  * Create the fetch request body
35
65
  */
@@ -51,6 +81,10 @@ requestBodyCompositionSelection) => {
51
81
  if (!example) {
52
82
  return null;
53
83
  }
84
+ const explicitText = getExplicitExampleText(getExampleValue(example), bodyContentType);
85
+ if (explicitText !== undefined) {
86
+ return { mode: 'raw', value: explicitText, contentType: bodyContentType };
87
+ }
54
88
  // Optional body properties default to "not sent", matching how optional parameters are
55
89
  // dropped by `isParamDisabled`. We only know a property is optional when the body has an
56
90
  // object schema that declares it outside `required`, so undeclared and required keys are
@@ -67,6 +101,51 @@ requestBodyCompositionSelection) => {
67
101
  // `Object.hasOwn` (not `in`) so an undeclared key named like an `Object.prototype` member
68
102
  // (`toString`, `constructor`, …) is not misread as declared-and-optional and dropped.
69
103
  const isOptionalBodyProperty = (key) => Boolean(bodyProperties && Object.hasOwn(bodyProperties, key) && !requiredBodyProperties.has(key));
104
+ const media = requestBody.content[bodyContentType];
105
+ if (media && needsMultipartEncoding(bodyContentType, media)) {
106
+ // Raw editors store JSON text, while form editors store named rows.
107
+ const value = (() => {
108
+ if (typeof example.value !== 'string') {
109
+ return example.value;
110
+ }
111
+ try {
112
+ return JSON.parse(example.value);
113
+ }
114
+ catch {
115
+ return example.value;
116
+ }
117
+ })();
118
+ // Already serialized payloads and uploaded bodies retain their bytes.
119
+ if (typeof value === 'string' || value instanceof Blob) {
120
+ return { mode: 'raw', value, contentType: bodyContentType };
121
+ }
122
+ const positional = isPositionalMultipart(bodyContentType, media);
123
+ const orderedValue = positional && parseMimeType(bodyContentType).essence === 'multipart/form-data' && Array.isArray(value)
124
+ ? value
125
+ .filter((item) => !(isObject(item) && typeof item.name === 'string' && 'value' in item && item.isDisabled))
126
+ .map((item, index) => {
127
+ if (!isObject(item) || typeof item.name !== 'string' || !('value' in item)) {
128
+ return item;
129
+ }
130
+ const itemSchema = getMultipartItemSchema(resolvedBodySchema, index, media.itemSchema);
131
+ return {
132
+ [item.name]: coerceLeafValueToSchemaType(item.value, resolveLeafSchema(itemSchema, [item.name])),
133
+ };
134
+ })
135
+ : value;
136
+ const multipartParts = !positional && Array.isArray(value)
137
+ ? regroupMultipartRows(value.filter((row) => !row.isDisabled), resolvedBodySchema).flatMap((row) => buildMultipart({
138
+ [row.name]: coerceLeafValueToSchemaType(row.value, resolveLeafSchema(resolvedBodySchema, [row.name])),
139
+ }, bodyContentType, media, resolvedBodySchema))
140
+ : buildMultipart(!positional && isObject(value)
141
+ ? Object.fromEntries(Object.entries(value).filter(([key]) => !isOptionalBodyProperty(key)))
142
+ : orderedValue, bodyContentType, media, resolvedBodySchema);
143
+ return {
144
+ mode: 'multipart',
145
+ contentType: bodyContentType,
146
+ value: multipartParts,
147
+ };
148
+ }
70
149
  // Form data - array format (from UI editor)
71
150
  if ((bodyContentType === 'multipart/form-data' || bodyContentType === 'application/x-www-form-urlencoded') &&
72
151
  Array.isArray(example.value)) {
@@ -95,29 +174,7 @@ requestBodyCompositionSelection) => {
95
174
  const multipartSchema = result.mode === 'formdata'
96
175
  ? getResolvedRef(requestBody.content[bodyContentType]?.schema, mergeSiblingReferences)
97
176
  : undefined;
98
- const isDottedNestedRow = result.mode === 'formdata' ? buildDottedNestedRowPredicate(multipartSchema) : () => false;
99
- const entries = [];
100
- const regroupedByTopKey = new Map();
101
- for (const row of exampleValue) {
102
- if (!isDottedNestedRow(row.name, row.value)) {
103
- entries.push(row);
104
- continue;
105
- }
106
- const segments = row.name.split('.');
107
- const topKey = segments[0];
108
- if (!topKey) {
109
- continue;
110
- }
111
- let target = regroupedByTopKey.get(topKey);
112
- if (!target) {
113
- target = {};
114
- regroupedByTopKey.set(topKey, target);
115
- entries.push({ name: topKey, value: target });
116
- }
117
- // The form table stringifies leaf values; restore the schema-declared type so the
118
- // regrouped JSON part keeps booleans/numbers/arrays instead of string-typing them.
119
- setValueAtPath(target, segments.slice(1), coerceLeafValueToSchemaType(row.value, resolveLeafSchema(multipartSchema, segments)));
120
- }
177
+ const entries = result.mode === 'formdata' ? regroupMultipartRows(exampleValue, multipartSchema) : exampleValue;
121
178
  // Loop over all entries and add them to the form
122
179
  entries.forEach(({ name, value }) => {
123
180
  if (!name) {
@@ -1,9 +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;
1
+ import type { MultipartPart } from './build-multipart.js';
2
+ /** Serialize nested multipart bodies for both fetch and synchronous code snippets. */
3
+ export declare const serializeMultipartBody: (parts: MultipartPart[], contentType?: string, replace?: (value: string) => string, nesting?: number) => {
4
+ chunks: BlobPart[];
5
+ contentType: string;
6
+ };
7
+ /** Encode multipart parts without losing binary bytes or assigning filenames to typed text. */
8
+ export declare const encodeMultipartBody: (parts: MultipartPart[], contentType?: string, replace?: (value: string) => string) => Blob;
9
9
  //# sourceMappingURL=encode-multipart-body.d.ts.map
@@ -1 +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"}
1
+ {"version":3,"file":"encode-multipart-body.d.ts","sourceRoot":"","sources":["../../../../src/request-example/builder/body/encode-multipart-body.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AA0BtD,sFAAsF;AACtF,eAAO,MAAM,sBAAsB,GACjC,OAAO,aAAa,EAAE,EACtB,oBAAmC,EACnC,UAAS,CAAC,KAAK,EAAE,MAAM,KAAK,MAAyB,EACrD,gBAAW,KACV;IAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CA6C3C,CAAA;AAED,+FAA+F;AAC/F,eAAO,MAAM,mBAAmB,GAC9B,OAAO,aAAa,EAAE,EACtB,oBAAmC,EACnC,UAAU,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,KAClC,IAGF,CAAA"}
@@ -1,29 +1,67 @@
1
+ import { parseMimeType } from '@scalar/helpers/http/mime-type';
2
+ import { MAX_MULTIPART_NESTING } from './multipart-limits.js';
1
3
  /** Escape disposition parameters using the same percent escapes as browser form submissions. */
2
4
  const escapeParameter = (value) => value
3
5
  .replace(/\r\n|\r|\n/g, '\r\n')
4
6
  .replace(/\r/g, '%0D')
5
7
  .replace(/\n/g, '%0A')
6
8
  .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) => {
9
+ /** Keep equal-length boundaries distinct from nested delimiters and resolved text. */
10
+ const createBoundary = (chunks) => {
11
+ const text = chunks.filter((chunk) => typeof chunk === 'string').join('');
12
+ for (const _attempt of Array.from({ length: 10 })) {
13
+ // getRandomValues also works on HTTP playgrounds, where randomUUID is unavailable.
14
+ // Binary blobs stay untouched; 192 random bits make accidental byte collisions negligible.
15
+ const random = crypto.getRandomValues(new Uint8Array(24));
16
+ const boundary = `----scalar-${Array.from(random, (byte) => byte.toString(16).padStart(2, '0')).join('')}`;
17
+ if (!text.includes(boundary)) {
18
+ return boundary;
19
+ }
20
+ }
21
+ throw new Error('Unable to generate a distinct multipart boundary');
22
+ };
23
+ /** Serialize nested multipart bodies for both fetch and synchronous code snippets. */
24
+ export const serializeMultipartBody = (parts, contentType = 'multipart/form-data', replace = (value) => value, nesting = 0) => {
25
+ if (nesting >= MAX_MULTIPART_NESTING) {
26
+ throw new Error('Maximum multipart nesting exceeded');
27
+ }
28
+ if (/[\r\n\0]/.test(contentType)) {
29
+ throw new Error('Invalid multipart content type');
30
+ }
31
+ const mime = parseMimeType(contentType);
32
+ const partsChunks = parts.map((part) => {
33
+ const encoded = part.type === 'multipart'
34
+ ? serializeMultipartBody(part.value, part.contentType, replace, nesting + 1)
35
+ : { chunks: [part.type === 'text' ? replace(part.value) : part.value], contentType: part.contentType };
16
36
  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)) {
37
+ const partContentType = encoded.contentType ??
38
+ (part.type === 'file' || part.type === 'blob' ? part.value.type || 'application/octet-stream' : undefined);
39
+ if (partContentType && /[\r\n\0]/.test(partContentType)) {
20
40
  throw new Error('Invalid multipart content type');
21
41
  }
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'];
42
+ const disposition = part.key === undefined
43
+ ? ''
44
+ : `Content-Disposition: form-data; name="${escapeParameter(replace(part.key))}"${filename === undefined ? '' : `; filename="${escapeParameter(filename)}"`}\r\n`;
45
+ const extraHeaders = Object.entries(part.headers ?? {})
46
+ .map(([name, value]) => {
47
+ const resolved = replace(value);
48
+ if (!/^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/.test(name) || /[\r\n\0]/.test(resolved)) {
49
+ throw new Error('Invalid multipart header');
50
+ }
51
+ return name.toLowerCase() === 'content-type' ? '' : `${name}: ${resolved}\r\n`;
52
+ })
53
+ .join('');
54
+ const headers = `${disposition}${partContentType ? `Content-Type: ${partContentType}\r\n` : ''}${extraHeaders}\r\n`;
55
+ const chunks = encoded.chunks.map((value) => typeof value === 'string' && !part.contentType ? value.replace(/\r\n|\r|\n/g, '\r\n') : value);
56
+ return [headers, ...chunks, '\r\n'];
27
57
  });
28
- return new Blob([...chunks, `--${boundary}--\r\n`], { type: `multipart/form-data; boundary=${boundary}` });
58
+ const boundary = createBoundary(partsChunks.flat());
59
+ mime.parameters.set('boundary', boundary);
60
+ const chunks = partsChunks.flatMap((part) => [`--${boundary}\r\n`, ...part]);
61
+ return { chunks: [...chunks, `--${boundary}--\r\n`], contentType: mime.toString() };
62
+ };
63
+ /** Encode multipart parts without losing binary bytes or assigning filenames to typed text. */
64
+ export const encodeMultipartBody = (parts, contentType = 'multipart/form-data', replace) => {
65
+ const encoded = serializeMultipartBody(parts, contentType, replace);
66
+ return new Blob(encoded.chunks, { type: encoded.contentType });
29
67
  };
@@ -1 +1 @@
1
- {"version":3,"file":"get-request-body-example.d.ts","sourceRoot":"","sources":["../../../../src/request-example/builder/body/get-request-body-example.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,aAAa,EACb,iBAAiB,EAElB,MAAM,8DAA8D,CAAA;AAMrE;;;;;;;;;;GAUG;AACH,eAAO,MAAM,wBAAwB,GACnC,aAAa,iBAAiB,EAC9B,aAAa,MAAM,EACnB,kCAAkC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KACvD,OAkBF,CAAA;AAED;;GAEG;AACH,eAAO,MAAM,kBAAkB,GAC7B,aAAa,iBAAiB,EAC9B,aAAa,MAAM,EACnB,aAAa,MAAM,EACnB,kCAAkC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KACvD,aAAa,GAAG,IAgBlB,CAAA"}
1
+ {"version":3,"file":"get-request-body-example.d.ts","sourceRoot":"","sources":["../../../../src/request-example/builder/body/get-request-body-example.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,aAAa,EACb,iBAAiB,EAElB,MAAM,8DAA8D,CAAA;AAQrE;;;;;;;;;;GAUG;AACH,eAAO,MAAM,wBAAwB,GACnC,aAAa,iBAAiB,EAC9B,aAAa,MAAM,EACnB,kCAAkC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KACvD,OAwBF,CAAA;AAED;;GAEG;AACH,eAAO,MAAM,kBAAkB,GAC7B,aAAa,iBAAiB,EAC9B,aAAa,MAAM,EACnB,aAAa,MAAM,EACnB,kCAAkC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KACvD,aAAa,GAAG,IAwBlB,CAAA"}
@@ -1,5 +1,7 @@
1
- import { getResolvedRef } from '@scalar/workspace-store/helpers/get-resolved-ref';
1
+ import { parseMimeType } from '@scalar/helpers/http/mime-type';
2
+ import { getExampleValue } from '../../../helpers/get-example-value.js';
2
3
  import { getResolvedRefDeep } from '../../../helpers/get-resolved-ref-deep.js';
4
+ import { serializeStreamExample } from '../../../helpers/serialize-stream-example.js';
3
5
  import { getExample } from '../../../request-example/builder/helpers/get-example.js';
4
6
  import { getExampleFromSchema } from '../../../request-example/builder/helpers/get-example-from-schema.js';
5
7
  /**
@@ -14,17 +16,22 @@ import { getExampleFromSchema } from '../../../request-example/builder/helpers/g
14
16
  * Returns `undefined` when there is no schema for the content type.
15
17
  */
16
18
  export const getSchemaExampleFromBody = (requestBody, contentType, requestBodyCompositionSelection) => {
17
- const schema = getResolvedRef(requestBody.content?.[contentType]?.schema);
19
+ const mediaType = requestBody.content?.[contentType];
20
+ const schema = mediaType?.schema ??
21
+ (mediaType?.itemSchema && parseMimeType(contentType).type === 'multipart'
22
+ ? { type: 'array', items: mediaType.itemSchema }
23
+ : mediaType?.itemSchema);
18
24
  if (!schema) {
19
25
  return undefined;
20
26
  }
21
27
  const resolvedSchema = getResolvedRefDeep(schema);
22
- return getExampleFromSchema(resolvedSchema, {
28
+ const value = getExampleFromSchema(resolvedSchema, {
23
29
  mode: 'write',
24
30
  compositionSelection: requestBodyCompositionSelection,
25
31
  }, {
26
32
  schemaPath: ['requestBody'],
27
33
  });
34
+ return serializeStreamExample(value, contentType, mediaType?.schema === undefined) ?? value;
28
35
  };
29
36
  /**
30
37
  * Basically getExample + we generate an example from the schema if no example is found
@@ -34,12 +41,19 @@ export const getExampleFromBody = (requestBody, contentType, exampleName, reques
34
41
  // `externalValue` (not yet resolved to a `value`) is treated as missing, so we fall back to a
35
42
  // schema-generated example instead of building an empty request body.
36
43
  const example = getExample(requestBody, exampleName, contentType);
37
- if (example && example.value !== undefined) {
38
- return example;
44
+ const selected = getExampleValue(example);
45
+ if (example && selected) {
46
+ const stream = typeof selected.value === 'string' ? undefined : serializeStreamExample(selected.value, contentType, false);
47
+ if (stream !== undefined) {
48
+ return selected.source === 'data'
49
+ ? { ...example, value: stream, serializedValue: stream }
50
+ : { ...example, value: stream };
51
+ }
52
+ return selected.source === 'value' ? example : { ...example, value: selected.value };
39
53
  }
40
54
  // Generate an example from the schema
41
55
  const schemaExample = getSchemaExampleFromBody(requestBody, contentType, requestBodyCompositionSelection);
42
- if (!schemaExample) {
56
+ if (schemaExample === undefined || schemaExample === null) {
43
57
  return null;
44
58
  }
45
59
  return { value: schemaExample };
@@ -0,0 +1,3 @@
1
+ /** Match the response parser: the outer body counts toward the eight multipart levels. */
2
+ export declare const MAX_MULTIPART_NESTING = 8;
3
+ //# sourceMappingURL=multipart-limits.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"multipart-limits.d.ts","sourceRoot":"","sources":["../../../../src/request-example/builder/body/multipart-limits.ts"],"names":[],"mappings":"AAAA,0FAA0F;AAC1F,eAAO,MAAM,qBAAqB,IAAI,CAAA"}
@@ -0,0 +1,2 @@
1
+ /** Match the response parser: the outer body counts toward the eight multipart levels. */
2
+ export const MAX_MULTIPART_NESTING = 8;
@@ -1,4 +1,6 @@
1
1
  import type { EncodingObject } from '@scalar/workspace-store/schemas/v3.2/strict/openapi-document';
2
+ /** Explicit RFC6570 fields select style serialization instead of media-type encoding. */
3
+ export declare const hasEncodingStyle: (encoding: EncodingObject | undefined) => boolean;
2
4
  /** A single serialized form field, ready to become a multipart part or a urlencoded pair. */
3
5
  export type SerializedFormProperty = {
4
6
  key: string;
@@ -1 +1 @@
1
- {"version":3,"file":"serialize-form-property.d.ts","sourceRoot":"","sources":["../../../../src/request-example/builder/body/serialize-form-property.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8DAA8D,CAAA;AASlG,6FAA6F;AAC7F,MAAM,MAAM,sBAAsB,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAA;AAcnE;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,iCAAiC,GAC5C,KAAK,MAAM,EACX,OAAO,OAAO,EACd,UAAU,cAAc,GAAG,SAAS,KACnC,sBAAsB,EAAE,GAAG,IA4E7B,CAAA"}
1
+ {"version":3,"file":"serialize-form-property.d.ts","sourceRoot":"","sources":["../../../../src/request-example/builder/body/serialize-form-property.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8DAA8D,CAAA;AASlG,yFAAyF;AACzF,eAAO,MAAM,gBAAgB,GAAI,UAAU,cAAc,GAAG,SAAS,KAAG,OACgD,CAAA;AAExH,6FAA6F;AAC7F,MAAM,MAAM,sBAAsB,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAA;AAcnE;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,iCAAiC,GAC5C,KAAK,MAAM,EACX,OAAO,OAAO,EACd,UAAU,cAAc,GAAG,SAAS,KACnC,sBAAsB,EAAE,GAAG,IA0E7B,CAAA"}
@@ -1,5 +1,7 @@
1
1
  import { unpackProxyObject } from '@scalar/workspace-store/helpers/unpack-proxy';
2
2
  import { serializeDeepObjectStyle, serializeFormStyle, serializePipeDelimitedStyle, serializeSpaceDelimitedStyle, } from '../header/serialize-parameter.js';
3
+ /** Explicit RFC6570 fields select style serialization instead of media-type encoding. */
4
+ export const hasEncodingStyle = (encoding) => !!encoding && (encoding.style !== undefined || encoding.explode !== undefined || encoding.allowReserved !== undefined);
3
5
  /**
4
6
  * Stringify a value emitted by `serializeFormStyle` into a form field value.
5
7
  *
@@ -30,8 +32,7 @@ export const serializeFormPropertyWithEncoding = (key, value, encoding) => {
30
32
  * Per OpenAPI 3.1.x: when style, explode, or allowReserved is explicitly set, the value
31
33
  * is serialized as if it were a query-style parameter and contentType is ignored.
32
34
  */
33
- const hasFormStyle = !!encoding &&
34
- (encoding.style !== undefined || encoding.explode !== undefined || encoding.allowReserved !== undefined);
35
+ const hasFormStyle = hasEncodingStyle(encoding);
35
36
  if (!hasFormStyle) {
36
37
  return null;
37
38
  }
@@ -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 multipart array item shared by the request builder and code snippets. */
3
3
  export type MultipartArrayPart = {
4
4
  key: string;
@@ -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;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;AAwClD;;;;;;;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":"AAGA,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;AAwClD;;;;;;;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,3 +1,4 @@
1
+ import { isHttpMethod } from '@scalar/helpers/http/is-http-method';
1
2
  import { X_SCALAR_DATE, X_SCALAR_DNT, X_SCALAR_REFERER, X_SCALAR_USER_AGENT } from '@scalar/helpers/http/scalar-headers';
2
3
  import { replaceEnvVariables } from '@scalar/helpers/regex/replace-variables';
3
4
  import { err, ok } from '@scalar/helpers/types/result';
@@ -88,6 +89,11 @@ const buildRequestInner = (request, options) => {
88
89
  })();
89
90
  /** Create a new body object with the replaced values */
90
91
  const body = (() => {
92
+ if (request.body?.mode === 'multipart') {
93
+ const encoded = encodeMultipartBody(request.body.value, request.body.contentType, (value) => replaceEnvVariables(value, replace));
94
+ headers.set('content-type', encoded.type);
95
+ return encoded;
96
+ }
91
97
  if (request.body?.mode === 'raw') {
92
98
  if (typeof request.body.value === 'string') {
93
99
  return replaceEnvVariables(request.body.value, replace);
@@ -201,7 +207,7 @@ const buildRequestInner = (request, options) => {
201
207
  *
202
208
  * @see https://github.com/whatwg/fetch/issues/50
203
209
  */
204
- method: request.method.toUpperCase(),
210
+ method: isHttpMethod(request.method) ? request.method.toUpperCase() : request.method,
205
211
  headers,
206
212
  body,
207
213
  cache: request.cache,
@@ -20,5 +20,7 @@ exampleName?: string) => {
20
20
  pathVariables: Record<string, string>;
21
21
  allowReservedQueryParameters: Set<string>;
22
22
  urlParams: URLSearchParams;
23
+ serializedQuery?: string[];
24
+ serializedPathParameters?: Set<string>;
23
25
  };
24
26
  //# sourceMappingURL=build-request-parameters.d.ts.map
@@ -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;CA6F3B,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;AAqBlH;;;;;;;;;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;IAC1B,eAAe,CAAC,EAAE,MAAM,EAAE,CAAA;IAC1B,wBAAwB,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;CA8IvC,CAAA"}
@@ -3,10 +3,10 @@ import { isObject } from '@scalar/helpers/object/is-object';
3
3
  import { getResolvedRef } from '@scalar/workspace-store/helpers/get-resolved-ref';
4
4
  import { xScalarCookieSchema, } from '@scalar/workspace-store/schemas/extensions/general/x-scalar-cookies';
5
5
  import { coerceValue } from '@scalar/workspace-store/schemas/typebox-coerce';
6
+ import { getParameterExample } from '../../../helpers/get-parameter-example.js';
6
7
  import { deSerializeParameter } from '../../../request-example/builder/header/de-serialize-parameter.js';
7
- import { getExample } from '../helpers/get-example.js';
8
8
  import { isParamDisabled } from './is-param-disabled.js';
9
- import { serializeContentValue, serializeDeepObjectStyle, serializeFormStyle, serializeFormStyleForCookies, serializePipeDelimitedStyle, serializeSimpleStyle, serializeSpaceDelimitedStyle, } from './serialize-parameter.js';
9
+ import { serializeContentValue, serializeCookieStyle, serializeDeepObjectStyle, serializeFormStyle, serializeFormStyleForCookies, serializePipeDelimitedStyle, serializeSimpleStyle, serializeSpaceDelimitedStyle, } from './serialize-parameter.js';
10
10
  /** Helper to get explode value with default */
11
11
  const getExplode = (param, defaultValue) => 'explode' in param && param.explode !== undefined ? param.explode : defaultValue;
12
12
  /**
@@ -38,15 +38,58 @@ 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)
41
+ if (!param) {
42
42
  continue;
43
- const example = getExample(param, exampleName, undefined);
43
+ }
44
+ const selected = getParameterExample(param, exampleName);
45
+ const { example, value } = selected;
44
46
  // Skip disabled examples
45
47
  if (!example || isParamDisabled(param, example)) {
46
48
  continue;
47
49
  }
48
- /** Replace environment variables in the key and value */
49
- const value = example.value;
50
+ /** Parameter-level wire text already contains its name, delimiters, and encoding. */
51
+ if (selected.serialized) {
52
+ const wireValue = String(value);
53
+ switch (param.in) {
54
+ case 'query':
55
+ ;
56
+ (result.serializedQuery ??= []).push(wireValue);
57
+ break;
58
+ case 'path':
59
+ result.pathVariables[param.name] = wireValue;
60
+ (result.serializedPathParameters ??= new Set()).add(param.name);
61
+ break;
62
+ case 'header':
63
+ result.headers[param.name] = wireValue;
64
+ break;
65
+ case 'cookie':
66
+ result.headers.Cookie = [result.headers.Cookie, wireValue].filter(Boolean).join('; ');
67
+ break;
68
+ }
69
+ continue;
70
+ }
71
+ if (selected.mediaSerialized) {
72
+ const text = String(value);
73
+ switch (param.in) {
74
+ case 'query':
75
+ result.urlParams.set(param.name, text);
76
+ break;
77
+ case 'header':
78
+ result.headers[param.name] = text;
79
+ break;
80
+ case 'path':
81
+ result.pathVariables[param.name] = text;
82
+ break;
83
+ case 'cookie':
84
+ result.cookies.push(coerceValue(xScalarCookieSchema, {
85
+ name: encodeURIComponent(param.name),
86
+ value: encodeURIComponent(text),
87
+ path: '/',
88
+ }));
89
+ break;
90
+ }
91
+ continue;
92
+ }
50
93
  /** De-serialize the example value if it is a string and matches the schema type */
51
94
  const deSerializedValue = deSerializeParameter(value, param);
52
95
  const paramName = param.name;
@@ -82,15 +125,15 @@ exampleName = 'default') => {
82
125
  result.pathVariables[paramName] = String(serialized);
83
126
  break;
84
127
  }
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': {
128
+ case 'query': {
90
129
  processQueryParameter(param, paramName, deSerializedValue, result.urlParams, result.allowReservedQueryParameters);
91
130
  break;
92
131
  }
93
132
  case 'cookie': {
133
+ if ('style' in param && param.style === 'cookie') {
134
+ result.cookies.push(...serializeCookieStyle(paramName, deSerializedValue, getExplode(param, true)).map((cookie) => coerceValue(xScalarCookieSchema, { ...cookie, path: '/' })));
135
+ break;
136
+ }
94
137
  processCookieParameter(paramName, deSerializedValue, getExplode(param, true), result.cookies);
95
138
  break;
96
139
  }
@@ -1 +1 @@
1
- {"version":3,"file":"is-param-disabled.d.ts","sourceRoot":"","sources":["../../../../src/request-example/builder/header/is-param-disabled.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,8DAA8D,CAAA;AAElH;;;;;;;;;;GAUG;AACH,eAAO,MAAM,eAAe,GAC1B,OAAO,eAAe,EACtB,SAAS,aAAa,GAAG,SAAS,EAClC,kBAAiB,OAAc,KAC9B,OAgBF,CAAA"}
1
+ {"version":3,"file":"is-param-disabled.d.ts","sourceRoot":"","sources":["../../../../src/request-example/builder/header/is-param-disabled.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,8DAA8D,CAAA;AAIlH;;;;;;;;;;GAUG;AACH,eAAO,MAAM,eAAe,GAC1B,OAAO,eAAe,EACtB,SAAS,aAAa,GAAG,SAAS,EAClC,kBAAiB,OAAc,KAC9B,OAiBF,CAAA"}
@@ -1,3 +1,4 @@
1
+ import { getExampleValue } from '../../../helpers/get-example-value.js';
1
2
  /**
2
3
  * Determines if a parameter is disabled
3
4
  *
@@ -16,7 +17,8 @@ export const isParamDisabled = (param, example, defaultDisabled = true) => {
16
17
  return xDisabled;
17
18
  }
18
19
  // Keep the editor, generated snippets, and outgoing requests aligned for pre-populated values.
19
- const hasValue = example?.value !== undefined && example.value !== '' && example.value !== null;
20
+ const value = getExampleValue(example)?.value;
21
+ const hasValue = value !== undefined && value !== '' && value !== null;
20
22
  if (!defaultDisabled || hasValue) {
21
23
  return false;
22
24
  }
@@ -94,4 +94,15 @@ export declare const serializeDeepObjectStyle: (paramName: string, value: unknow
94
94
  key: string;
95
95
  value: string;
96
96
  }>;
97
+ /**
98
+ * Serializes OpenAPI 3.2 cookie style without escaping names or values.
99
+ * Cookie arrays and objects always expand into separate entries: explode: false
100
+ * is invalid because comma-separated cookie values violate RFC6265.
101
+ * Invalid declarations warn once per parameter name and retain the expanded fallback.
102
+ * Header builders join these entries with a semicolon and a single space.
103
+ */
104
+ export declare const serializeCookieStyle: (name: string, value: unknown, explode?: boolean) => Array<{
105
+ name: string;
106
+ value: string;
107
+ }>;
97
108
  //# sourceMappingURL=serialize-parameter.d.ts.map
@@ -1 +1 @@
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"}
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;AAKD;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB,GAC/B,MAAM,MAAM,EACZ,OAAO,OAAO,EACd,iBAAc,KACb,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAcvC,CAAA"}
@@ -232,3 +232,25 @@ export const serializeDeepObjectStyle = (paramName, value) => {
232
232
  }
233
233
  return result;
234
234
  };
235
+ // Request rebuilding and snippet rendering can serialize the same parameter repeatedly.
236
+ const warnedCookieParameterNames = new Set();
237
+ /**
238
+ * Serializes OpenAPI 3.2 cookie style without escaping names or values.
239
+ * Cookie arrays and objects always expand into separate entries: explode: false
240
+ * is invalid because comma-separated cookie values violate RFC6265.
241
+ * Invalid declarations warn once per parameter name and retain the expanded fallback.
242
+ * Header builders join these entries with a semicolon and a single space.
243
+ */
244
+ export const serializeCookieStyle = (name, value, explode = true) => {
245
+ if (!explode && !warnedCookieParameterNames.has(name)) {
246
+ warnedCookieParameterNames.add(name);
247
+ console.warn(`Cookie parameter "${name}" uses invalid explode: false with style: cookie; serializing with explode: true.`);
248
+ }
249
+ if (Array.isArray(value)) {
250
+ return value.map((item) => ({ name, value: String(item) }));
251
+ }
252
+ if (isObjectLike(value)) {
253
+ return Object.entries(value).map(([key, item]) => ({ name: key, value: String(item) }));
254
+ }
255
+ return [{ name, value: String(value) }];
256
+ };
@@ -1 +1 @@
1
- {"version":3,"file":"get-example-from-schema.d.ts","sourceRoot":"","sources":["../../../../src/request-example/builder/helpers/get-example-from-schema.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,YAAY,EAAqD,MAAM,uBAAuB,CAAA;AAG5G,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wCAAwC,CAAA;AA+wB1E,KAAK,2BAA2B,GAAG;IACjC,+CAA+C;IAC/C,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,4CAA4C;IAC5C,GAAG,CAAC,EAAE,OAAO,CAAA;IACb,uDAAuD;IACvD,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;IACvB,iEAAiE;IACjE,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACnC,qDAAqD;IACrD,8BAA8B,CAAC,EAAE,OAAO,CAAA;IACxC;;;;;;OAMG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAA;IAC3B,0DAA0D;IAC1D,oBAAoB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAC9C,CAAA;AAuPD;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,oBAAoB,GAC/B,QAAQ,YAAY,EACpB,UAAU,2BAA2B,EACrC,iEAOG,OAAO,CAAC;IACT,KAAK,EAAE,MAAM,CAAA;IACb,YAAY,EAAE,YAAY,CAAA;IAC1B,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,CAAA;IACrB,6EAA6E;IAC7E,UAAU,EAAE,MAAM,EAAE,CAAA;IACpB,qGAAqG;IACrG,YAAY,EAAE,YAAY,CAAA;CAC3B,CAAM,KACN,OAmNF,CAAA"}
1
+ {"version":3,"file":"get-example-from-schema.d.ts","sourceRoot":"","sources":["../../../../src/request-example/builder/helpers/get-example-from-schema.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,YAAY,EAAqD,MAAM,uBAAuB,CAAA;AAG5G,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wCAAwC,CAAA;AAiyB1E,KAAK,2BAA2B,GAAG;IACjC,+CAA+C;IAC/C,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,4CAA4C;IAC5C,GAAG,CAAC,EAAE,OAAO,CAAA;IACb,uDAAuD;IACvD,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;IACvB,iEAAiE;IACjE,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACnC,qDAAqD;IACrD,8BAA8B,CAAC,EAAE,OAAO,CAAA;IACxC;;;;;;OAMG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAA;IAC3B,0DAA0D;IAC1D,oBAAoB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAC9C,CAAA;AAuPD;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,oBAAoB,GAC/B,QAAQ,YAAY,EACpB,UAAU,2BAA2B,EACrC,iEAOG,OAAO,CAAC;IACT,KAAK,EAAE,MAAM,CAAA;IACb,YAAY,EAAE,YAAY,CAAA;IAC1B,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,CAAA;IACrB,6EAA6E;IAC7E,UAAU,EAAE,MAAM,EAAE,CAAA;IACpB,qGAAqG;IACrG,YAAY,EAAE,YAAY,CAAA;CAC3B,CAAM,KACN,OAuPF,CAAA"}