@scalar/workspace-store 0.63.0 → 0.65.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 (118) hide show
  1. package/CHANGELOG.md +110 -0
  2. package/README.md +123 -0
  3. package/dist/client.d.ts +28 -0
  4. package/dist/client.d.ts.map +1 -1
  5. package/dist/client.js +320 -144
  6. package/dist/entities/auth/schema.d.ts +236 -0
  7. package/dist/entities/auth/schema.d.ts.map +1 -1
  8. package/dist/entities/auth/schema.js +3 -1
  9. package/dist/helpers/chunk-index.d.ts +113 -0
  10. package/dist/helpers/chunk-index.d.ts.map +1 -0
  11. package/dist/helpers/chunk-index.js +152 -0
  12. package/dist/helpers/external-examples.d.ts +19 -0
  13. package/dist/helpers/external-examples.d.ts.map +1 -0
  14. package/dist/helpers/external-examples.js +51 -0
  15. package/dist/helpers/for-each-path-item-operation.js +4 -4
  16. package/dist/helpers/get-example-value.d.ts +15 -0
  17. package/dist/helpers/get-example-value.d.ts.map +1 -0
  18. package/dist/helpers/get-example-value.js +28 -0
  19. package/dist/helpers/get-resolved-ref.d.ts.map +1 -1
  20. package/dist/helpers/get-resolved-ref.js +12 -1
  21. package/dist/helpers/normalize-boolean-schemas.d.ts +10 -0
  22. package/dist/helpers/normalize-boolean-schemas.d.ts.map +1 -0
  23. package/dist/helpers/normalize-boolean-schemas.js +193 -0
  24. package/dist/helpers/operation-examples.d.ts +6 -0
  25. package/dist/helpers/operation-examples.d.ts.map +1 -0
  26. package/dist/helpers/operation-examples.js +48 -0
  27. package/dist/helpers/serialize-stream-example.d.ts +8 -0
  28. package/dist/helpers/serialize-stream-example.d.ts.map +1 -0
  29. package/dist/helpers/serialize-stream-example.js +52 -0
  30. package/dist/helpers/use-external-examples.d.ts +15 -0
  31. package/dist/helpers/use-external-examples.d.ts.map +1 -0
  32. package/dist/helpers/use-external-examples.js +62 -0
  33. package/dist/mutators/index.d.ts +3 -3
  34. package/dist/mutators/operation/body.d.ts.map +1 -1
  35. package/dist/mutators/operation/body.js +9 -2
  36. package/dist/mutators/operation/parameters.d.ts.map +1 -1
  37. package/dist/mutators/operation/parameters.js +28 -9
  38. package/dist/plugins/bundler/index.d.ts +6 -2
  39. package/dist/plugins/bundler/index.d.ts.map +1 -1
  40. package/dist/plugins/bundler/index.js +20 -4
  41. package/dist/plugins/bundler/openapi-document.d.ts +6 -0
  42. package/dist/plugins/bundler/openapi-document.d.ts.map +1 -0
  43. package/dist/plugins/bundler/openapi-document.js +57 -0
  44. package/dist/request-example/builder/body/build-multipart.d.ts +29 -0
  45. package/dist/request-example/builder/body/build-multipart.d.ts.map +1 -0
  46. package/dist/request-example/builder/body/build-multipart.js +191 -0
  47. package/dist/request-example/builder/body/build-request-body.d.ts +6 -1
  48. package/dist/request-example/builder/body/build-request-body.d.ts.map +1 -1
  49. package/dist/request-example/builder/body/build-request-body.js +80 -23
  50. package/dist/request-example/builder/body/encode-multipart-body.d.ts +8 -8
  51. package/dist/request-example/builder/body/encode-multipart-body.d.ts.map +1 -1
  52. package/dist/request-example/builder/body/encode-multipart-body.js +56 -18
  53. package/dist/request-example/builder/body/get-request-body-example.d.ts.map +1 -1
  54. package/dist/request-example/builder/body/get-request-body-example.js +20 -6
  55. package/dist/request-example/builder/body/multipart-limits.d.ts +3 -0
  56. package/dist/request-example/builder/body/multipart-limits.d.ts.map +1 -0
  57. package/dist/request-example/builder/body/multipart-limits.js +2 -0
  58. package/dist/request-example/builder/body/serialize-form-property.d.ts +2 -0
  59. package/dist/request-example/builder/body/serialize-form-property.d.ts.map +1 -1
  60. package/dist/request-example/builder/body/serialize-form-property.js +3 -2
  61. package/dist/request-example/builder/body/serialize-multipart-array.d.ts +1 -1
  62. package/dist/request-example/builder/build-request.js +5 -0
  63. package/dist/request-example/builder/header/build-request-parameters.d.ts.map +1 -1
  64. package/dist/request-example/builder/header/build-request-parameters.js +5 -1
  65. package/dist/request-example/builder/header/is-param-disabled.d.ts +2 -2
  66. package/dist/request-example/builder/header/is-param-disabled.d.ts.map +1 -1
  67. package/dist/request-example/builder/header/is-param-disabled.js +5 -4
  68. package/dist/request-example/builder/header/serialize-parameter.d.ts +11 -0
  69. package/dist/request-example/builder/header/serialize-parameter.d.ts.map +1 -1
  70. package/dist/request-example/builder/header/serialize-parameter.js +22 -0
  71. package/dist/request-example/builder/helpers/get-example-from-schema.d.ts.map +1 -1
  72. package/dist/request-example/builder/helpers/get-example-from-schema.js +42 -3
  73. package/dist/request-example/builder/helpers/get-example.d.ts +2 -0
  74. package/dist/request-example/builder/helpers/get-example.d.ts.map +1 -1
  75. package/dist/request-example/builder/helpers/get-example.js +14 -9
  76. package/dist/request-example/builder/index.d.ts +2 -2
  77. package/dist/request-example/builder/index.d.ts.map +1 -1
  78. package/dist/request-example/builder/index.js +1 -1
  79. package/dist/request-example/builder/security/secret-types.d.ts +4 -1
  80. package/dist/request-example/builder/security/secret-types.d.ts.map +1 -1
  81. package/dist/request-example/context/headers.d.ts.map +1 -1
  82. package/dist/request-example/context/headers.js +3 -4
  83. package/dist/request-example/context/security/extract-security-scheme-secrets.d.ts.map +1 -1
  84. package/dist/request-example/context/security/extract-security-scheme-secrets.js +15 -0
  85. package/dist/request-example/index.d.ts +5 -2
  86. package/dist/request-example/index.d.ts.map +1 -1
  87. package/dist/request-example/index.js +4 -1
  88. package/dist/request-example/xml/serialize-xml-part.d.ts +9 -0
  89. package/dist/request-example/xml/serialize-xml-part.d.ts.map +1 -0
  90. package/dist/request-example/xml/serialize-xml-part.js +12 -0
  91. package/dist/schemas/extensions.d.ts +9 -0
  92. package/dist/schemas/extensions.d.ts.map +1 -1
  93. package/dist/schemas/extensions.js +9 -0
  94. package/dist/schemas/reference-config/index.d.ts +1 -0
  95. package/dist/schemas/reference-config/index.d.ts.map +1 -1
  96. package/dist/schemas/reference-config/settings.d.ts +1 -0
  97. package/dist/schemas/reference-config/settings.d.ts.map +1 -1
  98. package/dist/schemas/v3.1/strict/oauthflows.d.ts +26 -0
  99. package/dist/schemas/v3.1/strict/oauthflows.d.ts.map +1 -1
  100. package/dist/schemas/v3.1/strict/oauthflows.js +3 -0
  101. package/dist/schemas/v3.1/strict/openapi-document.d.ts +812 -7
  102. package/dist/schemas/v3.1/strict/openapi-document.d.ts.map +1 -1
  103. package/dist/schemas/v3.2/openapi/index.d.ts +5 -0
  104. package/dist/schemas/v3.2/openapi/index.d.ts.map +1 -1
  105. package/dist/schemas/v3.2/openapi/index.js +46 -12
  106. package/dist/schemas/v3.2/strict/openapi-document.d.ts +37 -0
  107. package/dist/schemas/v3.2/strict/openapi-document.d.ts.map +1 -1
  108. package/dist/schemas/v3.2/strict/openapi-document.js +8 -0
  109. package/dist/server.d.ts +21 -1
  110. package/dist/server.d.ts.map +1 -1
  111. package/dist/server.js +60 -34
  112. package/package.json +19 -9
  113. package/dist/schemas/v3.1/openapi/index.d.ts +0 -149
  114. package/dist/schemas/v3.1/openapi/index.d.ts.map +0 -1
  115. package/dist/schemas/v3.1/openapi/index.js +0 -732
  116. package/dist/schemas/v3.1/openapi/reference.d.ts +0 -4
  117. package/dist/schemas/v3.1/openapi/reference.d.ts.map +0 -1
  118. package/dist/schemas/v3.1/openapi/reference.js +0 -29
