@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.
- package/CHANGELOG.md +110 -0
- package/README.md +123 -0
- package/dist/client.d.ts +28 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +320 -144
- package/dist/entities/auth/schema.d.ts +236 -0
- package/dist/entities/auth/schema.d.ts.map +1 -1
- package/dist/entities/auth/schema.js +3 -1
- package/dist/helpers/chunk-index.d.ts +113 -0
- package/dist/helpers/chunk-index.d.ts.map +1 -0
- package/dist/helpers/chunk-index.js +152 -0
- package/dist/helpers/external-examples.d.ts +19 -0
- package/dist/helpers/external-examples.d.ts.map +1 -0
- package/dist/helpers/external-examples.js +51 -0
- package/dist/helpers/for-each-path-item-operation.js +4 -4
- package/dist/helpers/get-example-value.d.ts +15 -0
- package/dist/helpers/get-example-value.d.ts.map +1 -0
- package/dist/helpers/get-example-value.js +28 -0
- package/dist/helpers/get-resolved-ref.d.ts.map +1 -1
- package/dist/helpers/get-resolved-ref.js +12 -1
- package/dist/helpers/normalize-boolean-schemas.d.ts +10 -0
- package/dist/helpers/normalize-boolean-schemas.d.ts.map +1 -0
- package/dist/helpers/normalize-boolean-schemas.js +193 -0
- package/dist/helpers/operation-examples.d.ts +6 -0
- package/dist/helpers/operation-examples.d.ts.map +1 -0
- package/dist/helpers/operation-examples.js +48 -0
- package/dist/helpers/serialize-stream-example.d.ts +8 -0
- package/dist/helpers/serialize-stream-example.d.ts.map +1 -0
- package/dist/helpers/serialize-stream-example.js +52 -0
- package/dist/helpers/use-external-examples.d.ts +15 -0
- package/dist/helpers/use-external-examples.d.ts.map +1 -0
- package/dist/helpers/use-external-examples.js +62 -0
- package/dist/mutators/index.d.ts +3 -3
- package/dist/mutators/operation/body.d.ts.map +1 -1
- package/dist/mutators/operation/body.js +9 -2
- package/dist/mutators/operation/parameters.d.ts.map +1 -1
- package/dist/mutators/operation/parameters.js +28 -9
- package/dist/plugins/bundler/index.d.ts +6 -2
- package/dist/plugins/bundler/index.d.ts.map +1 -1
- package/dist/plugins/bundler/index.js +20 -4
- package/dist/plugins/bundler/openapi-document.d.ts +6 -0
- package/dist/plugins/bundler/openapi-document.d.ts.map +1 -0
- package/dist/plugins/bundler/openapi-document.js +57 -0
- package/dist/request-example/builder/body/build-multipart.d.ts +29 -0
- package/dist/request-example/builder/body/build-multipart.d.ts.map +1 -0
- package/dist/request-example/builder/body/build-multipart.js +191 -0
- package/dist/request-example/builder/body/build-request-body.d.ts +6 -1
- package/dist/request-example/builder/body/build-request-body.d.ts.map +1 -1
- package/dist/request-example/builder/body/build-request-body.js +80 -23
- package/dist/request-example/builder/body/encode-multipart-body.d.ts +8 -8
- package/dist/request-example/builder/body/encode-multipart-body.d.ts.map +1 -1
- package/dist/request-example/builder/body/encode-multipart-body.js +56 -18
- package/dist/request-example/builder/body/get-request-body-example.d.ts.map +1 -1
- package/dist/request-example/builder/body/get-request-body-example.js +20 -6
- package/dist/request-example/builder/body/multipart-limits.d.ts +3 -0
- package/dist/request-example/builder/body/multipart-limits.d.ts.map +1 -0
- package/dist/request-example/builder/body/multipart-limits.js +2 -0
- package/dist/request-example/builder/body/serialize-form-property.d.ts +2 -0
- package/dist/request-example/builder/body/serialize-form-property.d.ts.map +1 -1
- package/dist/request-example/builder/body/serialize-form-property.js +3 -2
- package/dist/request-example/builder/body/serialize-multipart-array.d.ts +1 -1
- package/dist/request-example/builder/build-request.js +5 -0
- package/dist/request-example/builder/header/build-request-parameters.d.ts.map +1 -1
- package/dist/request-example/builder/header/build-request-parameters.js +5 -1
- package/dist/request-example/builder/header/is-param-disabled.d.ts +2 -2
- package/dist/request-example/builder/header/is-param-disabled.d.ts.map +1 -1
- package/dist/request-example/builder/header/is-param-disabled.js +5 -4
- package/dist/request-example/builder/header/serialize-parameter.d.ts +11 -0
- package/dist/request-example/builder/header/serialize-parameter.d.ts.map +1 -1
- package/dist/request-example/builder/header/serialize-parameter.js +22 -0
- package/dist/request-example/builder/helpers/get-example-from-schema.d.ts.map +1 -1
- package/dist/request-example/builder/helpers/get-example-from-schema.js +42 -3
- package/dist/request-example/builder/helpers/get-example.d.ts +2 -0
- package/dist/request-example/builder/helpers/get-example.d.ts.map +1 -1
- package/dist/request-example/builder/helpers/get-example.js +14 -9
- package/dist/request-example/builder/index.d.ts +2 -2
- package/dist/request-example/builder/index.d.ts.map +1 -1
- package/dist/request-example/builder/index.js +1 -1
- package/dist/request-example/builder/security/secret-types.d.ts +4 -1
- package/dist/request-example/builder/security/secret-types.d.ts.map +1 -1
- package/dist/request-example/context/headers.d.ts.map +1 -1
- package/dist/request-example/context/headers.js +3 -4
- package/dist/request-example/context/security/extract-security-scheme-secrets.d.ts.map +1 -1
- package/dist/request-example/context/security/extract-security-scheme-secrets.js +15 -0
- package/dist/request-example/index.d.ts +5 -2
- package/dist/request-example/index.d.ts.map +1 -1
- package/dist/request-example/index.js +4 -1
- package/dist/request-example/xml/serialize-xml-part.d.ts +9 -0
- package/dist/request-example/xml/serialize-xml-part.d.ts.map +1 -0
- package/dist/request-example/xml/serialize-xml-part.js +12 -0
- package/dist/schemas/extensions.d.ts +9 -0
- package/dist/schemas/extensions.d.ts.map +1 -1
- package/dist/schemas/extensions.js +9 -0
- package/dist/schemas/reference-config/index.d.ts +1 -0
- package/dist/schemas/reference-config/index.d.ts.map +1 -1
- package/dist/schemas/reference-config/settings.d.ts +1 -0
- package/dist/schemas/reference-config/settings.d.ts.map +1 -1
- package/dist/schemas/v3.1/strict/oauthflows.d.ts +26 -0
- package/dist/schemas/v3.1/strict/oauthflows.d.ts.map +1 -1
- package/dist/schemas/v3.1/strict/oauthflows.js +3 -0
- package/dist/schemas/v3.1/strict/openapi-document.d.ts +812 -7
- package/dist/schemas/v3.1/strict/openapi-document.d.ts.map +1 -1
- package/dist/schemas/v3.2/openapi/index.d.ts +5 -0
- package/dist/schemas/v3.2/openapi/index.d.ts.map +1 -1
- package/dist/schemas/v3.2/openapi/index.js +46 -12
- package/dist/schemas/v3.2/strict/openapi-document.d.ts +37 -0
- package/dist/schemas/v3.2/strict/openapi-document.d.ts.map +1 -1
- package/dist/schemas/v3.2/strict/openapi-document.js +8 -0
- package/dist/server.d.ts +21 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +60 -34
- package/package.json +19 -9
- package/dist/schemas/v3.1/openapi/index.d.ts +0 -149
- package/dist/schemas/v3.1/openapi/index.d.ts.map +0 -1
- package/dist/schemas/v3.1/openapi/index.js +0 -732
- package/dist/schemas/v3.1/openapi/reference.d.ts +0 -4
- package/dist/schemas/v3.1/openapi/reference.d.ts.map +0 -1
- 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":"
|
|
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
|
|
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 {
|
|
2
|
-
/**
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
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":"
|
|
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
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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
|
|
18
|
-
|
|
19
|
-
if (
|
|
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 =
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
const
|
|
26
|
-
|
|
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
|
-
|
|
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;
|
|
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 {
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
38
|
-
|
|
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 (
|
|
56
|
+
if (schemaExample === undefined || schemaExample === null) {
|
|
43
57
|
return null;
|
|
44
58
|
}
|
|
45
59
|
return { value: schemaExample };
|
|
@@ -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"}
|
|
@@ -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,
|
|
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 =
|
|
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
|
+
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;
|
|
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
|
}
|