@scalar/mock-server 0.10.16 → 0.10.17

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 CHANGED
@@ -1,5 +1,13 @@
1
1
  # @scalar/mock-server
2
2
 
3
+ ## 0.10.17
4
+
5
+ ### Patch Changes
6
+
7
+ - [#9437](https://github.com/scalar/scalar/pull/9437): chore(mock-server): resolve OpenAPI references with `@scalar/workspace-store` instead of `@scalar/openapi-parser`
8
+
9
+ The mock server no longer eagerly dereferences the whole document. It now keeps `$ref` nodes intact via the `@scalar/json-magic` magic proxy and resolves them lazily with `getResolvedRef`/`getResolvedRefDeep` from `@scalar/workspace-store`. The `/openapi.{json,yaml}` endpoints use `@scalar/json-magic` and `yaml` for normalization and serialization. This drops the `@scalar/openapi-parser` dependency. Behavior is unchanged.
10
+
3
11
  ## 0.10.16
4
12
 
5
13
  ## 0.10.15
@@ -1 +1 @@
1
- {"version":3,"file":"create-mock-server.d.ts","sourceRoot":"","sources":["../src/create-mock-server.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAG3B,OAAO,KAAK,EAAc,iBAAiB,EAAE,MAAM,SAAS,CAAA;AAgB5D;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,aAAa,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAoFtF"}
1
+ {"version":3,"file":"create-mock-server.d.ts","sourceRoot":"","sources":["../src/create-mock-server.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAG3B,OAAO,KAAK,EAAc,iBAAiB,EAAE,MAAM,SAAS,CAAA;AAgB5D;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,aAAa,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAuFtF"}
@@ -1,3 +1,4 @@
1
+ import { getResolvedRef, mergeSiblingReferences } from '@scalar/workspace-store/helpers/get-resolved-ref';
1
2
  import { Hono } from 'hono';
2
3
  import { cors } from 'hono/cors';
3
4
  import { buildSeedContext } from './utils/build-seed-context.js';
@@ -25,7 +26,8 @@ export async function createMockServer(configuration) {
25
26
  const schemas = schema?.components?.schemas;
26
27
  if (schemas) {
27
28
  for (const [schemaName, schemaObject] of Object.entries(schemas)) {
28
- const seedCode = schemaObject?.['x-seed'];
29
+ // Merge `$ref` siblings so an `x-seed` placed next to a `$ref` is preserved (OpenAPI 3.1 semantics)
30
+ const seedCode = getResolvedRef(schemaObject, mergeSiblingReferences)?.['x-seed'];
29
31
  if (seedCode && typeof seedCode === 'string') {
30
32
  try {
31
33
  // Check if collection is empty (idempotent seeding)
@@ -53,11 +55,13 @@ export async function createMockServer(configuration) {
53
55
  /** Paths specified in the OpenAPI document */
54
56
  const paths = schema?.paths ?? {};
55
57
  Object.keys(paths).forEach((path) => {
56
- const methods = Object.keys(getOperations(paths[path]));
58
+ // A path item may itself be a `$ref`, so resolve it before reading its operations.
59
+ const pathItem = getResolvedRef(paths[path]);
60
+ const methods = Object.keys(getOperations(pathItem));
57
61
  /** Keys for all operations of a specified path */
58
62
  methods.forEach((method) => {
59
63
  const route = honoRouteFromPath(path);
60
- const operation = schema?.paths?.[path]?.[method];
64
+ const operation = pathItem?.[method];
61
65
  // Check if authentication is required for this operation
62
66
  if (isAuthenticationRequired(operation.security)) {
63
67
  app[method](route, handleAuthentication(schema, operation));
@@ -1 +1 @@
1
- {"version":3,"file":"mock-any-response.d.ts","sourceRoot":"","sources":["../../src/routes/mock-any-response.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AAExD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAA;AAEnC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AAExD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAA;AAGhD;;GAEG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,WAAW,CAAC,eAAe,EAAE,OAAO,EAAE,iBAAiB;;yPAqF7G"}
1
+ {"version":3,"file":"mock-any-response.d.ts","sourceRoot":"","sources":["../../src/routes/mock-any-response.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AAIxD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAA;AAEnC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AAExD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAA;AAGhD;;GAEG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,WAAW,CAAC,eAAe,EAAE,OAAO,EAAE,iBAAiB;;yPAwF7G"}
@@ -1,4 +1,6 @@
1
1
  import { json2xml } from '@scalar/helpers/file/json2xml';
2
+ import { getResolvedRef } from '@scalar/workspace-store/helpers/get-resolved-ref';
3
+ import { getResolvedRefDeep } from '@scalar/workspace-store/helpers/get-resolved-ref-deep';
2
4
  import { getExampleFromSchema } from '@scalar/workspace-store/request-example';
3
5
  import { accepts } from 'hono/accepts';
4
6
  import { findPreferredResponseKey } from '../utils/find-preferred-response-key.js';
@@ -16,7 +18,7 @@ export function mockAnyResponse(c, operation, options) {
16
18
  // Response
17
19
  // default, 200, 201 …
18
20
  const preferredResponseKey = findPreferredResponseKey(Object.keys(operation.responses ?? {}));
19
- const preferredResponse = preferredResponseKey ? operation.responses?.[preferredResponseKey] : null;
21
+ const preferredResponse = preferredResponseKey ? getResolvedRef(operation.responses?.[preferredResponseKey]) : null;
20
22
  if (!preferredResponse) {
21
23
  c.status(500);
22
24
  return c.json({ error: 'No response defined for this operation.' });
@@ -26,7 +28,10 @@ export function mockAnyResponse(c, operation, options) {
26
28
  // Headers
27
29
  const headers = preferredResponse?.headers ?? {};
28
30
  Object.keys(headers).forEach((header) => {
29
- const value = headers[header].schema ? getExampleFromSchema(headers[header].schema) : null;
31
+ const headerObject = getResolvedRef(headers[header]);
32
+ const value = headerObject?.schema
33
+ ? getExampleFromSchema(getResolvedRefDeep(headerObject.schema))
34
+ : null;
30
35
  if (value !== null) {
31
36
  c.header(header, value);
32
37
  }
@@ -56,7 +61,7 @@ export function mockAnyResponse(c, operation, options) {
56
61
  const body = acceptedResponse?.example
57
62
  ? acceptedResponse.example
58
63
  : acceptedResponse?.schema
59
- ? getExampleFromSchema(acceptedResponse.schema, {
64
+ ? getExampleFromSchema(getResolvedRefDeep(acceptedResponse.schema), {
60
65
  emptyString: 'string',
61
66
  variables: c.req.param(),
62
67
  mode: 'read',
@@ -1 +1 @@
1
- {"version":3,"file":"mock-handler-response.d.ts","sourceRoot":"","sources":["../../src/routes/mock-handler-response.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AAExD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAA;AAEnC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AAExD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAA;AAoHhD;;;GAGG;AACH,wBAAsB,mBAAmB,CACvC,CAAC,EAAE,OAAO,EACV,SAAS,EAAE,WAAW,CAAC,eAAe,EACtC,OAAO,EAAE,iBAAiB,gMAkE3B"}
1
+ {"version":3,"file":"mock-handler-response.d.ts","sourceRoot":"","sources":["../../src/routes/mock-handler-response.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AAIxD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAA;AAEnC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AAExD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAA;AAoHhD;;;GAGG;AACH,wBAAsB,mBAAmB,CACvC,CAAC,EAAE,OAAO,EACV,SAAS,EAAE,WAAW,CAAC,eAAe,EACtC,OAAO,EAAE,iBAAiB,gMAkE3B"}
@@ -1,3 +1,5 @@
1
+ import { getResolvedRef } from '@scalar/workspace-store/helpers/get-resolved-ref';
2
+ import { getResolvedRefDeep } from '@scalar/workspace-store/helpers/get-resolved-ref-deep';
1
3
  import { getExampleFromSchema } from '@scalar/workspace-store/request-example';
2
4
  import { accepts } from 'hono/accepts';
3
5
  import { buildHandlerContext } from '../utils/build-handler-context.js';
@@ -11,7 +13,7 @@ function getExampleFromResponse(c, statusCode, responses) {
11
13
  return null;
12
14
  }
13
15
  const statusCodeStr = statusCode.toString();
14
- const response = responses[statusCodeStr] || responses.default;
16
+ const response = getResolvedRef(responses[statusCodeStr] || responses.default);
15
17
  if (!response) {
16
18
  return null;
17
19
  }
@@ -36,7 +38,7 @@ function getExampleFromResponse(c, statusCode, responses) {
36
38
  return acceptedResponse.example !== undefined
37
39
  ? acceptedResponse.example
38
40
  : acceptedResponse.schema
39
- ? getExampleFromSchema(acceptedResponse.schema, {
41
+ ? getExampleFromSchema(getResolvedRefDeep(acceptedResponse.schema), {
40
42
  emptyString: 'string',
41
43
  variables: c.req.param(),
42
44
  mode: 'read',
@@ -2,17 +2,7 @@ import type { Context } from 'hono';
2
2
  /**
3
3
  * OpenAPI endpoints
4
4
  */
5
- export declare function respondWithOpenApiDocument(c: Context, input?: string | Record<string, any>, format?: 'json' | 'yaml'): (Response & import("hono").TypedResponse<"Not found", 404, "text">) | (Response & import("hono").TypedResponse<{
6
- [x: string]: import("hono/utils/types").JSONValue;
7
- } | {
8
- dir: string;
9
- isEntrypoint: boolean;
10
- references: string[];
11
- filename: string;
12
- specification: {
13
- [x: string]: import("hono/utils/types").JSONValue;
14
- };
15
- }[], import("hono/utils/http-status").ContentfulStatusCode, "json">) | (Response & import("hono").TypedResponse<string, 200, "text">) | (Response & import("hono").TypedResponse<{
5
+ export declare function respondWithOpenApiDocument(c: Context, input?: string | Record<string, any>, format?: 'json' | 'yaml'): (Response & import("hono").TypedResponse<"Not found", 404, "text">) | (Response & import("hono").TypedResponse<import("hono/utils/types").JSONValue, import("hono/utils/http-status").ContentfulStatusCode, "json">) | (Response & import("hono").TypedResponse<string, 200, "text">) | (Response & import("hono").TypedResponse<{
16
6
  error: string;
17
7
  message: string;
18
8
  }, 500, "json">) | (Response & import("hono").TypedResponse<{
@@ -1 +1 @@
1
- {"version":3,"file":"respond-with-openapi-document.d.ts","sourceRoot":"","sources":["../../src/routes/respond-with-openapi-document.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAA;AAEnC;;GAEG;AACH,wBAAgB,0BAA0B,CACxC,CAAC,EAAE,OAAO,EACV,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACpC,MAAM,GAAE,MAAM,GAAG,MAAe;;;;;;;;;;;;;;;;iBAwCjC"}
1
+ {"version":3,"file":"respond-with-openapi-document.d.ts","sourceRoot":"","sources":["../../src/routes/respond-with-openapi-document.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAA;AAGnC;;GAEG;AACH,wBAAgB,0BAA0B,CACxC,CAAC,EAAE,OAAO,EACV,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACpC,MAAM,GAAE,MAAM,GAAG,MAAe;;;;;;iBAwCjC"}
@@ -1,4 +1,5 @@
1
- import { normalize, toYaml } from '@scalar/openapi-parser';
1
+ import { normalize } from '@scalar/json-magic/helpers/normalize';
2
+ import { stringify as toYaml } from 'yaml';
2
3
  /**
3
4
  * OpenAPI endpoints
4
5
  */
@@ -1 +1 @@
1
- {"version":3,"file":"build-handler-context.d.ts","sourceRoot":"","sources":["../../src/utils/build-handler-context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAA;AACvC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AAExD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAA;AAInC,OAAO,EAAE,KAAK,sBAAsB,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;AAEjF;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B,KAAK,EAAE,UAAU,CAAC,OAAO,kBAAkB,CAAC,CAAC,cAAc,CAAC,CAAA;IAC5D,KAAK,EAAE,OAAO,KAAK,CAAA;IACnB,GAAG,EAAE;QACH,IAAI,EAAE,GAAG,CAAA;QACT,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;QAC9B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;QAC7B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAChC,CAAA;IACD,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;CACzB,CAAA;AAED;;GAEG;AACH,KAAK,oBAAoB,GAAG;IAC1B,OAAO,EAAE,cAAc,CAAA;IACvB,QAAQ,EAAE,sBAAsB,CAAA;CACjC,CAAA;AAuDD;;GAEG;AACH,wBAAsB,mBAAmB,CACvC,CAAC,EAAE,OAAO,EACV,SAAS,CAAC,EAAE,WAAW,CAAC,eAAe,GACtC,OAAO,CAAC,oBAAoB,CAAC,CA4C/B"}
1
+ {"version":3,"file":"build-handler-context.d.ts","sourceRoot":"","sources":["../../src/utils/build-handler-context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAA;AACvC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AAIxD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAA;AAInC,OAAO,EAAE,KAAK,sBAAsB,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;AAEjF;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B,KAAK,EAAE,UAAU,CAAC,OAAO,kBAAkB,CAAC,CAAC,cAAc,CAAC,CAAA;IAC5D,KAAK,EAAE,OAAO,KAAK,CAAA;IACnB,GAAG,EAAE;QACH,IAAI,EAAE,GAAG,CAAA;QACT,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;QAC9B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;QAC7B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAChC,CAAA;IACD,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;CACzB,CAAA;AAED;;GAEG;AACH,KAAK,oBAAoB,GAAG;IAC1B,OAAO,EAAE,cAAc,CAAA;IACvB,QAAQ,EAAE,sBAAsB,CAAA;CACjC,CAAA;AAuDD;;GAEG;AACH,wBAAsB,mBAAmB,CACvC,CAAC,EAAE,OAAO,EACV,SAAS,CAAC,EAAE,WAAW,CAAC,eAAe,GACtC,OAAO,CAAC,oBAAoB,CAAC,CA4C/B"}
@@ -1,4 +1,6 @@
1
1
  import { faker } from '@faker-js/faker';
2
+ import { getResolvedRef } from '@scalar/workspace-store/helpers/get-resolved-ref';
3
+ import { getResolvedRefDeep } from '@scalar/workspace-store/helpers/get-resolved-ref-deep';
2
4
  import { getExampleFromSchema } from '@scalar/workspace-store/request-example';
3
5
  import { accepts } from 'hono/accepts';
4
6
  import { store } from '../libs/store.js';
@@ -11,7 +13,7 @@ function getExampleFromResponse(c, statusCode, responses) {
11
13
  if (!responses) {
12
14
  return null;
13
15
  }
14
- const response = responses[statusCode] || responses.default;
16
+ const response = getResolvedRef(responses[statusCode] || responses.default);
15
17
  if (!response) {
16
18
  return null;
17
19
  }
@@ -36,7 +38,7 @@ function getExampleFromResponse(c, statusCode, responses) {
36
38
  return acceptedResponse.example !== undefined
37
39
  ? acceptedResponse.example
38
40
  : acceptedResponse.schema
39
- ? getExampleFromSchema(acceptedResponse.schema, {
41
+ ? getExampleFromSchema(getResolvedRefDeep(acceptedResponse.schema), {
40
42
  emptyString: 'string',
41
43
  variables: c.req.param(),
42
44
  mode: 'read',
@@ -1 +1 @@
1
- {"version":3,"file":"get-open-auth-token-urls.d.ts","sourceRoot":"","sources":["../../src/utils/get-open-auth-token-urls.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAA0B,MAAM,uBAAuB,CAAA;AAE5E;;GAEG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAYlD;AAiBD,wBAAgB,oBAAoB,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,QAAQ,GAAG,MAAM,EAAE,CAoCxE"}
1
+ {"version":3,"file":"get-open-auth-token-urls.d.ts","sourceRoot":"","sources":["../../src/utils/get-open-auth-token-urls.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAA0B,MAAM,uBAAuB,CAAA;AAG5E;;GAEG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAYlD;AAiBD,wBAAgB,oBAAoB,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,QAAQ,GAAG,MAAM,EAAE,CAuCxE"}
@@ -1,3 +1,4 @@
1
+ import { getResolvedRef } from '@scalar/workspace-store/helpers/get-resolved-ref';
1
2
  /**
2
3
  * Extract path from URL
3
4
  */
@@ -33,8 +34,10 @@ export function getOpenAuthTokenUrls(schema) {
33
34
  // Use Set from the start for better memory efficiency
34
35
  const oauthUrls = new Set();
35
36
  // Iterate through all security schemes
36
- for (const scheme of Object.values(securitySchemes)) {
37
- if (!isOAuth2Scheme(scheme)) {
37
+ for (const rawScheme of Object.values(securitySchemes)) {
38
+ const scheme = getResolvedRef(rawScheme);
39
+ // Skip schemes that could not be resolved (e.g. a `$ref` to a missing component)
40
+ if (!scheme || !isOAuth2Scheme(scheme)) {
38
41
  continue;
39
42
  }
40
43
  const flows = scheme.flows; // Type assertion no longer needed
@@ -1 +1 @@
1
- {"version":3,"file":"handle-authentication.d.ts","sourceRoot":"","sources":["../../src/utils/handle-authentication.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AACxD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAA;AAGnC;;GAEG;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,CA6H/E"}
1
+ {"version":3,"file":"handle-authentication.d.ts","sourceRoot":"","sources":["../../src/utils/handle-authentication.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AAExD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAA;AAGnC;;GAEG;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,CAwH/E"}
@@ -1,3 +1,4 @@
1
+ import { getResolvedRef } from '@scalar/workspace-store/helpers/get-resolved-ref';
1
2
  import { getCookie } from 'hono/cookie';
2
3
  /**
3
4
  * Handles authentication for incoming requests based on the OpenAPI document.
@@ -11,11 +12,7 @@ export function handleAuthentication(schema, operation) {
11
12
  for (const securityRequirement of operationSecuritySchemes) {
12
13
  let securitySchemeAuthenticated = true;
13
14
  for (const [schemeName] of Object.entries(securityRequirement)) {
14
- const scheme = schema?.components?.securitySchemes?.[schemeName];
15
- // Skip if scheme is a reference object (should be dereferenced already, but check to be safe)
16
- if (scheme && '$ref' in scheme) {
17
- continue;
18
- }
15
+ const scheme = getResolvedRef(schema?.components?.securitySchemes?.[schemeName]);
19
16
  if (scheme && 'type' in scheme) {
20
17
  const securityScheme = scheme;
21
18
  switch (securityScheme.type) {
@@ -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;AAIxD;;GAEG;AACH,wBAAgB,6BAA6B,CAAC,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,oBAAoB,CAAC,QAmH9G"}
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;AAKxD;;GAEG;AACH,wBAAgB,6BAA6B,CAAC,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,oBAAoB,CAAC,QA0H9G"}
@@ -1,3 +1,4 @@
1
+ import { getResolvedRef } from '@scalar/workspace-store/helpers/get-resolved-ref';
1
2
  import { getPathFromUrl } from './get-open-auth-token-urls.js';
2
3
  /**
3
4
  * Log authentication instructions for different security schemes
@@ -8,7 +9,12 @@ export function logAuthenticationInstructions(securitySchemes) {
8
9
  }
9
10
  console.log('Authentication:');
10
11
  console.log();
11
- Object.entries(securitySchemes).forEach(([_, scheme]) => {
12
+ Object.entries(securitySchemes).forEach(([_, rawScheme]) => {
13
+ const scheme = getResolvedRef(rawScheme);
14
+ // Skip schemes that could not be resolved (e.g. a `$ref` to a missing component)
15
+ if (!scheme) {
16
+ return;
17
+ }
12
18
  switch (scheme.type) {
13
19
  case 'apiKey':
14
20
  if (scheme.in === 'header') {
@@ -1,10 +1,15 @@
1
1
  import type { OpenAPIV3_1 } from '@scalar/openapi-types';
2
2
  /**
3
3
  * Processes an OpenAPI document by bundling external references, upgrading to OpenAPI 3.1,
4
- * and dereferencing the document.
4
+ * and wrapping it so internal references stay intact but resolve lazily.
5
+ *
6
+ * Unlike a full dereference, the returned document keeps `$ref` nodes in place. Consumers
7
+ * resolve them on demand with `getResolvedRef` from `@scalar/workspace-store`, which reads the
8
+ * `$ref-value` exposed by the magic proxy. This avoids eagerly flattening (and duplicating)
9
+ * the whole document up front.
5
10
  *
6
11
  * @param document - The OpenAPI document to process. Can be a string (URL/path) or an object.
7
- * @returns A promise that resolves to the dereferenced OpenAPI 3.1 document.
12
+ * @returns A promise that resolves to the OpenAPI 3.1 document with lazily resolvable references.
8
13
  * @throws Error if the document cannot be processed or is invalid.
9
14
  */
10
15
  export declare function processOpenApiDocument(document: string | Record<string, any> | undefined): Promise<OpenAPIV3_1.Document>;
@@ -1 +1 @@
1
- {"version":3,"file":"process-openapi-document.d.ts","sourceRoot":"","sources":["../../src/utils/process-openapi-document.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AAGxD;;;;;;;GAOG;AACH,wBAAsB,sBAAsB,CAC1C,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,SAAS,GACjD,OAAO,CAAC,WAAW,CAAC,QAAQ,CAAC,CAoE/B"}
1
+ {"version":3,"file":"process-openapi-document.d.ts","sourceRoot":"","sources":["../../src/utils/process-openapi-document.ts"],"names":[],"mappings":"AAGA,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,CAiD/B"}
@@ -1,16 +1,18 @@
1
1
  import { bundle } from '@scalar/json-magic/bundle';
2
- import { parseJson } from '@scalar/json-magic/bundle/plugins/node';
3
- import { parseYaml } from '@scalar/json-magic/bundle/plugins/node';
4
- import { readFiles } from '@scalar/json-magic/bundle/plugins/node';
5
- import { fetchUrls } from '@scalar/json-magic/bundle/plugins/node';
6
- import { dereference } from '@scalar/openapi-parser';
2
+ import { fetchUrls, parseJson, parseYaml, readFiles } from '@scalar/json-magic/bundle/plugins/node';
3
+ import { createMagicProxy } from '@scalar/json-magic/magic-proxy';
7
4
  import { upgrade } from '@scalar/openapi-upgrader';
8
5
  /**
9
6
  * Processes an OpenAPI document by bundling external references, upgrading to OpenAPI 3.1,
10
- * and dereferencing the document.
7
+ * and wrapping it so internal references stay intact but resolve lazily.
8
+ *
9
+ * Unlike a full dereference, the returned document keeps `$ref` nodes in place. Consumers
10
+ * resolve them on demand with `getResolvedRef` from `@scalar/workspace-store`, which reads the
11
+ * `$ref-value` exposed by the magic proxy. This avoids eagerly flattening (and duplicating)
12
+ * the whole document up front.
11
13
  *
12
14
  * @param document - The OpenAPI document to process. Can be a string (URL/path) or an object.
13
- * @returns A promise that resolves to the dereferenced OpenAPI 3.1 document.
15
+ * @returns A promise that resolves to the OpenAPI 3.1 document with lazily resolvable references.
14
16
  * @throws Error if the document cannot be processed or is invalid.
15
17
  */
16
18
  export async function processOpenApiDocument(document) {
@@ -52,21 +54,7 @@ export async function processOpenApiDocument(document) {
52
54
  if (!upgraded) {
53
55
  throw new Error('Upgraded document is invalid: upgrade returned null or undefined');
54
56
  }
55
- // Dereference the document
56
- const dereferenceResult = dereference(upgraded);
57
- // Check for dereference errors
58
- if (dereferenceResult.errors && dereferenceResult.errors.length > 0) {
59
- const errorMessages = dereferenceResult.errors.map((err) => err.message).join(', ');
60
- throw new Error(`Failed to dereference OpenAPI document: ${errorMessages}`);
61
- }
62
- // Extract the schema from the dereference result
63
- const schema = dereferenceResult.schema;
64
- if (!schema) {
65
- throw new Error('Dereference result does not contain a schema');
66
- }
67
- // Ensure the schema is a valid OpenAPI 3.1 document
68
- if (typeof schema !== 'object') {
69
- throw new Error('Dereferenced schema is invalid: expected an object');
70
- }
71
- return schema;
57
+ // Wrap the document in a magic proxy so internal references resolve lazily via `$ref-value`.
58
+ // External references were already pulled inline by `bundle` above, so only local `$ref`s remain.
59
+ return createMagicProxy(upgraded);
72
60
  }
@@ -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;AAC5E,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAOhC;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,IAAI,EAAE,MAAM,CAAC,EAAE,OAAO,CAAC,QAAQ,QA4F7E"}
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;AAOhC;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,IAAI,EAAE,MAAM,CAAC,EAAE,OAAO,CAAC,QAAQ,QAmG7E"}
@@ -1,3 +1,4 @@
1
+ import { getResolvedRef } from '@scalar/workspace-store/helpers/get-resolved-ref';
1
2
  import { respondWithAuthorizePage } from '../routes/respond-with-authorize-page.js';
2
3
  import { respondWithToken } from '../routes/respond-with-token.js';
3
4
  import { getOpenAuthTokenUrls, getPathFromUrl } from './get-open-auth-token-urls.js';
@@ -28,7 +29,12 @@ export function setUpAuthenticationRoutes(app, schema) {
28
29
  // Set up routes for different OAuth 2.0 flows
29
30
  const authorizeUrls = new Set();
30
31
  const tokenUrls = new Set();
31
- Object.entries(securitySchemes).forEach(([_, scheme]) => {
32
+ Object.entries(securitySchemes).forEach(([_, rawScheme]) => {
33
+ const scheme = getResolvedRef(rawScheme);
34
+ // Skip schemes that could not be resolved (e.g. a `$ref` to a missing component)
35
+ if (!scheme) {
36
+ return;
37
+ }
32
38
  if (scheme.type === 'oauth2') {
33
39
  if (scheme.flows?.authorizationCode) {
34
40
  const authorizeRoute = scheme.flows.authorizationCode.authorizationUrl ?? '/oauth/authorize';
package/package.json CHANGED
@@ -16,7 +16,7 @@
16
16
  "swagger",
17
17
  "cli"
18
18
  ],
19
- "version": "0.10.16",
19
+ "version": "0.10.17",
20
20
  "engines": {
21
21
  "node": ">=22"
22
22
  },
@@ -53,12 +53,12 @@
53
53
  "dependencies": {
54
54
  "@faker-js/faker": "10.4.0",
55
55
  "hono": "^4.12.7",
56
- "@scalar/openapi-types": "0.9.1",
57
- "@scalar/json-magic": "0.12.15",
56
+ "yaml": "^2.8.3",
58
57
  "@scalar/helpers": "0.8.1",
58
+ "@scalar/json-magic": "0.12.15",
59
+ "@scalar/openapi-types": "0.9.1",
59
60
  "@scalar/openapi-upgrader": "0.2.9",
60
- "@scalar/openapi-parser": "0.28.6",
61
- "@scalar/workspace-store": "0.53.0"
61
+ "@scalar/workspace-store": "0.54.0"
62
62
  },
63
63
  "devDependencies": {
64
64
  "@types/node": "^24.1.0",