schema-components 1.21.0 → 1.23.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 (91) hide show
  1. package/README.md +3 -1
  2. package/dist/core/adapter.d.mts +115 -4
  3. package/dist/core/adapter.mjs +405 -75
  4. package/dist/core/constraints.d.mts +2 -2
  5. package/dist/core/constraints.mjs +0 -7
  6. package/dist/core/cssClasses.d.mts +52 -0
  7. package/dist/core/cssClasses.mjs +51 -0
  8. package/dist/core/diagnostics.d.mts +1 -1
  9. package/dist/core/errors.d.mts +1 -1
  10. package/dist/core/errors.mjs +5 -13
  11. package/dist/core/fieldOrder.d.mts +1 -1
  12. package/dist/core/formats.d.mts +30 -2
  13. package/dist/core/formats.mjs +33 -1
  14. package/dist/core/idPath.d.mts +54 -0
  15. package/dist/core/idPath.mjs +66 -0
  16. package/dist/core/limits.d.mts +2 -0
  17. package/dist/core/limits.mjs +23 -0
  18. package/dist/core/merge.d.mts +10 -1
  19. package/dist/core/merge.mjs +49 -10
  20. package/dist/core/normalise.d.mts +40 -3
  21. package/dist/core/normalise.mjs +2 -2
  22. package/dist/core/openapi30.d.mts +15 -1
  23. package/dist/core/openapi30.mjs +2 -2
  24. package/dist/core/openapiConstants.d.mts +67 -0
  25. package/dist/core/openapiConstants.mjs +90 -0
  26. package/dist/core/ref.d.mts +2 -2
  27. package/dist/core/ref.mjs +85 -6
  28. package/dist/core/refChain.d.mts +70 -0
  29. package/dist/core/refChain.mjs +44 -0
  30. package/dist/core/renderer.d.mts +1 -1
  31. package/dist/core/renderer.mjs +0 -2
  32. package/dist/core/swagger2.d.mts +1 -1
  33. package/dist/core/swagger2.mjs +1 -1
  34. package/dist/core/typeInference.d.mts +982 -2
  35. package/dist/core/types.d.mts +2 -2
  36. package/dist/core/types.mjs +1 -4
  37. package/dist/core/unionMatch.d.mts +36 -0
  38. package/dist/core/unionMatch.mjs +53 -0
  39. package/dist/core/version.d.mts +1 -1
  40. package/dist/core/version.mjs +29 -17
  41. package/dist/core/walkBuilders.d.mts +23 -4
  42. package/dist/core/walkBuilders.mjs +27 -7
  43. package/dist/core/walker.d.mts +1 -1
  44. package/dist/core/walker.mjs +123 -47
  45. package/dist/{diagnostics-CbBPsxSt.d.mts → diagnostics-BS2kaUyE.d.mts} +1 -1
  46. package/dist/{errors-QEwOtQAA.d.mts → errors-g_MCTQel.d.mts} +10 -16
  47. package/dist/html/a11y.d.mts +9 -4
  48. package/dist/html/a11y.mjs +10 -12
  49. package/dist/html/renderToHtml.d.mts +10 -3
  50. package/dist/html/renderToHtml.mjs +13 -3
  51. package/dist/html/renderToHtmlStream.d.mts +2 -2
  52. package/dist/html/renderToHtmlStream.mjs +12 -1
  53. package/dist/html/renderers.d.mts +43 -8
  54. package/dist/html/renderers.mjs +136 -116
  55. package/dist/html/streamRenderers.d.mts +6 -6
  56. package/dist/html/streamRenderers.mjs +129 -89
  57. package/dist/limits-Cw5QZND8.d.mts +29 -0
  58. package/dist/{normalise-DaSrnr8g.mjs → normalise-DCYp06Sr.mjs} +770 -227
  59. package/dist/openapi/ApiCallbacks.d.mts +1 -1
  60. package/dist/openapi/ApiLinks.d.mts +1 -1
  61. package/dist/openapi/ApiResponseHeaders.d.mts +1 -1
  62. package/dist/openapi/ApiSecurity.d.mts +1 -1
  63. package/dist/openapi/ApiSecurity.mjs +16 -2
  64. package/dist/openapi/components.d.mts +234 -23
  65. package/dist/openapi/components.mjs +183 -52
  66. package/dist/openapi/parser.d.mts +9 -8
  67. package/dist/openapi/parser.mjs +252 -70
  68. package/dist/openapi/resolve.d.mts +31 -15
  69. package/dist/openapi/resolve.mjs +260 -40
  70. package/dist/react/SchemaComponent.d.mts +126 -36
  71. package/dist/react/SchemaComponent.mjs +95 -57
  72. package/dist/react/SchemaView.d.mts +30 -10
  73. package/dist/react/SchemaView.mjs +2 -2
  74. package/dist/react/a11y.d.mts +21 -0
  75. package/dist/react/a11y.mjs +24 -0
  76. package/dist/react/fieldPath.d.mts +1 -1
  77. package/dist/react/headless.d.mts +1 -1
  78. package/dist/react/headless.mjs +1 -2
  79. package/dist/react/headlessRenderers.d.mts +9 -11
  80. package/dist/react/headlessRenderers.mjs +51 -102
  81. package/dist/{ref-si8ViYun.d.mts → ref-DjLEKa_E.d.mts} +38 -3
  82. package/dist/{renderer-DI6ZYf7a.d.mts → renderer-CXJ8y0qw.d.mts} +2 -2
  83. package/dist/themes/mantine.d.mts +1 -1
  84. package/dist/themes/mui.d.mts +1 -1
  85. package/dist/themes/radix.d.mts +1 -1
  86. package/dist/themes/shadcn.d.mts +1 -1
  87. package/dist/themes/shadcn.mjs +2 -1
  88. package/dist/{types-BnxPEElk.d.mts → types-BTB73MB8.d.mts} +35 -14
  89. package/dist/{version-D-u7aMfy.d.mts → version-BFTVLsdb.d.mts} +7 -1
  90. package/package.json +1 -3
  91. package/dist/typeInference-Bxw3NOG1.d.mts +0 -647
