schema-components 1.16.3 → 1.18.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 (48) hide show
  1. package/dist/core/adapter.d.mts +1 -1
  2. package/dist/core/constraints.d.mts +1 -1
  3. package/dist/core/diagnostics.d.mts +1 -1
  4. package/dist/core/merge.d.mts +14 -8
  5. package/dist/core/merge.mjs +81 -12
  6. package/dist/core/normalise.d.mts +1 -1
  7. package/dist/core/ref.d.mts +1 -1
  8. package/dist/core/renderer.d.mts +2 -2
  9. package/dist/core/renderer.mjs +50 -1
  10. package/dist/core/swagger2.d.mts +1 -1
  11. package/dist/core/typeInference.d.mts +2 -2
  12. package/dist/core/walkBuilders.d.mts +2 -2
  13. package/dist/core/walker.mjs +2 -2
  14. package/dist/{diagnostics-DzbZmcLI.d.mts → diagnostics-BYk63jsC.d.mts} +1 -1
  15. package/dist/html/a11y.d.mts +13 -2
  16. package/dist/html/a11y.mjs +26 -2
  17. package/dist/html/renderToHtml.d.mts +1 -1
  18. package/dist/html/renderToHtml.mjs +5 -3
  19. package/dist/html/renderToHtmlStream.d.mts +1 -1
  20. package/dist/html/renderers.d.mts +4 -3
  21. package/dist/html/renderers.mjs +9 -13
  22. package/dist/html/streamRenderers.d.mts +1 -1
  23. package/dist/openapi/bundle.d.mts +9 -4
  24. package/dist/openapi/bundle.mjs +73 -15
  25. package/dist/openapi/components.d.mts +1 -1
  26. package/dist/openapi/components.mjs +61 -27
  27. package/dist/openapi/parser.mjs +8 -8
  28. package/dist/openapi/resolve.d.mts +13 -2
  29. package/dist/openapi/resolve.mjs +19 -3
  30. package/dist/react/SchemaComponent.d.mts +35 -7
  31. package/dist/react/SchemaComponent.mjs +49 -43
  32. package/dist/react/SchemaView.d.mts +12 -4
  33. package/dist/react/SchemaView.mjs +24 -49
  34. package/dist/react/headless.d.mts +1 -1
  35. package/dist/react/headlessRenderers.d.mts +15 -2
  36. package/dist/react/headlessRenderers.mjs +52 -37
  37. package/dist/{ref-DvWoULcy.d.mts → ref-Ckt5liZs.d.mts} +1 -1
  38. package/dist/{renderer-BdSqllx5.d.mts → renderer-DXo-rXHJ.d.mts} +28 -2
  39. package/dist/themes/mantine.d.mts +6 -1
  40. package/dist/themes/mantine.mjs +44 -11
  41. package/dist/themes/mui.d.mts +1 -1
  42. package/dist/themes/mui.mjs +23 -8
  43. package/dist/themes/radix.d.mts +1 -1
  44. package/dist/themes/radix.mjs +43 -11
  45. package/dist/themes/shadcn.d.mts +1 -1
  46. package/dist/themes/shadcn.mjs +28 -10
  47. package/dist/{typeInference-k7FXfTVO.d.mts → typeInference-5JiqIZ8t.d.mts} +57 -4
  48. package/package.json +5 -2
@@ -29,28 +29,38 @@ import { isObject } from "../core/guards.mjs";
29
29
  *
30
30
  * Walks every $ref in the document. For external refs (not starting with `#`),
31
31
  * calls the resolver to fetch the external document, extracts the referenced
32
- * schema, inlines it into `components.schemas` with a synthesised name, and
33
- * rewrites the $ref to point to the inlined copy.
32
+ * schema, inlines it into `components.schemas` under a synthesised name, and
33
+ * rewrites the original $ref to point at the new internal location
34
+ * (`#/components/schemas/<name>`).
35
+ *
36
+ * Identical external refs share a single entry — the second occurrence of
37
+ * the same `(uri, fragment)` pair reuses the name produced for the first.
38
+ * Name collisions between different refs are resolved by suffixing a counter.
34
39
  *
35
40
  * The resolver is called once per unique URI and the result is cached.