@@ -0,0 +1,191 @@
1
+ import { parseMimeType } from '@scalar/helpers/http/mime-type';
2
+ import { isObject } from '@scalar/helpers/object/is-object';
3
+ import { getResolvedRef, mergeSiblingReferences } from '@scalar/workspace-store/helpers/get-resolved-ref';
4
+ import { unpackProxyObject } from '@scalar/workspace-store/helpers/unpack-proxy';
5
+ import { isArraySchema } from '@scalar/workspace-store/schemas/v3.2/strict/type-guards';
6
+ import { serializeXmlPart } from '../../xml/serialize-xml-part.js';
7
+ import { MAX_MULTIPART_NESTING } from './multipart-limits.js';
8
+ import { resolveLeafSchema } from './schema-value-coercion.js';
9
+ import { hasEncodingStyle, serializeFormPropertyWithEncoding } from './serialize-form-property.js';
10
+ /** Identify ordered multipart independently of the editor's row representation. */
11
+ export const isPositionalMultipart = (contentType, media) => {
12
+ const schema = getResolvedRef(media.schema);
13
+ return (parseMimeType(contentType).essence !== 'multipart/form-data' ||
14
+ media.prefixEncoding !== undefined ||
15
+ media.itemEncoding !== undefined ||
16
+ (schema !== undefined && isArraySchema(schema)) ||
17
+ media.itemSchema !== undefined);
18
+ };
19
+ /** Whether a media type needs ordered or nested multipart serialization. */
20
+ export const needsMultipartEncoding = (contentType, media) => {
21
+ const mime = parseMimeType(contentType);
22
+ return (mime.type === 'multipart' &&
23
+ (isPositionalMultipart(contentType, media) ||
24
+ Object.values(media.encoding ?? {}).some((encoding) => parseMimeType(encoding.contentType).type === 'multipart' || encoding.encoding !== undefined)));
25
+ };
26
+ const defaultContentType = (schema, value) => {
27
+ if (value instanceof Blob) {
28
+ return value.type || 'application/octet-stream';
29
+ }
30
+ // OpenAPI 3.2.1 Encoding Object defaults explicitly treat absent type as binary.
31
+ // With no schema at all, infer from the example so existing editor values remain usable.
32
+ if (schema && (!('type' in schema) || !schema.type || (schema.type === 'string' && schema.contentEncoding))) {
33
+ return 'application/octet-stream';
34
+ }
35
+ return typeof value === 'object' && value !== null ? 'application/json' : 'text/plain';
36
+ };
37
+ /** Encoding accepts ranges, but each wire header needs a single concrete media type. */
38
+ const selectContentType = (encoding, schema, value) => {
39
+ const fallback = defaultContentType(schema, value);
40
+ if (!encoding) {
41
+ return fallback;
42
+ }
43
+ if (/[\r\n\0]/.test(encoding)) {
44
+ throw new Error('Invalid multipart content type');
45
+ }
46
+ const choices = encoding.split(',').map((choice) => choice.trim());
47
+ const preferred = parseMimeType(fallback);
48
+ const match = choices.find((choice) => {
49
+ const mime = parseMimeType(choice);
50
+ return ((mime.type === '*' || mime.type === preferred.type) &&
51
+ (mime.subtype === '*' || mime.subtype === preferred.subtype));
52
+ });
53
+ if (match) {
54
+ const mime = parseMimeType(match);
55
+ if (!mime.essence.includes('*')) {
56
+ return match;
57
+ }
58
+ // Keep parameters from the range while replacing only its wildcard essence.
59
+ return match.replace(/^[^;]+/, preferred.essence);
60
+ }
61
+ // A nonmatching range cannot supply a concrete wire type; keep the inferred fallback.
62
+ return choices.find((choice) => !parseMimeType(choice).essence.includes('*')) ?? fallback;
63
+ };
64
+ /** Resolve a positional item's schema independently of the encoding prefix length. */
65
+ export const getMultipartItemSchema = (schema, index, itemSchema) => getResolvedRef((schema && 'prefixItems' in schema ? schema.prefixItems?.[index] : undefined) ??
66
+ (schema && 'items' in schema ? schema.items : undefined) ??
67
+ itemSchema, mergeSiblingReferences);
68
+ /** Serialize structured values to the selected part format; strings already containing XML stay intact. */
69
+ const serializePartValue = (value, contentType, schema) => {
70
+ const subtype = contentType ? parseMimeType(contentType).subtype : undefined;
71
+ if ((subtype === 'xml' || subtype?.endsWith('+xml')) && isObject(value)) {
72
+ return serializeXmlPart(value, schema);
73
+ }
74
+ const json = subtype === 'json' || subtype?.endsWith('+json');
75
+ return json || (value !== null && typeof value === 'object')
76
+ ? JSON.stringify(unpackProxyObject(value))
77
+ : String(value ?? '');
78
+ };
79
+ /** Build ordered parts, applying prefix encodings independently of schema prefix lengths. */
80
+ export const buildMultipart = (value, contentType, encoding = {}, schema, nesting = 0) => {
81
+ if (nesting >= MAX_MULTIPART_NESTING) {
82
+ throw new Error('Maximum multipart nesting exceeded');
83
+ }
84
+ const named = parseMimeType(contentType).essence === 'multipart/form-data';
85
+ const positional = Array.isArray(value);
86
+ const entries = positional
87
+ ? value.map((item, index) => {
88
+ const itemEncoding = index < (encoding.prefixEncoding?.length ?? 0) ? encoding.prefixEncoding?.[index] : encoding.itemEncoding;
89
+ const itemSchema = getMultipartItemSchema(schema, index, encoding.itemSchema);
90
+ if (named && item !== null && typeof item === 'object' && !Array.isArray(item)) {
91
+ // Each position describes one named part; accepting more keys would silently discard data.
92
+ const entries = Object.entries(item);
93
+ const entry = entries[0];
94
+ if (entries.length !== 1 || !entry) {
95
+ throw new Error('Named positional multipart items must contain exactly one property');
96
+ }
97
+ return [entry[0], entry[1], itemEncoding, resolveLeafSchema(itemSchema, [entry[0]])];
98
+ }
99
+ return [undefined, item, itemEncoding, itemSchema];
100
+ })
101
+ : value !== null && typeof value === 'object'
102
+ ? Object.entries(value).flatMap(([key, item]) => {
103
+ const propertySchema = resolveLeafSchema(schema, [key]);
104
+ return (Array.isArray(item) ? item : [item]).map((part) => [
105
+ key,
106
+ part,
107
+ encoding.encoding?.[key],
108
+ Array.isArray(item)
109
+ ? getResolvedRef(propertySchema && isArraySchema(propertySchema) ? propertySchema.items : undefined, mergeSiblingReferences)
110
+ : propertySchema,
111
+ ]);
112
+ })
113
+ : [];
114
+ return entries.flatMap(([key, item, partEncoding, partSchema]) => {
115
+ const style = named && hasEncodingStyle(partEncoding);
116
+ const styleParts = style ? serializeFormPropertyWithEncoding(key ?? '', item, partEncoding) : null;
117
+ const partContentType = style ? undefined : selectContentType(partEncoding?.contentType, partSchema, item);
118
+ const headers = Object.fromEntries(Object.entries(partEncoding?.headers ?? {}).flatMap(([name, ref]) => {
119
+ if (name.toLowerCase() === 'content-type') {
120
+ return [];
121
+ }
122
+ const header = getResolvedRef(ref);
123
+ if (!header || !('schema' in header)) {
124
+ return [];
125
+ }
126
+ const headerSchema = getResolvedRef(header.schema);
127
+ const example = header?.example ??
128
+ getResolvedRef(Object.values(header?.examples ?? {})[0])?.value ??
129
+ headerSchema?.const ??
130
+ headerSchema?.default;
131
+ return example === undefined ? [] : [[name, String(example)]];
132
+ }));
133
+ if (partSchema?.contentEncoding &&
134
+ !Object.keys(headers).some((name) => name.toLowerCase() === 'content-transfer-encoding')) {
135
+ headers['Content-Transfer-Encoding'] = partSchema.contentEncoding;
136
+ }
137
+ const metadata = {
138
+ ...(key === undefined ? {} : { key }),
139
+ ...(partContentType ? { contentType: partContentType } : {}),
140
+ ...(Object.keys(headers).length ? { headers } : {}),
141
+ };
142
+ if (styleParts) {
143
+ return styleParts.map((part) => ({ ...metadata, type: 'text', ...part }));
144
+ }
145
+ if (partContentType &&
146
+ parseMimeType(partContentType).type === 'multipart' &&
147
+ typeof item !== 'string' &&
148
+ !(item instanceof Blob)) {
149
+ return [
150
+ {
151
+ ...metadata,
152
+ type: 'multipart',
153
+ contentType: partContentType,
154
+ value: buildMultipart(item, partContentType, partEncoding, partSchema, nesting + 1),
155
+ },
156
+ ];
157
+ }
158
+ if (partContentType &&
159
+ parseMimeType(partContentType).essence === 'application/x-www-form-urlencoded' &&
160
+ item !== null &&
161
+ typeof item === 'object') {
162
+ const params = Object.entries(item).flatMap(([name, fieldValue]) => (Array.isArray(fieldValue) ? fieldValue : [fieldValue]).flatMap((field) => {
163
+ const fieldEncoding = partEncoding?.encoding?.[name];
164
+ const styled = serializeFormPropertyWithEncoding(name, field, fieldEncoding);
165
+ if (styled) {
166
+ return styled;
167
+ }
168
+ return [
169
+ {
170
+ key: name,
171
+ value: serializePartValue(field, hasEncodingStyle(fieldEncoding) ? undefined : fieldEncoding?.contentType),
172
+ },
173
+ ];
174
+ }));
175
+ return [
176
+ {
177
+ ...metadata,
178
+ type: 'text',
179
+ value: new URLSearchParams(params.map((part) => [part.key, part.value])).toString(),
180
+ },
181
+ ];
182
+ }
183
+ if (item instanceof File) {
184
+ return [{ ...metadata, type: 'file', value: unpackProxyObject(item) }];
185
+ }
186
+ if (item instanceof Blob) {
187
+ return [{ ...metadata, type: 'blob', value: unpackProxyObject(item) }];
188
+ }
189
+ return [{ ...metadata, type: 'text', value: serializePartValue(item, partContentType, partSchema) }];
190
+ });
191
+ };
@@ -1,4 +1,5 @@
1
1
  import type { RequestBodyObject } from '@scalar/workspace-store/schemas/v3.2/strict/request-body';