@@ -1,11 +1,17 @@
1
1
  import { isObject } from "../core/guards.mjs";
2
+ import "../core/limits.mjs";
3
+ import { emitDiagnostic } from "../core/diagnostics.mjs";
4
+ import { SC_CLASSES } from "../core/cssClasses.mjs";
2
5
  import { getHtmlRenderFn } from "../core/renderer.mjs";
6
+ import { matchUnionOption, resolveDiscriminatedActive } from "../core/unionMatch.mjs";
3
7
  import { VOID_ELEMENTS, h, raw, serialize, serializeAttributes } from "./html.mjs";
4
- import { ariaLabelAttrs, buildHintElement, buildInputId, requiredIndicator } from "./a11y.mjs";
8
+ import { ariaLabelAttrs, buildHintElement, buildInputId, joinPath, requiredIndicator } from "./a11y.mjs";
9
+ import { panelId, tabId } from "./renderers.mjs";
10
+ import { recursionSentinelHtml } from "./renderToHtml.mjs";
5
11
  //#region src/html/streamRenderers.ts
6
12
  function yieldOpen(el) {
7
13
  const attrStr = serializeAttributes(el.attributes);
8
- if (el.children.length === 0 && VOID_ELEMENTS.has(el.tag)) return `<${el.tag}${attrStr}>`;
14
+ if (VOID_ELEMENTS.has(el.tag)) return `<${el.tag}${attrStr} />`;
9
15
  return `<${el.tag}${attrStr}>`;
10
16
  }
11
17
  function yieldClose(el) {
@@ -24,20 +30,32 @@ function renderLeaf(tree, value, mergedResolver, path) {
24
30
  tree,
25
31
  renderChild: () => ""
26
32
  });
27
- if (value === void 0 || value === null) return serialize(h("span", { class: "sc-value sc-value--empty" }, "—"));
28
- return serialize(h("span", { class: "sc-value" }, typeof value === "string" ? value : JSON.stringify(value)));
33
+ if (value === void 0 || value === null) return serialize(h("span", { class: SC_CLASSES.valueEmpty }, "—"));
34
+ return serialize(h("span", { class: SC_CLASSES.value }, typeof value === "string" ? value : JSON.stringify(value)));
29
35
  }
