@scalar/mock-server 0.14.4 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/CHANGELOG.md +66 -0
  2. package/dist/create-asyncapi-mock-server.d.ts +8 -10
  3. package/dist/create-asyncapi-mock-server.d.ts.map +1 -1
  4. package/dist/create-asyncapi-mock-server.js +6 -10
  5. package/dist/create-mock-server.d.ts.map +1 -1
  6. package/dist/create-mock-server.js +36 -17
  7. package/dist/routes/mock-any-response.d.ts.map +1 -1
  8. package/dist/routes/mock-any-response.js +28 -13
  9. package/dist/routes/mock-handler-response.d.ts +1 -2
  10. package/dist/routes/mock-handler-response.d.ts.map +1 -1
  11. package/dist/routes/mock-handler-response.js +15 -10
  12. package/dist/types.d.ts +2 -2
  13. package/dist/types.d.ts.map +1 -1
  14. package/dist/utils/build-handler-context.d.ts +3 -2
  15. package/dist/utils/build-handler-context.d.ts.map +1 -1
  16. package/dist/utils/build-handler-context.js +23 -2
  17. package/dist/utils/get-oauth2-metadata.d.ts +1 -0
  18. package/dist/utils/get-oauth2-metadata.d.ts.map +1 -1
  19. package/dist/utils/get-oauth2-metadata.js +5 -1
  20. package/dist/utils/get-open-auth-token-urls.d.ts +2 -2
  21. package/dist/utils/get-open-auth-token-urls.d.ts.map +1 -1
  22. package/dist/utils/get-open-auth-token-urls.js +5 -3
  23. package/dist/utils/get-operation.d.ts +4 -2
  24. package/dist/utils/get-operation.d.ts.map +1 -1
  25. package/dist/utils/get-operation.js +9 -5
  26. package/dist/utils/handle-authentication.d.ts +2 -2
  27. package/dist/utils/handle-authentication.d.ts.map +1 -1
  28. package/dist/utils/log-authentication-instructions.d.ts +2 -2
  29. package/dist/utils/log-authentication-instructions.d.ts.map +1 -1
  30. package/dist/utils/log-authentication-instructions.js +7 -0
  31. package/dist/utils/negotiate-content-type.d.ts +4 -0
  32. package/dist/utils/negotiate-content-type.d.ts.map +1 -0
  33. package/dist/utils/negotiate-content-type.js +12 -0
  34. package/dist/utils/process-openapi-document.d.ts +7 -4
  35. package/dist/utils/process-openapi-document.d.ts.map +1 -1
  36. package/dist/utils/process-openapi-document.js +26 -11
  37. package/dist/utils/querystring-parameter.d.ts +11 -0
  38. package/dist/utils/querystring-parameter.d.ts.map +1 -0
  39. package/dist/utils/querystring-parameter.js +135 -0
  40. package/dist/utils/select-response-example.d.ts +3 -1
  41. package/dist/utils/select-response-example.d.ts.map +1 -1
  42. package/dist/utils/set-up-authentication-routes.d.ts.map +1 -1
  43. package/dist/utils/set-up-authentication-routes.js +2 -0
  44. package/dist/utils/set-up-device-authorization.d.ts +5 -0
  45. package/dist/utils/set-up-device-authorization.d.ts.map +1 -0
  46. package/dist/utils/set-up-device-authorization.js +192 -0
  47. package/dist/utils/streaming-response.d.ts +24 -0
  48. package/dist/utils/streaming-response.d.ts.map +1 -0
  49. package/dist/utils/streaming-response.js +65 -0
  50. package/dist/utils/validate-request.d.ts.map +1 -1
  51. package/dist/utils/validate-request.js +29 -1
  52. package/package.json +12 -9
@@ -1,7 +1,7 @@
1
1
  import { getResolvedRef } from '@scalar/workspace-store/helpers/get-resolved-ref';
2
2
  /**
3
- * Extract path from URL. Metadata is fetched at its exact declared URL, so its routes
4
- * preserve trailing slashes instead of using the token-route normalization.
3
+ * Extract path from URL. Metadata routes preserve trailing slashes because discovery
4
+ * fetches the exact declared URL, unlike normalized token routes.
5
5
  */
