@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.
Files changed (37) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/dist/events/definitions/hooks.d.ts +2 -1
  3. package/dist/events/definitions/hooks.d.ts.map +1 -1
  4. package/dist/mutators/operation/helpers/fetch-request-to-har.d.ts +19 -29
  5. package/dist/mutators/operation/helpers/fetch-request-to-har.d.ts.map +1 -1
  6. package/dist/mutators/operation/helpers/fetch-request-to-har.js +74 -70
  7. package/dist/mutators/operation/history.js +1 -1
  8. package/dist/request-example/builder/build-request.d.ts +19 -4
  9. package/dist/request-example/builder/build-request.d.ts.map +1 -1
  10. package/dist/request-example/builder/build-request.js +46 -30
  11. package/dist/request-example/builder/index.d.ts +1 -1
  12. package/dist/request-example/builder/index.d.ts.map +1 -1
  13. package/dist/request-example/builder/resolve-request-factory-url.d.ts +1 -1
  14. package/dist/request-example/builder/resolve-request-factory-url.d.ts.map +1 -1
  15. package/dist/request-example/builder/resolve-request-factory-url.js +11 -6
  16. package/dist/request-example/context/get-request-example-context.d.ts +1 -2
  17. package/dist/request-example/context/get-request-example-context.d.ts.map +1 -1
  18. package/dist/request-example/context/get-request-example-context.js +1 -1
  19. package/dist/request-example/context/index.d.ts +1 -1
  20. package/dist/request-example/context/index.d.ts.map +1 -1
  21. package/dist/request-example/context/security/get-security-schemes.d.ts +1 -1
  22. package/dist/request-example/context/security/get-security-schemes.d.ts.map +1 -1
  23. package/dist/request-example/context/security/get-security-schemes.js +2 -2
  24. package/dist/request-example/functions.d.ts +486 -0
  25. package/dist/request-example/functions.d.ts.map +1 -0
  26. package/dist/request-example/functions.js +545 -0
  27. package/dist/request-example/index.d.ts +3 -2
  28. package/dist/request-example/index.d.ts.map +1 -1
  29. package/dist/request-example/index.js +1 -0
  30. package/dist/request-example/random-data.d.ts +99 -0
  31. package/dist/request-example/random-data.d.ts.map +1 -0
  32. package/dist/request-example/random-data.js +1181 -0
  33. package/dist/schemas/inmemory-workspace.d.ts +1 -1
  34. package/dist/schemas/reference-config/index.d.ts +1 -1
  35. package/dist/schemas/workspace-specification/index.d.ts +1 -1
  36. package/dist/schemas/workspace.d.ts +2 -2
  37. 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
