schema-components 1.20.0 → 1.22.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 (77) hide show
  1. package/README.md +1 -1
  2. package/dist/core/adapter.d.mts +28 -4
  3. package/dist/core/adapter.mjs +408 -71
  4. package/dist/core/constraints.d.mts +2 -2
  5. package/dist/core/constraints.mjs +0 -2
  6. package/dist/core/diagnostics.d.mts +1 -1
  7. package/dist/core/errors.d.mts +1 -1
  8. package/dist/core/errors.mjs +9 -15
  9. package/dist/core/fieldOrder.d.mts +1 -1
  10. package/dist/core/formats.d.mts +22 -1
  11. package/dist/core/formats.mjs +21 -0
  12. package/dist/core/limits.d.mts +2 -0
  13. package/dist/core/limits.mjs +23 -0
  14. package/dist/core/merge.d.mts +11 -2
  15. package/dist/core/merge.mjs +11 -0
  16. package/dist/core/normalise.d.mts +36 -4
  17. package/dist/core/normalise.mjs +2 -2
  18. package/dist/core/openapi30.d.mts +24 -1
  19. package/dist/core/openapi30.mjs +2 -2
  20. package/dist/core/ref.d.mts +1 -1
  21. package/dist/core/ref.mjs +35 -9
  22. package/dist/core/renderer.d.mts +1 -1
  23. package/dist/core/renderer.mjs +0 -2
  24. package/dist/core/swagger2.d.mts +1 -1
  25. package/dist/core/swagger2.mjs +1 -1
  26. package/dist/core/typeInference.d.mts +2 -2
  27. package/dist/core/types.d.mts +2 -2
  28. package/dist/core/types.mjs +1 -4
  29. package/dist/core/version.d.mts +1 -1
  30. package/dist/core/walkBuilders.d.mts +13 -5
  31. package/dist/core/walkBuilders.mjs +11 -3
  32. package/dist/core/walker.d.mts +1 -1
  33. package/dist/core/walker.mjs +110 -26
  34. package/dist/{diagnostics-CbBPsxSt.d.mts → diagnostics-D0QCYGv0.d.mts} +1 -1
  35. package/dist/{errors-C2iABcn9.d.mts → errors-DpFwqs5C.d.mts} +7 -11
  36. package/dist/html/a11y.d.mts +2 -2
  37. package/dist/html/a11y.mjs +10 -3
  38. package/dist/html/renderToHtml.d.mts +10 -3
  39. package/dist/html/renderToHtml.mjs +13 -3
  40. package/dist/html/renderToHtmlStream.d.mts +2 -2
  41. package/dist/html/renderers.d.mts +2 -2
  42. package/dist/html/renderers.mjs +1 -6
  43. package/dist/html/streamRenderers.d.mts +5 -4
  44. package/dist/html/streamRenderers.mjs +91 -30
  45. package/dist/limits-Cw5QZND8.d.mts +29 -0
  46. package/dist/{normalise-CMMEl4cd.mjs → normalise-DVEJQmF7.mjs} +791 -141
  47. package/dist/openapi/ApiCallbacks.d.mts +1 -1
  48. package/dist/openapi/ApiLinks.d.mts +1 -1
  49. package/dist/openapi/ApiResponseHeaders.d.mts +1 -1
  50. package/dist/openapi/ApiSecurity.d.mts +1 -1
  51. package/dist/openapi/ApiSecurity.mjs +127 -7
  52. package/dist/openapi/components.d.mts +175 -21
  53. package/dist/openapi/components.mjs +145 -21
  54. package/dist/openapi/parser.d.mts +1 -1
  55. package/dist/openapi/parser.mjs +74 -7
  56. package/dist/openapi/resolve.d.mts +70 -12
  57. package/dist/openapi/resolve.mjs +265 -42
  58. package/dist/react/SchemaComponent.d.mts +100 -35
  59. package/dist/react/SchemaComponent.mjs +66 -24
  60. package/dist/react/SchemaView.d.mts +3 -3
  61. package/dist/react/SchemaView.mjs +2 -2
  62. package/dist/react/fieldPath.d.mts +1 -1
  63. package/dist/react/headless.d.mts +1 -1
  64. package/dist/react/headless.mjs +1 -2
  65. package/dist/react/headlessRenderers.d.mts +3 -4
  66. package/dist/react/headlessRenderers.mjs +11 -31
  67. package/dist/{ref-C8JbwfiS.d.mts → ref-D-_JBZkF.d.mts} +7 -2
  68. package/dist/{renderer-SOIbJBtk.d.mts → renderer-BaRlQIuN.d.mts} +2 -2
  69. package/dist/themes/mantine.d.mts +1 -1
  70. package/dist/themes/mui.d.mts +1 -1
  71. package/dist/themes/radix.d.mts +1 -1
  72. package/dist/themes/shadcn.d.mts +1 -1
  73. package/dist/typeInference-DkcUHfaM.d.mts +982 -0
  74. package/dist/{types-C9zw9wbX.d.mts → types-BrRMV0en.d.mts} +15 -12
  75. package/package.json +1 -3
  76. package/dist/typeInference-CDoD_LZ_.d.mts +0 -533
  77. /package/dist/{version-D-u7aMfy.d.mts → version-D2jfdX6E.d.mts} +0 -0
