schema-components 1.18.0 → 1.18.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,10 @@
1
+ import { M as WalkedField } from "../types-D_5ST7SS.mjs";
2
+
3
+ //#region src/core/fieldOrder.d.ts
4
+ /**
5
+ * Sort `Object.entries(fields)` by `meta.order`. Lower values come
6
+ * first; fields without `meta.order` fall back to `Infinity` (last).
7
+ */
8
+ declare function sortFieldsByOrder(fields: Record<string, WalkedField>): [string, WalkedField][];
9
+ //#endregion
10
+ export { sortFieldsByOrder };
@@ -0,0 +1,12 @@
1
+ //#region src/core/fieldOrder.ts
2
+ /**
3
+ * Sort `Object.entries(fields)` by `meta.order`. Lower values come
4
+ * first; fields without `meta.order` fall back to `Infinity` (last).
5
+ */
6
+ function sortFieldsByOrder(fields) {
7
+ return Object.entries(fields).sort((a, b) => {
8
+ return (typeof a[1].meta.order === "number" ? a[1].meta.order : Infinity) - (typeof b[1].meta.order === "number" ? b[1].meta.order : Infinity);
9
+ });
10
+ }
11
+ //#endregion
12
+ export { sortFieldsByOrder };
@@ -23,27 +23,55 @@ function getObject(obj, key) {
23
23
  * Structural equality for arbitrary JSON-like values. Used to decide
24
24
  * whether a duplicated keyword across `allOf` branches genuinely
25
25
  * conflicts (different values) or is benign (identical values).
26
+ *
27
+ * Cycle-safe: bundling external $refs via `structuredClone` preserves
28
+ * object cycles, so constraint values reaching this comparator can be
29
+ * cyclic. The co-recursive convention applies — when a pair of objects
30
+ * (or arrays) is re-encountered during the same comparison we assume
31
+ * equality holds and return true, letting any genuine inequality
32
+ * elsewhere in the structure surface naturally without recursing
33
+ * forever.
26
34
  */
27
35
  function deepEqual(a, b) {
36
+ return deepEqualInner(a, b, /* @__PURE__ */ new WeakMap());
37
+ }
38
+ function deepEqualInner(a, b, seen) {
28
39
  if (a === b) return true;
29
40
  if (typeof a !== typeof b) return false;
30
41
  if (Array.isArray(a)) {
31
42
  if (!Array.isArray(b) || a.length !== b.length) return false;
32
- for (let i = 0; i < a.length; i++) if (!deepEqual(a[i], b[i])) return false;
43
+ if (hasSeenPair(seen, a, b)) return true;
44
+ recordPair(seen, a, b);
45
+ for (let i = 0; i < a.length; i++) if (!deepEqualInner(a[i], b[i], seen)) return false;
33
46
  return true;
34
47
  }
35
48
  if (isObject(a) && isObject(b)) {
36
49
  const keysA = Object.keys(a);
37
50
  const keysB = Object.keys(b);
38
51
  if (keysA.length !== keysB.length) return false;
52
+ if (hasSeenPair(seen, a, b)) return true;
53
+ recordPair(seen, a, b);
39
54
  for (const key of keysA) {
40
55
  if (!Object.prototype.hasOwnProperty.call(b, key)) return false;
41
- if (!deepEqual(a[key], b[key])) return false;
56
+ if (!deepEqualInner(a[key], b[key], seen)) return false;
42
57
  }
43
58
  return true;
44
59
  }
45
60
  return false;
46
61
  }
62
+ function hasSeenPair(seen, a, b) {
63
+ return seen.get(a)?.has(b) === true;
64
+ }
65
+ function recordPair(seen, a, b) {
66
+ const existing = seen.get(a);
67
+ if (existing === void 0) {
68
+ const partners = /* @__PURE__ */ new WeakSet();
69
+ partners.add(b);
70
+ seen.set(a, partners);
71
+ return;
72
+ }
73
+ existing.add(b);
74
+ }
47
75
  /**
48
76
  * Annotation keywords that can appear as siblings of `$ref` per
49
77
  * Draft 2020-12 / OpenAPI 3.1. Structural keywords (type, properties,
@@ -1,2 +1,2 @@
1
- import { a as HtmlRenderProps, c as RenderFunction, d as getHtmlRenderFn, f as getRenderFunction, h as typeToKey, i as HtmlRenderFunction, l as RenderProps, m as mergeResolvers, n as BaseFieldProps, o as HtmlResolver, p as mergeHtmlResolvers, r as ComponentResolver, s as RESOLVER_KEYS, t as AllConstraints, u as buildRenderProps } from "../renderer-DXo-rXHJ.mjs";
1
+ import { a as HtmlRenderProps, c as RenderFunction, d as getHtmlRenderFn, f as getRenderFunction, h as typeToKey, i as HtmlRenderFunction, l as RenderProps, m as mergeResolvers, n as BaseFieldProps, o as HtmlResolver, p as mergeHtmlResolvers, r as ComponentResolver, s as RESOLVER_KEYS, t as AllConstraints, u as buildRenderProps } from "../renderer-BAGoX4AK.mjs";
2
2
  export { AllConstraints, BaseFieldProps, ComponentResolver, HtmlRenderFunction, HtmlRenderProps, HtmlResolver, RESOLVER_KEYS, RenderFunction, RenderProps, buildRenderProps, getHtmlRenderFn, getRenderFunction, mergeHtmlResolvers, mergeResolvers, typeToKey };
@@ -11,11 +11,6 @@ function noopOnChange() {}
11
11
  * a noop `onChange` is wired up, `readOnly` is forced to `true`, and
12
12
  * `writeOnly` is forced to `false`. Otherwise the editability is taken
13
13
  * from `tree.editability`.
14
- *
15
- * The duplicate sibling fields (`enumValues`, `element`, `fields`, etc.)
16
- * are populated for backwards compatibility with renderers that have not
17
- * yet migrated to reading from `tree` directly. New renderers should
18
- * narrow on `tree.type` and read from `tree`.
19
14
  */
20
15
  function buildRenderProps(tree, value, onChange, renderChild, path) {
21
16
  const isReadOnly = onChange === void 0 || tree.editability === "presentation";
@@ -31,20 +26,6 @@ function buildRenderProps(tree, value, onChange, renderChild, path) {
31
26
  tree,
32
27
  renderChild
33
28
  };
34
- if (tree.type === "enum") props.enumValues = tree.enumValues;
35
- if (tree.type === "array" && tree.element !== void 0) props.element = tree.element;
36
- if (tree.type === "object") props.fields = tree.fields;
37
- if (tree.type === "union" || tree.type === "discriminatedUnion") props.options = tree.options;
38
- if (tree.type === "discriminatedUnion") props.discriminator = tree.discriminator;
39
- if (tree.type === "record") props.keyType = tree.keyType;
40
- if (tree.type === "record") props.valueType = tree.valueType;
41
- if (tree.type === "tuple") props.prefixItems = tree.prefixItems;
42
- if (tree.type === "conditional") props.ifClause = tree.ifClause;
43
- if (tree.type === "conditional" && tree.thenClause !== void 0) props.thenClause = tree.thenClause;
44
- if (tree.type === "conditional" && tree.elseClause !== void 0) props.elseClause = tree.elseClause;
45
- if (tree.type === "negation") props.negated = tree.negated;
46
- if (tree.type === "recursive") props.refTarget = tree.refTarget;
47
- if (tree.type === "literal") props.literalValues = tree.literalValues;
48
29
  if (tree.examples !== void 0) props.examples = tree.examples;
49
30
  return props;
50
31
  }
@@ -1,5 +1,5 @@
1
1
  import { M as WalkedField } from "../types-D_5ST7SS.mjs";
2
- import { t as AllConstraints } from "../renderer-DXo-rXHJ.mjs";
2
+ import { t as AllConstraints } from "../renderer-BAGoX4AK.mjs";
3
3
  import { HtmlAttributes, HtmlNode } from "./html.mjs";
4
4
 
5
5
  //#region src/html/a11y.d.ts
@@ -1,5 +1,5 @@
1
1
  import { T as SchemaMeta } from "../types-D_5ST7SS.mjs";
2
- import { o as HtmlResolver } from "../renderer-DXo-rXHJ.mjs";
2
+ import { o as HtmlResolver } from "../renderer-BAGoX4AK.mjs";
3
3
 
4
4
  //#region src/html/renderToHtml.d.ts
5
5
  interface RenderToHtmlOptions {
@@ -1,7 +1,6 @@
1
1
  import { normaliseSchema } from "../core/adapter.mjs";
2
2
  import { getHtmlRenderFn, mergeHtmlResolvers } from "../core/renderer.mjs";
3
3
  import { walk } from "../core/walker.mjs";
4
- import { h, serialize } from "./html.mjs";
5
4
  import { joinPath } from "./a11y.mjs";
6
5
  import { defaultHtmlResolver } from "./renderers.mjs";
7
6
  //#region src/html/renderToHtml.ts
@@ -59,35 +58,19 @@ function renderFieldHtml(tree, value, resolver, path, renderChild) {
59
58
  const effectiveValue = value ?? tree.defaultValue;
60
59
  const mergedResolver = mergeHtmlResolvers(resolver, defaultHtmlResolver);
61
60
  const renderFn = getHtmlRenderFn(tree.type, mergedResolver);
62
- if (renderFn !== void 0) {
63
- const props = {
64
- value: effectiveValue,
65
- readOnly: tree.editability === "presentation",
66
- writeOnly: tree.editability === "input",
67
- meta: tree.meta,
68
- constraints: tree.constraints,
69
- path,
70
- tree,
71
- renderChild
72
- };
73
- if (tree.type === "enum") props.enumValues = tree.enumValues;
74
- if (tree.type === "array" && tree.element !== void 0) props.element = tree.element;
75
- if (tree.type === "object") props.fields = tree.fields;
76
- if (tree.type === "union" || tree.type === "discriminatedUnion") props.options = tree.options;
77
- if (tree.type === "discriminatedUnion") props.discriminator = tree.discriminator;
78
- if (tree.type === "record") props.keyType = tree.keyType;
79
- if (tree.type === "record") props.valueType = tree.valueType;
80
- if (tree.type === "tuple") props.prefixItems = tree.prefixItems;
81
- if (tree.type === "conditional") props.ifClause = tree.ifClause;
82
- if (tree.type === "conditional" && tree.thenClause !== void 0) props.thenClause = tree.thenClause;
83
- if (tree.type === "conditional" && tree.elseClause !== void 0) props.elseClause = tree.elseClause;
84
- if (tree.type === "negation") props.negated = tree.negated;
85
- if (tree.type === "literal") props.literalValues = tree.literalValues;
86
- if (tree.examples !== void 0) props.examples = tree.examples;
87
- return renderFn(props);
88
- }
89
- if (effectiveValue === void 0 || effectiveValue === null) return serialize(h("span", { class: "sc-value sc-value--empty" }, "—"));
90
- return serialize(h("span", { class: "sc-value" }, typeof effectiveValue === "string" ? effectiveValue : JSON.stringify(effectiveValue)));
61
+ if (renderFn === void 0) throw new Error(`renderToHtml: no HTML renderer registered for type "${tree.type}"`);
62
+ const props = {
63
+ value: effectiveValue,
64
+ readOnly: tree.editability === "presentation",
65
+ writeOnly: tree.editability === "input",
66
+ meta: tree.meta,
67
+ constraints: tree.constraints,
68
+ path,
69
+ tree,
70
+ renderChild
71
+ };
72
+ if (tree.examples !== void 0) props.examples = tree.examples;
73
+ return renderFn(props);
91
74
  }
92
75
  //#endregion
93
76
  export { renderToHtml };
@@ -1,5 +1,5 @@
1
1
  import { T as SchemaMeta } from "../types-D_5ST7SS.mjs";
2
- import { o as HtmlResolver } from "../renderer-DXo-rXHJ.mjs";
2
+ import { o as HtmlResolver } from "../renderer-BAGoX4AK.mjs";
3
3
 
4
4
  //#region src/html/renderToHtmlStream.d.ts
5
5
  interface StreamRenderOptions {
@@ -1,5 +1,5 @@
1
1
  import { M as WalkedField } from "../types-D_5ST7SS.mjs";
2
- import { o as HtmlResolver } from "../renderer-DXo-rXHJ.mjs";
2
+ import { o as HtmlResolver } from "../renderer-BAGoX4AK.mjs";
3
3
 
4
4
  //#region src/html/renderers.d.ts
5
5
  declare function dateInputType(format: string | undefined): string | undefined;
@@ -1,3 +1,4 @@
1
+ import { sortFieldsByOrder } from "../core/fieldOrder.mjs";
1
2
  import { h, raw, serialize } from "./html.mjs";
2
3
  import { ariaDescribedByAttrs, ariaLabelAttrs, ariaReadonlyAttrs, ariaRequiredAttrs, buildHintElement, buildInputId, requiredIndicator } from "./a11y.mjs";
3
4
  //#region src/html/renderers.ts
@@ -135,7 +136,8 @@ function renderEnumEditable(props) {
135
136
  const enumValue = typeof props.value === "string" ? props.value : "";
136
137
  const id = fieldId(props.path);
137
138
  const selectedValue = props.writeOnly ? "" : enumValue;
138
- const optionNodes = [h("option", { value: "" }, "Select…"), ...(props.enumValues ?? []).map((v) => {
139
+ const enumValues = props.tree.type === "enum" ? props.tree.enumValues : [];
140
+ const optionNodes = [h("option", { value: "" }, "Select…"), ...enumValues.map((v) => {
139
141
  const display = v === null ? "null" : typeof v === "string" ? v : String(v);
140
142
  const attrs = { value: display };
141
143
  if (display === selectedValue) attrs.selected = true;
@@ -153,15 +155,13 @@ function renderObjectHtml(props) {
153
155
  return serialize(renderObjectNode(props));
154
156
  }
155
157
  function renderObjectNode(props) {
156
- const fields = props.fields;
157
- if (fields === void 0) return "";
158
+ if (props.tree.type !== "object") return "";
159
+ const fields = props.tree.fields;
158
160
  const isRecord = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
159
161
  const obj = isRecord(props.value) ? props.value : {};
160
162
  const descriptionText = typeof props.meta.description === "string" ? props.meta.description : void 0;
161
163
  const legend = descriptionText !== void 0 ? h("legend", {}, descriptionText) : void 0;
162
- const sortedEntries = Object.entries(fields).sort((a, b) => {
163
- return (typeof a[1].meta.order === "number" ? a[1].meta.order : Infinity) - (typeof b[1].meta.order === "number" ? b[1].meta.order : Infinity);
164
- }).filter(([, field]) => field.meta.visible !== false);
164
+ const sortedEntries = sortFieldsByOrder(fields).filter(([, field]) => field.meta.visible !== false);
165
165
  if (props.readOnly) {
166
166
  const children = [];
167
167
  if (legend !== void 0) children.push(legend);
@@ -203,7 +203,7 @@ function renderArrayHtml(props) {
203
203
  }
204
204
  function renderArrayNode(props) {
205
205
  const arr = Array.isArray(props.value) ? props.value : [];
206
- const element = props.element;
206
+ const element = props.tree.type === "array" ? props.tree.element : void 0;
207
207
  if (element === void 0) return "";
208
208
  const childHtmls = arr.map((item, i) => props.renderChild(element, item, `[${String(i)}]`));
209
209
  if (props.readOnly) return h("ul", { class: "sc-array" }, ...childHtmls.map((childHtml) => h("li", { class: "sc-item" }, raw(childHtml))));
@@ -213,10 +213,10 @@ function renderRecordHtml(props) {
213
213
  return serialize(renderRecordNode(props));
214
214
  }
215
215
  function renderRecordNode(props) {
216
+ if (props.tree.type !== "record") return "";
216
217
  const isRecord = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
217
218
  const obj = isRecord(props.value) ? props.value : {};
218
- const valueType = props.valueType;
219
- if (valueType === void 0) return "";
219
+ const valueType = props.tree.valueType;
220
220
  const attrs = {
221
221
  class: "sc-record",
222
222
  role: "group"
@@ -238,12 +238,13 @@ function renderRecordNode(props) {
238
238
  return h("div", attrs, ...children);
239
239
  }
240
240
  function renderLiteralHtml(props) {
241
- const values = props.literalValues;
242
- if (values === void 0 || values.length === 0) return serialize(h("span", { class: "sc-value sc-value--empty" }, "—"));
241
+ if (props.tree.type !== "literal") return serialize(h("span", { class: "sc-value sc-value--empty" }, "—"));
242
+ const values = props.tree.literalValues;
243
+ if (values.length === 0) return serialize(h("span", { class: "sc-value sc-value--empty" }, "—"));
243
244
  return serialize(h("span", { class: "sc-value" }, values.map((v) => v === null ? "null" : String(v)).join(", ")));
244
245
  }
245
246
  function renderUnionHtml(props) {
246
- const options = props.options;
247
+ const options = props.tree.type === "union" || props.tree.type === "discriminatedUnion" ? props.tree.options : void 0;
247
248
  if (options === void 0 || options.length === 0) {
248
249
  if (props.value === void 0 || props.value === null) return serialize(h("span", { class: "sc-value sc-value--empty" }, "—"));
249
250
  return serialize(h("span", { class: "sc-value" }, JSON.stringify(props.value)));
@@ -255,8 +256,8 @@ function renderUnionHtml(props) {
255
256
  return serialize(h("span", { class: "sc-value sc-value--empty" }, "—"));
256
257
  }
257
258
  function renderDiscriminatedUnionHtml(props) {
258
- const options = props.options;
259
- const discriminator = props.discriminator;
259
+ const options = props.tree.type === "discriminatedUnion" ? props.tree.options : void 0;
260
+ const discriminator = props.tree.type === "discriminatedUnion" ? props.tree.discriminator : void 0;
260
261
  if (options === void 0 || options.length === 0) {
261
262
  if (props.value === void 0 || props.value === null) return serialize(h("span", { class: "sc-value sc-value--empty" }, "—"));
262
263
  return serialize(h("span", { class: "sc-value" }, JSON.stringify(props.value)));
@@ -285,7 +286,9 @@ function renderDiscriminatedUnionHtml(props) {
285
286
  if (activeOption !== void 0) return props.renderChild(activeOption, props.value);
286
287
  return serialize(h("span", { class: "sc-value sc-value--empty" }, "—"));
287
288
  }
288
- const panelId = `sc-${props.path}-panel`;
289
+ const baseId = fieldId(props.path);
290
+ const panelId = `${baseId}-panel`;
291
+ const tabId = (i) => `${baseId}-tab-${String(i)}`;
289
292
  const children = [h("div", {
290
293
  role: "tablist",
291
294
  class: "sc-tabs",
@@ -295,7 +298,7 @@ function renderDiscriminatedUnionHtml(props) {
295
298
  type: "button",
296
299
  role: "tab",
297
300
  class: i === activeIndex ? "sc-tab sc-tab--active" : "sc-tab",
298
- id: `sc-${props.path}-tab-${String(i)}`,
301
+ id: tabId(i),
299
302
  "aria-selected": i === activeIndex ? "true" : void 0,
300
303
  "aria-controls": panelId,
301
304
  tabindex: i === activeIndex ? "0" : "-1"
@@ -306,7 +309,7 @@ function renderDiscriminatedUnionHtml(props) {
306
309
  children.push(h("div", {
307
310
  role: "tabpanel",
308
311
  id: panelId,
309
- "aria-labelledby": `sc-${props.path}-tab-${String(activeIndex)}`
312
+ "aria-labelledby": tabId(activeIndex)
310
313
  }, raw(childHtml)));
311
314
  }
312
315
  return serialize(h("div", { class: "sc-discriminated-union" }, ...children));
@@ -331,7 +334,7 @@ function renderFileHtml(props) {
331
334
  return serialize(h("input", attrs));
332
335
  }
333
336
  function renderRecursiveHtml(props) {
334
- const refTarget = props.refTarget ?? "";
337
+ const refTarget = props.tree.type === "recursive" ? props.tree.refTarget : "";
335
338
  return serialize(h("fieldset", { class: "sc-recursive" }, `↻ ${typeof props.meta.description === "string" ? props.meta.description : refTarget} (recursive)`));
336
339
  }
337
340
  function renderUnknownHtml(props) {
@@ -350,9 +353,9 @@ function renderUnknownHtml(props) {
350
353
  return serialize(h("input", attrs));
351
354
  }
352
355
  function renderTupleHtml(props) {
356
+ if (props.tree.type !== "tuple") return renderUnknownHtml(props);
353
357
  const arr = Array.isArray(props.value) ? props.value : [];
354
- const prefixItems = props.prefixItems;
355
- if (prefixItems === void 0) return renderUnknownHtml(props);
358
+ const prefixItems = props.tree.prefixItems;
356
359
  const children = [];
357
360
  for (let i = 0; i < prefixItems.length; i++) {
358
361
  const itemValue = arr[i];
@@ -365,9 +368,11 @@ function renderTupleHtml(props) {
365
368
  }
366
369
  function renderConditionalHtml(props) {
367
370
  const children = [];
368
- if (props.ifClause !== void 0) children.push(h("div", { class: "sc-conditional-if" }, raw("if: ...")));
369
- if (props.thenClause !== void 0) children.push(h("div", { class: "sc-conditional-then" }, raw("then: ...")));
370
- if (props.elseClause !== void 0) children.push(h("div", { class: "sc-conditional-else" }, raw("else: ...")));
371
+ if (props.tree.type === "conditional") {
372
+ children.push(h("div", { class: "sc-conditional-if" }, raw("if: ...")));
373
+ if (props.tree.thenClause !== void 0) children.push(h("div", { class: "sc-conditional-then" }, raw("then: ...")));
374
+ if (props.tree.elseClause !== void 0) children.push(h("div", { class: "sc-conditional-else" }, raw("else: ...")));
375
+ }
371
376
  return serialize(h("div", { class: "sc-conditional" }, ...children));
372
377
  }
373
378
  function renderNegationHtml(props) {
@@ -1,5 +1,5 @@
1
1
  import { M as WalkedField } from "../types-D_5ST7SS.mjs";
2
- import { o as HtmlResolver } from "../renderer-DXo-rXHJ.mjs";
2
+ import { o as HtmlResolver } from "../renderer-BAGoX4AK.mjs";
3
3
  import { HtmlElement } from "./html.mjs";
4
4
 
5
5
  //#region src/html/streamRenderers.d.ts
@@ -14,27 +14,16 @@ function yieldClose(el) {
14
14
  }
15
15
  function renderLeaf(tree, value, mergedResolver, path) {
16
16
  const renderFn = getHtmlRenderFn(tree.type, mergedResolver);
17
- if (renderFn !== void 0) {
18
- const props = {
19
- value,
20
- readOnly: tree.editability === "presentation",
21
- writeOnly: tree.editability === "input",
22
- meta: tree.meta,
23
- constraints: tree.constraints,
24
- path,
25
- tree,
26
- renderChild: () => ""
27
- };
28
- if (tree.type === "enum") props.enumValues = tree.enumValues;
29
- if (tree.type === "literal") props.literalValues = tree.literalValues;
30
- if (tree.type === "array" && tree.element !== void 0) props.element = tree.element;
31
- if (tree.type === "object") props.fields = tree.fields;
32
- if (tree.type === "union") props.options = tree.options;
33
- if (tree.type === "discriminatedUnion") props.discriminator = tree.discriminator;
34
- if (tree.type === "record") props.keyType = tree.keyType;
35
- if (tree.type === "record") props.valueType = tree.valueType;
36
- return renderFn(props);
37
- }
17
+ if (renderFn !== void 0) return renderFn({
18
+ value,
19
+ readOnly: tree.editability === "presentation",
20
+ writeOnly: tree.editability === "input",
21
+ meta: tree.meta,
22
+ constraints: tree.constraints,
23
+ path,
24
+ tree,
25
+ renderChild: () => ""
26
+ });
38
27
  if (value === void 0 || value === null) return serialize(h("span", { class: "sc-value sc-value--empty" }, "—"));
39
28
  return serialize(h("span", { class: "sc-value" }, typeof value === "string" ? value : JSON.stringify(value)));
40
29
  }
@@ -13,7 +13,7 @@ function ApiSecurity({ requirements, schemes }) {
13
13
  "data-security-name": true,
14
14
  children: req.name
15
15
  }),
16
- scheme !== void 0 && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("span", {
16
+ scheme !== void 0 && /* @__PURE__ */ jsxs(Fragment, { children: [scheme.type !== void 0 && /* @__PURE__ */ jsx("span", {
17
17
  "data-security-type": true,
18
18
  children: scheme.type
19
19
  }), scheme.description && /* @__PURE__ */ jsx("span", {
@@ -88,6 +88,7 @@ async function walkAndInline(node, schemasNode, uriCache, inlineCache, resolver)
88
88
  }
89
89
  if (inlinedName !== void 0) node.$ref = `#/components/schemas/${inlinedName}`;
90
90
  }
91
+ return;
91
92
  }
92
93
  for (const value of Object.values(node)) if (isObject(value)) await walkAndInline(value, schemasNode, uriCache, inlineCache, resolver);
93
94
  else if (Array.isArray(value)) for (const item of value) await walkAndInline(item, schemasNode, uriCache, inlineCache, resolver);
@@ -1,14 +1,14 @@
1
1
  import { isObject, toRecordOrUndefined } from "../core/guards.mjs";
2
2
  import { walk } from "../core/walker.mjs";
3
- import { joinPath, renderField, sanitisePrefix } from "../react/SchemaComponent.mjs";
4
3
  import { ApiCallbacks } from "./ApiCallbacks.mjs";
5
4
  import { ApiLinks } from "./ApiLinks.mjs";
6
5
  import { ApiResponseHeaders } from "./ApiResponseHeaders.mjs";
7
6
  import { ApiSecurity } from "./ApiSecurity.mjs";
8
7
  import { getLinks, getSecurityRequirements, getSecuritySchemes, listCallbacks } from "./parser.mjs";
8
+ import { joinPath, renderField, sanitisePrefix } from "../react/SchemaComponent.mjs";
9
9
  import { getParsed, resolveOperation, resolveParameters, resolveRequestBody, resolveResponse, toDoc } from "./resolve.mjs";
10
- import { useId } from "react";
11
10
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
11
+ import { useId } from "react";
12
12
  //#region src/openapi/components.tsx
13
13
  /**
14
14
  * OpenAPI React components with type-safe generics.
@@ -41,7 +41,7 @@ interface SecurityRequirement {
41
41
  scopes: string[];
42
42
  }
43
43
  interface SecurityScheme {
44
- type: string;
44
+ type: string | undefined;
45
45
  description: string | undefined;
46
46
  name: string | undefined;
47
47
  location: string | undefined;
@@ -92,7 +92,7 @@ declare function getRequestBody(parsed: OpenApiDocument, path: string, method: s
92
92
  declare function getResponses(parsed: OpenApiDocument, path: string, method: string): ResponseInfo[];
93
93
  declare function getSecurityRequirements(parsed: OpenApiDocument, path: string, method: string): SecurityRequirement[];
94
94
  declare function getSecuritySchemes(parsed: OpenApiDocument): Map<string, SecurityScheme>;
95
- declare function getResponseHeaders(response: JsonObject): Map<string, HeaderInfo>;
95
+ declare function getResponseHeaders(response: JsonObject, doc?: JsonObject): Map<string, HeaderInfo>;
96
96
  declare function listWebhooks(parsed: OpenApiDocument): WebhookInfo[];
97
97
  declare function getExternalDocs(obj: JsonObject): ExternalDocs | undefined;
98
98
  declare function getXmlInfo(schema: JsonObject): XmlInfo | undefined;
@@ -143,7 +143,7 @@ function getResponses(parsed, path, method) {
143
143
  const content = getProperty(response, "content");
144
144
  const contentTypes = isObject(content) ? Object.keys(content) : [];
145
145
  const schema = isObject(content) ? extractSchemaFromContent(content) : void 0;
146
- const headers = getResponseHeaders(response);
146
+ const headers = getResponseHeaders(response, parsed.doc);
147
147
  result.push({
148
148
  statusCode,
149
149
  description: getString(response, "description"),
@@ -196,7 +196,7 @@ function getSecuritySchemes(parsed) {
196
196
  if (!isObject(scheme)) continue;
197
197
  const flowsProp = getProperty(scheme, "flows");
198
198
  result.set(name, {
199
- type: getString(scheme, "type") ?? "",
199
+ type: getString(scheme, "type"),
200
200
  description: getString(scheme, "description"),
201
201
  name: getString(scheme, "name"),
202
202
  location: getString(scheme, "in"),
@@ -208,14 +208,14 @@ function getSecuritySchemes(parsed) {
208
208
  }
209
209
  return result;
210
210
  }
211
- function getResponseHeaders(response) {
211
+ function getResponseHeaders(response, doc) {
212
212
  const result = /* @__PURE__ */ new Map();
213
213
  const headers = getProperty(response, "headers");
214
214
  if (!isObject(headers)) return result;
215
215
  for (const [name, headerObj] of Object.entries(headers)) {
216
216
  if (!isObject(headerObj)) continue;
217
217
  const ref = getString(headerObj, "$ref");
218
- const header = (ref !== void 0 ? resolveRefInDoc(headerObj, ref) : void 0) ?? headerObj;
218
+ const header = (ref !== void 0 && doc !== void 0 ? resolveRefInDoc(doc, ref) : void 0) ?? headerObj;
219
219
  const schemaProp = getProperty(header, "schema");
220
220
  result.set(name, {
221
221
  name,
@@ -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
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-DXo-rXHJ.mjs";
4
+ import { l as RenderProps, r as ComponentResolver } from "../renderer-BAGoX4AK.mjs";
5
5
  import { c as PathOfType, l as ResolveOpenAPIRef, n as FromJSONSchema } from "../typeInference-5JiqIZ8t.mjs";
6
6
  import { z } from "zod";
7
- import { ReactNode } from "react";
8
7
  import * as _$react_jsx_runtime0 from "react/jsx-runtime";
8
+ import { ReactNode } from "react";
9
9
 
10
10
  //#region src/react/SchemaComponent.d.ts
11
11
  /**
@@ -7,8 +7,8 @@ import { walk } from "../core/walker.mjs";
7
7
  import { headlessResolver } from "./headless.mjs";
8
8
  import { resolvePath, resolveValue, setNestedValue } from "./fieldPath.mjs";
9
9
  import { z } from "zod";
10
- import { createContext, isValidElement, useCallback, useContext, useId, useMemo } from "react";
11
10
  import { jsx, jsxs } from "react/jsx-runtime";
11
+ import { createContext, isValidElement, useCallback, useContext, useId, useMemo } from "react";
12
12
  //#region src/react/SchemaComponent.tsx
13
13
  /**
14
14
  * <SchemaComponent> — renders UI from Zod, JSON Schema, or OpenAPI schemas.
@@ -1,6 +1,6 @@
1
1
  import { T as SchemaMeta } from "../types-D_5ST7SS.mjs";
2
2
  import { t as Diagnostic } from "../diagnostics-BYk63jsC.mjs";
3
- import { r as ComponentResolver } from "../renderer-DXo-rXHJ.mjs";
3
+ import { r as ComponentResolver } from "../renderer-BAGoX4AK.mjs";
4
4
  import { WidgetMap } from "./SchemaComponent.mjs";
5
5
  import { ReactNode } from "react";
6
6
 
@@ -4,8 +4,8 @@ import { buildRenderProps, getRenderFunction, mergeResolvers } from "../core/ren
4
4
  import { walk } from "../core/walker.mjs";
5
5
  import { headlessResolver } from "./headless.mjs";
6
6
  import { joinPath, sanitisePrefix } from "./SchemaComponent.mjs";
7
- import { createElement, isValidElement, useId } from "react";
8
7
  import { jsx } from "react/jsx-runtime";
8
+ import { createElement, isValidElement, useId } from "react";
9
9
  //#region src/react/SchemaView.tsx
10
10
  /**
11
11
  * React Server Component for read-only schema rendering.
@@ -1,4 +1,4 @@
1
- import { r as ComponentResolver } from "../renderer-DXo-rXHJ.mjs";
1
+ import { r as ComponentResolver } from "../renderer-BAGoX4AK.mjs";
2
2
 
3
3
  //#region src/react/headless.d.ts
4
4
  /**
@@ -1,5 +1,5 @@
1
1
  import { M as WalkedField } from "../types-D_5ST7SS.mjs";
2
- import { l as RenderProps } from "../renderer-DXo-rXHJ.mjs";
2
+ import { l as RenderProps } from "../renderer-BAGoX4AK.mjs";
3
3
  import { ReactNode } from "react";
4
4
 
5
5
  //#region src/react/headlessRenderers.d.ts
@@ -1,6 +1,7 @@
1
1
  import { isObject } from "../core/guards.mjs";
2
- import { isValidElement, useCallback, useEffect, useRef } from "react";
2
+ import { sortFieldsByOrder } from "../core/fieldOrder.mjs";
3
3
  import { jsx, jsxs } from "react/jsx-runtime";
4
+ import { isValidElement, useCallback, useEffect, useRef } from "react";
4
5
  //#region src/react/headlessRenderers.tsx
5
6
  /**
6
7
  * Headless renderer functions — one per schema type.
@@ -128,24 +129,27 @@ function renderString(props) {
128
129
  },
129
130
  ...ariaAttrs
130
131
  });
131
- if (props.enumValues !== void 0 && props.enumValues.length > 0) return /* @__PURE__ */ jsxs("select", {
132
- id,
133
- value: strValue,
134
- onChange: (e) => {
135
- props.onChange(e.target.value);
136
- },
137
- ...ariaAttrs,
138
- children: [/* @__PURE__ */ jsxs("option", {
139
- value: "",
140
- children: ["Select", "…"]
141
- }), props.enumValues.map((v) => {
142
- const display = v === null ? "null" : typeof v === "string" ? v : String(v);
143
- return /* @__PURE__ */ jsx("option", {
144
- value: display,
145
- children: display
146
- }, display);
147
- })]
148
- });
132
+ if (props.tree.type === "enum" && props.tree.enumValues.length > 0) {
133
+ const enumValues = props.tree.enumValues;
134
+ return /* @__PURE__ */ jsxs("select", {
135
+ id,
136
+ value: strValue,
137
+ onChange: (e) => {
138
+ props.onChange(e.target.value);
139
+ },
140
+ ...ariaAttrs,
141
+ children: [/* @__PURE__ */ jsxs("option", {
142
+ value: "",
143
+ children: ["Select", ""]
144
+ }), enumValues.map((v) => {
145
+ const display = v === null ? "null" : typeof v === "string" ? v : String(v);
146
+ return /* @__PURE__ */ jsx("option", {
147
+ value: display,
148
+ children: display
149
+ }, display);
150
+ })]
151
+ });
152
+ }
149
153
  return /* @__PURE__ */ jsx("input", {
150
154
  id,
151
155
  type: props.constraints.format === "email" ? "email" : props.constraints.format === "uri" ? "url" : "text",
@@ -225,6 +229,7 @@ function renderEnum(props) {
225
229
  });
226
230
  const ariaAttrs = {};
227
231
  if (props.tree.isOptional === false) ariaAttrs["aria-required"] = "true";
232
+ const enumValues = props.tree.type === "enum" ? props.tree.enumValues : [];
228
233
  return /* @__PURE__ */ jsxs("select", {
229
234
  id,
230
235
  value: props.writeOnly ? "" : enumValue,
@@ -235,7 +240,7 @@ function renderEnum(props) {
235
240
  children: [/* @__PURE__ */ jsxs("option", {
236
241
  value: "",
237
242
  children: ["Select", "…"]
238
- }), props.enumValues?.map((v) => {
243
+ }), enumValues.map((v) => {
239
244
  const display = v === null ? "null" : typeof v === "string" ? v : String(v);
240
245
  return /* @__PURE__ */ jsx("option", {
241
246
  value: display,
@@ -245,12 +250,10 @@ function renderEnum(props) {
245
250
  });
246
251
  }
247
252
  function renderObject(props) {
253
+ if (props.tree.type !== "object") return null;
248
254
  const obj = isObject(props.value) ? props.value : {};
249
- const fields = props.fields;
250
- if (fields === void 0) return null;
251
- const sortedEntries = Object.entries(fields).sort((a, b) => {
252
- return (typeof a[1].meta.order === "number" ? a[1].meta.order : Infinity) - (typeof b[1].meta.order === "number" ? b[1].meta.order : Infinity);
253
- });
255
+ const fields = props.tree.fields;
256
+ const sortedEntries = sortFieldsByOrder(fields);
254
257
  return /* @__PURE__ */ jsxs("fieldset", { children: [typeof props.meta.description === "string" && /* @__PURE__ */ jsx("legend", { children: props.meta.description }), sortedEntries.filter(([, field]) => field.meta.visible !== false).map(([key, field]) => {
255
258
  const childValue = obj[key];
256
259
  const childId = inputId(`${props.path}.${key}`);
@@ -312,9 +315,9 @@ function renameRecordKey(obj, oldKey, newKey) {
312
315
  return renamed;
313
316
  }
314
317
  function renderRecord(props) {
318
+ if (props.tree.type !== "record") return null;
315
319
  const obj = isObject(props.value) ? props.value : {};
316
- const valueType = props.valueType;
317
- if (valueType === void 0) return null;
320
+ const valueType = props.tree.valueType;
318
321
  const entries = Object.entries(obj);
319
322
  if (props.readOnly) {
320
323
  if (entries.length === 0) return /* @__PURE__ */ jsx("span", {
@@ -392,8 +395,9 @@ function renderRecord(props) {
392
395
  });
393
396
  }
394
397
  function renderArray(props) {
398
+ if (props.tree.type !== "array") return null;
395
399
  const arr = Array.isArray(props.value) ? props.value : [];
396
- const element = props.element;
400
+ const element = props.tree.element;
397
401
  if (element === void 0) return null;
398
402
  if (arr.length === 0) return null;
399
403
  return /* @__PURE__ */ jsx("div", {
@@ -410,7 +414,7 @@ function renderArray(props) {
410
414
  });
411
415
  }
412
416
  function renderUnion(props) {
413
- const options = props.options;
417
+ const options = props.tree.type === "union" || props.tree.type === "discriminatedUnion" ? props.tree.options : void 0;
414
418
  if (options === void 0 || options.length === 0) {
415
419
  if (props.value === void 0 || props.value === null) return /* @__PURE__ */ jsx("span", { children: "—" });
416
420
  return /* @__PURE__ */ jsx("span", { children: JSON.stringify(props.value) });
@@ -422,8 +426,8 @@ function renderUnion(props) {
422
426
  return /* @__PURE__ */ jsx("span", { children: "—" });
423
427
  }
424
428
  function renderDiscriminatedUnion(props) {
425
- const options = props.options;
426
- const discriminator = props.discriminator;
429
+ const options = props.tree.type === "discriminatedUnion" ? props.tree.options : void 0;
430
+ const discriminator = props.tree.type === "discriminatedUnion" ? props.tree.discriminator : void 0;
427
431
  if (options === void 0 || options.length === 0) {
428
432
  if (props.value === void 0 || props.value === null) return /* @__PURE__ */ jsx("span", { children: "—" });
429
433
  return /* @__PURE__ */ jsx("span", { children: JSON.stringify(props.value) });
@@ -584,7 +588,7 @@ function renderFile(props) {
584
588
  });
585
589
  }
586
590
  function renderRecursive(props) {
587
- const refTarget = props.refTarget ?? "";
591
+ const refTarget = props.tree.type === "recursive" ? props.tree.refTarget : "";
588
592
  return /* @__PURE__ */ jsx("fieldset", { children: /* @__PURE__ */ jsxs("em", { children: [
589
593
  "↻ ",
590
594
  typeof props.meta.description === "string" ? props.meta.description : refTarget,
@@ -13,6 +13,13 @@ type AllConstraints = StringConstraints & NumberConstraints & ArrayConstraints &
13
13
  /**
14
14
  * Properties available on every schema field, regardless of rendering target.
15
15
  * Both React and HTML renderers receive these.
16
+ *
17
+ * Per-type schema data — enum values, object fields, array element schema,
18
+ * union options, record key/value types, tuple `prefixItems`, conditional
19
+ * if/then/else clauses, negation `negated`, recursive `refTarget`, literal
20
+ * values — lives on the discriminated `tree`. Renderers narrow on
21
+ * `tree.type` and read from the matching variant; there are no duplicate
22
+ * sibling fields on these props.
16
23
  */
17
24
  interface BaseFieldProps {
18
25
  /** Current field value. */
@@ -27,31 +34,6 @@ interface BaseFieldProps {
27
34
  constraints: AllConstraints;
28
35
  /** Dot-separated path from root (e.g. "address.city"). */
29
36
  path: string;
30
- /** For enums: the allowed values. */
31
- enumValues?: (string | number | boolean | null)[];
32
- /** For arrays: the element schema. */
33
- element?: WalkedField;
34
- /** For tuples: positional element schemas from prefixItems. */
35
- prefixItems?: WalkedField[];
36
- /** For conditionals: the if/then/else sub-schemas. */
37
- ifClause?: WalkedField;
38
- thenClause?: WalkedField;
39
- elseClause?: WalkedField;
40
- /** For negations: the negated sub-schema. */
41
- negated?: WalkedField;
42
- /** For recursive fields: the $ref string that would create the cycle. */
43
- refTarget?: string;
44
- /** For objects: map of field name → WalkedField. */
45
- fields?: Record<string, WalkedField>;
46
- /** For unions: the option schemas. */
47
- options?: WalkedField[];
48
- /** For discriminated unions: the discriminator key. */
49
- discriminator?: string;
50
- /** For records: key and value schemas. */
51
- keyType?: WalkedField;
52
- valueType?: WalkedField;
53
- /** For literals: the literal value(s). */
54
- literalValues?: (string | number | boolean | null)[];
55
37
  /** Example values from the schema's `examples` keyword. */
56
38
  examples?: unknown[];
57
39
  /** Walked field tree for recursive rendering. */
@@ -109,11 +91,6 @@ interface HtmlRenderProps extends BaseFieldProps {
109
91
  * a noop `onChange` is wired up, `readOnly` is forced to `true`, and
110
92
  * `writeOnly` is forced to `false`. Otherwise the editability is taken
111
93
  * from `tree.editability`.
112
- *
113
- * The duplicate sibling fields (`enumValues`, `element`, `fields`, etc.)
114
- * are populated for backwards compatibility with renderers that have not
115
- * yet migrated to reading from `tree` directly. New renderers should
116
- * narrow on `tree.type` and read from `tree`.
117
94
  */
118
95
  declare function buildRenderProps(tree: WalkedField, value: unknown, onChange: ((next: unknown) => void) | undefined, renderChild: RenderProps["renderChild"], path: string): RenderProps;
119
96
  type RenderFunction = (props: RenderProps) => unknown;
@@ -1,4 +1,4 @@
1
- import { r as ComponentResolver } from "../renderer-DXo-rXHJ.mjs";
1
+ import { r as ComponentResolver } from "../renderer-BAGoX4AK.mjs";
2
2
 
3
3
  //#region src/themes/mantine.d.ts
4
4
  /**
@@ -1,4 +1,5 @@
1
1
  import { isObject } from "../core/guards.mjs";
2
+ import { sortFieldsByOrder } from "../core/fieldOrder.mjs";
2
3
  import { inputId, toReactNode } from "../react/headlessRenderers.mjs";
3
4
  import { headlessResolver } from "../react/headless.mjs";
4
5
  import { jsx } from "react/jsx-runtime";
@@ -123,7 +124,7 @@ function renderObjectContainer(props) {
123
124
  const obj = isObject(props.value) ? props.value : {};
124
125
  return /* @__PURE__ */ jsx(MantineFieldset, {
125
126
  legend: getLabel(props),
126
- children: Object.entries(fields).filter(([, field]) => field.meta.visible !== false).map(([key, field]) => {
127
+ children: sortFieldsByOrder(fields).filter(([, field]) => field.meta.visible !== false).map(([key, field]) => {
127
128
  const childValue = obj[key];
128
129
  const childOnChange = (v) => {
129
130
  const updated = {};
@@ -1,4 +1,4 @@
1
- import { r as ComponentResolver } from "../renderer-DXo-rXHJ.mjs";
1
+ import { r as ComponentResolver } from "../renderer-BAGoX4AK.mjs";
2
2
 
3
3
  //#region src/themes/mui.d.ts
4
4
  /**
@@ -1,8 +1,9 @@
1
1
  import { isObject } from "../core/guards.mjs";
2
+ import { sortFieldsByOrder } from "../core/fieldOrder.mjs";
2
3
  import { inputId, toReactNode } from "../react/headlessRenderers.mjs";
3
4
  import { headlessResolver } from "../react/headless.mjs";
4
- import { isValidElement } from "react";
5
5
  import { jsx, jsxs } from "react/jsx-runtime";
6
+ import { isValidElement } from "react";
6
7
  //#region src/themes/mui.tsx
7
8
  function ariaRequired(tree) {
8
9
  return { required: tree.isOptional === false };
@@ -136,7 +137,7 @@ function renderObjectContainer(props) {
136
137
  children: [typeof props.meta.description === "string" && /* @__PURE__ */ jsx(MuiTypography, {
137
138
  variant: "h6",
138
139
  children: props.meta.description
139
- }), Object.entries(fields).map(([key, field]) => {
140
+ }), sortFieldsByOrder(fields).map(([key, field]) => {
140
141
  const childValue = obj[key];
141
142
  const childOnChange = (v) => {
142
143
  const updated = {};
@@ -1,4 +1,4 @@
1
- import { r as ComponentResolver } from "../renderer-DXo-rXHJ.mjs";
1
+ import { r as ComponentResolver } from "../renderer-BAGoX4AK.mjs";
2
2
 
3
3
  //#region src/themes/radix.d.ts
4
4
  /**
@@ -1,4 +1,5 @@
1
1
  import { isObject } from "../core/guards.mjs";
2
+ import { sortFieldsByOrder } from "../core/fieldOrder.mjs";
2
3
  import { inputId, toReactNode } from "../react/headlessRenderers.mjs";
3
4
  import { headlessResolver } from "../react/headless.mjs";
4
5
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
@@ -161,7 +162,7 @@ function renderObjectContainer(props) {
161
162
  }), /* @__PURE__ */ jsx(RadixFlex, {
162
163
  direction: "column",
163
164
  gap: "3",
164
- children: Object.entries(fields).filter(([, field]) => field.meta.visible !== false).map(([key, field]) => {
165
+ children: sortFieldsByOrder(fields).filter(([, field]) => field.meta.visible !== false).map(([key, field]) => {
165
166
  const childValue = obj[key];
166
167
  const childOnChange = (v) => {
167
168
  const updated = {};
@@ -1,4 +1,4 @@
1
- import { r as ComponentResolver } from "../renderer-DXo-rXHJ.mjs";
1
+ import { r as ComponentResolver } from "../renderer-BAGoX4AK.mjs";
2
2
 
3
3
  //#region src/themes/shadcn.d.ts
4
4
  declare const shadcnResolver: ComponentResolver;
@@ -1,4 +1,5 @@
1
1
  import { toRecord } from "../core/guards.mjs";
2
+ import { sortFieldsByOrder } from "../core/fieldOrder.mjs";
2
3
  import { inputId, toReactNode } from "../react/headlessRenderers.mjs";
3
4
  import { headlessResolver } from "../react/headless.mjs";
4
5
  import { jsx, jsxs } from "react/jsx-runtime";
@@ -102,7 +103,7 @@ function renderObjectContainer(props) {
102
103
  children: [typeof props.meta.description === "string" && /* @__PURE__ */ jsx("h3", {
103
104
  className: "text-lg font-medium",
104
105
  children: props.meta.description
105
- }), Object.entries(fields).map(([key, field]) => {
106
+ }), sortFieldsByOrder(fields).map(([key, field]) => {
106
107
  const childValue = toRecord(obj)[key];
107
108
  const childId = inputId(`${props.path}.${key}`);
108
109
  const childOnChange = (v) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "schema-components",
3
- "version": "1.18.0",
3
+ "version": "1.18.1",
4
4
  "description": "React components that render UI from Zod schemas, JSON Schema, and OpenAPI documents",
5
5
  "type": "module",
6
6
  "main": "./dist/index.mjs",