36
41
  *
37
- * Returns a deep-cloned document with all external refs resolved.
38
- * The original document is never mutated.
42
+ * Returns a deep-cloned document with all external refs replaced by internal
43
+ * refs. The original document is never mutated.
39
44
  */
40
45
  async function bundleOpenApiDoc(doc, resolver) {
41
46
  const result = structuredClone(doc);
42
47
  const uriCache = /* @__PURE__ */ new Map();
48
+ const inlineCache = /* @__PURE__ */ new Map();
43
49
  if (!isObject(result.components)) result.components = {};
44
- if (!isObject(result.components)) result.components = {};
45
- if (isObject(result.components) && !isObject(result.components.schemas)) result.components.schemas = {};
46
- await walkAndInline(result, uriCache, resolver);
50
+ const components = result.components;
51
+ if (!isObject(components)) throw new Error("bundleOpenApiDoc: components is not an object");
52
+ if (!isObject(components.schemas)) components.schemas = {};
53
+ const schemasNode = components.schemas;
54
+ if (!isObject(schemasNode)) throw new Error("bundleOpenApiDoc: components.schemas is not an object");
55
+ await walkAndInline(result, schemasNode, uriCache, inlineCache, resolver);
47
56
  return result;
48
57
  }
49
58
  /**
50
59
  * Walk a document tree, find external $ref strings, resolve them,
51
- * inline the targets, and rewrite the refs.
60
+ * inline the targets into `components.schemas`, and rewrite each $ref
61
+ * to point at the new internal location.
52
62
  */