@@ -1,12 +1,13 @@
1
1
  import { isObject, toRecordOrUndefined } from "../core/guards.mjs";
2
+ import { emitDiagnostic } from "../core/diagnostics.mjs";
2
3
  import { walk } from "../core/walker.mjs";
3
4
  import { ApiCallbacks } from "./ApiCallbacks.mjs";
4
5
  import { ApiLinks } from "./ApiLinks.mjs";
5
6
  import { ApiResponseHeaders } from "./ApiResponseHeaders.mjs";
6
7
  import { ApiSecurity } from "./ApiSecurity.mjs";
7
- import { getLinks, getSecurityRequirements, getSecuritySchemes, listCallbacks } from "./parser.mjs";
8
+ import { getExternalDocs, getLinks, getSecurityRequirements, getSecuritySchemes, getXmlInfo, listCallbacks, listWebhooks } from "./parser.mjs";
8
9
  import { joinPath, renderField, sanitisePrefix } from "../react/SchemaComponent.mjs";
9
- import { getParsed, resolveOperation, resolveParameters, resolveRequestBody, resolveResponse, toDoc } from "./resolve.mjs";
10
+ import { getParsed, resolveOperationFromParsed, resolveParametersFromParsed, resolveRequestBodyFromParsed, resolveResponseFromParsed, toDoc } from "./resolve.mjs";
10
11
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
11
12
  import { useId } from "react";
12
13
  //#region src/openapi/components.tsx
@@ -23,6 +24,32 @@ import { useId } from "react";
23
24
  * rendered via the walker + headless resolver directly, bypassing
24
25
  * SchemaComponent to avoid deferred-conditional-type compatibility issues.
25
26
  */
27
+ function buildDiagnostics(onDiagnostic, strict) {
28
+ if (onDiagnostic === void 0 && strict !== true) return void 0;
29
+ const opts = {};
30
+ if (onDiagnostic !== void 0) opts.diagnostics = onDiagnostic;
31
+ if (strict === true) opts.strict = true;
32
+ return opts;
33
+ }
34
+ /**
35
+ * Coerce an `unknown` `schema` prop to a document record. Returns
36
+ * `undefined` when the prop is not a plain object, surfacing a
37
+ * `doc-not-object` diagnostic so silent "empty document" misbehaviour
38
+ * (the historic `toDoc` `{}` fallback) is impossible.
39
+ *
40
+ * Components MUST short-circuit when this returns `undefined` rather
41
+ * than rendering empty operation lists.
42
+ */
43
+ function resolveRootDoc(doc, diagnostics) {
44
+ const resolved = toDoc(doc);
45
+ if (resolved === void 0) emitDiagnostic(diagnostics, {
46
+ code: "doc-not-object",
47
+ message: "OpenAPI document prop is not a plain object; nothing to render",
48
+ pointer: "",
49
+ detail: { received: typeof doc }
50
+ });
51
+ return resolved;
52
+ }
26
53
  function noop() {}
27
54
  function renderSchema(schema, rootDocument, options) {
28
55
  if (!isObject(schema)) throw new Error("renderSchema received a non-object schema from the resolver.");
@@ -42,14 +69,55 @@ function renderSchema(schema, rootDocument, options) {
42
69
  };
43
70
  return renderField(tree, options.value, options.onChange ?? noop, void 0, makeRenderChild(options.rootPath), options.rootPath, options.widgets);
44
71
  }