- request: Request;
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;AAE1E;;;;;;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,OAAO,EAAE,OAAO,CAAA;YAChB,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
+ {"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 Fetch API Request object to convert */
4
- request: Request;
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 (since Fetch API does not expose this)
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
- * The maximum size of the request body to include in the HAR postData.
18
- * @default 1MB
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 Fetch API Request object to HAR (HTTP Archive) Request format.
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 (excluding sensitive headers if needed)
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
- * - Form data bodies are converted to params array
42
- * - Other body types are read as text
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 request = new Request('https://api.example.com/users', {
52
- * method: 'POST',
53
- * headers: { 'Content-Type': 'application/json' },
54
- * body: JSON.stringify({ name: 'John' })
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: ({ request, includeBody, httpVersion, bodySizeLimit, }: FetchRequestToHarProps) => Promise<HarRequest>;
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,8CAA8C;IAC9C,OAAO,EAAE,OAAO,CAAA;IAChB;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,eAAO,MAAM,iBAAiB,GAAU,uDAMrC,sBAAsB,KAAG,OAAO,CAAC,UAAU,CA+C7C,CAAA"}
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 Fetch API Request object to HAR (HTTP Archive) Request format.
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 (excluding sensitive headers if needed)
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
- * - Form data bodies are converted to params array
21
- * - Other body types are read as text
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 request = new Request('https://api.example.com/users', {
31
- * method: 'POST',
32
- * headers: { 'Content-Type': 'application/json' },
33
- * body: JSON.stringify({ name: 'John' })
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 ({ request, includeBody = true, httpVersion = 'HTTP/1.1',
40
- // Default to 1MB
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(request.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
- // Extract the headers from the request
47
- const { headers, headersSize, cookies } = processRequestHeaders(request);
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 = request.headers.get('content-type')?.split(';')[0]?.trim() ?? 'text/plain';
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 && request.body) {
53
- const details = await processRequestBody(request.clone());
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: request.method,
63
- url: request.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
- const processRequestBody = async (request) => {
83
- const formData = await tryGetRequestFormData(request.clone());
84
- if (formData) {
85
- return Array.from(formData.entries()).reduce((acc, [name, value]) => {
86
- if (value instanceof File) {
87
- const fileName = `@${value.name}`;
88
- acc.params.push({ name, value: fileName });
89
- acc.size += fileName.length;
90
- return acc;
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
- // Skip binary bodies
98
- if (request.headers.get('content-type')?.includes('application/octet-stream')) {
99
- return { text: '', size: -1 };
83
+ if (body instanceof URLSearchParams) {
84
+ return extractUrlSearchParams(body);
100
85
  }
101
- // Read the request body as text
102
- const arrayBuffer = await request.arrayBuffer();
103
- const size = arrayBuffer.byteLength;
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 (request.bodyUsed) {
111
- return null;
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
- const contentType = request.headers.get('content-type') ?? '';
114
- if (!contentType.includes('multipart/form-data') && !contentType.includes('application/x-www-form-urlencoded')) {
115
- return null;
95
+ if (body instanceof Blob) {
96
+ const text = await body.text();
97
+ return { text, size: body.size };
116
98
  }
117
- try {
118
- return await request.formData();
99
+ if (body instanceof ArrayBuffer) {
100
+ return { text: new TextDecoder().decode(body), size: body.byteLength };
119
101
  }
120
- catch {
121
- return null;
102
+ if (ArrayBuffer.isView(body)) {
103
+ return { text: new TextDecoder().decode(body), size: body.byteLength };
122
104
  }
123
- }
124
- const processRequestHeaders = (request) => {
125
- return Array.from(request.headers.entries()).reduce((acc, [name, value]) => {
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({ request: payload.request });
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
- export declare const buildRequest: (request: RequestFactory, options: {
3
- envVariables: Record<string, string>;
4
- }) => {
5
- request: Request;
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;AAI/E,eAAO,MAAM,YAAY,GACvB,SAAS,cAAc,EACvB,SAAS;IACP,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CACrC;;;;CA4IF,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, variables), replaceEnvVariables(value, variables));
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, variables);
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, variables), replaceEnvVariables(item.value, variables));
38
+ form.append(replaceEnvVariables(item.key, replace), replaceEnvVariables(item.value, replace));
30
39
  return;
31
40
  }
32
- form.append(replaceEnvVariables(item.key, variables), item.value);
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, variables),
39
- replaceEnvVariables(item.value, variables),
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
- // Build the request security unless the consumer opted out via disableSecurity
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, options.envVariables);
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, options.envVariables);
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.set(name, securityValue);
75
+ headers.append(name, securityValue);
67
76
  return;
68
77
  }
69
78
  if (security.in === 'query') {
70
- securityQueryParams.set(name, securityValue);
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: options.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, options.envVariables),
90
- value: replaceEnvVariables(c.value, options.envVariables),
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
- // Add the cookie header to the headers
111
+ /** Add the cookie header to the headers */
99
112
  if (cookieHeader) {
100
113
  headers.set(cookieHeader.name, cookieHeader.value);
101
114
  }
102
- // final url
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
- request: new Request(finalUrl, {
107
- /**
108
- * Ensure that all methods are uppercased (though only needed for patch)
109
- *
110
- * @see https://github.com/whatwg/fetch/issues/50
111
- */
112
- method: request.method.toUpperCase(),
113
- headers,
114
- body,
115
- cache: request.cache,
116
- signal: controller.signal,
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;AAC9C,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"}
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;IAAE,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,mBAAmB,EAAE,eAAe,CAAA;CAAE,KACtF,MA2BF,CAAA"}
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
- const urlBase = globalThis.window?.location?.origin ?? 'http://localhost:3000';
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
- // Merge in operation query params
23
+ const operationQueryParams = new URLSearchParams();
20
24
  for (const [key, value] of request.query.entries()) {
21
- url.searchParams.set(replaceEnvVariables(key, variables), replaceEnvVariables(value, variables));
25
+ operationQueryParams.append(replaceEnvVariables(key, variables), replaceEnvVariables(value, variables));
22
26
  }
23
- // Merge in security query params
27
+ const securityQueryParams = new URLSearchParams();
24
28
  for (const [key, value] of options.securityQueryParams.entries()) {
25
- url.searchParams.set(key, value);
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