53
- async function walkAndInline(node, uriCache, resolver) {
63
+ async function walkAndInline(node, schemasNode, uriCache, inlineCache, resolver) {
54
64
  if (!isObject(node)) return;
55
65
  if (typeof node.$ref === "string" && !node.$ref.startsWith("#")) {
56
66
  const ref = node.$ref;
@@ -66,15 +76,21 @@ async function walkAndInline(node, uriCache, resolver) {
66
76
  }
67
77
  }
68
78
  if (externalDoc !== void 0) {
69
- const target = resolveFragment(externalDoc, fragment);
70
- if (isObject(target)) {
71
- delete node.$ref;
72
- for (const [key, value] of Object.entries(target)) node[key] = value;
79
+ const cacheKey = `${uri}${fragment}`;
80
+ let inlinedName = inlineCache.get(cacheKey);
81
+ if (inlinedName === void 0) {
82
+ const target = resolveFragment(externalDoc, fragment);
83
+ if (isObject(target)) {
84
+ inlinedName = registerInline(schemasNode, uri, fragment, target);
85
+ inlineCache.set(cacheKey, inlinedName);
86
+ await walkAndInline(schemasNode[inlinedName], schemasNode, uriCache, inlineCache, resolver);
87
+ }
73
88
  }
89
+ if (inlinedName !== void 0) node.$ref = `#/components/schemas/${inlinedName}`;
74
90
  }
75
91
  }
76
- for (const value of Object.values(node)) if (isObject(value)) await walkAndInline(value, uriCache, resolver);
77
- else if (Array.isArray(value)) for (const item of value) await walkAndInline(item, uriCache, resolver);
92
+ for (const value of Object.values(node)) if (isObject(value)) await walkAndInline(value, schemasNode, uriCache, inlineCache, resolver);
93
+ else if (Array.isArray(value)) for (const item of value) await walkAndInline(item, schemasNode, uriCache, inlineCache, resolver);
78
94
  }
79
95
  /**
80
96
  * Resolve a JSON Pointer fragment within a document.
@@ -91,5 +107,47 @@ function resolveFragment(doc, fragment) {
91
107
  }
92
108
  return isObject(current) ? current : void 0;
93
109
  }
110
+ /**
111
+ * Derive a candidate identifier for an inlined external schema. Prefers
112
+ * the last meaningful segment of the JSON Pointer fragment; falls back
113
+ * to the URI's filename (sans extension), then to a generic prefix.
114
+ */
115
+ function deriveCandidateName(uri, fragment) {
116
+ if (fragment.startsWith("#/")) {
117
+ const last = fragment.slice(2).split("/").at(-1);
118
+ if (last !== void 0 && last.length > 0) return sanitiseName(last);
119
+ }
120
+ const pathOnly = uri.split(/[?#]/)[0] ?? uri;
121
+ const lastSlash = pathOnly.lastIndexOf("/");
122
+ const filename = lastSlash >= 0 ? pathOnly.slice(lastSlash + 1) : pathOnly;
123
+ const dot = filename.lastIndexOf(".");
124
+ const stem = dot > 0 ? filename.slice(0, dot) : filename;
125
+ if (stem.length > 0) return sanitiseName(stem);
126
+ return "ExternalSchema";
127
+ }
128
+ /**
129
+ * Sanitise a string into a JSON Pointer-safe identifier: alphanumerics
130
+ * and underscores only. An empty result falls back to "Schema".
131
+ */
132
+ function sanitiseName(raw) {
133
+ const cleaned = raw.replace(/[^A-Za-z0-9_]/g, "_");
134
+ return cleaned.length > 0 ? cleaned : "Schema";
135
+ }
136
+ /**
137
+ * Place the resolved target into `components.schemas` under a unique
138
+ * name derived from the ref, and return the chosen name. Collisions
139
+ * with existing entries are resolved by suffixing a counter.
140
+ */
141
+ function registerInline(schemasNode, uri, fragment, target) {
142
+ const base = deriveCandidateName(uri, fragment);
143
+ let name = base;
144
+ let counter = 2;
145
+ while (name in schemasNode) {
146
+ name = `${base}_${String(counter)}`;
147
+ counter++;
148
+ }
149
+ schemasNode[name] = structuredClone(target);
150
+ return name;
151
+ }
94
152
  //#endregion
95
153
  export { bundleOpenApiDoc };
@@ -1,5 +1,5 @@
1
1
  import { T as SchemaMeta, u as FieldOverride } from "../types-D_5ST7SS.mjs";
2
- import { i as InferResponseFields, n as InferParameterOverrides, r as InferRequestBodyFields, u as UnsafeFields } from "../typeInference-k7FXfTVO.mjs";
2
+ import { a as InferResponseFields, d as UnsafeFields, i as InferRequestBodyFields, r as InferParameterOverrides } from "../typeInference-5JiqIZ8t.mjs";
3
3
  import { WidgetMap } from "../react/SchemaComponent.mjs";
4
4
  import { ReactNode } from "react";
5
5
 
@@ -1,39 +1,46 @@
1
- import { toRecordOrUndefined } from "../core/guards.mjs";
2
- import { SchemaNormalisationError } from "../core/errors.mjs";
3
- import { normaliseSchema } from "../core/adapter.mjs";
1
+ import { isObject, toRecordOrUndefined } from "../core/guards.mjs";
4
2
  import { walk } from "../core/walker.mjs";
3
+ import { joinPath, renderField, sanitisePrefix } from "../react/SchemaComponent.mjs";
5
4
  import { ApiCallbacks } from "./ApiCallbacks.mjs";
6
5
  import { ApiLinks } from "./ApiLinks.mjs";
7
6
  import { ApiResponseHeaders } from "./ApiResponseHeaders.mjs";
8
7
  import { ApiSecurity } from "./ApiSecurity.mjs";
9
8
  import { getLinks, getSecurityRequirements, getSecuritySchemes, listCallbacks } from "./parser.mjs";
10
- import { renderField } from "../react/SchemaComponent.mjs";
11
9
  import { getParsed, resolveOperation, resolveParameters, resolveRequestBody, resolveResponse, toDoc } from "./resolve.mjs";
10
+ import { useId } from "react";
12
11
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
13
12
  //#region src/openapi/components.tsx
13
+ /**
14
+ * OpenAPI React components with type-safe generics.
15
+ *
16
+ * Render API operations, parameters, request bodies, and response schemas
17
+ * from OpenAPI 3.x documents. When the document is typed `as const`,
18
+ * the `fields` / `overrides` props get full autocomplete.
19
+ *
20
+ * Type safety is enforced at the outer component's props level via
21
+ * conditional types (InferRequestBodyFields, InferResponseFields,
22
+ * InferParameterOverrides). Internally, schemas are extracted and
23
+ * rendered via the walker + headless resolver directly, bypassing
24
+ * SchemaComponent to avoid deferred-conditional-type compatibility issues.
25
+ */
14
26
  function noop() {}
15
27
  function renderSchema(schema, rootDocument, options) {
16
- let jsonSchema;
17
- let rootMeta;
18
- try {
19
- const normalised = normaliseSchema(schema);
20
- jsonSchema = normalised.jsonSchema;
21
- rootMeta = normalised.rootMeta;
22
- } catch (err) {
23
- throw new SchemaNormalisationError(err instanceof Error ? err.message : "Failed to normalise schema", schema, "unknown");
24
- }
28
+ if (!isObject(schema)) throw new Error("renderSchema received a non-object schema from the resolver.");
29
+ const rootMeta = extractRootMetaFromSchema(schema);
25
30
  const componentMeta = {};
26
31
  if (options.readOnly === true) componentMeta.readOnly = true;
27
32
  if (options.meta !== void 0) for (const [k, v] of Object.entries(options.meta)) componentMeta[k] = v;
28
- const walkOpts = {
33
+ const tree = walk(schema, {
29
34
  componentMeta,
30
35
  rootMeta,
31
36
  fieldOverrides: toRecordOrUndefined(options.fields),
32
37
  rootDocument
38
+ });
39
+ const makeRenderChild = (parentPath) => (childTree, childValue, childOnChange, pathSuffix) => {
40
+ const childPath = joinPath(parentPath, pathSuffix);
41
+ return renderField(childTree, childValue, childOnChange, void 0, makeRenderChild(childPath), childPath, options.widgets);
33
42
  };
34
- const tree = walk(jsonSchema, walkOpts);
35
- const renderChild = (childTree, childValue, childOnChange) => renderField(childTree, childValue, childOnChange, void 0, renderChild, options.widgets);
36
- return renderField(tree, options.value, options.onChange ?? noop, void 0, renderChild, options.widgets);
43
+ return renderField(tree, options.value, options.onChange ?? noop, void 0, makeRenderChild(options.rootPath), options.rootPath, options.widgets);
37
44
  }
38
45
  function ApiOperation({ schema: doc, path, method, requestBodyValue, onRequestBodyChange, responseValue, meta, requestBodyFields, widgets }) {
39
46
  const rootDoc = toDoc(doc);
@@ -42,6 +49,7 @@ function ApiOperation({ schema: doc, path, method, requestBodyValue, onRequestBo
42
49
  const securityReqs = getSecurityRequirements(parsed, path, method);
43
50
  const securitySchemes = getSecuritySchemes(parsed);
44
51
  const callbacks = listCallbacks(parsed, path, method);
52
+ const instancePrefix = sanitisePrefix(useId());
45
53
  return /* @__PURE__ */ jsxs("section", {
46
54
  "data-operation": `${method.toUpperCase()} ${path}`,
47
55
  children: [
@@ -57,7 +65,8 @@ function ApiOperation({ schema: doc, path, method, requestBodyValue, onRequestBo
57
65
  parameters: resolved.parameters,
58
66
  rootDoc,
59
67
  meta,
60
- widgets
68
+ widgets,
69
+ idPrefix: joinPath(instancePrefix, "params")
61
70
  })]
62
71
  }),
63
72
  resolved.requestBody?.schema !== void 0 && /* @__PURE__ */ jsxs("section", {
@@ -77,7 +86,8 @@ function ApiOperation({ schema: doc, path, method, requestBodyValue, onRequestBo
77
86
  onChange: onRequestBodyChange,
78
87
  fields: requestBodyFields,
79
88
  meta,
80
- widgets
89
+ widgets,
90
+ rootPath: joinPath(instancePrefix, "requestBody")
81
91
  })
82
92
  ]
83
93
  }),
@@ -90,7 +100,8 @@ function ApiOperation({ schema: doc, path, method, requestBodyValue, onRequestBo
90
100
  meta,
91
101
  widgets,
92
102
  path,
93
- method
103
+ method,
104
+ idPrefix: joinPath(instancePrefix, `response-${response.statusCode}`)
94
105
  }, response.statusCode))]
95
106
  })
96
107
  ]
@@ -99,6 +110,7 @@ function ApiOperation({ schema: doc, path, method, requestBodyValue, onRequestBo
99
110
  function ApiParameters({ schema: doc, path, method, meta, overrides, widgets }) {
100
111
  const rootDoc = toDoc(doc);
101
112
  const params = resolveParameters(rootDoc, path, method);
113
+ const instancePrefix = sanitisePrefix(useId());
102
114
  if (params.length === 0) return null;
103
115
  return /* @__PURE__ */ jsxs("section", {
104
116
  "data-parameters": true,
@@ -107,13 +119,15 @@ function ApiParameters({ schema: doc, path, method, meta, overrides, widgets })
107
119
  rootDoc,
108
120
  overrides,
109
121
  meta,
110
- widgets
122
+ widgets,
123
+ idPrefix: instancePrefix
111
124
  })]
112
125
  });
113
126
  }