45
- function ApiOperation({ schema: doc, path, method, requestBodyValue, onRequestBodyChange, responseValue, meta, requestBodyFields, widgets }) {
46
- const rootDoc = toDoc(doc);
47
- const resolved = resolveOperation(rootDoc, path, method);
48
- const parsed = getParsed(rootDoc);
72
+ /**
73
+ * Render a Schema Object or Operation Object's `externalDocs` as a
74
+ * simple anchor with optional descriptive text. Returns `null` when no
75
+ * externalDocs are attached so callers can drop it into JSX without an
76
+ * extra guard.
77
+ */
78
+ function ExternalDocsLink({ externalDocs }) {
79
+ if (externalDocs === void 0) return null;
80
+ return /* @__PURE__ */ jsx("p", {
81
+ "data-external-docs": true,
82
+ children: /* @__PURE__ */ jsx("a", {
83
+ href: externalDocs.url,
84
+ children: externalDocs.description ?? externalDocs.url
85
+ })
86
+ });
87
+ }
88
+ /**
89
+ * Render a Schema Object's `xml` metadata as a footnote. The library
90
+ * does not render XML payloads natively, but the metadata still
91
+ * carries author intent (namespaces, element names, wrapping rules).
92
+ * Surface it so consumers can audit the dropped feature without
93
+ * losing the underlying information.
94
+ */
95
+ function SchemaXmlFootnote({ xml }) {
96
+ if (xml === void 0) return null;
97
+ return /* @__PURE__ */ jsx("aside", {
98
+ "data-schema-xml": true,
99
+ children: /* @__PURE__ */ jsxs("small", { children: [
100
+ "XML representation",
101
+ xml.name !== void 0 && ` — name: ${xml.name}`,
102
+ xml.namespace !== void 0 && ` — namespace: ${xml.namespace}`,
103
+ xml.prefix !== void 0 && ` — prefix: ${xml.prefix}`,
104
+ xml.attribute && " — attribute",
105
+ xml.wrapped && " — wrapped"
106
+ ] })
107
+ });
108
+ }
109
+ function ApiOperation({ schema: doc, path, method, requestBodyValue, onRequestBodyChange, responseValue, meta, requestBodyFields, widgets, onDiagnostic, strict }) {
110
+ const diagnostics = buildDiagnostics(onDiagnostic, strict);
111
+ const instancePrefix = sanitisePrefix(useId());
112
+ const rootDoc = resolveRootDoc(doc, diagnostics);
113
+ if (rootDoc === void 0) return null;
114
+ const parsed = getParsed(rootDoc, diagnostics);
115
+ const resolved = resolveOperationFromParsed(parsed, path, method, diagnostics);
49
116
  const securityReqs = getSecurityRequirements(parsed, path, method);
50
117
  const securitySchemes = getSecuritySchemes(parsed);
51
118
  const callbacks = listCallbacks(parsed, path, method);
52
- const instancePrefix = sanitisePrefix(useId());
119
+ const operationExternalDocs = getExternalDocs(resolved.operation.operation);
120
+ const requestBodyXml = resolved.requestBody?.schema !== void 0 ? getXmlInfo(resolved.requestBody.schema) : void 0;
53
121
  return /* @__PURE__ */ jsxs("section", {
54
122
  "data-operation": `${method.toUpperCase()} ${path}`,
55
123
  children: [
@@ -57,6 +125,7 @@ function ApiOperation({ schema: doc, path, method, requestBodyValue, onRequestBo
57
125
  operation: resolved.operation,
58
126
  pathItem: resolved.pathItem
59
127
  }),
128
+ /* @__PURE__ */ jsx(ExternalDocsLink, { externalDocs: operationExternalDocs }),
60
129
  /* @__PURE__ */ jsx(ApiSecurity, {
61
130
  requirements: securityReqs,
62
131
  schemes: securitySchemes
@@ -91,7 +160,8 @@ function ApiOperation({ schema: doc, path, method, requestBodyValue, onRequestBo
91
160
  meta,
92
161
  widgets,
93
162
  rootPath: joinPath(instancePrefix, "requestBody")
94
- })
163
+ }),
164
+ /* @__PURE__ */ jsx(SchemaXmlFootnote, { xml: requestBodyXml })
95
165
  ]
96
166
  }),
97
167
  resolved.responses.length > 0 && /* @__PURE__ */ jsxs("section", {
@@ -99,6 +169,7 @@ function ApiOperation({ schema: doc, path, method, requestBodyValue, onRequestBo
99
169
  children: [/* @__PURE__ */ jsx("h4", { children: "Responses" }), resolved.responses.map((response) => /* @__PURE__ */ jsx(ResponseCard, {
100
170
  response,
101
171
  rootDoc,
172
+ parsed,
102
173
  value: responseValue,
103
174
  meta,
104
175
  widgets,
@@ -110,10 +181,12 @@ function ApiOperation({ schema: doc, path, method, requestBodyValue, onRequestBo
110
181
  ]
111
182
  });
112
183
  }
113
- function ApiParameters({ schema: doc, path, method, meta, overrides, widgets }) {
114
- const rootDoc = toDoc(doc);
115
- const params = resolveParameters(rootDoc, path, method);
184
+ function ApiParameters({ schema: doc, path, method, meta, overrides, widgets, onDiagnostic, strict }) {
185
+ const diagnostics = buildDiagnostics(onDiagnostic, strict);
116
186
  const instancePrefix = sanitisePrefix(useId());
187
+ const rootDoc = resolveRootDoc(doc, diagnostics);
188
+ if (rootDoc === void 0) return null;
189
+ const params = resolveParametersFromParsed(getParsed(rootDoc, diagnostics), path, method);
117
190
  if (params.length === 0) return null;
118
191
  return /* @__PURE__ */ jsxs("section", {
119
192
  "data-parameters": true,
@@ -127,11 +200,14 @@ function ApiParameters({ schema: doc, path, method, meta, overrides, widgets })
127
200
  })]
128
201
  });
129
202
  }
130
- function ApiRequestBody({ schema: doc, path, method, value, onChange, meta, fields, widgets }) {
131
- const rootDoc = toDoc(doc);
132
- const requestBody = resolveRequestBody(rootDoc, path, method);
203
+ function ApiRequestBody({ schema: doc, path, method, value, onChange, meta, fields, widgets, onDiagnostic, strict }) {
204
+ const diagnostics = buildDiagnostics(onDiagnostic, strict);
133
205
  const instancePrefix = sanitisePrefix(useId());
206
+ const rootDoc = resolveRootDoc(doc, diagnostics);
207
+ if (rootDoc === void 0) return null;
208
+ const requestBody = resolveRequestBodyFromParsed(getParsed(rootDoc, diagnostics), path, method);
134
209
  if (requestBody?.schema === void 0) return null;
210
+ const requestBodyXml = getXmlInfo(requestBody.schema);
135
211
  return /* @__PURE__ */ jsxs("section", {
136
212
  "data-request-body": true,
137
213
  children: [
@@ -151,14 +227,18 @@ function ApiRequestBody({ schema: doc, path, method, value, onChange, meta, fiel
151
227
  meta,
152
228
  widgets,
153
229
  rootPath: instancePrefix
154
- })
230
+ }),
231
+ /* @__PURE__ */ jsx(SchemaXmlFootnote, { xml: requestBodyXml })
155
232
  ]
156
233
  });
157
234
  }
158
- function ApiResponse({ schema: doc, path, method, status, value, meta, fields, widgets }) {
159
- const rootDoc = toDoc(doc);
160
- const response = resolveResponse(rootDoc, path, method, status);
235
+ function ApiResponse({ schema: doc, path, method, status, value, meta, fields, widgets, onDiagnostic, strict }) {
236
+ const diagnostics = buildDiagnostics(onDiagnostic, strict);
161
237
  const instancePrefix = sanitisePrefix(useId());
238
+ const rootDoc = resolveRootDoc(doc, diagnostics);
239
+ if (rootDoc === void 0) return null;
240
+ const parsed = getParsed(rootDoc, diagnostics);
241
+ const response = resolveResponseFromParsed(parsed, path, method, status);
162
242
  if (response.schema === void 0) return /* @__PURE__ */ jsxs("div", {
163
243
  "data-status": status,
164
244
  children: [
@@ -170,6 +250,7 @@ function ApiResponse({ schema: doc, path, method, status, value, meta, fields, w
170
250
  return /* @__PURE__ */ jsx(ResponseCard, {
171
251
  response,
172
252
  rootDoc,
253
+ parsed,
173
254
  value,
174
255
  fields,
175
256
  meta,
@@ -179,6 +260,49 @@ function ApiResponse({ schema: doc, path, method, status, value, meta, fields, w
179
260
  idPrefix: instancePrefix
180
261
  });
181
262
  }
263
+ function ApiWebhook({ schema: doc, name, widgets, meta, onDiagnostic, strict }) {
264
+ const diagnostics = buildDiagnostics(onDiagnostic, strict);
265
+ const instancePrefix = sanitisePrefix(useId());
266
+ const rootDoc = resolveRootDoc(doc, diagnostics);
267
+ if (rootDoc === void 0) return null;
268
+ const webhook = listWebhooks(getParsed(rootDoc, diagnostics)).find((w) => w.name === name);
269
+ if (webhook === void 0) return null;
270
+ return /* @__PURE__ */ jsxs("section", {
271
+ "data-webhook": name,
272
+ "data-instance": instancePrefix,
273
+ children: [/* @__PURE__ */ jsxs("h3", { children: ["Webhook: ", name] }), webhook.operations.map((op) => {
274
+ const opProps = {
275
+ schema: rootDoc,
276
+ path: name,
277
+ method: op.method
278
+ };
279
+ if (widgets !== void 0) opProps.widgets = widgets;
280
+ if (meta !== void 0) opProps.meta = meta;
281
+ return /* @__PURE__ */ jsx(ApiOperation, { ...opProps }, `${name}-${op.method}`);
282
+ })]
283
+ });
284
+ }
285
+ function ApiWebhooks({ schema: doc, widgets, meta, onDiagnostic, strict }) {
286
+ const diagnostics = buildDiagnostics(onDiagnostic, strict);
287
+ const instancePrefix = sanitisePrefix(useId());
288
+ const rootDoc = resolveRootDoc(doc, diagnostics);
289
+ if (rootDoc === void 0) return null;
290
+ const webhooks = listWebhooks(getParsed(rootDoc, diagnostics));
291
+ if (webhooks.length === 0) return null;
292
+ return /* @__PURE__ */ jsxs("section", {
293
+ "data-webhooks": true,
294
+ "data-instance": instancePrefix,
295
+ children: [/* @__PURE__ */ jsx("h2", { children: "Webhooks" }), webhooks.map((webhook) => {
296
+ const props = {
297
+ schema: rootDoc,
298
+ name: webhook.name
299
+ };
300
+ if (widgets !== void 0) props.widgets = widgets;
301
+ if (meta !== void 0) props.meta = meta;
302
+ return /* @__PURE__ */ jsx(ApiWebhook, { ...props }, webhook.name);
303
+ })]
304
+ });
305
+ }
182
306
  function OperationHeader({ operation, pathItem }) {
183
307
  return /* @__PURE__ */ jsxs("header", { children: [
184
308
  (pathItem.summary !== void 0 || pathItem.description !== void 0) && /* @__PURE__ */ jsxs("div", {
@@ -227,7 +351,7 @@ function ParameterList({ parameters, rootDoc, overrides, meta, widgets, idPrefix
227
351
  ]
228
352
  }, param.name)) });
229
353
  }
230
- function ResponseCard({ response, rootDoc, value, fields, meta, widgets, path, method, idPrefix }) {
354
+ function ResponseCard({ response, rootDoc, parsed, value, fields, meta, widgets, path, method, idPrefix }) {
231
355
  if (response.schema === void 0) return /* @__PURE__ */ jsxs("div", {
232
356
  "data-status": response.statusCode,
233
357
  children: [
@@ -237,7 +361,7 @@ function ResponseCard({ response, rootDoc, value, fields, meta, widgets, path, m
237
361
  ]
238
362
  });
239
363
  let links = [];
240
- if (path !== void 0 && method !== void 0) links = getLinks(getParsed(rootDoc), path, method, response.statusCode);
364
+ if (path !== void 0 && method !== void 0) links = getLinks(parsed, path, method, response.statusCode);
241
365
  return /* @__PURE__ */ jsxs("div", {
242
366
  "data-status": response.statusCode,
243
367
  children: [
@@ -283,4 +407,4 @@ function extractRootMetaFromSchema(jsonSchema) {
283
407
  return Object.keys(meta).length > 0 ? meta : void 0;
284
408
  }
285
409
  //#endregion
286
- export { ApiOperation, ApiParameters, ApiRequestBody, ApiResponse };
410
+ export { ApiOperation, ApiParameters, ApiRequestBody, ApiResponse, ApiWebhook, ApiWebhooks };
@@ -1,4 +1,4 @@
1
- import { m as JsonObject } from "../types-C9zw9wbX.mjs";
1
+ import { m as JsonObject } from "../types-BrRMV0en.mjs";
2
2
 
3
3
  //#region src/openapi/parser.d.ts
4
4
  interface OpenApiDocument {
@@ -1,4 +1,5 @@
1
1
  import { getProperty, isObject } from "../core/guards.mjs";
2
+ import "../core/limits.mjs";
2
3
  import { isPrototypePollutingKey } from "../core/uri.mjs";
3
4
  //#region src/openapi/parser.ts
4
5
  function getString(value, key) {
@@ -44,11 +45,27 @@ const METHODS = [
44
45
  * (OpenAPI 3.1) if present. Returns `undefined` when the value is not a
45
46
  * path item, the ref is malformed, or the target does not resolve.
46
47
  */
48
+ /**
49
+ * Follow Path Item Object `$ref` chains (up to MAX_PATH_ITEM_REF_HOPS).
50
+ * Returns the resolved Path Item, or `undefined` when the chain cycles,
51
+ * exceeds the cap, or any intermediate ref fails to resolve. The
52
+ * detailed diagnostics for cycle and depth-cap are emitted by the
53
+ * mirroring resolver in `resolve.ts` — this parser-side resolver simply
54
+ * stops walking.
55
+ */
47
56
  function resolvePathItem(parsed, pathItem) {
48
57
  if (!isObject(pathItem)) return void 0;
49
- const ref = getString(pathItem, "$ref");
50
- if (ref === void 0) return pathItem;
51
- return resolveRefInDoc(parsed.doc, ref) ?? void 0;
58
+ const visited = /* @__PURE__ */ new Set();
59
+ let current = pathItem;
60
+ for (let hop = 0; hop < 8; hop++) {
61
+ const ref = getString(current, "$ref");
62
+ if (ref === void 0) return current;
63
+ if (visited.has(ref)) return void 0;
64
+ visited.add(ref);
65
+ const target = resolveRefInDoc(parsed.doc, ref);
66
+ if (target === void 0) return void 0;
67
+ current = target;
68
+ }
52
69
  }
53
70
  function lookupPathItem(parsed, path) {
54
71
  const resolved = resolvePathItem(parsed, getProperty(getProperty(parsed.doc, "paths"), path));
@@ -120,8 +137,10 @@ function resolveParam(doc, param) {
120
137
  return param;
121
138
  }
122
139
  function getRequestBody(parsed, path, method) {
123
- const requestBody = getProperty(getProperty(lookupPathItem(parsed, path), method), "requestBody");
124
- if (!isObject(requestBody)) return void 0;
140
+ const requestBodyRaw = getProperty(getProperty(lookupPathItem(parsed, path), method), "requestBody");
141
+ if (!isObject(requestBodyRaw)) return void 0;
142
+ const requestBody = resolveWrapperRef(parsed.doc, requestBodyRaw);
143
+ if (requestBody === void 0) return void 0;
125
144
  const content = getProperty(requestBody, "content");
126
145
  if (!isObject(content)) return {
127
146
  required: getProperty(requestBody, "required") === true,
@@ -142,8 +161,10 @@ function getResponses(parsed, path, method) {
142
161
  const responses = getProperty(getProperty(lookupPathItem(parsed, path), method), "responses");
143
162
  if (!isObject(responses)) return [];
144
163
  const result = [];
145
- for (const [statusCode, response] of Object.entries(responses)) {
146
- if (!isObject(response)) continue;
164
+ for (const [statusCode, responseRaw] of Object.entries(responses)) {
165
+ if (!isObject(responseRaw)) continue;
166
+ const response = resolveWrapperRef(parsed.doc, responseRaw);
167
+ if (response === void 0) continue;
147
168
  const content = getProperty(response, "content");
148
169
  const contentTypes = isObject(content) ? Object.keys(content) : [];
149
170
  const schema = isObject(content) ? extractSchemaFromContent(content) : void 0;
@@ -158,15 +179,61 @@ function getResponses(parsed, path, method) {
158
179
  }
159
180
  return result;
160
181
  }
182
+ /**
183
+ * Resolve a single-hop `$ref` on a wrapper object — Response Object,
184
+ * Request Body Object, etc. — against the document root. Returns the
185
+ * referenced node when the wrapper is a `$ref`, the wrapper itself when
186
+ * it has no `$ref`, or `undefined` when the `$ref` is malformed or
187
+ * cannot be resolved (so the caller skips the entry rather than reading
188
+ * stale fields from the bare `{ $ref }` envelope).
189
+ */
190
+ function resolveWrapperRef(doc, wrapper) {
191
+ const ref = getString(wrapper, "$ref");
192
+ if (ref === void 0) return wrapper;
193
+ return resolveRefInDoc(doc, ref);
194
+ }
161
195
  function extractSchemaFromContent(content) {
162
196
  const jsonSchema = getProperty(getProperty(content, "application/json"), "schema");
163
197
  if (isObject(jsonSchema)) return jsonSchema;
198
+ for (const [mediaType, mediaObj] of Object.entries(content)) {
199
+ if (!isJsonSuffixMediaType(mediaType)) continue;
200
+ if (!isObject(mediaObj)) continue;
201
+ const schema = getProperty(mediaObj, "schema");
202
+ if (isObject(schema)) return schema;
203
+ }
164
204
  for (const mediaType of Object.values(content)) {
165
205
  if (!isObject(mediaType)) continue;
166
206
  const schema = getProperty(mediaType, "schema");
167
207
  if (isObject(schema)) return schema;
168
208
  }
169
209
  }
210
+ /**
211
+ * Detect RFC 6839 structured-syntax-suffix media types that encode JSON.
212
+ * Matches `application/<anything>+json`, optionally with parameters
213
+ * (`; charset=utf-8`). Excludes the literal `application/json`, which
214
+ * the caller checks separately to preserve preference order.
215
+ */
216
+ function isJsonSuffixMediaType(mediaType) {
217
+ const lower = mediaType.toLowerCase();
218
+ if (lower === "application/json") return false;
219
+ const baseType = lower.split(";", 1)[0]?.trim() ?? "";
220
+ return baseType.startsWith("application/") && baseType.endsWith("+json");
221
+ }
222
+ /**
223
+ * Resolve an in-document `$ref` against the supplied doc root.
224
+ *
225
+ * Limitation — cross-Schema-Object relative refs: refs that do NOT
226
+ * start with `#/` are not resolved here. The `normaliseOpenApiSchemas`
227
+ * pipeline (see `resolveRelativeRefs` in `core/normalise.ts`) rewrites
228
+ * relative refs WITHIN a Schema Object using that schema's `$id` base
229
+ * URI, but it does not currently model `$id` scopes that span Schema
230
+ * Object boundaries (e.g. a sibling component schema with its own
231
+ * `$id` that another schema's relative ref targets). Such refs survive
232
+ * normalisation unchanged and fall through this function returning
233
+ * `undefined`. `resolve.ts:detectUnsupportedCrossSchemaRefs` walks the
234
+ * normalised doc and emits `cross-schema-relative-ref-unsupported` per
235
+ * offending ref so consumers notice the silent failure.
236
+ */
170
237
  function resolveRefInDoc(doc, ref) {
171
238
  if (!ref.startsWith("#/")) return void 0;
172
239
  const parts = ref.slice(2).split("/");
@@ -1,3 +1,4 @@
1
+ import { i as DiagnosticsOptions } from "../diagnostics-D0QCYGv0.mjs";
1
2
  import { OpenApiDocument, OperationInfo, ParameterInfo, ResponseInfo, getRequestBody } from "./parser.mjs";
2
3
 
3
4
  //#region src/openapi/resolve.d.ts
@@ -14,15 +15,31 @@ import { OpenApiDocument, OperationInfo, ParameterInfo, ResponseInfo, getRequest
14
15
  * same form `<SchemaComponent>` does, keeping the OpenAPI components on
15
16
  * the same pipeline as the top-level adapter.
16
17
  *
17
- * The cache is keyed by the caller-supplied document so subsequent calls
18
- * with the same input bypass both normalisation and parsing.
18
+ * When `diagnostics` is supplied, normalisation events
19
+ * (`duplicate-body-parameter`, `dropped-swagger-feature`,
20
+ * `unknown-json-schema-dialect`, `divisible-by-conflict`,
21
+ * `relative-ref-resolved`, etc.) are forwarded to the sink. Passing
22
+ * diagnostics also bypasses the cache so each call observes the
23
+ * normalisation pipeline running against the supplied sink — caching
24
+ * would silently swallow every emission after the first.
25
+ *
26
+ * The cache is keyed by the caller-supplied document so subsequent
27
+ * cache-eligible calls with the same input bypass both normalisation
28
+ * and parsing.
19
29
  */
20
- declare function getParsed(doc: Record<string, unknown>): OpenApiDocument;
30
+ declare function getParsed(doc: Record<string, unknown>, diagnostics?: DiagnosticsOptions): OpenApiDocument;
21
31
  /**
22
- * Coerce an unknown value to a record, returning an empty record
23
- * for non-objects.
32
+ * Coerce an unknown value to a record, returning `undefined` when the
33
+ * value is not a plain object. Callers MUST handle the `undefined` case
34
+ * explicitly — typically by rendering a "doc not an object" diagnostic
35
+ * and short-circuiting, never by silently substituting `{}`.
36
+ *
37
+ * A previous implementation fell back to `{}` for non-objects, which
38
+ * masked configuration mistakes (passing a string, `null`, an array, or
39
+ * `undefined` as the OpenAPI document) as an empty document with no
40
+ * operations.
24
41
  */
25
- declare function toDoc(value: unknown): Record<string, unknown>;
42
+ declare function toDoc(value: unknown): Record<string, unknown> | undefined;
26
43
  /**
27
44
  * Path-Item-level metadata. OpenAPI 3.1 added `summary` and `description`
28
45
  * to Path Item Objects alongside the existing operation-level fields.
@@ -39,26 +56,67 @@ interface ResolvedOperation {
39
56
  requestBody: ReturnType<typeof getRequestBody>;
40
57
  responses: ResponseInfo[];
41
58
  }
59
+ /**
60
+ * Resolve an operation against an already-parsed document. Throws if
61
+ * the operation is not found.
62
+ *
63
+ * Used by callers that have already obtained a parsed document via
64
+ * {@link getParsed} — most importantly the React components, which
65
+ * supply `diagnostics` to `getParsed` and must avoid re-running the
66
+ * normalisation pipeline (every re-run would emit each diagnostic
67
+ * again into the sink).
68
+ */
69
+ declare function resolveOperationFromParsed(parsed: OpenApiDocument, path: string, method: string, diagnostics?: DiagnosticsOptions): ResolvedOperation;
42
70
  /**
43
71
  * Resolve an operation from an OpenAPI document by path and method.
44
72
  * Throws if the operation is not found.
73
+ *
74
+ * `diagnostics` is forwarded to {@link getParsed} so normalisation
75
+ * events surface to the caller's sink.
76
+ */
77
+ declare function resolveOperation(doc: Record<string, unknown>, path: string, method: string, diagnostics?: DiagnosticsOptions): ResolvedOperation;
78
+ /**
79
+ * Resolve parameters against an already-parsed document. See
80
+ * {@link resolveOperationFromParsed} for the rationale.
45
81
  */
46
- declare function resolveOperation(doc: Record<string, unknown>, path: string, method: string): ResolvedOperation;
82
+ declare function resolveParametersFromParsed(parsed: OpenApiDocument, path: string, method: string): ParameterInfo[];
47
83
  /**
48
84
  * Resolve parameters for an operation. Returns empty array if none.
85
+ *
86
+ * `diagnostics` is forwarded to {@link getParsed} so normalisation
87
+ * events surface to the caller's sink.
88
+ */
89
+ declare function resolveParameters(doc: Record<string, unknown>, path: string, method: string, diagnostics?: DiagnosticsOptions): ParameterInfo[];
90
+ /**
91
+ * Resolve a request body against an already-parsed document. See
92
+ * {@link resolveOperationFromParsed} for the rationale.
49
93
  */
50
- declare function resolveParameters(doc: Record<string, unknown>, path: string, method: string): ParameterInfo[];
94
+ declare function resolveRequestBodyFromParsed(parsed: OpenApiDocument, path: string, method: string): ReturnType<typeof getRequestBody>;
51
95
  /**
52
96
  * Resolve request body for an operation. Returns undefined if none.
97
+ *
98
+ * `diagnostics` is forwarded to {@link getParsed} so normalisation
99
+ * events surface to the caller's sink.
53
100
  */
54
- declare function resolveRequestBody(doc: Record<string, unknown>, path: string, method: string): ReturnType<typeof getRequestBody>;
101
+ declare function resolveRequestBody(doc: Record<string, unknown>, path: string, method: string, diagnostics?: DiagnosticsOptions): ReturnType<typeof getRequestBody>;
102
+ /**
103
+ * Resolve a specific response against an already-parsed document. See
104
+ * {@link resolveOperationFromParsed} for the rationale.
105
+ */
106
+ declare function resolveResponseFromParsed(parsed: OpenApiDocument, path: string, method: string, statusCode: string): ResponseInfo;
55
107
  /**
56
108
  * Resolve a specific response by status code. Throws if not found.
109
+ *
110
+ * `diagnostics` is forwarded to {@link getParsed} so normalisation
111
+ * events surface to the caller's sink.
57
112
  */
58
- declare function resolveResponse(doc: Record<string, unknown>, path: string, method: string, statusCode: string): ResponseInfo;
113
+ declare function resolveResponse(doc: Record<string, unknown>, path: string, method: string, statusCode: string, diagnostics?: DiagnosticsOptions): ResponseInfo;
59
114
  /**
60
115
  * Resolve all responses for an operation.
116
+ *
117
+ * `diagnostics` is forwarded to {@link getParsed} so normalisation
118
+ * events surface to the caller's sink.
61
119
  */
62
- declare function resolveResponses(doc: Record<string, unknown>, path: string, method: string): ResponseInfo[];
120
+ declare function resolveResponses(doc: Record<string, unknown>, path: string, method: string, diagnostics?: DiagnosticsOptions): ResponseInfo[];
63
121
  //#endregion
64
- export { PathItemInfo, ResolvedOperation, getParsed, resolveOperation, resolveParameters, resolveRequestBody, resolveResponse, resolveResponses, toDoc };
122
+ export { PathItemInfo, ResolvedOperation, getParsed, resolveOperation, resolveOperationFromParsed, resolveParameters, resolveParametersFromParsed, resolveRequestBody, resolveRequestBodyFromParsed, resolveResponse, resolveResponseFromParsed, resolveResponses, toDoc };