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
@@ -1,5 +1,5 @@
1
1
  import { isObject } from "../core/guards.mjs";
2
- import { toReactNode } from "../react/headlessRenderers.mjs";
2
+ import { inputId, toReactNode } from "../react/headlessRenderers.mjs";
3
3
  import { headlessResolver } from "../react/headless.mjs";
4
4
  import { jsx } from "react/jsx-runtime";
5
5
  //#region src/themes/mantine.tsx
@@ -17,9 +17,14 @@ let MantineSwitch = (props) => /* @__PURE__ */ jsx("input", {
17
17
  });
18
18
  let MantineSelect = (props) => /* @__PURE__ */ jsx("select", { ...props });
19
19
  let MantineFieldset = (props) => /* @__PURE__ */ jsx("fieldset", { ...props });
20
+ let MantineText = (props) => /* @__PURE__ */ jsx("span", { ...props });
20
21
  /**
21
22
  * Register real Mantine components for the resolver to use.
22
23
  * Call once at app startup before rendering.
24
+ *
25
+ * `Text` is required so read-only scalars render as a styled Mantine
26
+ * `<Text>` element instead of a bare `<span>`, matching the visual
27
+ * weight of the editable variants.
23
28
  */
24
29
  function registerMantineComponents(components) {
25
30
  MantineTextInput = components.TextInput;
@@ -27,12 +32,18 @@ function registerMantineComponents(components) {
27
32
  MantineSwitch = components.Switch;
28
33
  MantineSelect = components.Select;
29
34
  MantineFieldset = components.Fieldset;
35
+ MantineText = components.Text;
30
36
  }
31
37
  function renderStringInput(props) {
32
38
  const strValue = typeof props.value === "string" ? props.value : "";
33
39
  const label = getLabel(props);
34
- if (props.readOnly) return /* @__PURE__ */ jsx("span", { children: strValue || "—" });
40
+ const id = inputId(props.path);
41
+ if (props.readOnly) return /* @__PURE__ */ jsx(MantineText, {
42
+ id,
43
+ children: strValue || "—"
44
+ });
35
45
  return /* @__PURE__ */ jsx(MantineTextInput, {
46
+ id,
36
47
  label,
37
48
  value: props.writeOnly ? "" : strValue,
38
49
  onChange: (e) => {
@@ -42,11 +53,19 @@ function renderStringInput(props) {
42
53
  }
43
54
  function renderNumberInput(props) {
44
55
  const label = getLabel(props);
56
+ const id = inputId(props.path);
45
57
  if (props.readOnly) {
46
- if (typeof props.value !== "number") return /* @__PURE__ */ jsx("span", { children: "—" });
47
- return /* @__PURE__ */ jsx("span", { children: props.value.toLocaleString() });
58
+ if (typeof props.value !== "number") return /* @__PURE__ */ jsx(MantineText, {
59
+ id,
60
+ children: "—"
61
+ });
62
+ return /* @__PURE__ */ jsx(MantineText, {
63
+ id,
64
+ children: props.value.toLocaleString()
65
+ });
48
66
  }
49
67
  return /* @__PURE__ */ jsx(MantineNumberInput, {
68
+ id,
50
69
  label,
51
70
  value: props.writeOnly ? void 0 : typeof props.value === "number" ? props.value : void 0,
52
71
  onChange: (v) => {
@@ -56,11 +75,19 @@ function renderNumberInput(props) {
56
75
  }
57
76
  function renderBooleanInput(props) {
58
77
  const label = getLabel(props);
78
+ const id = inputId(props.path);
59
79
  if (props.readOnly) {
60
- if (typeof props.value !== "boolean") return /* @__PURE__ */ jsx("span", { children: "—" });
61
- return /* @__PURE__ */ jsx("span", { children: props.value ? "Yes" : "No" });
80
+ if (typeof props.value !== "boolean") return /* @__PURE__ */ jsx(MantineText, {
81
+ id,
82
+ children: "—"
83
+ });
84
+ return /* @__PURE__ */ jsx(MantineText, {
85
+ id,
86
+ children: props.value ? "Yes" : "No"
87
+ });
62
88
  }
63
89
  return /* @__PURE__ */ jsx(MantineSwitch, {
90
+ id,
64
91
  label,
65
92
  checked: props.writeOnly ? false : props.value === true,
66
93
  onChange: (e) => {
@@ -71,22 +98,28 @@ function renderBooleanInput(props) {
71
98
  function renderEnumInput(props) {
72
99
  const enumValue = typeof props.value === "string" ? props.value : "";
73
100
  const label = getLabel(props);
74
- if (props.readOnly) return /* @__PURE__ */ jsx("span", { children: enumValue || "—" });
101
+ const id = inputId(props.path);
102
+ if (props.readOnly) return /* @__PURE__ */ jsx(MantineText, {
103
+ id,
104
+ children: enumValue || "—"
105
+ });
106
+ const enumValues = props.tree.type === "enum" ? props.tree.enumValues : [];
75
107
  return /* @__PURE__ */ jsx(MantineSelect, {
108
+ id,
76
109
  label,
77
110
  value: props.writeOnly ? null : enumValue || null,
78
111
  onChange: (v) => {
79
112
  if (typeof v === "string") props.onChange(v);
80
113
  },
81
- data: (props.enumValues ?? []).map((v) => ({
114
+ data: enumValues.map((v) => ({
82
115
  value: v,
83
116
  label: v
84
117
  }))
85
118
  });
86
119
  }
87
120
  function renderObjectContainer(props) {
88
- const fields = props.fields;
89
- if (fields === void 0) return null;
121
+ if (props.tree.type !== "object") return null;
122
+ const fields = props.tree.fields;
90
123
  const obj = isObject(props.value) ? props.value : {};
91
124
  return /* @__PURE__ */ jsx(MantineFieldset, {
92
125
  legend: getLabel(props),
@@ -100,7 +133,7 @@ function renderObjectContainer(props) {
100
133
  };
101
134
  return /* @__PURE__ */ jsx("div", {
102
135
  style: { marginBottom: "0.5rem" },
103
- children: toReactNode(props.renderChild(field, childValue, childOnChange))
136
+ children: toReactNode(props.renderChild(field, childValue, childOnChange, key))
104
137
  }, key);
105
138
  })
106
139
  });
@@ -1,4 +1,4 @@
1
- import { r as ComponentResolver } from "../renderer-BdSqllx5.mjs";
1
+ import { r as ComponentResolver } from "../renderer-DXo-rXHJ.mjs";
2
2
 
3
3
  //#region src/themes/mui.d.ts
4
4
  /**
@@ -1,8 +1,8 @@
1
1
  import { isObject } from "../core/guards.mjs";
2
- import { toReactNode } from "../react/headlessRenderers.mjs";
2
+ import { inputId, toReactNode } from "../react/headlessRenderers.mjs";
3
3
  import { headlessResolver } from "../react/headless.mjs";
4
- import { jsx, jsxs } from "react/jsx-runtime";
5
4
  import { isValidElement } from "react";
5
+ import { jsx, jsxs } from "react/jsx-runtime";
6
6
  //#region src/themes/mui.tsx
7
7
  function ariaRequired(tree) {
8
8
  return { required: tree.isOptional === false };
@@ -10,11 +10,14 @@ function ariaRequired(tree) {
10
10
  function renderStringInput(props) {
11
11
  const strValue = typeof props.value === "string" ? props.value : "";
12
12
  const label = typeof props.meta.description === "string" ? props.meta.description : void 0;
13
+ const id = inputId(props.path);
13
14
  if (props.readOnly) return /* @__PURE__ */ jsx(MuiTypography, {
15
+ id,
14
16
  variant: "body2",
15
17
  children: strValue || "—"
16
18
  });
17
19
  return /* @__PURE__ */ jsx(MuiTextField, {
20
+ id,
18
21
  label,
19
22
  type: props.constraints.format === "email" ? "email" : props.constraints.format === "uri" ? "url" : "text",
20
23
  value: props.writeOnly ? "" : strValue,
@@ -33,17 +36,21 @@ function renderStringInput(props) {
33
36
  }
34
37
  function renderNumberInput(props) {
35
38
  const label = typeof props.meta.description === "string" ? props.meta.description : void 0;
39
+ const id = inputId(props.path);
36
40
  if (props.readOnly) {
37
41
  if (typeof props.value !== "number") return /* @__PURE__ */ jsx(MuiTypography, {
42
+ id,
38
43
  variant: "body2",
39
44
  children: "—"
40
45
  });
41
46
  return /* @__PURE__ */ jsx(MuiTypography, {
47
+ id,
42
48
  variant: "body2",
43
49
  children: props.value.toLocaleString()
44
50
  });
45
51
  }
46
52
  return /* @__PURE__ */ jsx(MuiTextField, {
53
+ id,
47
54
  label,
48
55
  type: "number",
49
56
  value: props.writeOnly ? "" : typeof props.value === "number" ? props.value : "",
@@ -62,18 +69,22 @@ function renderNumberInput(props) {
62
69
  }
63
70
  function renderBooleanInput(props) {
64
71
  const label = typeof props.meta.description === "string" ? props.meta.description : void 0;
72
+ const id = inputId(props.path);
65
73
  if (props.readOnly) {
66
74
  if (typeof props.value !== "boolean") return /* @__PURE__ */ jsx(MuiTypography, {
75
+ id,
67
76
  variant: "body2",
68
77
  children: "—"
69
78
  });
70
79
  return /* @__PURE__ */ jsx(MuiTypography, {
80
+ id,
71
81
  variant: "body2",
72
82
  children: props.value ? "Yes" : "No"
73
83
  });
74
84
  }
75
85
  return /* @__PURE__ */ jsx(MuiFormControlLabel, {
76
86
  control: /* @__PURE__ */ jsx(MuiCheckbox, {
87
+ id,
77
88
  checked: props.writeOnly ? false : props.value === true,
78
89
  onChange: (e) => {
79
90
  props.onChange(e.target.checked);
@@ -85,11 +96,14 @@ function renderBooleanInput(props) {
85
96
  function renderEnumInput(props) {
86
97
  const enumValue = typeof props.value === "string" ? props.value : "";
87
98
  const label = typeof props.meta.description === "string" ? props.meta.description : void 0;
99
+ const id = inputId(props.path);
88
100
  if (props.readOnly) return /* @__PURE__ */ jsx(MuiTypography, {
101
+ id,
89
102
  variant: "body2",
90
103
  children: enumValue || "—"
91
104
  });
92
105
  return /* @__PURE__ */ jsxs(MuiTextField, {
106
+ id,
93
107
  select: true,
94
108
  label,
95
109
  value: props.writeOnly ? "" : enumValue,
@@ -103,15 +117,15 @@ function renderEnumInput(props) {
103
117
  children: [/* @__PURE__ */ jsxs(MuiMenuItem, {
104
118
  value: "",
105
119
  children: ["Select", "…"]
106
- }), (props.enumValues ?? []).map((v) => /* @__PURE__ */ jsx(MuiMenuItem, {
120
+ }), (props.tree.type === "enum" ? props.tree.enumValues : []).map((v) => /* @__PURE__ */ jsx(MuiMenuItem, {
107
121
  value: v,
108
122
  children: v
109
123
  }, v))]
110
124
  });
111
125
  }
112
126
  function renderObjectContainer(props) {
113
- const fields = props.fields;
114
- if (fields === void 0) return null;
127
+ if (props.tree.type !== "object") return null;
128
+ const fields = props.tree.fields;
115
129
  const obj = isObject(props.value) ? props.value : {};
116
130
  return /* @__PURE__ */ jsxs(MuiBox, {
117
131
  sx: {
@@ -130,13 +144,14 @@ function renderObjectContainer(props) {
130
144
  updated[key] = v;
131
145
  props.onChange(updated);
132
146
  };
133
- return /* @__PURE__ */ jsx("div", { children: toReactNode(props.renderChild(field, childValue, childOnChange)) }, key);
147
+ return /* @__PURE__ */ jsx("div", { children: toReactNode(props.renderChild(field, childValue, childOnChange, key)) }, key);
134
148
  })]
135
149
  });
136
150
  }
137
151
  function renderArrayContainer(props) {
138
152
  const arr = Array.isArray(props.value) ? props.value : [];
139
- const element = props.element;
153
+ if (props.tree.type !== "array") return null;
154
+ const element = props.tree.element;
140
155
  if (element === void 0) return null;
141
156
  return /* @__PURE__ */ jsx(MuiBox, {
142
157
  sx: {
@@ -150,7 +165,7 @@ function renderArrayContainer(props) {
150
165
  next[i] = v;
151
166
  props.onChange(next);
152
167
  };
153
- return /* @__PURE__ */ jsx("div", { children: toReactNode(props.renderChild(element, item, childOnChange)) }, String(i));
168
+ return /* @__PURE__ */ jsx("div", { children: toReactNode(props.renderChild(element, item, childOnChange, `[${String(i)}]`)) }, String(i));
154
169
  })
155
170
  });
156
171
  }
@@ -1,4 +1,4 @@
1
- import { r as ComponentResolver } from "../renderer-BdSqllx5.mjs";
1
+ import { r as ComponentResolver } from "../renderer-DXo-rXHJ.mjs";
2
2
 
3
3
  //#region src/themes/radix.d.ts
4
4
  /**
@@ -1,5 +1,5 @@
1
1
  import { isObject } from "../core/guards.mjs";
2
- import { toReactNode } from "../react/headlessRenderers.mjs";
2
+ import { inputId, toReactNode } from "../react/headlessRenderers.mjs";
3
3
  import { headlessResolver } from "../react/headless.mjs";
4
4
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
5
5
  //#region src/themes/radix.tsx
@@ -41,13 +41,19 @@ function registerRadixComponents(components) {
41
41
  function renderStringInput(props) {
42
42
  const strValue = typeof props.value === "string" ? props.value : "";
43
43
  const label = getLabel(props);
44
- if (props.readOnly) return /* @__PURE__ */ jsx(RadixText, { children: strValue || "—" });
44
+ const id = inputId(props.path);
45
+ if (props.readOnly) return /* @__PURE__ */ jsx(RadixText, {
46
+ id,
47
+ children: strValue || "—"
48
+ });
45
49
  return /* @__PURE__ */ jsxs(RadixBox, { children: [label !== void 0 && /* @__PURE__ */ jsx(RadixText, {
46
50
  as: "label",
47
51
  size: "2",
48
52
  weight: "medium",
53
+ htmlFor: id,
49
54
  children: label
50
55
  }), /* @__PURE__ */ jsx(RadixTextField, {
56
+ id,
51
57
  type: props.constraints.format === "email" ? "email" : props.constraints.format === "uri" ? "url" : "text",
52
58
  value: props.writeOnly ? "" : strValue,
53
59
  onChange: (e) => {
@@ -58,16 +64,25 @@ function renderStringInput(props) {
58
64
  }
59
65
  function renderNumberInput(props) {
60
66
  const label = getLabel(props);
67
+ const id = inputId(props.path);
61
68
  if (props.readOnly) {
62
- if (typeof props.value !== "number") return /* @__PURE__ */ jsx(RadixText, { children: "—" });
63
- return /* @__PURE__ */ jsx(RadixText, { children: props.value.toLocaleString() });
69
+ if (typeof props.value !== "number") return /* @__PURE__ */ jsx(RadixText, {
70
+ id,
71
+ children: "—"
72
+ });
73
+ return /* @__PURE__ */ jsx(RadixText, {
74
+ id,
75
+ children: props.value.toLocaleString()
76
+ });
64
77
  }
65
78
  return /* @__PURE__ */ jsxs(RadixBox, { children: [label !== void 0 && /* @__PURE__ */ jsx(RadixText, {
66
79
  as: "label",
67
80
  size: "2",
68
81
  weight: "medium",
82
+ htmlFor: id,
69
83
  children: label
70
84
  }), /* @__PURE__ */ jsx(RadixTextField, {
85
+ id,
71
86
  type: "number",
72
87
  value: props.writeOnly ? "" : typeof props.value === "number" ? props.value : "",
73
88
  onChange: (e) => {
@@ -78,20 +93,29 @@ function renderNumberInput(props) {
78
93
  }
79
94
  function renderBooleanInput(props) {
80
95
  const label = getLabel(props);
96
+ const id = inputId(props.path);
81
97
  if (props.readOnly) {
82
- if (typeof props.value !== "boolean") return /* @__PURE__ */ jsx(RadixText, { children: "—" });
83
- return /* @__PURE__ */ jsx(RadixText, { children: props.value ? "Yes" : "No" });
98
+ if (typeof props.value !== "boolean") return /* @__PURE__ */ jsx(RadixText, {
99
+ id,
100
+ children: "—"
101
+ });
102
+ return /* @__PURE__ */ jsx(RadixText, {
103
+ id,
104
+ children: props.value ? "Yes" : "No"
105
+ });
84
106
  }
85
107
  return /* @__PURE__ */ jsxs(RadixFlex, {
86
108
  align: "center",
87
109
  gap: "2",
88
110
  children: [/* @__PURE__ */ jsx(RadixCheckbox, {
111
+ id,
89
112
  checked: props.writeOnly ? false : props.value === true,
90
113
  onCheckedChange: (checked) => {
91
114
  if (typeof checked === "boolean") props.onChange(checked);
92
115
  }
93
116
  }), label !== void 0 && /* @__PURE__ */ jsx(RadixText, {
94
117
  as: "label",
118
+ htmlFor: id,
95
119
  children: label
96
120
  })]
97
121
  });
@@ -99,26 +123,34 @@ function renderBooleanInput(props) {
99
123
  function renderEnumInput(props) {
100
124
  const enumValue = typeof props.value === "string" ? props.value : "";
101
125
  const label = getLabel(props);
102
- if (props.readOnly) return /* @__PURE__ */ jsx(RadixText, { children: enumValue || "—" });
126
+ const id = inputId(props.path);
127
+ if (props.readOnly) return /* @__PURE__ */ jsx(RadixText, {
128
+ id,
129
+ children: enumValue || "—"
130
+ });
103
131
  return /* @__PURE__ */ jsxs(RadixBox, { children: [label !== void 0 && /* @__PURE__ */ jsx(RadixText, {
104
132
  as: "label",
105
133
  size: "2",
106
134
  weight: "medium",
135
+ htmlFor: id,
107
136
  children: label
108
137
  }), /* @__PURE__ */ jsxs(RadixSelectRoot, {
109
138
  value: props.writeOnly ? "" : enumValue,
110
139
  onValueChange: (value) => {
111
140
  props.onChange(value);
112
141
  },
113
- children: [/* @__PURE__ */ jsx(RadixSelectTrigger, { mt: "1" }), /* @__PURE__ */ jsx(RadixSelectContent, { children: (props.enumValues ?? []).map((value) => /* @__PURE__ */ jsx(RadixSelectItem, {
142
+ children: [/* @__PURE__ */ jsx(RadixSelectTrigger, {
143
+ id,
144
+ mt: "1"
145
+ }), /* @__PURE__ */ jsx(RadixSelectContent, { children: (props.tree.type === "enum" ? props.tree.enumValues : []).map((value) => /* @__PURE__ */ jsx(RadixSelectItem, {
114
146
  value,
115
147
  children: value
116
148
  }, value)) })]
117
149
  })] });
118
150
  }
119
151
  function renderObjectContainer(props) {
120
- const fields = props.fields;
121
- if (fields === void 0) return null;
152
+ if (props.tree.type !== "object") return null;
153
+ const fields = props.tree.fields;
122
154
  const obj = isObject(props.value) ? props.value : {};
123
155
  return /* @__PURE__ */ jsxs(RadixBox, { children: [typeof props.meta.description === "string" && /* @__PURE__ */ jsx(RadixText, {
124
156
  as: "div",
@@ -137,7 +169,7 @@ function renderObjectContainer(props) {
137
169
  updated[key] = v;
138
170
  props.onChange(updated);
139
171
  };
140
- return /* @__PURE__ */ jsx(RadixBox, { children: toReactNode(props.renderChild(field, childValue, childOnChange)) }, key);
172
+ return /* @__PURE__ */ jsx(RadixBox, { children: toReactNode(props.renderChild(field, childValue, childOnChange, key)) }, key);
141
173
  })
142
174
  })] });
143
175
  }
@@ -1,4 +1,4 @@
1
- import { r as ComponentResolver } from "../renderer-BdSqllx5.mjs";
1
+ import { r as ComponentResolver } from "../renderer-DXo-rXHJ.mjs";
2
2
 
3
3
  //#region src/themes/shadcn.d.ts
4
4
  declare const shadcnResolver: ComponentResolver;
@@ -1,5 +1,5 @@
1
1
  import { toRecord } from "../core/guards.mjs";
2
- import { toReactNode } from "../react/headlessRenderers.mjs";
2
+ import { inputId, toReactNode } from "../react/headlessRenderers.mjs";
3
3
  import { headlessResolver } from "../react/headless.mjs";
4
4
  import { jsx, jsxs } from "react/jsx-runtime";
5
5
  //#region src/themes/shadcn.tsx
@@ -11,12 +11,15 @@ function buildClassNames(...classes) {
11
11
  }
12
12
  function renderStringInput(props) {
13
13
  const strValue = typeof props.value === "string" ? props.value : "";
14
+ const id = inputId(props.path);
14
15
  const className = buildClassNames("flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors", "file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground", "placeholder:text-muted-foreground", "focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring", "disabled:cursor-not-allowed disabled:opacity-50");
15
16
  if (props.readOnly) return /* @__PURE__ */ jsx("span", {
17
+ id,
16
18
  className: "text-sm",
17
19
  children: strValue || "—"
18
20
  });
19
21
  if (props.writeOnly) return /* @__PURE__ */ jsx("input", {
22
+ id,
20
23
  type: props.constraints.format === "email" ? "email" : "text",
21
24
  className,
22
25
  placeholder: typeof props.meta.description === "string" ? props.meta.description : void 0,
@@ -26,6 +29,7 @@ function renderStringInput(props) {
26
29
  }
27
30
  });
28
31
  return /* @__PURE__ */ jsx("input", {
32
+ id,
29
33
  type: props.constraints.format === "email" ? "email" : "text",
30
34
  className,
31
35
  value: strValue,
@@ -38,18 +42,22 @@ function renderStringInput(props) {
38
42
  });
39
43
  }
40
44
  function renderNumberInput(props) {
45
+ const id = inputId(props.path);
41
46
  const className = buildClassNames("flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors", "placeholder:text-muted-foreground", "focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring", "disabled:cursor-not-allowed disabled:opacity-50");
42
47
  if (props.readOnly) {
43
48
  if (typeof props.value !== "number") return /* @__PURE__ */ jsx("span", {
49
+ id,
44
50
  className: "text-sm",
45
51
  children: "—"
46
52
  });
47
53
  return /* @__PURE__ */ jsx("span", {
54
+ id,
48
55
  className: "text-sm",
49
56
  children: props.value.toLocaleString()
50
57
  });
51
58
  }
52
59
  return /* @__PURE__ */ jsx("input", {
60
+ id,
53
61
  type: "number",
54
62
  className,
55
63
  value: props.writeOnly ? "" : typeof props.value === "number" ? props.value : "",
@@ -61,18 +69,22 @@ function renderNumberInput(props) {
61
69
  });
62
70
  }
63
71
  function renderBooleanInput(props) {
72
+ const id = inputId(props.path);
64
73
  const className = buildClassNames("h-4 w-4 rounded border border-primary shadow", "focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring", "disabled:cursor-not-allowed disabled:opacity-50");
65
74
  if (props.readOnly) {
66
75
  if (typeof props.value !== "boolean") return /* @__PURE__ */ jsx("span", {
76
+ id,
67
77
  className: "text-sm",
68
78
  children: "—"
69
79
  });
70
80
  return /* @__PURE__ */ jsx("span", {
81
+ id,
71
82
  className: "text-sm",
72
83
  children: props.value ? "Yes" : "No"
73
84
  });
74
85
  }
75
86
  return /* @__PURE__ */ jsx("input", {
87
+ id,
76
88
  type: "checkbox",
77
89
  className,
78
90
  checked: props.writeOnly ? false : props.value === true,
@@ -82,8 +94,8 @@ function renderBooleanInput(props) {
82
94
  });
83
95
  }
84
96
  function renderObjectContainer(props) {
85
- const fields = props.fields;
86
- if (fields === void 0) return null;
97
+ if (props.tree.type !== "object") return null;
98
+ const fields = props.tree.fields;
87
99
  const obj = typeof props.value === "object" && props.value !== null && !Array.isArray(props.value) ? props.value : {};
88
100
  return /* @__PURE__ */ jsxs("div", {
89
101
  className: "space-y-4",
@@ -92,6 +104,7 @@ function renderObjectContainer(props) {
92
104
  children: props.meta.description
93
105
  }), Object.entries(fields).map(([key, field]) => {
94
106
  const childValue = toRecord(obj)[key];
107
+ const childId = inputId(`${props.path}.${key}`);
95
108
  const childOnChange = (v) => {
96
109
  const updated = {};
97
110
  for (const [k, val] of Object.entries(obj)) updated[k] = val;
@@ -101,16 +114,18 @@ function renderObjectContainer(props) {
101
114
  return /* @__PURE__ */ jsxs("div", {
102
115
  className: "space-y-1",
103
116
  children: [/* @__PURE__ */ jsx("label", {
117
+ htmlFor: childId,
104
118
  className: "text-sm font-medium leading-none",
105
119
  children: field.meta.description ?? key
106
- }), toReactNode(props.renderChild(field, childValue, childOnChange))]
120
+ }), toReactNode(props.renderChild(field, childValue, childOnChange, key))]
107
121
  }, key);
108
122
  })]
109
123
  });
110
124
  }
111
125
  function renderArrayContainer(props) {
112
126
  const arr = Array.isArray(props.value) ? props.value : [];
113
- const element = props.element;
127
+ if (props.tree.type !== "array") return null;
128
+ const element = props.tree.element;
114
129
  if (element === void 0) return null;
115
130
  return /* @__PURE__ */ jsx("div", {
116
131
  className: "space-y-2",
@@ -120,33 +135,36 @@ function renderArrayContainer(props) {
120
135
  next[i] = v;
121
136
  props.onChange(next);
122
137
  };
123
- return /* @__PURE__ */ jsx("div", { children: toReactNode(props.renderChild(element, item, childOnChange)) }, String(i));
138
+ return /* @__PURE__ */ jsx("div", { children: toReactNode(props.renderChild(element, item, childOnChange, `[${String(i)}]`)) }, String(i));
124
139
  })
125
140
  });
126
141
  }
127
142
  function renderEnumInput(props) {
143
+ const id = inputId(props.path);
128
144
  const className = buildClassNames("flex h-9 w-full items-center justify-between rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm", "focus:outline-none focus:ring-1 focus:ring-ring", "disabled:cursor-not-allowed disabled:opacity-50");
129
145
  const enumValue = typeof props.value === "string" ? props.value : "";
130
146
  if (props.readOnly) return /* @__PURE__ */ jsx("span", {
147
+ id,
131
148
  className: "text-sm",
132
149
  children: enumValue || "—"
133
150
  });
134
151
  return /* @__PURE__ */ jsxs("select", {
152
+ id,
135
153
  className,
136
154
  value: props.writeOnly ? "" : enumValue,
137
155
  onChange: (e) => {
138
156
  props.onChange(e.target.value);
139
157
  },
140
- children: [/* @__PURE__ */ jsx("option", {
158
+ children: [/* @__PURE__ */ jsxs("option", {
141
159
  value: "",
142
- children: "Select\\u2026"
143
- }), props.enumValues?.map((v) => {
160
+ children: ["Select", "…"]
161
+ }), props.tree.type === "enum" ? props.tree.enumValues.map((v) => {
144
162
  const display = v === null ? "null" : typeof v === "string" ? v : String(v);
145
163
  return /* @__PURE__ */ jsx("option", {
146
164
  value: display,
147
165
  children: display
148
166
  }, display);
149
- })]
167
+ }) : null]
150
168
  });
151
169
  }
152
170
  function buildResolver() {
@@ -84,26 +84,71 @@ type UnsafeFields = Record<string, FieldOverride> & {
84
84
  * Returns the original type when the schema is non-recursive.
85
85
  */
86
86
  type DetectRecursiveFallback<T> = unknown extends T ? __SchemaInferenceFellBack : T;
87
+ /**
88
+ * Type-level recursion bound for $ref resolution.
89
+ *
90
+ * The TypeScript type system imposes its own recursion limit; without an
91
+ * explicit bound a cyclic schema graph would exhaust it and degrade to
92
+ * `any`/`unknown` silently. Ten levels is the runtime walker's parallel
93
+ * — see `countDistinctRefs` in `ref.ts` (lines 52-55), which derives its
94
+ * bound from the number of distinct `$ref` strings in the document.
95
+ *
96
+ * A fixed bound is used here rather than a derived one because the type
97
+ * system has no way to count distinct strings across a recursive `Defs`
98
+ * map without itself recursing — which is the problem the bound exists
99
+ * to solve. Ten covers every realistic schema graph encountered in
100
+ * practice; deeper graphs surface as `__SchemaInferenceFellBack` so
101
+ * consumers can detect the limit explicitly.
102
+ */
103
+ type DEFAULT_MAX_DEPTH = 10;
87
104
  /**
88
105
  * Resolve a $ref against the local definitions context.
106
+ *
107
+ * SOURCE-OF-TRUTH: mirrors runtime `resolveRef` in
108
+ * `packages/core/src/core/ref.ts` (line 90). Any change to the runtime
109
+ * ref-resolution rules (new ref forms, different cycle handling) must be
110
+ * reflected here and pinned in
111
+ * `packages/core/tests/typeInference-walker-parity.test.ts`.
112
+ *
89
113
  * Supports:
90
114
  * - `#` (root)
91
115
  * - `#/$defs/Name` and `#/definitions/Name` (named definitions)
92
116
  * - `#SomeName` ($anchor, $dynamicAnchor resolved from definitions)
93
117
  */
94
- type ResolveSchemaRef<R extends string, Defs extends Record<string, unknown>, Depth extends number = 0> = Depth extends 10 ? __SchemaInferenceFellBack : R extends "#" ? unknown : R extends `#/$defs/${infer Name}` ? Name extends keyof Defs ? DetectRecursiveFallback<FromJSONSchema<Defs[Name], Defs>> : unknown : R extends `#/definitions/${infer Name}` ? Name extends keyof Defs ? DetectRecursiveFallback<FromJSONSchema<Defs[Name], Defs>> : unknown : R extends `#${infer AnchorName}` ? AnchorName extends keyof Defs ? DetectRecursiveFallback<FromJSONSchema<Defs[AnchorName], Defs>> : unknown : unknown;
118
+ type ResolveSchemaRef<R extends string, Defs extends Record<string, unknown>, Depth extends number = 0> = Depth extends DEFAULT_MAX_DEPTH ? __SchemaInferenceFellBack : R extends "#" ? unknown : R extends `#/$defs/${infer Name}` ? Name extends keyof Defs ? DetectRecursiveFallback<FromJSONSchema<Defs[Name], Defs>> : unknown : R extends `#/definitions/${infer Name}` ? Name extends keyof Defs ? DetectRecursiveFallback<FromJSONSchema<Defs[Name], Defs>> : unknown : R extends `#${infer AnchorName}` ? AnchorName extends keyof Defs ? DetectRecursiveFallback<FromJSONSchema<Defs[AnchorName], Defs>> : unknown : unknown;
95
119
  /**
96
120
  * Merge an allOf array into an intersection type.
97
121
  */
98
122
  type AllOfToType<A, Defs extends Record<string, unknown>> = A extends readonly unknown[] ? UnionToIntersection<FromJSONSchema<A[number], Defs>> : unknown;
99
123
  /**
100
124
  * Convert an anyOf/oneOf array into a union type.
101
- * Filters out `{ type: "null" }` members and instead makes the result nullable
102
- * when at least one null member is present -- mirrors the walker's normaliseAnyOf.
125
+ *
126
+ * SOURCE-OF-TRUTH: mirrors runtime `walkUnion` (and the
127
+ * `walkDiscriminatedUnion` fast path) in
128
+ * `packages/core/src/core/walker.ts` (lines 723-752), together with
129
+ * `detectDiscriminated` and `normaliseAnyOf` in
130
+ * `packages/core/src/core/merge.ts` (lines 190-260).
131
+ *
132
+ * Deliberate divergence: the walker collapses qualifying `oneOf` members
133
+ * into a `discriminatedUnion` field at runtime. The type-level helper
134
+ * produces a plain TypeScript union because a discriminated union and a
135
+ * plain union over the same members are structurally indistinguishable
136
+ * at the type level. Parity is pinned in
137
+ * `packages/core/tests/typeInference-walker-parity.test.ts`.
138
+ *
139
+ * Filters out `{ type: "null" }` members and instead makes the result
140
+ * nullable when at least one null member is present — mirrors the
141
+ * walker's `normaliseAnyOf`.
103
142
  */
104
143
  type UnionOfMembers<A, Defs extends Record<string, unknown>> = A extends readonly unknown[] ? HasNullMember<A> extends true ? Exclude<FromJSONSchema<A[number], Defs>, null> | null : FromJSONSchema<A[number], Defs> : unknown;
105
144
  /**
106
145
  * Check whether an anyOf/oneOf array contains a `{ type: "null" }` member.
146
+ *
147
+ * SOURCE-OF-TRUTH: mirrors runtime `normaliseAnyOf` in
148
+ * `packages/core/src/core/merge.ts` (lines 190-209). Both implementations
149
+ * only recognise schema-shaped null members (`{ type: "null" }`); a bare
150
+ * `null` literal in the array is treated as non-nullable. Parity is
151
+ * pinned in `packages/core/tests/typeInference-walker-parity.test.ts`.
107
152
  */
108
153
  type HasNullMember<A> = A extends readonly unknown[] ? null extends A[number] ? false : {
109
154
  type: "null";
@@ -214,6 +259,14 @@ type UnionToIntersection<U> = (U extends unknown ? (k: U) => void : never) exten
214
259
  /**
215
260
  * Resolves an OpenAPI `ref` string to its JSON Schema, then parses it.
216
261
  *
262
+ * SOURCE-OF-TRUTH: mirrors runtime `resolveRef` in
263
+ * `packages/core/src/core/ref.ts` (line 90), which is invoked by the
264
+ * walker entry point in `packages/core/src/core/walker.ts` (lines
265
+ * 144-154) for OpenAPI documents. Any change to the runtime ref-resolution
266
+ * rules (new ref forms, different cycle handling, JSON Pointer decoding)
267
+ * must be reflected here and pinned in
268
+ * `packages/core/tests/typeInference-walker-parity.test.ts`.
269
+ *
217
270
  * Handles:
218
271
  * - `#/components/schemas/Name` (OpenAPI 3.x)
219
272
  * - `#/definitions/Name` (Swagger 2.0)
@@ -332,4 +385,4 @@ type PathOfType<T, Prefix extends string = ""> = IsNarrowObject<T> extends true
332
385
  */
333
386
  type TypeAtPath<T, P extends string> = P extends `${infer Key}.${infer Rest}` ? Key extends keyof T ? TypeAtPath<T[Key], Rest> : unknown : P extends keyof T ? T[P] : unknown;
334
387
  //#endregion
335
- export { OpenAPIRequestBodyType as a, ResolveOpenAPIRef as c, __SchemaInferenceFellBack as d, InferResponseFields as i, TypeAtPath as l, InferParameterOverrides as n, OpenAPIResponseType as o, InferRequestBodyFields as r, PathOfType as s, FromJSONSchema as t, UnsafeFields as u };
388
+ export { InferResponseFields as a, PathOfType as c, UnsafeFields as d, __SchemaInferenceFellBack as f, InferRequestBodyFields as i, ResolveOpenAPIRef as l, FromJSONSchema as n, OpenAPIRequestBodyType as o, InferParameterOverrides as r, OpenAPIResponseType as s, DEFAULT_MAX_DEPTH as t, TypeAtPath as u };