30
- function renderFieldSync(tree, value, mergedResolver, path, rawResolver) {
31
- return [...streamField(tree, value, mergedResolver, path, rawResolver)].join("");
36
+ function renderFieldSync(tree, value, mergedResolver, path, rawResolver, currentDepth, diagnostics) {
37
+ return [...streamField(tree, value, mergedResolver, path, rawResolver, currentDepth, diagnostics)].join("");
32
38
  }
33
- function matchUnionOption(options, value) {
34
- if (typeof value === "string") return options.find((o) => o.type === "string" || o.type === "enum");
35
- if (typeof value === "number") return options.find((o) => o.type === "number");
36
- if (typeof value === "boolean") return options.find((o) => o.type === "boolean");
37
- if (Array.isArray(value)) return options.find((o) => o.type === "array");
38
- if (typeof value === "object" && value !== null) return options.find((o) => o.type === "object");
39
+ /**
40
+ * Build a visible placeholder element used when a value does not match
41
+ * the shape implied by its field type. The expected-shape label is
42
+ * passed verbatim into `h()` so the serialiser escapes it.
43
+ *
44
+ * Streaming must keep producing output, so we never throw here the
45
+ * diagnostic surfaces the problem to the caller (when a sink is wired)
46
+ * while the rendered output remains structurally valid.
47
+ */
48
+ function typeMismatchPlaceholder(expectedShape) {
49
+ return serialize(h("span", {
50
+ class: "sc-value sc-value--invalid",
51
+ role: "alert"
52
+ }, `invalid value (expected ${expectedShape})`));
39
53
  }
40
- function* streamField(tree, value, mergedResolver, path, rawResolver) {
54
+ function* streamField(tree, value, mergedResolver, path, rawResolver, currentDepth = 0, diagnostics) {
55
+ if (currentDepth >= 10) {
56
+ yield recursionSentinelHtml(typeof tree.meta.description === "string" ? tree.meta.description : "schema");
57
+ return;
58
+ }
41
59
  const effectiveValue = value ?? tree.defaultValue;
42
60
  const type = tree.type;
43
61
  if (type === "string" || type === "number" || type === "boolean" || type === "enum" || type === "literal" || type === "file" || type === "unknown") {
@@ -45,36 +63,50 @@ function* streamField(tree, value, mergedResolver, path, rawResolver) {
45
63
  return;
46
64
  }
47
65
  if (type === "union") {
48
- yield* streamUnion(tree, effectiveValue, mergedResolver, path, rawResolver);
66
+ yield* streamUnion(tree, effectiveValue, mergedResolver, path, rawResolver, currentDepth, diagnostics);
49
67
  return;
50
68
  }
51
69
  if (type === "discriminatedUnion") {
52
- yield* streamDiscriminatedUnion(tree, value, mergedResolver, path, rawResolver);
70
+ yield* streamDiscriminatedUnion(tree, value, mergedResolver, path, rawResolver, currentDepth, diagnostics);
53
71
  return;
54
72
  }
55
73
  if (type === "object") {
56
- yield* streamObject(tree, value, mergedResolver, path, rawResolver);
74
+ yield* streamObject(tree, value, mergedResolver, path, rawResolver, currentDepth, diagnostics);
57
75
  return;
58
76
  }
59
77
  if (type === "array") {
60
- yield* streamArray(tree, value, mergedResolver, path, rawResolver);
78
+ yield* streamArray(tree, value, mergedResolver, path, rawResolver, currentDepth, diagnostics);
61
79
  return;
62
80
  }
63
81
  if (type === "record") {
64
- yield* streamRecord(tree, value, mergedResolver, path, rawResolver);
82
+ yield* streamRecord(tree, value, mergedResolver, path, rawResolver, currentDepth, diagnostics);
65
83
  return;
66
84
  }
67
85
  yield renderLeaf(tree, value, mergedResolver, path);
68
86
  }
69
- function* streamObject(tree, value, mergedResolver, path, rawResolver) {
87
+ function* streamObject(tree, value, mergedResolver, path, rawResolver, currentDepth, diagnostics) {
70
88
  if (tree.type !== "object") return;
71
89
  const fields = tree.fields;
90
+ if (value !== void 0 && value !== null && !isObject(value)) {
91
+ emitDiagnostic(diagnostics, {
92
+ code: "type-mismatch",
93
+ message: "Object schema received non-object value during streaming render",
94
+ pointer: path === "" ? "/" : `/${path}`,
95
+ detail: {
96
+ expected: "object",
97
+ actualType: typeof value,
98
+ path
99
+ }
100
+ });
101
+ yield typeMismatchPlaceholder("object");
102
+ return;
103
+ }
72
104
  const obj = isObject(value) ? value : {};
73
105
  const readOnly = tree.editability === "presentation";
74
106
  const descriptionText = typeof tree.meta.description === "string" ? tree.meta.description : void 0;
75
107
  const labelAttrs = ariaLabelAttrs(descriptionText);
76
108
  if (readOnly) {
77
- const dlAttrs = { class: "sc-object" };
109
+ const dlAttrs = { class: SC_CLASSES.object };
78
110
  Object.assign(dlAttrs, labelAttrs);
79
111
  const dl = h("dl", dlAttrs);
80
112
  const legend = descriptionText !== void 0 ? serialize(h("legend", {}, descriptionText)) : "";
@@ -82,12 +114,12 @@ function* streamObject(tree, value, mergedResolver, path, rawResolver) {
82
114
  for (const [key, field] of Object.entries(fields)) {
83
115
  const label = typeof field.meta.description === "string" ? field.meta.description : key;
84
116
  const childValue = obj[key];
85
- const childHtml = renderFieldSync(field, childValue, mergedResolver, key, rawResolver);
86
- yield `${serialize(h("dt", { class: "sc-label" }, label))}${serialize(h("dd", { class: "sc-value" }, raw(childHtml)))}`;
117
+ const childHtml = renderFieldSync(field, childValue, mergedResolver, joinPath(path, key), rawResolver, currentDepth + 1, diagnostics);
118
+ yield `${serialize(h("dt", { class: SC_CLASSES.label }, label))}${serialize(h("dd", { class: SC_CLASSES.value }, raw(childHtml)))}`;
87
119
  }
88
120
  yield yieldClose(dl);
89
121
  } else {
90
- const fieldsetAttrs = { class: "sc-object" };
122
+ const fieldsetAttrs = { class: SC_CLASSES.object };
91
123
  Object.assign(fieldsetAttrs, labelAttrs);
92
124
  const fieldset = h("fieldset", fieldsetAttrs);
93
125
  const legend = descriptionText !== void 0 ? serialize(h("legend", {}, descriptionText)) : "";
@@ -96,137 +128,145 @@ function* streamObject(tree, value, mergedResolver, path, rawResolver) {
96
128
  const label = typeof field.meta.description === "string" ? field.meta.description : key;
97
129
  const fieldId = buildInputId(path, key);
98
130
  const childValue = obj[key];
99
- const childChunks = [...streamField(field, childValue, mergedResolver, key, rawResolver)].join("");
131
+ const childChunks = [...streamField(field, childValue, mergedResolver, joinPath(path, key), rawResolver, currentDepth + 1, diagnostics)].join("");
100
132
  const required = requiredIndicator(field);
101
133
  const labelContent = [label];
102
134
  if (required !== void 0) labelContent.push(required);
103
135
  const fieldChildren = [h("label", {
104
- class: "sc-label",
136
+ class: SC_CLASSES.label,
105
137
  for: fieldId
106
138
  }, ...labelContent), raw(childChunks)];
107
139
  const hint = buildHintElement(key, field.constraints);
108
140
  if (hint !== void 0) fieldChildren.push(hint);
109
- yield serialize(h("div", { class: "sc-field" }, ...fieldChildren));
141
+ yield serialize(h("div", { class: SC_CLASSES.field }, ...fieldChildren));
110
142
  }
111
143
  yield yieldClose(fieldset);
112
144
  }
113
145
  }
114
- function* streamArray(tree, value, mergedResolver, path, rawResolver) {
115
- const arr = Array.isArray(value) ? value : [];
146
+ function* streamArray(tree, value, mergedResolver, path, rawResolver, currentDepth, diagnostics) {
116
147
  if (tree.type !== "array") return;
117
148
  const element = tree.element;
118
149
  if (element === void 0) return;
119
- const readOnly = tree.editability === "presentation";
120
- const elementPath = typeof element.meta.description === "string" ? element.meta.description : "";
121
- if (readOnly) {
122
- const ul = h("ul", { class: "sc-array" });
150
+ if (value !== void 0 && value !== null && !Array.isArray(value)) {
151
+ emitDiagnostic(diagnostics, {
152
+ code: "type-mismatch",
153
+ message: "Array schema received non-array value during streaming render",
154
+ pointer: path === "" ? "/" : `/${path}`,
155
+ detail: {
156
+ expected: "array",
157
+ actualType: typeof value,
158
+ path
159
+ }
160
+ });
161
+ yield typeMismatchPlaceholder("array");
162
+ return;
163
+ }
164
+ const arr = Array.isArray(value) ? value : [];
165
+ if (tree.editability === "presentation") {
166
+ const ul = h("ul", { class: SC_CLASSES.array });
123
167
  yield yieldOpen(ul);
124
- for (const item of arr) yield serialize(h("li", { class: "sc-item" }, raw(renderFieldSync(element, item, mergedResolver, elementPath, rawResolver))));
168
+ for (const [i, item] of arr.entries()) yield serialize(h("li", { class: "sc-item" }, raw(renderFieldSync(element, item, mergedResolver, joinPath(path, `[${String(i)}]`), rawResolver, currentDepth + 1, diagnostics))));
125
169
  yield yieldClose(ul);
126
170
  } else {
127
- const div = h("div", { class: "sc-array" });
171
+ const div = h("div", { class: SC_CLASSES.array });
128
172
  yield yieldOpen(div);
129
- for (const item of arr) yield serialize(h("div", {}, raw(renderFieldSync(element, item, mergedResolver, elementPath, rawResolver))));
173
+ for (const [i, item] of arr.entries()) yield serialize(h("div", {}, raw(renderFieldSync(element, item, mergedResolver, joinPath(path, `[${String(i)}]`), rawResolver, currentDepth + 1, diagnostics))));
130
174
  yield yieldClose(div);
131
175
  }
132
176
  }
133
- function* streamRecord(tree, value, mergedResolver, path, rawResolver) {
134
- const obj = isObject(value) ? value : {};
177
+ function* streamRecord(tree, value, mergedResolver, path, rawResolver, currentDepth, diagnostics) {
135
178
  if (tree.type !== "record") return;
136
179
  const valueType = tree.valueType;
180
+ if (value !== void 0 && value !== null && !isObject(value)) {
181
+ emitDiagnostic(diagnostics, {
182
+ code: "type-mismatch",
183
+ message: "Record schema received non-object value during streaming render",
184
+ pointer: path === "" ? "/" : `/${path}`,
185
+ detail: {
186
+ expected: "object",
187
+ actualType: typeof value,
188
+ path
189
+ }
190
+ });
191
+ yield typeMismatchPlaceholder("object");
192
+ return;
193
+ }
194
+ const obj = isObject(value) ? value : {};
137
195
  const readOnly = tree.editability === "presentation";
138
196
  const attrs = {
139
- class: "sc-record",
197
+ class: SC_CLASSES.record,
140
198
  role: "group"
141
199
  };
142
200
  if (readOnly) {
143
201
  const dl = h("dl", attrs);
144
202
  yield yieldOpen(dl);
145
203
  for (const [key, val] of Object.entries(obj)) {
146
- const childHtml = renderFieldSync(valueType, val, mergedResolver, key, rawResolver);
147
- yield `${serialize(h("dt", { class: "sc-label" }, key))}${serialize(h("dd", { class: "sc-value" }, raw(childHtml)))}`;
204
+ const childHtml = renderFieldSync(valueType, val, mergedResolver, joinPath(path, key), rawResolver, currentDepth + 1, diagnostics);
205
+ yield `${serialize(h("dt", { class: SC_CLASSES.label }, key))}${serialize(h("dd", { class: SC_CLASSES.value }, raw(childHtml)))}`;
148
206
  }
149
207
  yield yieldClose(dl);
150
208
  } else {
151
209
  const container = h("div", attrs);
152
210
  yield yieldOpen(container);
153
211
  for (const [key, val] of Object.entries(obj)) {
154
- const childHtml = renderFieldSync(valueType, val, mergedResolver, key, rawResolver);
155
- yield serialize(h("div", { class: "sc-field" }, h("label", { class: "sc-label" }, key), raw(childHtml)));
212
+ const childHtml = renderFieldSync(valueType, val, mergedResolver, joinPath(path, key), rawResolver, currentDepth + 1, diagnostics);
213
+ yield serialize(h("div", { class: SC_CLASSES.field }, h("label", { class: SC_CLASSES.label }, key), raw(childHtml)));
156
214
  }
157
215
  yield yieldClose(container);
158
216
  }
159
217
  }
160
- function* streamUnion(tree, value, mergedResolver, path, rawResolver) {
218
+ function* streamUnion(tree, value, mergedResolver, path, rawResolver, currentDepth, diagnostics) {
161
219
  const options = tree.type === "union" ? tree.options : void 0;
162
220
  if (options === void 0 || options.length === 0) {
163
- if (value === void 0 || value === null) yield serialize(h("span", { class: "sc-value sc-value--empty" }, "—"));
164
- else yield serialize(h("span", { class: "sc-value" }, JSON.stringify(value)));
221
+ if (value === void 0 || value === null) yield serialize(h("span", { class: SC_CLASSES.valueEmpty }, "—"));
222
+ else yield serialize(h("span", { class: SC_CLASSES.value }, JSON.stringify(value)));
165
223
  return;
166
224
  }
167
225
  const target = matchUnionOption(options, value) ?? options[0];
168
- if (target !== void 0) yield* streamField(target, value, mergedResolver, typeof target.meta.description === "string" ? target.meta.description : "", rawResolver);
169
- else yield serialize(h("span", { class: "sc-value sc-value--empty" }, "—"));
226
+ if (target !== void 0) yield* streamField(target, value, mergedResolver, path, rawResolver, currentDepth + 1, diagnostics);
227
+ else yield serialize(h("span", { class: SC_CLASSES.valueEmpty }, "—"));
170
228
  }
171
- function* streamDiscriminatedUnion(tree, value, mergedResolver, path, rawResolver) {
172
- const options = tree.type === "discriminatedUnion" ? tree.options : void 0;
173
- const discriminator = tree.type === "discriminatedUnion" ? tree.discriminator : void 0;
174
- if (options === void 0 || options.length === 0) {
175
- if (value === void 0 || value === null) yield serialize(h("span", { class: "sc-value sc-value--empty" }, "—"));
176
- else yield serialize(h("span", { class: "sc-value" }, JSON.stringify(value)));
229
+ function* streamDiscriminatedUnion(tree, value, mergedResolver, path, rawResolver, currentDepth, diagnostics) {
230
+ if (tree.type !== "discriminatedUnion") return;
231
+ const { options, discriminator } = tree;
232
+ if (options.length === 0) {
233
+ if (value === void 0 || value === null) yield serialize(h("span", { class: SC_CLASSES.valueEmpty }, "—"));
234
+ else yield serialize(h("span", { class: SC_CLASSES.value }, JSON.stringify(value)));
177
235
  return;
178
236
  }
179
- const isRecord = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
180
- const obj = isRecord(value) ? value : {};
181
- const discKey = discriminator ?? "";
182
- const currentDiscriminatorValue = typeof obj[discKey] === "string" ? obj[discKey] : void 0;
183
- const optionLabels = options.map((opt) => {
184
- if (opt.type === "object") {
185
- const discriminatorField = opt.fields[discKey];
186
- if (discriminatorField?.type === "literal") {
187
- const constVal = discriminatorField.literalValues[0];
188
- if (typeof constVal === "string") return constVal;
189
- }
190
- }
191
- return typeof opt.meta.title === "string" ? opt.meta.title : opt.type;
192
- });
193
- let activeIndex = 0;
194
- if (currentDiscriminatorValue !== void 0) {
195
- const found = optionLabels.indexOf(currentDiscriminatorValue);
196
- if (found !== -1) activeIndex = found;
197
- }
198
- const activeOption = options[activeIndex];
237
+ const { optionLabels, activeIndex, activeOption } = resolveDiscriminatedActive(options, discriminator, isObject(value) ? value : void 0);
199
238
  if (tree.editability === "presentation") {
200
- if (activeOption !== void 0) yield* streamField(activeOption, value, mergedResolver, typeof activeOption.meta.description === "string" ? activeOption.meta.description : "", rawResolver);
239
+ if (activeOption !== void 0) yield* streamField(activeOption, value, mergedResolver, path, rawResolver, currentDepth + 1, diagnostics);
201
240
  return;
202
241
  }
203
- const panelId = `sc-${path}-panel`;
204
- const wrapper = h("div", { class: "sc-discriminated-union" });
242
+ const tabPanelId = panelId(path);
243
+ const wrapper = h("div", { class: SC_CLASSES.discriminatedUnion });
205
244
  yield yieldOpen(wrapper);
206
- yield serialize(h("div", {
207
- role: "tablist",
208
- class: "sc-tabs",
209
- "aria-label": "Select variant"
210
- }, ...options.map((_opt, i) => {
245
+ const tabButtons = options.map((_opt, i) => {
211
246
  return h("button", {
212
247
  type: "button",
213
248
  role: "tab",
214
- class: i === activeIndex ? "sc-tab sc-tab--active" : "sc-tab",
215
- id: `sc-${path}-tab-${String(i)}`,
249
+ class: i === activeIndex ? SC_CLASSES.tabActive : SC_CLASSES.tab,
250
+ id: tabId(path, i),
216
251
  "aria-selected": i === activeIndex ? "true" : void 0,
217
- "aria-controls": panelId,
252
+ "aria-controls": tabPanelId,
218
253
  tabindex: i === activeIndex ? "0" : "-1"
219
254
  }, optionLabels[i]);
220
- })));
255
+ });
256
+ yield serialize(h("div", {
257
+ role: "tablist",
258
+ class: SC_CLASSES.tabs,
259
+ "aria-label": "Select variant"
260
+ }, ...tabButtons));
221
261
  const panelOpen = h("div", {
222
262
  role: "tabpanel",
223
- id: panelId,
224
- "aria-labelledby": `sc-${path}-tab-${String(activeIndex)}`
263
+ id: tabPanelId,
264
+ "aria-labelledby": tabId(path, activeIndex)
225
265
  });
226
266
  yield yieldOpen(panelOpen);
227
- if (activeOption !== void 0) yield* streamField(activeOption, value, mergedResolver, typeof activeOption.meta.description === "string" ? activeOption.meta.description : "", rawResolver);
267
+ if (activeOption !== void 0) yield* streamField(activeOption, value, mergedResolver, path, rawResolver, currentDepth + 1, diagnostics);
228
268
  yield yieldClose(panelOpen);
229
269
  yield yieldClose(wrapper);
230
270
  }
231
271
  //#endregion
232
- export { matchUnionOption, renderFieldSync, renderLeaf, streamField, yieldClose, yieldOpen };
272
+ export { renderFieldSync, renderLeaf, streamField, yieldClose, yieldOpen };
@@ -0,0 +1,29 @@
1
+ //#region src/core/limits.d.ts
2
+ /**
3
+ * Shared depth caps and hop counts used to bound recursion across
4
+ * schema-components. All numeric limits live here so the renderer, the
5
+ * ref resolver, the OpenAPI parser, and the type-level inference engine
6
+ * agree on the same constants.
7
+ */
8
+ /**
9
+ * Maximum recursion depth for the schema walker, the React renderers,
10
+ * the streaming HTML renderer, and the server-side renderer. Beyond
11
+ * this depth a recursion sentinel is emitted instead of further descent
12
+ * — the only safe response to a cyclic walked-field graph.
13
+ */
14
+ declare const MAX_RENDER_DEPTH = 10;
15
+ /**
16
+ * Maximum depth for `$ref` resolution and Zod-tree walks. Mirrors the
17
+ * type-level `DEFAULT_MAX_DEPTH` ({@link MaxRefDepth}) so the runtime
18
+ * and compile-time bounds agree.
19
+ */
20
+ type MaxRefDepth = 64;
21
+ declare const MAX_REF_DEPTH: MaxRefDepth;
22
+ /**
23
+ * Maximum number of `$ref` hops permitted when walking a chain of
24
+ * OpenAPI Path Item Object references. Beyond this a
25
+ * `path-item-ref-too-deep` diagnostic is emitted and resolution stops.
26
+ */
27
+ declare const MAX_PATH_ITEM_REF_HOPS = 8;
28
+ //#endregion
29
+ export { MaxRefDepth as i, MAX_REF_DEPTH as n, MAX_RENDER_DEPTH as r, MAX_PATH_ITEM_REF_HOPS as t };