@scalar/workspace-store 0.45.0 → 0.46.1
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 +29 -0
- package/dist/events/definitions/hooks.d.ts +2 -1
- package/dist/events/definitions/hooks.d.ts.map +1 -1
- package/dist/mutators/operation/helpers/fetch-request-to-har.d.ts +19 -29
- package/dist/mutators/operation/helpers/fetch-request-to-har.d.ts.map +1 -1
- package/dist/mutators/operation/helpers/fetch-request-to-har.js +74 -70
- package/dist/mutators/operation/history.js +1 -1
- package/dist/request-example/builder/build-request.d.ts +19 -4
- package/dist/request-example/builder/build-request.d.ts.map +1 -1
- package/dist/request-example/builder/build-request.js +46 -30
- package/dist/request-example/builder/index.d.ts +1 -1
- package/dist/request-example/builder/index.d.ts.map +1 -1
- package/dist/request-example/builder/resolve-request-factory-url.d.ts +1 -1
- package/dist/request-example/builder/resolve-request-factory-url.d.ts.map +1 -1
- package/dist/request-example/builder/resolve-request-factory-url.js +11 -6
- package/dist/request-example/context/get-request-example-context.d.ts +1 -2
- package/dist/request-example/context/get-request-example-context.d.ts.map +1 -1
- package/dist/request-example/context/get-request-example-context.js +1 -1
- package/dist/request-example/context/index.d.ts +1 -1
- package/dist/request-example/context/index.d.ts.map +1 -1
- package/dist/request-example/context/security/get-security-schemes.d.ts +1 -1
- package/dist/request-example/context/security/get-security-schemes.d.ts.map +1 -1
- package/dist/request-example/context/security/get-security-schemes.js +2 -2
- package/dist/request-example/functions.d.ts +486 -0
- package/dist/request-example/functions.d.ts.map +1 -0
- package/dist/request-example/functions.js +545 -0
- package/dist/request-example/index.d.ts +3 -2
- package/dist/request-example/index.d.ts.map +1 -1
- package/dist/request-example/index.js +1 -0
- package/dist/request-example/random-data.d.ts +99 -0
- package/dist/request-example/random-data.d.ts.map +1 -0
- package/dist/request-example/random-data.js +1181 -0
- package/dist/schemas/inmemory-workspace.d.ts +1 -1
- package/dist/schemas/reference-config/index.d.ts +1 -1
- package/dist/schemas/workspace-specification/index.d.ts +1 -1
- package/dist/schemas/workspace.d.ts +2 -2
- package/package.json +8 -8
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,34 @@
|
|
|
1
1
|
# @scalar/workspace-store
|
|
2
2
|
|
|
3
|
+
## 0.46.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#8862](https://github.com/scalar/scalar/pull/8862): add `python/aiohttp` as a supported code snippet client and expose it through shared snippet client types and config schemas
|
|
8
|
+
- [#8911](https://github.com/scalar/scalar/pull/8911): feat: switch from Request to RequestPayload to support body with GET
|
|
9
|
+
- [#8888](https://github.com/scalar/scalar/pull/8888): fix: resolve selected security schemes using selectedIndex
|
|
10
|
+
|
|
11
|
+
## 0.46.0
|
|
12
|
+
|
|
13
|
+
### Minor Changes
|
|
14
|
+
|
|
15
|
+
- [#8632](https://github.com/scalar/scalar/pull/8632): feat: support generation of random data
|
|
16
|
+
|
|
17
|
+
### Patch Changes
|
|
18
|
+
|
|
19
|
+
- [#8791](https://github.com/scalar/scalar/pull/8791): fix test requests so exploded array query parameters keep all values instead of collapsing to the last one
|
|
20
|
+
- [#8852](https://github.com/scalar/scalar/pull/8852): fix: Allows sending requests when scalar is framed in srcdoc
|
|
21
|
+
- [#8817](https://github.com/scalar/scalar/pull/8817): feat(snippetz): add a Laravel HTTP client plugin for PHP snippets
|
|
22
|
+
|
|
23
|
+
Added a new `php/laravel` client generator in `@scalar/snippetz`, including comprehensive request coverage for headers, cookies, auth, query params, JSON, multipart, form-encoded, binary, and fallback bodies.
|
|
24
|
+
|
|
25
|
+
Updated generated client registries and schema wiring so the new client is available across Scalar:
|
|
26
|
+
- `@scalar/types` `GROUPED_CLIENTS` / `AVAILABLE_CLIENTS`
|
|
27
|
+
- `@scalar/workspace-store` reference-config schema
|
|
28
|
+
- generated docs and integration client enums
|
|
29
|
+
|
|
30
|
+
Updated api-client expectations for the increased total built-in client count.
|
|
31
|
+
|
|
3
32
|
## 0.45.0
|
|
4
33
|
|
|
5
34
|
### Minor Changes
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { OperationExampleMeta } from '../../events/definitions/operation.js';
|
|
2
|
+
import type { RequestPayload } from '../../request-example/builder/build-request.js';
|
|
2
3
|
/**
|
|
3
4
|
* Event definitions for hooking into the API client lifecycle.
|
|
4
5
|
*
|
|
@@ -26,7 +27,7 @@ export type HooksEvents = {
|
|
|
26
27
|
'hooks:on:request:complete': {
|
|
27
28
|
payload: {
|
|
28
29
|
response: Response;
|
|
29
|
-
|
|
30
|
+
requestPayload: RequestPayload;
|
|
30
31
|
duration: number;
|
|
31
32
|
timestamp: number;
|
|
32
33
|
} | undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../../../src/events/definitions/hooks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,gCAAgC,CAAA;
|
|
1
|
+
{"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../../../src/events/definitions/hooks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,gCAAgC,CAAA;AAC1E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yCAAyC,CAAA;AAE7E;;;;;;GAMG;AACH,MAAM,MAAM,WAAW,GAAG;IACxB;;;;;OAKG;IACH,uBAAuB,EAAE;QACvB,IAAI,EAAE,oBAAoB,CAAA;KAC3B,CAAA;IACD;;;;;;OAMG;IACH,2BAA2B,EAAE;QAC3B,OAAO,EACH;YACE,QAAQ,EAAE,QAAQ,CAAA;YAClB,cAAc,EAAE,cAAc,CAAA;YAC9B,QAAQ,EAAE,MAAM,CAAA;YAChB,SAAS,EAAE,MAAM,CAAA;SAClB,GACD,SAAS,CAAA;QACb,IAAI,EAAE,oBAAoB,CAAA;KAC3B,CAAA;IACD;;;;OAIG;IACH,mCAAmC,EAAE;QACnC,IAAI,EAAE;YACJ,YAAY,EAAE,MAAM,CAAA;SACrB,CAAA;KACF,CAAA;CACF,CAAA"}
|
|
@@ -1,62 +1,52 @@
|
|
|
1
1
|
import type { HarRequest } from '@scalar/snippetz';
|
|
2
|
+
import type { RequestPayload } from '../../../request-example/builder/build-request.js';
|
|
2
3
|
type FetchRequestToHarProps = {
|
|
3
|
-
/** The
|
|
4
|
-
|
|
4
|
+
/** The [url, RequestInit] tuple to convert, as returned by buildRequest */
|
|
5
|
+
requestPayload: RequestPayload;
|
|
5
6
|
/**
|
|
6
7
|
* Whether to include the request body in the HAR postData.
|
|
7
|
-
* Note: Reading the body consumes it, so the request will be cloned automatically.
|
|
8
8
|
* @default true
|
|
9
9
|
*/
|
|
10
10
|
includeBody?: boolean;
|
|
11
11
|
/**
|
|
12
|
-
* HTTP version string to use (
|
|
12
|
+
* HTTP version string to use (Fetch API does not expose this).
|
|
13
13
|
* @default 'HTTP/1.1'
|
|
14
14
|
*/
|
|
15
15
|
httpVersion?: string;
|
|
16
16
|
/**
|
|
17
|
-
*
|
|
18
|
-
*
|
|
17
|
+
* Maximum body size in bytes to capture in the HAR postData. Bodies larger
|
|
18
|
+
* than this are omitted and recorded with bodySize -1.
|
|
19
|
+
* @default 1048576 (1 MB)
|
|
19
20
|
*/
|
|
20
21
|
bodySizeLimit?: number;
|
|
21
22
|
};
|
|
22
23
|
/**
|
|
23
|
-
* Converts a
|
|
24
|
-
*
|
|
25
|
-
* This function transforms a standard JavaScript Fetch API Request into the
|
|
26
|
-
* HAR format, which is useful for:
|
|
27
|
-
* - Recording HTTP requests for replay or analysis
|
|
28
|
-
* - Creating request fixtures from real API calls
|
|
29
|
-
* - Debugging and monitoring HTTP traffic
|
|
30
|
-
* - Storing request history in a standard format
|
|
31
|
-
* - Generating API documentation from real requests
|
|
24
|
+
* Converts a RequestPayload (url + RequestInit tuple) to HAR (HTTP Archive) Request format.
|
|
32
25
|
*
|
|
33
26
|
* The conversion handles:
|
|
34
27
|
* - Request method and URL
|
|
35
|
-
* - Headers extraction
|
|
28
|
+
* - Headers extraction
|
|
36
29
|
* - Query parameters extraction from URL
|
|
37
30
|
* - Cookie extraction from headers
|
|
38
|
-
* - Request body reading (with automatic cloning to preserve the original)
|
|
39
31
|
* - Content-Type detection and MIME type extraction
|
|
40
32
|
* - Size calculations for headers and body
|
|
41
|
-
* -
|
|
42
|
-
* -
|
|
43
|
-
*
|
|
44
|
-
* Note: The Fetch API does not expose the HTTP version, so it defaults to HTTP/1.1
|
|
45
|
-
* unless specified otherwise.
|
|
33
|
+
* - FormData and URLSearchParams bodies are converted to a params array
|
|
34
|
+
* - String, Blob, and ArrayBuffer bodies are read as text
|
|
35
|
+
* - Binary (octet-stream) and ReadableStream bodies are skipped
|
|
46
36
|
*
|
|
47
37
|
* @see https://w3c.github.io/web-performance/specs/HAR/Overview.html
|
|
48
|
-
* @see https://developer.mozilla.org/en-US/docs/Web/API/Request
|
|
49
38
|
*
|
|
50
39
|
* @example
|
|
51
|
-
* const
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
40
|
+
* const harRequest = await fetchRequestToHar({
|
|
41
|
+
* requestPayload: ['https://api.example.com/users', {
|
|
42
|
+
* method: 'POST',
|
|
43
|
+
* headers: { 'Content-Type': 'application/json' },
|
|
44
|
+
* body: JSON.stringify({ name: 'John' }),
|
|
45
|
+
* }],
|
|
55
46
|
* })
|
|
56
|
-
* const harRequest = await fetchRequestToHar({ request })
|
|
57
47
|
* console.log(harRequest.method) // 'POST'
|
|
58
48
|
* console.log(harRequest.postData?.text) // '{"name":"John"}'
|
|
59
49
|
*/
|
|
60
|
-
export declare const fetchRequestToHar: ({
|
|
50
|
+
export declare const fetchRequestToHar: ({ requestPayload, includeBody, httpVersion, bodySizeLimit, }: FetchRequestToHarProps) => Promise<HarRequest>;
|
|
61
51
|
export {};
|
|
62
52
|
//# sourceMappingURL=fetch-request-to-har.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fetch-request-to-har.d.ts","sourceRoot":"","sources":["../../../../src/mutators/operation/helpers/fetch-request-to-har.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAElD,KAAK,sBAAsB,GAAG;IAC5B,
|
|
1
|
+
{"version":3,"file":"fetch-request-to-har.d.ts","sourceRoot":"","sources":["../../../../src/mutators/operation/helpers/fetch-request-to-har.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAElD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yCAAyC,CAAA;AAE7E,KAAK,sBAAsB,GAAG;IAC5B,2EAA2E;IAC3E,cAAc,EAAE,cAAc,CAAA;IAC9B;;;OAGG;IACH,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,eAAO,MAAM,iBAAiB,GAAU,8DAKrC,sBAAsB,KAAG,OAAO,CAAC,UAAU,CAoD7C,CAAA"}
|
|
@@ -1,56 +1,46 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Converts a
|
|
3
|
-
*
|
|
4
|
-
* This function transforms a standard JavaScript Fetch API Request into the
|
|
5
|
-
* HAR format, which is useful for:
|
|
6
|
-
* - Recording HTTP requests for replay or analysis
|
|
7
|
-
* - Creating request fixtures from real API calls
|
|
8
|
-
* - Debugging and monitoring HTTP traffic
|
|
9
|
-
* - Storing request history in a standard format
|
|
10
|
-
* - Generating API documentation from real requests
|
|
2
|
+
* Converts a RequestPayload (url + RequestInit tuple) to HAR (HTTP Archive) Request format.
|
|
11
3
|
*
|
|
12
4
|
* The conversion handles:
|
|
13
5
|
* - Request method and URL
|
|
14
|
-
* - Headers extraction
|
|
6
|
+
* - Headers extraction
|
|
15
7
|
* - Query parameters extraction from URL
|
|
16
8
|
* - Cookie extraction from headers
|
|
17
|
-
* - Request body reading (with automatic cloning to preserve the original)
|
|
18
9
|
* - Content-Type detection and MIME type extraction
|
|
19
10
|
* - Size calculations for headers and body
|
|
20
|
-
* -
|
|
21
|
-
* -
|
|
22
|
-
*
|
|
23
|
-
* Note: The Fetch API does not expose the HTTP version, so it defaults to HTTP/1.1
|
|
24
|
-
* unless specified otherwise.
|
|
11
|
+
* - FormData and URLSearchParams bodies are converted to a params array
|
|
12
|
+
* - String, Blob, and ArrayBuffer bodies are read as text
|
|
13
|
+
* - Binary (octet-stream) and ReadableStream bodies are skipped
|
|
25
14
|
*
|
|
26
15
|
* @see https://w3c.github.io/web-performance/specs/HAR/Overview.html
|
|
27
|
-
* @see https://developer.mozilla.org/en-US/docs/Web/API/Request
|
|
28
16
|
*
|
|
29
17
|
* @example
|
|
30
|
-
* const
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
18
|
+
* const harRequest = await fetchRequestToHar({
|
|
19
|
+
* requestPayload: ['https://api.example.com/users', {
|
|
20
|
+
* method: 'POST',
|
|
21
|
+
* headers: { 'Content-Type': 'application/json' },
|
|
22
|
+
* body: JSON.stringify({ name: 'John' }),
|
|
23
|
+
* }],
|
|
34
24
|
* })
|
|
35
|
-
* const harRequest = await fetchRequestToHar({ request })
|
|
36
25
|
* console.log(harRequest.method) // 'POST'
|
|
37
26
|
* console.log(harRequest.postData?.text) // '{"name":"John"}'
|
|
38
27
|
*/
|
|
39
|
-
export const fetchRequestToHar = async ({
|
|
40
|
-
|
|
41
|
-
bodySizeLimit = 1048576, }) => {
|
|
28
|
+
export const fetchRequestToHar = async ({ requestPayload, includeBody = true, httpVersion = 'HTTP/1.1', bodySizeLimit = 1048576, }) => {
|
|
29
|
+
const [originalUrl, requestInit] = requestPayload;
|
|
42
30
|
// Extract query string from URL
|
|
43
|
-
const url = new URL(
|
|
31
|
+
const url = new URL(originalUrl);
|
|
44
32
|
// Extract the query strings from the URL
|
|
45
33
|
const queryString = Array.from(url.searchParams.entries()).map(([name, value]) => ({ name, value }));
|
|
46
|
-
//
|
|
47
|
-
const
|
|
34
|
+
// Normalize HeadersInit to a Headers instance so we can call .get() and .entries() safely
|
|
35
|
+
const _headers = new Headers(requestInit.headers);
|
|
48
36
|
// Extract the MIME type from the request headers
|
|
49
|
-
const mimeType =
|
|
37
|
+
const mimeType = _headers.get('content-type')?.split(';')[0]?.trim() ?? 'text/plain';
|
|
38
|
+
// Extract the headers from the request
|
|
39
|
+
const { headers, headersSize, cookies } = processRequestHeaders(_headers);
|
|
50
40
|
// Read the request body if requested
|
|
51
41
|
const bodyDetails = await (async () => {
|
|
52
|
-
if (includeBody &&
|
|
53
|
-
const details = await processRequestBody(
|
|
42
|
+
if (includeBody && requestInit.body != null) {
|
|
43
|
+
const details = await processRequestBody(requestInit.body, mimeType);
|
|
54
44
|
if (details.size <= bodySizeLimit) {
|
|
55
45
|
return details;
|
|
56
46
|
}
|
|
@@ -59,8 +49,8 @@ bodySizeLimit = 1048576, }) => {
|
|
|
59
49
|
})();
|
|
60
50
|
// Create the HAR request object
|
|
61
51
|
const harRequest = {
|
|
62
|
-
method:
|
|
63
|
-
url:
|
|
52
|
+
method: requestInit.method ?? 'GET',
|
|
53
|
+
url: originalUrl,
|
|
64
54
|
httpVersion,
|
|
65
55
|
headers,
|
|
66
56
|
cookies,
|
|
@@ -79,50 +69,64 @@ bodySizeLimit = 1048576, }) => {
|
|
|
79
69
|
};
|
|
80
70
|
return harRequest;
|
|
81
71
|
};
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
acc.params.push({ name, value });
|
|
93
|
-
acc.size += value.length;
|
|
94
|
-
return acc;
|
|
95
|
-
}, { params: [], size: 0 });
|
|
72
|
+
/**
|
|
73
|
+
* Extracts HAR body details from a BodyInit value.
|
|
74
|
+
*
|
|
75
|
+
* Because we own the RequestInit tuple we can inspect the body by type directly —
|
|
76
|
+
* no stream cloning or header sniffing required.
|
|
77
|
+
*/
|
|
78
|
+
const processRequestBody = async (body, contentType) => {
|
|
79
|
+
// Structured form payloads become a params array so HAR viewers can render them as key/value tables
|
|
80
|
+
if (body instanceof FormData) {
|
|
81
|
+
return extractFormDataParams(body);
|
|
96
82
|
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
return { text: '', size: -1 };
|
|
83
|
+
if (body instanceof URLSearchParams) {
|
|
84
|
+
return extractUrlSearchParams(body);
|
|
100
85
|
}
|
|
101
|
-
//
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
return { size, text: new TextDecoder().decode(arrayBuffer) };
|
|
105
|
-
};
|
|
106
|
-
async function tryGetRequestFormData(request) {
|
|
107
|
-
if (typeof request.formData !== 'function') {
|
|
108
|
-
return null;
|
|
86
|
+
// HAR text fields cannot represent arbitrary binary; skip rather than corrupt the entry
|
|
87
|
+
if (contentType.includes('application/octet-stream')) {
|
|
88
|
+
return { text: '', size: -1 };
|
|
109
89
|
}
|
|
110
|
-
if (
|
|
111
|
-
|
|
90
|
+
if (typeof body === 'string') {
|
|
91
|
+
// Use byte length, not character length, to match what the wire sends for multi-byte UTF-8
|
|
92
|
+
const size = new TextEncoder().encode(body).byteLength;
|
|
93
|
+
return { text: body, size };
|
|
112
94
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
return
|
|
95
|
+
if (body instanceof Blob) {
|
|
96
|
+
const text = await body.text();
|
|
97
|
+
return { text, size: body.size };
|
|
116
98
|
}
|
|
117
|
-
|
|
118
|
-
return
|
|
99
|
+
if (body instanceof ArrayBuffer) {
|
|
100
|
+
return { text: new TextDecoder().decode(body), size: body.byteLength };
|
|
119
101
|
}
|
|
120
|
-
|
|
121
|
-
return
|
|
102
|
+
if (ArrayBuffer.isView(body)) {
|
|
103
|
+
return { text: new TextDecoder().decode(body), size: body.byteLength };
|
|
122
104
|
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
105
|
+
// ReadableStream cannot be read without consuming it
|
|
106
|
+
return { text: '', size: -1 };
|
|
107
|
+
};
|
|
108
|
+
const extractFormDataParams = (formData) => {
|
|
109
|
+
return Array.from(formData.entries()).reduce((acc, [name, value]) => {
|
|
110
|
+
if (value instanceof File) {
|
|
111
|
+
const fileName = `@${value.name}`;
|
|
112
|
+
acc.params.push({ name, value: fileName });
|
|
113
|
+
acc.size += fileName.length;
|
|
114
|
+
return acc;
|
|
115
|
+
}
|
|
116
|
+
acc.params.push({ name, value });
|
|
117
|
+
acc.size += value.length;
|
|
118
|
+
return acc;
|
|
119
|
+
}, { params: [], size: 0 });
|
|
120
|
+
};
|
|
121
|
+
const extractUrlSearchParams = (params) => {
|
|
122
|
+
return Array.from(params.entries()).reduce((acc, [name, value]) => {
|
|
123
|
+
acc.params.push({ name, value });
|
|
124
|
+
acc.size += name.length + value.length;
|
|
125
|
+
return acc;
|
|
126
|
+
}, { params: [], size: 0 });
|
|
127
|
+
};
|
|
128
|
+
const processRequestHeaders = (headers) => {
|
|
129
|
+
return Array.from(headers.entries()).reduce((acc, [name, value]) => {
|
|
126
130
|
if (name.toLowerCase() === 'cookie') {
|
|
127
131
|
const parsedCookies = parseCookieHeader(value);
|
|
128
132
|
acc.cookies.push(...parsedCookies.cookies);
|
|
@@ -24,7 +24,7 @@ export const addResponseToHistory = async (store, document, { payload, meta }) =
|
|
|
24
24
|
}
|
|
25
25
|
return acc;
|
|
26
26
|
}, {});
|
|
27
|
-
const requestHar = await fetchRequestToHar({
|
|
27
|
+
const requestHar = await fetchRequestToHar({ requestPayload: payload.requestPayload });
|
|
28
28
|
const responseHar = await fetchResponseToHar({ response: payload.response });
|
|
29
29
|
store?.history.addHistory(documentName, meta.path, meta.method, {
|
|
30
30
|
response: responseHar,
|
|
@@ -1,9 +1,24 @@
|
|
|
1
1
|
import type { RequestFactory } from '../../request-example/builder/request-factory.js';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
/**
|
|
3
|
+
* The payload to build a request, useful when bypassing limitations of the browser Request object
|
|
4
|
+
*/
|
|
5
|
+
export type RequestPayload = [string, RequestInit];
|
|
6
|
+
/**
|
|
7
|
+
* Built request response
|
|
8
|
+
*
|
|
9
|
+
* We no longer return a Request object, but a tuple of [url, init] that maps directly to the fetch() argument list so
|
|
10
|
+
* we can do things that the browser doesn't allow like GET + body
|
|
11
|
+
* */
|
|
12
|
+
type BuildRequestResponse = {
|
|
13
|
+
/** Create a new request payload object with the replaced values ready to be sent to the server */
|
|
14
|
+
requestPayload: RequestPayload;
|
|
15
|
+
/** The abort controller */
|
|
6
16
|
controller: AbortController;
|
|
17
|
+
/** The flag indicating if the request is being proxied */
|
|
7
18
|
isUsingProxy: boolean;
|
|
8
19
|
};
|
|
20
|
+
export declare const buildRequest: (request: RequestFactory, options: {
|
|
21
|
+
envVariables: Record<string, string>;
|
|
22
|
+
}) => BuildRequestResponse;
|
|
23
|
+
export {};
|
|
9
24
|
//# sourceMappingURL=build-request.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build-request.d.ts","sourceRoot":"","sources":["../../../src/request-example/builder/build-request.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2CAA2C,CAAA;
|
|
1
|
+
{"version":3,"file":"build-request.d.ts","sourceRoot":"","sources":["../../../src/request-example/builder/build-request.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2CAA2C,CAAA;AAK/E;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAAA;AAElD;;;;;KAKK;AACL,KAAK,oBAAoB,GAAG;IAC1B,kGAAkG;IAClG,cAAc,EAAE,cAAc,CAAA;IAC9B,2BAA2B;IAC3B,UAAU,EAAE,eAAe,CAAA;IAC3B,0DAA0D;IAC1D,YAAY,EAAE,OAAO,CAAA;CACtB,CAAA;AAED,eAAO,MAAM,YAAY,GACvB,SAAS,cAAc,EACvB,SAAS;IACP,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CACrC,KACA,oBA2JF,CAAA"}
|
|
@@ -4,21 +4,30 @@ import { encode as encodeBase64 } from 'js-base64';
|
|
|
4
4
|
import { buildRequestCookieHeader } from '../../request-example/builder/header/build-request-cookie-header.js';
|
|
5
5
|
import { applyAllowReservedToUrl } from '../../request-example/builder/helpers/apply-allow-reserved-to-url.js';
|
|
6
6
|
import { resolveRequestFactoryUrl } from '../../request-example/builder/resolve-request-factory-url.js';
|
|
7
|
+
import { contextFunctions, isContextFunctionName } from '../../request-example/functions.js';
|
|
7
8
|
export const buildRequest = (request, options) => {
|
|
9
|
+
/** Replace the value with the environment variable or context function */
|
|
10
|
+
const replace = (value) => {
|
|
11
|
+
if (isContextFunctionName(value)) {
|
|
12
|
+
return contextFunctions[value].fn() ?? null;
|
|
13
|
+
}
|
|
14
|
+
return options.envVariables[value] ?? null;
|
|
15
|
+
};
|
|
16
|
+
/** Create a new abort controller */
|
|
8
17
|
const controller = new AbortController();
|
|
18
|
+
/** Create a new headers object with the replaced values */
|
|
9
19
|
const headers = (() => {
|
|
10
|
-
const variables = options.envVariables;
|
|
11
20
|
const headers = new Headers();
|
|
12
21
|
request.headers.forEach((value, key) => {
|
|
13
|
-
headers.set(replaceEnvVariables(key,
|
|
22
|
+
headers.set(replaceEnvVariables(key, replace), replaceEnvVariables(value, replace));
|
|
14
23
|
});
|
|
15
24
|
return headers;
|
|
16
25
|
})();
|
|
26
|
+
/** Create a new body object with the replaced values */
|
|
17
27
|
const body = (() => {
|
|
18
|
-
const variables = options.envVariables;
|
|
19
28
|
if (request.body?.mode === 'raw') {
|
|
20
29
|
if (typeof request.body.value === 'string') {
|
|
21
|
-
return replaceEnvVariables(request.body.value,
|
|
30
|
+
return replaceEnvVariables(request.body.value, replace);
|
|
22
31
|
}
|
|
23
32
|
return request.body.value;
|
|
24
33
|
}
|
|
@@ -26,33 +35,33 @@ export const buildRequest = (request, options) => {
|
|
|
26
35
|
const form = new FormData();
|
|
27
36
|
request.body.value.forEach((item) => {
|
|
28
37
|
if (item.type === 'text') {
|
|
29
|
-
form.append(replaceEnvVariables(item.key,
|
|
38
|
+
form.append(replaceEnvVariables(item.key, replace), replaceEnvVariables(item.value, replace));
|
|
30
39
|
return;
|
|
31
40
|
}
|
|
32
|
-
form.append(replaceEnvVariables(item.key,
|
|
41
|
+
form.append(replaceEnvVariables(item.key, replace), item.value);
|
|
33
42
|
});
|
|
34
43
|
return form;
|
|
35
44
|
}
|
|
36
45
|
if (request.body?.mode === 'urlencoded') {
|
|
37
46
|
return new URLSearchParams(request.body.value.map((item) => [
|
|
38
|
-
replaceEnvVariables(item.key,
|
|
39
|
-
replaceEnvVariables(item.value,
|
|
47
|
+
replaceEnvVariables(item.key, replace),
|
|
48
|
+
replaceEnvVariables(item.value, replace),
|
|
40
49
|
]));
|
|
41
50
|
}
|
|
42
51
|
return null;
|
|
43
52
|
})();
|
|
44
53
|
const securityQueryParams = new URLSearchParams();
|
|
45
54
|
const securityCookies = [];
|
|
46
|
-
|
|
55
|
+
/** Build the request security unless the consumer opted out via disableSecurity */
|
|
47
56
|
if (!request.options?.disableSecurity) {
|
|
48
57
|
request.security.forEach((security) => {
|
|
49
|
-
const name = replaceEnvVariables(security.name,
|
|
58
|
+
const name = replaceEnvVariables(security.name, replace);
|
|
50
59
|
// Format the security value based on its authentication scheme.
|
|
51
60
|
// - For 'basic': prefix with 'Basic' and base64-encode the value (username:password).
|
|
52
61
|
// - For 'bearer': prefix with 'Bearer'.
|
|
53
62
|
// - Otherwise: use the substituted value as is (for API keys, etc).
|
|
54
63
|
const securityValue = (() => {
|
|
55
|
-
const substitutedValue = replaceEnvVariables(security.value,
|
|
64
|
+
const substitutedValue = replaceEnvVariables(security.value, replace);
|
|
56
65
|
if (security.format === 'basic') {
|
|
57
66
|
return `Basic ${encodeBase64(substitutedValue)}`;
|
|
58
67
|
}
|
|
@@ -63,11 +72,11 @@ export const buildRequest = (request, options) => {
|
|
|
63
72
|
})();
|
|
64
73
|
if (security.in === 'header') {
|
|
65
74
|
// Set the header (use replaced header name so {{ env }} placeholders work)
|
|
66
|
-
headers.
|
|
75
|
+
headers.append(name, securityValue);
|
|
67
76
|
return;
|
|
68
77
|
}
|
|
69
78
|
if (security.in === 'query') {
|
|
70
|
-
securityQueryParams.
|
|
79
|
+
securityQueryParams.append(name, securityValue);
|
|
71
80
|
return;
|
|
72
81
|
}
|
|
73
82
|
if (security.in === 'cookie') {
|
|
@@ -79,42 +88,49 @@ export const buildRequest = (request, options) => {
|
|
|
79
88
|
}
|
|
80
89
|
});
|
|
81
90
|
}
|
|
91
|
+
/** Resolve the request URL with the replaced values */
|
|
82
92
|
const requestUrl = resolveRequestFactoryUrl(request, {
|
|
83
|
-
envVariables:
|
|
93
|
+
envVariables: replace,
|
|
84
94
|
securityQueryParams: securityQueryParams,
|
|
85
95
|
});
|
|
96
|
+
/** Check if the request should be proxied */
|
|
86
97
|
const isUsingProxy = shouldUseProxy(request.proxyUrl, requestUrl);
|
|
98
|
+
/** Create a new cookies array with the replaced values */
|
|
87
99
|
const cookies = [...request.cookies, ...securityCookies].map((c) => ({
|
|
88
100
|
...c,
|
|
89
|
-
name: replaceEnvVariables(c.name,
|
|
90
|
-
value: replaceEnvVariables(c.value,
|
|
101
|
+
name: replaceEnvVariables(c.name, replace),
|
|
102
|
+
value: replaceEnvVariables(c.value, replace),
|
|
91
103
|
}));
|
|
104
|
+
/** Build the request cookie header */
|
|
92
105
|
const cookieHeader = buildRequestCookieHeader({
|
|
93
106
|
cookies,
|
|
94
107
|
originalCookieHeader: headers.get('cookie'),
|
|
95
108
|
url: requestUrl,
|
|
96
109
|
useCustomCookieHeader: (isUsingProxy || request.options?.isElectron) ?? false,
|
|
97
110
|
});
|
|
98
|
-
|
|
111
|
+
/** Add the cookie header to the headers */
|
|
99
112
|
if (cookieHeader) {
|
|
100
113
|
headers.set(cookieHeader.name, cookieHeader.value);
|
|
101
114
|
}
|
|
102
|
-
|
|
115
|
+
/** Encode the URL with the allowed reserved query parameters */
|
|
103
116
|
const encodedUrl = applyAllowReservedToUrl(requestUrl, request.allowedReservedQueryParameters ?? new Set());
|
|
104
117
|
const finalUrl = isUsingProxy ? redirectToProxy(request.proxyUrl, encodedUrl) : encodedUrl;
|
|
105
118
|
return {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
119
|
+
requestPayload: [
|
|
120
|
+
finalUrl,
|
|
121
|
+
{
|
|
122
|
+
/**
|
|
123
|
+
* Ensure that all methods are uppercased (though only needed for patch)
|
|
124
|
+
*
|
|
125
|
+
* @see https://github.com/whatwg/fetch/issues/50
|
|
126
|
+
*/
|
|
127
|
+
method: request.method.toUpperCase(),
|
|
128
|
+
headers,
|
|
129
|
+
body,
|
|
130
|
+
cache: request.cache,
|
|
131
|
+
signal: controller.signal,
|
|
132
|
+
},
|
|
133
|
+
],
|
|
118
134
|
controller,
|
|
119
135
|
isUsingProxy,
|
|
120
136
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { getExampleFromBody } from './body/get-request-body-example.js';
|
|
2
2
|
export { getSelectedBodyContentType } from './body/get-selected-body-content-type.js';
|
|
3
|
-
export { buildRequest } from './build-request.js';
|
|
3
|
+
export { type RequestPayload, buildRequest } from './build-request.js';
|
|
4
4
|
export { deSerializeParameter } from './header/de-serialize-parameter.js';
|
|
5
5
|
export { filterGlobalCookie } from './header/filter-global-cookies.js';
|
|
6
6
|
export { serializeContentValue, serializeDeepObjectStyle, serializeFormStyle, serializeFormStyleForCookies, serializePipeDelimitedStyle, serializeSimpleStyle, serializeSpaceDelimitedStyle, } from './header/serialize-parameter.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/request-example/builder/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAA;AACpE,OAAO,EAAE,0BAA0B,EAAE,MAAM,uCAAuC,CAAA;AAClF,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/request-example/builder/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAA;AACpE,OAAO,EAAE,0BAA0B,EAAE,MAAM,uCAAuC,CAAA;AAClF,OAAO,EAAE,KAAK,cAAc,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,iCAAiC,CAAA;AACtE,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAA;AACnE,OAAO,EACL,qBAAqB,EACrB,wBAAwB,EACxB,kBAAkB,EAClB,4BAA4B,EAC5B,2BAA2B,EAC3B,oBAAoB,EACpB,4BAA4B,GAC7B,MAAM,8BAA8B,CAAA;AACrC,OAAO,EAAE,uBAAuB,EAAE,MAAM,qCAAqC,CAAA;AAC7E,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAA;AAClD,OAAO,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAA;AACxE,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAA;AAC3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAA;AACnE,YAAY,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AAClD,OAAO,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAA;AACxE,YAAY,EACV,kBAAkB,EAClB,gBAAgB,EAChB,kBAAkB,EAClB,gCAAgC,EAChC,gCAAgC,EAChC,uBAAuB,EACvB,uBAAuB,EACvB,sBAAsB,EACtB,yBAAyB,EACzB,0BAA0B,GAC3B,MAAM,yBAAyB,CAAA"}
|
|
@@ -5,7 +5,7 @@ import type { RequestFactory } from '../../request-example/builder/request-facto
|
|
|
5
5
|
* without proxy rewriting or reserved-query encoding.
|
|
6
6
|
*/
|
|
7
7
|
export declare const resolveRequestFactoryUrl: (request: RequestFactory, options: {
|
|
8
|
-
envVariables: Record<string, string
|
|
8
|
+
envVariables: Record<string, string> | ((value: string) => string | null);
|
|
9
9
|
securityQueryParams: URLSearchParams;
|
|
10
10
|
}) => string;
|
|
11
11
|
//# sourceMappingURL=resolve-request-factory-url.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolve-request-factory-url.d.ts","sourceRoot":"","sources":["../../../src/request-example/builder/resolve-request-factory-url.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2CAA2C,CAAA;AAE/E;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,GACnC,SAAS,cAAc,EACvB,SAAS;
|
|
1
|
+
{"version":3,"file":"resolve-request-factory-url.d.ts","sourceRoot":"","sources":["../../../src/request-example/builder/resolve-request-factory-url.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2CAA2C,CAAA;AAE/E;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,GACnC,SAAS,cAAc,EACvB,SAAS;IACP,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC,CAAA;IACzE,mBAAmB,EAAE,eAAe,CAAA;CACrC,KACA,MAiCF,CAAA"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { replaceEnvVariables, replacePathVariables } from '@scalar/helpers/regex/replace-variables';
|
|
2
|
-
import { mergeUrls } from '@scalar/helpers/url/merge-urls';
|
|
2
|
+
import { mergeSearchParams, mergeUrls } from '@scalar/helpers/url/merge-urls';
|
|
3
3
|
/**
|
|
4
4
|
* Resolves the request URL string from a {@link RequestFactory} using the same
|
|
5
5
|
* rules as {@link buildRequest} (path variables, query, security query params),
|
|
@@ -14,15 +14,20 @@ export const resolveRequestFactoryUrl = (request, options) => {
|
|
|
14
14
|
const baseUrl = replaceEnvVariables(request.baseUrl, variables);
|
|
15
15
|
const path = replacePathVariables(request.path.raw, pathVariables);
|
|
16
16
|
const mergedUrl = mergeUrls(baseUrl, path);
|
|
17
|
-
|
|
17
|
+
// When rendered inside an iframe with srcdoc, the browser reports
|
|
18
|
+
// window.location.origin as the string "null" instead of a real origin.
|
|
19
|
+
// Fall back to localhost so relative URLs can still be resolved.
|
|
20
|
+
const origin = globalThis.window?.location?.origin;
|
|
21
|
+
const urlBase = origin && origin !== 'null' ? origin : 'http://localhost:3000';
|
|
18
22
|
const url = new URL(mergedUrl, urlBase);
|
|
19
|
-
|
|
23
|
+
const operationQueryParams = new URLSearchParams();
|
|
20
24
|
for (const [key, value] of request.query.entries()) {
|
|
21
|
-
|
|
25
|
+
operationQueryParams.append(replaceEnvVariables(key, variables), replaceEnvVariables(value, variables));
|
|
22
26
|
}
|
|
23
|
-
|
|
27
|
+
const securityQueryParams = new URLSearchParams();
|
|
24
28
|
for (const [key, value] of options.securityQueryParams.entries()) {
|
|
25
|
-
|
|
29
|
+
securityQueryParams.append(key, value);
|
|
26
30
|
}
|
|
31
|
+
url.search = mergeSearchParams(url.searchParams, operationQueryParams, securityQueryParams).toString();
|
|
27
32
|
return url.toString();
|
|
28
33
|
};
|
|
@@ -12,7 +12,7 @@ import type { OperationObject } from '../../schemas/v3.1/strict/operation.js';
|
|
|
12
12
|
import type { SecurityRequirementObject } from '../../schemas/v3.1/strict/security-requirement.js';
|
|
13
13
|
import type { ServerObject } from '../../schemas/v3.1/strict/server.js';
|
|
14
14
|
import type { WorkspaceDocument } from '../../schemas/workspace.js';
|
|
15
|
-
type BuildRequestExampleContext = {
|
|
15
|
+
export type BuildRequestExampleContext = {
|
|
16
16
|
operation: OperationObject;
|
|
17
17
|
environment: {
|
|
18
18
|
name: string | null;
|
|
@@ -56,5 +56,4 @@ export declare const getRequestExampleContext: (workspaceStore: WorkspaceStore,
|
|
|
56
56
|
*/
|
|
57
57
|
fallbackDocument: WorkspaceDocument | null;
|
|
58
58
|
}>) => Result<BuildRequestExampleContext>;
|
|
59
|
-
export {};
|
|
60
59
|
//# sourceMappingURL=get-request-example-context.d.ts.map
|