2
+ import { type MultipartPart } from './build-multipart.js';
2
3
  type FormData = {
3
4
  mode: 'formdata';
4
5
  value: ({
@@ -30,7 +31,11 @@ type Raw = {
30
31
  value: string | File | Blob;
31
32
  contentType?: string;
32
33
  };
33
- export type RequestBody = FormData | UrlEncoded | Raw;
34
+ export type RequestBody = FormData | UrlEncoded | Raw | {
35
+ mode: 'multipart';
36
+ contentType: string;
37
+ value: MultipartPart[];
38
+ };
34
39
  /**
35
40
  * Create the fetch request body
36
41
  */
@@ -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;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"}
1
+ {"version":3,"file":"build-request-body.d.ts","sourceRoot":"","sources":["../../../../src/request-example/builder/body/build-request-body.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0DAA0D,CAAA;AAKjG,OAAO,EACL,KAAK,aAAa,EAKnB,MAAM,mBAAmB,CAAA;AAO1B,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,GACnB,QAAQ,GACR,UAAU,GACV,GAAG,GACH;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,aAAa,EAAE,CAAA;CAAE,CAAA;AA2CtE;;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,IA8WhB,CAAA"}
@@ -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;
@@ -88,6 +88,11 @@ const buildRequestInner = (request, options) => {
88
88
  })();
89
89
  /** Create a new body object with the replaced values */
90
90
  const body = (() => {
91
+ if (request.body?.mode === 'multipart') {
92
+ const encoded = encodeMultipartBody(request.body.value, request.body.contentType, (value) => replaceEnvVariables(value, replace));
93
+ headers.set('content-type', encoded.type);
94
+ return encoded;
95
+ }
91
96
  if (request.body?.mode === 'raw') {
92
97
  if (typeof request.body.value === 'string') {
93
98
  return replaceEnvVariables(request.body.value, replace);
@@ -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;CAqG3B,CAAA"}
@@ -6,7 +6,7 @@ import { coerceValue } from '@scalar/workspace-store/schemas/typebox-coerce';
6
6
  import { deSerializeParameter } from '../../../request-example/builder/header/de-serialize-parameter.js';
7
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
  /**
@@ -91,6 +91,10 @@ exampleName = 'default') => {
91
91
  break;
92
92
  }
93
93
  case 'cookie': {
94
+ if ('style' in param && param.style === 'cookie') {
95
+ result.cookies.push(...serializeCookieStyle(paramName, deSerializedValue, getExplode(param, true)).map((cookie) => coerceValue(xScalarCookieSchema, { ...cookie, path: '/' })));
96
+ break;
97
+ }
94
98
  processCookieParameter(paramName, deSerializedValue, getExplode(param, true), result.cookies);
95
99
  break;
96
100
  }