114
127
  function ApiRequestBody({ schema: doc, path, method, value, onChange, meta, fields, widgets }) {
115
128
  const rootDoc = toDoc(doc);
116
129
  const requestBody = resolveRequestBody(rootDoc, path, method);
130
+ const instancePrefix = sanitisePrefix(useId());
117
131
  if (requestBody?.schema === void 0) return null;
118
132
  return /* @__PURE__ */ jsxs("section", {
119
133
  "data-request-body": true,
@@ -132,7 +146,8 @@ function ApiRequestBody({ schema: doc, path, method, value, onChange, meta, fiel
132
146
  onChange,
133
147
  fields,
134
148
  meta,
135
- widgets
149
+ widgets,
150
+ rootPath: instancePrefix
136
151
  })
137
152
  ]
138
153
  });
@@ -140,6 +155,7 @@ function ApiRequestBody({ schema: doc, path, method, value, onChange, meta, fiel
140
155
  function ApiResponse({ schema: doc, path, method, status, value, meta, fields, widgets }) {
141
156
  const rootDoc = toDoc(doc);
142
157
  const response = resolveResponse(rootDoc, path, method, status);
158
+ const instancePrefix = sanitisePrefix(useId());
143
159
  if (response.schema === void 0) return /* @__PURE__ */ jsxs("div", {
144
160
  "data-status": status,
145
161
  children: [
@@ -156,7 +172,8 @@ function ApiResponse({ schema: doc, path, method, status, value, meta, fields, w
156
172
  meta,
157
173
  widgets,
158
174
  path,
159
- method
175
+ method,
176
+ idPrefix: instancePrefix
160
177
  });
161
178
  }
162
179
  function OperationHeader({ operation }) {
@@ -173,7 +190,7 @@ function OperationHeader({ operation }) {
173
190
  })
174
191
  ] });
175
192
  }
176
- function ParameterList({ parameters, rootDoc, overrides, meta, widgets }) {
193
+ function ParameterList({ parameters, rootDoc, overrides, meta, widgets, idPrefix }) {
177
194
  return /* @__PURE__ */ jsx(Fragment, { children: parameters.map((param) => /* @__PURE__ */ jsxs("div", {
178
195
  "data-parameter": param.name,
179
196
  children: [
@@ -187,12 +204,13 @@ function ParameterList({ parameters, rootDoc, overrides, meta, widgets }) {
187
204
  }),
188
205
  renderSchema(param.schema ?? { type: "string" }, rootDoc, {
189
206
  meta: buildParamMeta(param, overrides, meta),
190
- widgets
207
+ widgets,
208
+ rootPath: joinPath(idPrefix, param.name)
191
209
  })
192
210
  ]
193
211
  }, param.name)) });
194
212
  }
195
- function ResponseCard({ response, rootDoc, value, fields, meta, widgets, path, method }) {
213
+ function ResponseCard({ response, rootDoc, value, fields, meta, widgets, path, method, idPrefix }) {
196
214
  if (response.schema === void 0) return /* @__PURE__ */ jsxs("div", {
197
215
  "data-status": response.statusCode,
198
216
  children: [
@@ -217,7 +235,8 @@ function ResponseCard({ response, rootDoc, value, fields, meta, widgets, path, m
217
235
  readOnly: true,
218
236
  ...meta
219
237
  },
220
- widgets
238
+ widgets,
239
+ rootPath: idPrefix
221
240
  }),
222
241
  /* @__PURE__ */ jsx(ApiResponseHeaders, { headers: response.headers }),
223
242
  /* @__PURE__ */ jsx(ApiLinks, { links })
@@ -233,5 +252,20 @@ function buildParamMeta(param, overrides, meta) {
233
252
  if (meta !== void 0) for (const [k, v] of Object.entries(meta)) result[k] = v;
234
253
  return Object.keys(result).length > 0 ? result : void 0;
235
254
  }
255
+ /**
256
+ * Extract root-level meta (title, description, readOnly, etc.) from a
257
+ * JSON Schema node. Mirrors `extractRootMetaFromJson` in the adapter so
258
+ * pre-normalised schemas (extracted from `getParsed`) still surface root
259
+ * meta to the walker without an extra adapter round-trip.
260
+ */
261
+ function extractRootMetaFromSchema(jsonSchema) {
262
+ const meta = {};
263
+ if (jsonSchema.readOnly === true) meta.readOnly = true;
264
+ if (jsonSchema.writeOnly === true) meta.writeOnly = true;
265
+ if (typeof jsonSchema.description === "string") meta.description = jsonSchema.description;
266
+ if (typeof jsonSchema.title === "string") meta.title = jsonSchema.title;
267
+ if (typeof jsonSchema.deprecated === "boolean") meta.deprecated = jsonSchema.deprecated;
268
+ return Object.keys(meta).length > 0 ? meta : void 0;
269
+ }
236
270
  //#endregion
237
271
  export { ApiOperation, ApiParameters, ApiRequestBody, ApiResponse };
@@ -79,22 +79,22 @@ function getParameters(parsed, path, method) {
79
79
  if (pathItem === void 0) return [];
80
80
  const operation = getProperty(pathItem, method);
81
81
  if (!isObject(operation)) return [];
82
- const pathParams = extractParameterList(getProperty(pathItem, "parameters"));
83
- const opParams = extractParameterList(getProperty(operation, "parameters"));
82
+ const pathParams = extractParameterList(parsed.doc, getProperty(pathItem, "parameters"));
83
+ const opParams = extractParameterList(parsed.doc, getProperty(operation, "parameters"));
84
84
  const map = /* @__PURE__ */ new Map();
85
85
  for (const param of pathParams) map.set(`${param.name}:${param.location}`, param);
86
86
  for (const param of opParams) map.set(`${param.name}:${param.location}`, param);
87
87
  return [...map.values()];
88
88
  }
89
- function extractParameterList(parameters) {
89
+ function extractParameterList(doc, parameters) {
90
90
  if (!Array.isArray(parameters)) return [];
91
91
  const result = [];
92
92
  for (const param of parameters) {
93
93
  if (!isObject(param)) continue;
94
- const name = getProperty(param, "name");
95
- const location = getProperty(param, "in");
94
+ const resolved = resolveParam(doc, param);
95
+ const name = getProperty(resolved, "name");
96
+ const location = getProperty(resolved, "in");
96
97
  if (typeof name !== "string" || typeof location !== "string") continue;
97
- const resolved = resolveParam(param);
98
98
  const schema = getProperty(resolved, "schema");
99
99
  result.push({
100
100
  name,
@@ -107,10 +107,10 @@ function extractParameterList(parameters) {
107
107
  }
108
108
  return result;
109
109
  }
110
- function resolveParam(param) {
110
+ function resolveParam(doc, param) {
111
111
  const ref = getProperty(param, "$ref");
112
112
  if (typeof ref === "string" && ref.startsWith("#/")) {
113
- const resolved = resolveRefInDoc(param, ref);
113
+ const resolved = resolveRefInDoc(doc, ref);
114
114
  if (resolved !== void 0) return resolved;
115
115
  }
116
116
  return param;
@@ -2,8 +2,19 @@ import { OpenApiDocument, OperationInfo, ParameterInfo, ResponseInfo, getRequest
2
2
 
3
3
  //#region src/openapi/resolve.d.ts
4
4
  /**
5
- * Parse and cache an OpenAPI document. Returns cached version if
6
- * the same object identity has been seen before.
5
+ * Parse and cache an OpenAPI document. Returns the cached parse for the
6
+ * same object identity.
7
+ *
8
+ * Before parsing, the document is run through the version-aware
9
+ * normalisation pipeline (`normaliseOpenApiSchemas`) so OpenAPI 3.0.x
10
+ * keywords (`nullable`, `discriminator`, `example`) and Swagger 2.0
11
+ * documents are converted to canonical Draft 2020-12 form. The parser
12
+ * and downstream extractors (`getRequestBody`, `getResponses`, etc.) then
13
+ * observe schemas in the same form `<SchemaComponent>` does, keeping the
14
+ * OpenAPI components on the same pipeline as the top-level adapter.
15
+ *
16
+ * The cache is keyed by the caller-supplied document so subsequent calls
17
+ * with the same input bypass both normalisation and parsing.
7
18
  */
8
19
  declare function getParsed(doc: Record<string, unknown>): OpenApiDocument;
9
20
  /**
@@ -1,4 +1,6 @@
1
1
  import { isObject } from "../core/guards.mjs";
2
+ import { detectOpenApiVersion } from "../core/version.mjs";
3
+ import { i as normaliseOpenApiSchemas } from "../normalise-tL9FckAk.mjs";
2
4
  import { getParameters, getRequestBody, getResponses, listOperations, parseOpenApiDocument } from "./parser.mjs";
3
5
  //#region src/openapi/resolve.ts
4
6
  /**
@@ -10,14 +12,28 @@ import { getParameters, getRequestBody, getResponses, listOperations, parseOpenA
10
12
  */
11
13
  const docCache = /* @__PURE__ */ new WeakMap();
12
14
  /**
13
- * Parse and cache an OpenAPI document. Returns cached version if
14
- * the same object identity has been seen before.
15
+ * Parse and cache an OpenAPI document. Returns the cached parse for the
16
+ * same object identity.
17
+ *
18
+ * Before parsing, the document is run through the version-aware
19
+ * normalisation pipeline (`normaliseOpenApiSchemas`) so OpenAPI 3.0.x
20
+ * keywords (`nullable`, `discriminator`, `example`) and Swagger 2.0
21
+ * documents are converted to canonical Draft 2020-12 form. The parser
22
+ * and downstream extractors (`getRequestBody`, `getResponses`, etc.) then
23
+ * observe schemas in the same form `<SchemaComponent>` does, keeping the
24
+ * OpenAPI components on the same pipeline as the top-level adapter.
25
+ *
26
+ * The cache is keyed by the caller-supplied document so subsequent calls
27
+ * with the same input bypass both normalisation and parsing.
15
28
  */
16
29
  function getParsed(doc) {
17
30
  const cached = docCache.get(doc);
18
31
  if (cached !== void 0) return cached;
19
- const parsed = parseOpenApiDocument(doc);
32
+ const version = detectOpenApiVersion(doc);
33
+ const normalisedDoc = version !== void 0 ? normaliseOpenApiSchemas(doc, version) : doc;
34
+ const parsed = parseOpenApiDocument(normalisedDoc);
20
35
  docCache.set(doc, parsed);
36
+ if (normalisedDoc !== doc) docCache.set(normalisedDoc, parsed);
21
37
  return parsed;
22
38
  }
23
39
  /**
@@ -1,11 +1,11 @@
1
1
  import { M as WalkedField, T as SchemaMeta, d as FieldOverrides, u as FieldOverride } from "../types-D_5ST7SS.mjs";
2
- import { t as Diagnostic } from "../diagnostics-DzbZmcLI.mjs";
2
+ import { t as Diagnostic } from "../diagnostics-BYk63jsC.mjs";
3
3
  import { t as SchemaError } from "../errors-C5zRC2PU.mjs";
4
- import { l as RenderProps, r as ComponentResolver } from "../renderer-BdSqllx5.mjs";
5
- import { c as ResolveOpenAPIRef, s as PathOfType, t as FromJSONSchema } from "../typeInference-k7FXfTVO.mjs";
4
+ import { l as RenderProps, r as ComponentResolver } from "../renderer-DXo-rXHJ.mjs";
5
+ import { c as PathOfType, l as ResolveOpenAPIRef, n as FromJSONSchema } from "../typeInference-5JiqIZ8t.mjs";
6
6
  import { z } from "zod";
7
- import * as _$react_jsx_runtime0 from "react/jsx-runtime";
8
7
  import { ReactNode } from "react";
8
+ import * as _$react_jsx_runtime0 from "react/jsx-runtime";
9
9
 
10
10
  //#region src/react/SchemaComponent.d.ts
11
11
  /**
@@ -70,6 +70,13 @@ interface SchemaComponentProps<T = unknown, Ref extends string | undefined = und
70
70
  description?: string;
71
71
  /** Instance-scoped widgets — override context and global widgets. */
72
72
  widgets?: WidgetMap;
73
+ /**
74
+ * Prefix used for every input `id`/label `htmlFor` in this component
75
+ * subtree. Defaults to a per-instance value from `useId()` so multiple
76
+ * `<SchemaComponent>` instances on the same page never collide. Override
77
+ * for deterministic ids in screenshot tests.
78
+ */
79
+ idPrefix?: string;
73
80
  }
74
81
  declare function SchemaComponent<T = unknown, Ref extends string | undefined = undefined>({
75
82
  schema: schemaInput,
@@ -86,9 +93,30 @@ declare function SchemaComponent<T = unknown, Ref extends string | undefined = u
86
93
  readOnly,
87
94
  writeOnly,
88
95
  description,
89
- widgets: instanceWidgets
96
+ widgets: instanceWidgets,
97
+ idPrefix
90
98
  }: SchemaComponentProps<T, Ref>): ReactNode;
91
- declare function renderField(tree: WalkedField, value: unknown, onChange: (v: unknown) => void, userResolver: ComponentResolver | undefined, renderChild: (tree: WalkedField, value: unknown, onChange: (v: unknown) => void) => ReactNode, instanceWidgets?: WidgetMap, contextWidgets?: WidgetMap, depth?: number): ReactNode;
99
+ /**
100
+ * Default root-path sentinel used when no `idPrefix` is supplied AND the
101
+ * component is rendered outside a React tree (e.g. server-side bundling
102
+ * test harnesses). Production callers receive a `useId()`-derived prefix
103
+ * that is unique per instance.
104
+ */
105
+ declare const ROOT_PATH = "root";
106
+ /**
107
+ * Append a child path suffix to a parent path. When the suffix is omitted
108
+ * (e.g. transparent wrappers like union options), the parent path is
109
+ * returned unchanged so the child inherits the parent's id.
110
+ */
111
+ declare function joinPath(parent: string, suffix: string | undefined): string;
112
+ /**
113
+ * Normalise a `useId()` value into a DOM-id-safe prefix. React's `useId`
114
+ * returns values containing `:` characters (e.g. `«:r0:»`) which are
115
+ * invalid in CSS selectors. Replace any run of non-alphanumeric characters
116
+ * with a single hyphen and trim leading/trailing hyphens.
117
+ */
118
+ declare function sanitisePrefix(value: string): string;
119
+ declare function renderField(tree: WalkedField, value: unknown, onChange: (v: unknown) => void, userResolver: ComponentResolver | undefined, renderChild: (tree: WalkedField, value: unknown, onChange: (v: unknown) => void, pathSuffix?: string) => ReactNode, path: string, instanceWidgets?: WidgetMap, contextWidgets?: WidgetMap, depth?: number): ReactNode;
92
120
  /**
93
121
  * Infer the schema's output type for SchemaField path inference.
94
122
  */
@@ -125,4 +153,4 @@ declare function SchemaField<T = unknown, Ref extends string | undefined = undef
125
153
  onValidationError
126
154
  }: SchemaFieldProps<T, Ref, P>): ReactNode;
127
155
  //#endregion
128
- export { SchemaComponent, SchemaComponentProps, SchemaField, SchemaFieldProps, SchemaProvider, WidgetMap, registerWidget, renderField };
156
+ export { ROOT_PATH, SchemaComponent, SchemaComponentProps, SchemaField, SchemaFieldProps, SchemaProvider, WidgetMap, joinPath, registerWidget, renderField, sanitisePrefix };