6
6
  export function getPathFromUrl(url, { preserveTrailingSlash = false } = {}) {
7
7
  try {
@@ -41,13 +41,15 @@ export function getOpenAuthTokenUrls(schema) {
41
41
  if (!scheme || !isOAuth2Scheme(scheme)) {
42
42
  continue;
43
43
  }
44
- const flows = scheme.flows; // Type assertion no longer needed
44
+ const flows = scheme.flows;
45
45
  // Helper to safely add valid OAuth URLs
46
46
  const addOAuthUrl = (url) => {
47
47
  if (url && isValidOAuthUrl(url)) {
48
48
  oauthUrls.add(getPathFromUrl(url));
49
49
  }
50
50
  };
51
+ addOAuthUrl(flows?.deviceAuthorization?.tokenUrl);
52
+ addOAuthUrl(flows?.deviceAuthorization?.refreshUrl);
51
53
  addOAuthUrl(flows?.password?.tokenUrl);
52
54
  addOAuthUrl(flows?.password?.refreshUrl);
53
55
  addOAuthUrl(flows?.clientCredentials?.tokenUrl);
@@ -1,8 +1,10 @@
1
1
  import type { OpenAPIV3_1 } from '@scalar/openapi-types';
2
- import { type HttpMethod } from '../types.js';
3
2
  /**
4
3
  * Takes a dereferenced OpenAPI document and returns all operations.
4
+ * Keys use their wire capitalization: fixed methods are uppercase, additional methods retain their case.
5
5
  * Ignores other attributes, like summary, parameters, etc.
6
6
  */
7
- export declare function getOperations(path?: OpenAPIV3_1.PathItemObject): Record<HttpMethod, OpenAPIV3_1.OperationObject>;
7
+ export declare const getOperations: (path?: OpenAPIV3_1.PathItemObject & {
8
+ additionalOperations?: Record<string, OpenAPIV3_1.OperationObject>;
9
+ }) => Record<string, OpenAPIV3_1.OperationObject>;
8
10
  //# sourceMappingURL=get-operation.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"get-operation.d.ts","sourceRoot":"","sources":["../../src/utils/get-operation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AAExD,OAAO,EAAE,KAAK,UAAU,EAAe,MAAM,SAAS,CAAA;AAEtD;;;GAGG;AACH,wBAAgB,aAAa,CAAC,IAAI,CAAC,EAAE,WAAW,CAAC,cAAc,GAAG,MAAM,CAAC,UAAU,EAAE,WAAW,CAAC,eAAe,CAAC,CAUhH"}
1
+ {"version":3,"file":"get-operation.d.ts","sourceRoot":"","sources":["../../src/utils/get-operation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AAIxD;;;;GAIG;AACH,eAAO,MAAM,aAAa,GACxB,OAAO,WAAW,CAAC,cAAc,GAAG;IAClC,oBAAoB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,eAAe,CAAC,CAAA;CACnE,KACA,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,eAAe,CAc5C,CAAA"}
@@ -1,14 +1,18 @@
1
1
  import { httpMethods } from '../types.js';
2
2
  /**
3
3
  * Takes a dereferenced OpenAPI document and returns all operations.
4
+ * Keys use their wire capitalization: fixed methods are uppercase, additional methods retain their case.
4
5
  * Ignores other attributes, like summary, parameters, etc.
5
6
  */
6
- export function getOperations(path) {
7
- const operations = {};
7
+ export const getOperations = (path) => {
8
+ const operations = new Map();
8
9
  for (const method of httpMethods) {
9
10
  if (path?.[method]) {
10
- operations[method] = path?.[method];
11
+ operations.set(method.toUpperCase(), path[method]);
11
12
  }
12
13
  }
13
- return operations;
14
- }
14
+ for (const [method, operation] of Object.entries(path?.additionalOperations ?? {})) {
15
+ operations.set(method, operation);
16
+ }
17
+ return Object.fromEntries(operations);
18
+ };
@@ -1,4 +1,4 @@
1
- import type { OpenAPIV3_1 } from '@scalar/openapi-types';
1
+ import type { OpenAPIV3_1, OpenAPIV3_2 } from '@scalar/openapi-types';
2
2
  import type { Context } from 'hono';
3
3
  /**
4
4
  * Handles authentication for incoming requests based on the OpenAPI document.
@@ -8,5 +8,5 @@ import type { Context } from 'hono';
8
8
  * only when *every* scheme it lists is satisfied. An empty requirement object (`{}`)
9
9
  * means authentication is optional and always passes.
10
10
  */
11
- export declare function handleAuthentication(schema?: OpenAPIV3_1.Document, operation?: OpenAPIV3_1.OperationObject): (c: Context, next: () => Promise<void>) => Promise<Response | void>;
11
+ export declare function handleAuthentication(schema?: OpenAPIV3_1.Document | OpenAPIV3_2.Document, operation?: OpenAPIV3_2.OperationObject): (c: Context, next: () => Promise<void>) => Promise<Response | void>;
12
12
  //# sourceMappingURL=handle-authentication.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"handle-authentication.d.ts","sourceRoot":"","sources":["../../src/utils/handle-authentication.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAa,WAAW,EAAE,MAAM,uBAAuB,CAAA;AAEnE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAA;AAiInC;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC,QAAQ,EAAE,SAAS,CAAC,EAAE,WAAW,CAAC,eAAe,IAC3F,GAAG,OAAO,EAAE,MAAM,MAAM,OAAO,CAAC,IAAI,CAAC,KAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CA4D/E"}
1
+ {"version":3,"file":"handle-authentication.d.ts","sourceRoot":"","sources":["../../src/utils/handle-authentication.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAa,WAAW,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AAEhF,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAA;AAiInC;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,CAAC,EAAE,WAAW,CAAC,QAAQ,GAAG,WAAW,CAAC,QAAQ,EACpD,SAAS,CAAC,EAAE,WAAW,CAAC,eAAe,IAEzB,GAAG,OAAO,EAAE,MAAM,MAAM,OAAO,CAAC,IAAI,CAAC,KAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CA4D/E"}
@@ -1,4 +1,4 @@
1
- import type { OpenAPIV3_1 } from '@scalar/openapi-types';
1
+ import type { OpenAPIV3_2 } from '@scalar/openapi-types';
2
2
  import type { MockServerLogger } from '../types.js';
3
3
  /**
4
4
  * Log authentication instructions for different security schemes.
@@ -7,5 +7,5 @@ import type { MockServerLogger } from '../types.js';
7
7
  * security schemes the mock server cannot handle are printed unconditionally, so a silenced startup
8
8
  * still surfaces schemes that will not work.
9
9
  */
10
- export declare function logAuthenticationInstructions(securitySchemes: Record<string, OpenAPIV3_1.SecuritySchemeObject>, log?: MockServerLogger): void;
10
+ export declare function logAuthenticationInstructions(securitySchemes: Record<string, OpenAPIV3_2.SecuritySchemeObject>, log?: MockServerLogger): void;
11
11
  //# sourceMappingURL=log-authentication-instructions.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"log-authentication-instructions.d.ts","sourceRoot":"","sources":["../../src/utils/log-authentication-instructions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AAGxD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAI/C;;;;;;GAMG;AACH,wBAAgB,6BAA6B,CAC3C,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,oBAAoB,CAAC,EACjE,GAAG,GAAE,gBAA8C,QAiIpD"}
1
+ {"version":3,"file":"log-authentication-instructions.d.ts","sourceRoot":"","sources":["../../src/utils/log-authentication-instructions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AAGxD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAI/C;;;;;;GAMG;AACH,wBAAgB,6BAA6B,CAC3C,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,oBAAoB,CAAC,EACjE,GAAG,GAAE,gBAA8C,QA4IpD"}
@@ -76,6 +76,13 @@ export function logAuthenticationInstructions(securitySchemes, log = (line) => c
76
76
  if (scheme.flows) {
77
77
  Object.keys(scheme.flows).forEach((flow) => {
78
78
  switch (flow) {
79
+ case 'deviceAuthorization':
80
+ log('✅ OAuth 2.0 Device Authorization Flow');
81
+ log(` POST ${getPathFromUrl(scheme.flows?.deviceAuthorization?.deviceAuthorizationUrl || '/oauth/device')}`);
82
+ log(' Send client_id and scope as form data, open verification_uri, and enter user_code.');
83
+ log(` Poll ${getPathFromUrl(scheme.flows?.deviceAuthorization?.tokenUrl || '/oauth/token')} with grant_type=urn:ietf:params:oauth:grant-type:device_code and device_code.`);
84
+ log('');
85
+ break;
79
86
  case 'implicit':
80
87
  log('✅ OAuth 2.0 Implicit Flow');
81
88
  log(' Use the following URL to initiate the OAuth 2.0 Implicit Flow:');
@@ -0,0 +1,4 @@
1
+ import type { Context } from 'hono';
2
+ /** Use the same response media-type preference for generated and custom-handler responses. */
3
+ export declare const negotiateContentType: (c: Context, content: Record<string, unknown> | undefined) => string;
4
+ //# sourceMappingURL=negotiate-content-type.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"negotiate-content-type.d.ts","sourceRoot":"","sources":["../../src/utils/negotiate-content-type.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAA;AAGnC,8FAA8F;AAC9F,eAAO,MAAM,oBAAoB,GAAI,GAAG,OAAO,EAAE,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,KAAG,MAS/F,CAAA"}
@@ -0,0 +1,12 @@
1
+ import { accepts } from 'hono/accepts';
2
+ /** Use the same response media-type preference for generated and custom-handler responses. */
3
+ export const negotiateContentType = (c, content) => {
4
+ const supportedContentTypes = Object.keys(content ?? {});
5
+ return accepts(c, {
6
+ header: 'Accept',
7
+ supports: supportedContentTypes,
8
+ default: supportedContentTypes.includes('application/json')
9
+ ? 'application/json'
10
+ : (supportedContentTypes[0] ?? 'text/plain;charset=UTF-8'),
11
+ });
12
+ };
@@ -1,6 +1,6 @@
1
- import type { OpenAPIV3_1 } from '@scalar/openapi-types';
1
+ import type { OpenAPIV3_1, OpenAPIV3_2 } from '@scalar/openapi-types';
2
2
  /**
3
- * Processes an OpenAPI document by bundling external references, upgrading to OpenAPI 3.1,
3
+ * Processes an OpenAPI document by bundling external references, upgrading compatible input to OpenAPI 3.2,
4
4
  * and wrapping it so internal references stay intact but resolve lazily.
5
5
  *
6
6
  * Unlike a full dereference, the returned document keeps `$ref` nodes in place. Consumers
@@ -8,9 +8,12 @@ import type { OpenAPIV3_1 } from '@scalar/openapi-types';
8
8
  * `$ref-value` exposed by the magic proxy. This avoids eagerly flattening (and duplicating)
9
9
  * the whole document up front.
10
10
  *
11
+ * Compatibility failures retain the OpenAPI 3.1 document and its version, without applying partial migrations.
12
+ *
11
13
  * @param document - The OpenAPI document to process. Can be a string (URL/path) or an object.
12
- * @returns A promise that resolves to the OpenAPI 3.1 document with lazily resolvable references.
14
+ * @param origin - Source file path or URL for resolving references in an already loaded document.
15
+ * @returns A promise that resolves to the document with lazily resolvable references.
13
16
  * @throws Error if the document cannot be processed or is invalid.
14
17
  */
15
- export declare function processOpenApiDocument(document: string | Record<string, any> | undefined): Promise<OpenAPIV3_1.Document>;
18
+ export declare function processOpenApiDocument(document: string | Record<string, any> | undefined, origin?: string): Promise<OpenAPIV3_1.Document | OpenAPIV3_2.Document>;
16
19
  //# sourceMappingURL=process-openapi-document.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"process-openapi-document.d.ts","sourceRoot":"","sources":["../../src/utils/process-openapi-document.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AAGxD;;;;;;;;;;;;GAYG;AACH,wBAAsB,sBAAsB,CAC1C,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,SAAS,GACjD,OAAO,CAAC,WAAW,CAAC,QAAQ,CAAC,CAsD/B"}
1
+ {"version":3,"file":"process-openapi-document.d.ts","sourceRoot":"","sources":["../../src/utils/process-openapi-document.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AAIrE;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,sBAAsB,CAC1C,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,SAAS,EAClD,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,WAAW,CAAC,QAAQ,GAAG,WAAW,CAAC,QAAQ,CAAC,CAkEtD"}
@@ -3,10 +3,12 @@ import { cwd } from 'node:process';
3
3
  import { bundle } from '@scalar/json-magic/bundle';
4
4
  import { fetchUrls, parseJson, parseYaml, readFiles } from '@scalar/json-magic/bundle/plugins/node';
5
5
  import { isFilePath } from '@scalar/json-magic/helpers/is-file-path';
6
+ import { isHttpUrl } from '@scalar/json-magic/helpers/is-http-url';
6
7
  import { createMagicProxy } from '@scalar/json-magic/magic-proxy';
7
8
  import { upgrade } from '@scalar/openapi-upgrader';
9
+ import { openApiDocument, resolveOpenApiDocument } from '@scalar/workspace-store/plugins/bundler';
8
10
  /**
9
- * Processes an OpenAPI document by bundling external references, upgrading to OpenAPI 3.1,
11
+ * Processes an OpenAPI document by bundling external references, upgrading compatible input to OpenAPI 3.2,
10
12
  * and wrapping it so internal references stay intact but resolve lazily.
11
13
  *
12
14
  * Unlike a full dereference, the returned document keeps `$ref` nodes in place. Consumers
@@ -14,16 +16,19 @@ import { upgrade } from '@scalar/openapi-upgrader';
14
16
  * `$ref-value` exposed by the magic proxy. This avoids eagerly flattening (and duplicating)
15
17
  * the whole document up front.
16
18
  *
19
+ * Compatibility failures retain the OpenAPI 3.1 document and its version, without applying partial migrations.
20
+ *
17
21
  * @param document - The OpenAPI document to process. Can be a string (URL/path) or an object.
18
- * @returns A promise that resolves to the OpenAPI 3.1 document with lazily resolvable references.
22
+ * @param origin - Source file path or URL for resolving references in an already loaded document.
23
+ * @returns A promise that resolves to the document with lazily resolvable references.
19
24
  * @throws Error if the document cannot be processed or is invalid.
20
25
  */
21
- export async function processOpenApiDocument(document) {
26
+ export async function processOpenApiDocument(document, origin) {
22
27
  // Handle empty/undefined input gracefully
23
28
  if (!document || (typeof document === 'object' && Object.keys(document).length === 0)) {
24
- // Return a minimal valid OpenAPI 3.1 document
29
+ // Return a minimal valid OpenAPI 3.2 document
25
30
  return {
26
- openapi: '3.1.0',
31
+ openapi: '3.2.0',
27
32
  info: {
28
33
  title: 'Mock API',
29
34
  version: '1.0.0',
@@ -35,12 +40,20 @@ export async function processOpenApiDocument(document) {
35
40
  // Confine local file `$ref`s to the document's own directory (or the working directory when the
36
41
  // document is an object or inline string), and refuse to fetch private or internal addresses.
37
42
  // Without these guards a `$ref` could read arbitrary local files or reach internal services.
38
- const basePath = typeof document === 'string' && isFilePath(document) ? path.dirname(path.resolve(document)) : cwd();
43
+ const source = origin ?? document;
44
+ const basePath = typeof source === 'string' && isFilePath(source) ? path.dirname(path.resolve(source)) : cwd();
39
45
  try {
40
46
  // Bundle external references with Node.js plugins
41
47
  // Include parseJson and parseYaml to handle string inputs
42
48
  bundled = await bundle(document, {
43
- plugins: [parseJson(), parseYaml(), readFiles({ basePath }), fetchUrls({ blockPrivateNetworks: true })],
49
+ origin,
50
+ plugins: [
51
+ openApiDocument(),
52
+ parseJson(),
53
+ parseYaml(),
54
+ readFiles({ basePath }),
55
+ fetchUrls({ blockPrivateNetworks: true }),
56
+ ],
44
57
  treeShake: false,
45
58
  });
46
59
  }
@@ -50,18 +63,20 @@ export async function processOpenApiDocument(document) {
50
63
  if (!bundled || typeof bundled !== 'object') {
51
64
  throw new Error('Bundled document is invalid: expected an object');
52
65
  }
66
+ // Upgrading must not activate a $self field authored in an older OpenAPI version.
67
+ const retrievalUri = origin ?? (typeof document === 'string' && (isFilePath(document) || isHttpUrl(document)) ? document : '/');
68
+ const documentUri = resolveOpenApiDocument(bundled, retrievalUri)?.baseUri;
53
69
  let upgraded;
54
70
  try {
55
- // Upgrade to OpenAPI 3.1
56
- upgraded = upgrade(bundled, '3.1');
71
+ upgraded = upgrade(bundled, '3.2', { onIncompatible: 'collect' }).document;
57
72
  }
58
73
  catch (error) {
59
- throw new Error(`Failed to upgrade OpenAPI document to 3.1: ${error instanceof Error ? error.message : String(error)}`);
74
+ throw new Error(`Failed to upgrade OpenAPI document to 3.2: ${error instanceof Error ? error.message : String(error)}`);
60
75
  }
61
76
  if (!upgraded) {
62
77
  throw new Error('Upgraded document is invalid: upgrade returned null or undefined');
63
78
  }
64
79
  // Wrap the document in a magic proxy so internal references resolve lazily via `$ref-value`.
65
80
  // External references were already pulled inline by `bundle` above, so only local `$ref`s remain.
66
- return createMagicProxy(upgraded);
81
+ return createMagicProxy(upgraded, { documentUri });
67
82
  }
@@ -0,0 +1,11 @@
1
+ import type { OpenAPIV3_2 } from '@scalar/openapi-types';
2
+ /**
3
+ * Find the whole-query parameter, giving operation declarations precedence over path declarations.
4
+ * This module decodes incoming queries; workspace-store/src/helpers/querystring-parameter.ts serializes outgoing ones.
5
+ */
6
+ export declare const findQuerystringParameter: (operation?: OpenAPIV3_2.OperationObject, pathParameters?: OpenAPIV3_2.PathItemObject["parameters"]) => OpenAPIV3_2.ParameterObject | undefined;
7
+ /** Validate JSON-encoded form properties before the coercing form validator can alter their native types. */
8
+ export declare const getQuerystringJsonSchema: (parameter: OpenAPIV3_2.ParameterObject | undefined) => Record<string, unknown> | null;
9
+ /** Decode the complete query without introducing the parameter's documentary name. Throws on malformed JSON or URI escaping. */
10
+ export declare const parseQuerystringParameter: (url: string, parameter: OpenAPIV3_2.ParameterObject) => unknown;
11
+ //# sourceMappingURL=querystring-parameter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"querystring-parameter.d.ts","sourceRoot":"","sources":["../../src/utils/querystring-parameter.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AAaxD;;;GAGG;AACH,eAAO,MAAM,wBAAwB,GACnC,YAAY,WAAW,CAAC,eAAe,EACvC,iBAAiB,WAAW,CAAC,cAAc,CAAC,YAAY,CAAC,KACxD,WAAW,CAAC,eAAe,GAAG,SAMwB,CAAA;AAkBzD,6GAA6G;AAC7G,eAAO,MAAM,wBAAwB,GACnC,WAAW,WAAW,CAAC,eAAe,GAAG,SAAS,KACjD,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAkC5B,CAAA;AAED,gIAAgI;AAChI,eAAO,MAAM,yBAAyB,GAAI,KAAK,MAAM,EAAE,WAAW,WAAW,CAAC,eAAe,KAAG,OA0E/F,CAAA"}
@@ -0,0 +1,135 @@
1
+ import { getFirstMediaType } from '@scalar/helpers/http/get-first-media-type';
2
+ import { isJsonMediaType } from '@scalar/helpers/http/is-json-media-type';
3
+ import { parseMimeType } from '@scalar/helpers/http/mime-type';
4
+ import { getResolvedRef } from '@scalar/workspace-store/helpers/get-resolved-ref';
5
+ import { getResolvedRefDeep } from '@scalar/workspace-store/helpers/get-resolved-ref-deep';
6
+ import { deserializeArrayParameter, deserializeObjectParameter, getObjectPropertyNames, isArraySchema, isObjectSchema, resolveSerialization, } from './deserialize-parameter.js';
7
+ /**
8
+ * Find the whole-query parameter, giving operation declarations precedence over path declarations.
9
+ * This module decodes incoming queries; workspace-store/src/helpers/querystring-parameter.ts serializes outgoing ones.
10
+ */
11
+ export const findQuerystringParameter = (operation, pathParameters) => [
12
+ ...(Array.isArray(operation?.parameters) ? operation.parameters : []),
13
+ ...(Array.isArray(pathParameters) ? pathParameters : []),
14
+ ]
15
+ .map((parameter) => getResolvedRef(parameter))
16
+ .find((parameter) => parameter?.in === 'querystring');
17
+ /** Collect form properties through compositions so nullable and composed objects retain their encoding. */
18
+ const getPropertySchemas = (schema) => {
19
+ const properties = {};
20
+ for (const keyword of ['allOf', 'anyOf', 'oneOf']) {
21
+ const branches = schema?.[keyword];
22
+ if (Array.isArray(branches)) {
23
+ for (const branch of branches) {
24
+ if (branch && typeof branch === 'object') {
25
+ Object.assign(properties, getPropertySchemas(branch));
26
+ }
27
+ }
28
+ }
29
+ }
30
+ return { ...properties, ...schema?.properties };
31
+ };
32
+ /** Validate JSON-encoded form properties before the coercing form validator can alter their native types. */
33
+ export const getQuerystringJsonSchema = (parameter) => {
34
+ const [contentType, media] = getFirstMediaType(parameter?.content) ?? [];
35
+ if (parseMimeType(contentType).essence !== 'application/x-www-form-urlencoded') {
36
+ return null;
37
+ }
38
+ const selectJsonProperties = (schema) => {
39
+ const properties = (schema?.properties ?? {});
40
+ const jsonProperties = Object.fromEntries(Object.entries(properties).filter(([name, property]) => {
41
+ const encoding = media?.encoding?.[name];
42
+ if (encoding?.style !== undefined || encoding?.explode !== undefined || encoding?.allowReserved !== undefined) {
43
+ return false;
44
+ }
45
+ const item = isArraySchema(property) ? property.items : property;
46
+ return isJsonMediaType(encoding?.contentType) || isObjectSchema(item) || isArraySchema(item);
47
+ }));
48
+ const result = { properties: jsonProperties };
49
+ for (const keyword of ['allOf', 'anyOf', 'oneOf']) {
50
+ const branches = schema?.[keyword];
51
+ if (Array.isArray(branches)) {
52
+ // Non-JSON fields may distinguish oneOf branches. The complete validator enforces exclusivity.
53
+ const target = keyword === 'oneOf' ? 'anyOf' : keyword;
54
+ const selected = branches.map((branch) => selectJsonProperties(branch));
55
+ if (target in result) {
56
+ result.allOf = [...(Array.isArray(result.allOf) ? result.allOf : []), { [target]: selected }];
57
+ }
58
+ else {
59
+ result[target] = selected;
60
+ }
61
+ }
62
+ }
63
+ return result;
64
+ };
65
+ return selectJsonProperties(getResolvedRefDeep(media?.schema));
66
+ };
67
+ /** Decode the complete query without introducing the parameter's documentary name. Throws on malformed JSON or URI escaping. */
68
+ export const parseQuerystringParameter = (url, parameter) => {
69
+ const query = new URL(url).search.slice(1);
70
+ if (!query) {
71
+ return undefined;
72
+ }
73
+ const [contentType, media] = getFirstMediaType(parameter.content) ?? [];
74
+ const mediaType = parseMimeType(contentType).essence;
75
+ if (mediaType !== 'application/x-www-form-urlencoded') {
76
+ const decoded = decodeURIComponent(query);
77
+ return isJsonMediaType(mediaType) ? JSON.parse(decoded) : decoded;
78
+ }
79
+ const params = new URLSearchParams(query);
80
+ const map = Object.fromEntries([...new Set(params.keys())].map((key) => {
81
+ const values = params.getAll(key);
82
+ return [key, values.length === 1 ? (values[0] ?? '') : values];
83
+ }));
84
+ const result = { ...map };
85
+ const schema = getResolvedRefDeep(media?.schema);
86
+ const properties = getPropertySchemas(schema);
87
+ for (const name of getObjectPropertyNames(schema)) {
88
+ const property = properties?.[name];
89
+ const encoding = media?.encoding?.[name];
90
+ const single = params.get(name) ?? undefined;
91
+ const styleBased = encoding?.style !== undefined || encoding?.explode !== undefined || encoding?.allowReserved !== undefined;
92
+ if (styleBased) {
93
+ const { style, explode } = resolveSerialization('query', encoding?.style, encoding?.explode);
94
+ const value = isObjectSchema(property)
95
+ ? deserializeObjectParameter({
96
+ style,
97
+ explode,
98
+ single,
99
+ map,
100
+ name,
101
+ propertyNames: getObjectPropertyNames(property),
102
+ reservedKeys: new Set(Object.keys(properties)),
103
+ })
104
+ : isArraySchema(property)
105
+ ? deserializeArrayParameter({
106
+ style,
107
+ explode,
108
+ single,
109
+ multi: params.has(name) ? params.getAll(name) : undefined,
110
+ })
111
+ : single;
112
+ if (value !== undefined) {
113
+ if (isObjectSchema(property) &&
114
+ (style === 'deepObject' || (style === 'form' && explode)) &&
115
+ typeof value === 'object' &&
116
+ value !== null) {
117
+ for (const key of Object.keys(value)) {
118
+ delete result[style === 'deepObject' ? `${name}[${key}]` : key];
119
+ }
120
+ }
121
+ result[name] = value;
122
+ }
123
+ continue;
124
+ }
125
+ if (single === undefined) {
126
+ continue;
127
+ }
128
+ const json = isJsonMediaType(encoding?.contentType);
129
+ const decode = (value, itemSchema) => json || isObjectSchema(itemSchema) || isArraySchema(itemSchema) ? JSON.parse(value) : value;
130
+ result[name] = isArraySchema(property)
131
+ ? params.getAll(name).map((value) => decode(value, property?.items))
132
+ : decode(single, property);
133
+ }
134
+ return result;
135
+ };
@@ -16,7 +16,9 @@ import type { OpenAPIV3_1 } from '@scalar/openapi-types';
16
16
  * caller still gets a schema-generated body. An unknown `exampleName` simply
17
17
  * falls through to the later steps.
18
18
  */
19
- export declare const selectResponseExample: (mediaType: OpenAPIV3_1.MediaTypeObject | undefined, exampleName?: string) => {
19
+ export declare const selectResponseExample: <T extends Pick<OpenAPIV3_1.MediaTypeObject, "example" | "examples"> & {
20
+ schema?: unknown;
21
+ }>(mediaType: T | undefined, exampleName?: string) => {
20
22
  value: unknown;
21
23
  } | undefined;
22
24
  //# sourceMappingURL=select-response-example.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"select-response-example.d.ts","sourceRoot":"","sources":["../../src/utils/select-response-example.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AAGxD;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,qBAAqB,GAChC,WAAW,WAAW,CAAC,eAAe,GAAG,SAAS,EAClD,cAAc,MAAM,KACnB;IAAE,KAAK,EAAE,OAAO,CAAA;CAAE,GAAG,SAoCvB,CAAA"}
1
+ {"version":3,"file":"select-response-example.d.ts","sourceRoot":"","sources":["../../src/utils/select-response-example.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AAGxD;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,qBAAqB,GAChC,CAAC,SAAS,IAAI,CAAC,WAAW,CAAC,eAAe,EAAE,SAAS,GAAG,UAAU,CAAC,GAAG;IAAE,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,EAE1F,WAAW,CAAC,GAAG,SAAS,EACxB,cAAc,MAAM,KACnB;IAAE,KAAK,EAAE,OAAO,CAAA;CAAE,GAAG,SAoCvB,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"set-up-authentication-routes.d.ts","sourceRoot":"","sources":["../../src/utils/set-up-authentication-routes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAA0B,MAAM,uBAAuB,CAAA;AAE5E,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAQhC;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,IAAI,EAAE,MAAM,CAAC,EAAE,OAAO,CAAC,QAAQ,QA+G7E"}
1
+ {"version":3,"file":"set-up-authentication-routes.d.ts","sourceRoot":"","sources":["../../src/utils/set-up-authentication-routes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAA0B,MAAM,uBAAuB,CAAA;AAE5E,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAShC;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,IAAI,EAAE,MAAM,CAAC,EAAE,OAAO,CAAC,QAAQ,QAiH7E"}
@@ -3,11 +3,13 @@ import { respondWithAuthorizePage } from '../routes/respond-with-authorize-page.
3
3
  import { respondWithToken } from '../routes/respond-with-token.js';
4
4
  import { getOAuth2Metadata } from './get-oauth2-metadata.js';
5
5
  import { getOpenAuthTokenUrls, getPathFromUrl } from './get-open-auth-token-urls.js';
6
+ import { setUpDeviceAuthorization } from './set-up-device-authorization.js';
6
7
  /**
7
8
  * Helper function to set up authentication routes for OAuth 2.0 flows
8
9
  */
9
10
  export function setUpAuthenticationRoutes(app, schema) {
10
11
  const securitySchemes = schema?.components?.securitySchemes || {};
12
+ setUpDeviceAuthorization(app, schema);
11
13
  // Set up authentication routes for OAuth 2.0 flows
12
14
  getOpenAuthTokenUrls(schema).forEach((tokenUrl) => {
13
15
  app.post(tokenUrl, (c) => {
@@ -0,0 +1,5 @@
1
+ import type { OpenAPI } from '@scalar/openapi-types';
2
+ import type { Hono } from 'hono';
3
+ /** Registers a local RFC8628 approval flow before the permissive mock token handlers. */
4
+ export declare const setUpDeviceAuthorization: (app: Hono, document?: OpenAPI.Document) => void;
5
+ //# sourceMappingURL=set-up-device-authorization.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"set-up-device-authorization.d.ts","sourceRoot":"","sources":["../../src/utils/set-up-device-authorization.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAe,MAAM,uBAAuB,CAAA;AAEjE,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AA0FhC,yFAAyF;AACzF,eAAO,MAAM,wBAAwB,GAAI,KAAK,IAAI,EAAE,WAAW,OAAO,CAAC,QAAQ,KAAG,IAsJjF,CAAA"}