schema-components 1.17.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.
Files changed (53) 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/fieldOrder.d.mts +10 -0
  5. package/dist/core/fieldOrder.mjs +12 -0
  6. package/dist/core/merge.d.mts +14 -8
  7. package/dist/core/merge.mjs +109 -12
  8. package/dist/core/normalise.d.mts +1 -1
  9. package/dist/core/ref.d.mts +1 -1
  10. package/dist/core/renderer.d.mts +2 -2
  11. package/dist/core/renderer.mjs +31 -1
  12. package/dist/core/swagger2.d.mts +1 -1
  13. package/dist/core/typeInference.d.mts +2 -2
  14. package/dist/core/walkBuilders.d.mts +2 -2
  15. package/dist/core/walker.mjs +2 -2
  16. package/dist/{diagnostics-DzbZmcLI.d.mts → diagnostics-BYk63jsC.d.mts} +1 -1
  17. package/dist/html/a11y.d.mts +13 -2
  18. package/dist/html/a11y.mjs +26 -2
  19. package/dist/html/renderToHtml.d.mts +1 -1
  20. package/dist/html/renderToHtml.mjs +18 -33
  21. package/dist/html/renderToHtmlStream.d.mts +1 -1
  22. package/dist/html/renderers.d.mts +4 -3
  23. package/dist/html/renderers.mjs +37 -36
  24. package/dist/html/streamRenderers.d.mts +1 -1
  25. package/dist/html/streamRenderers.mjs +10 -21
  26. package/dist/openapi/ApiSecurity.mjs +1 -1
  27. package/dist/openapi/bundle.d.mts +9 -4
  28. package/dist/openapi/bundle.mjs +74 -15
  29. package/dist/openapi/components.d.mts +1 -1
  30. package/dist/openapi/components.mjs +20 -15
  31. package/dist/openapi/parser.d.mts +2 -2
  32. package/dist/openapi/parser.mjs +12 -12
  33. package/dist/openapi/resolve.d.mts +13 -2
  34. package/dist/openapi/resolve.mjs +19 -3
  35. package/dist/react/SchemaComponent.d.mts +3 -3
  36. package/dist/react/SchemaComponent.mjs +1 -30
  37. package/dist/react/SchemaView.d.mts +4 -3
  38. package/dist/react/SchemaView.mjs +12 -43
  39. package/dist/react/headless.d.mts +1 -1
  40. package/dist/react/headlessRenderers.d.mts +1 -1
  41. package/dist/react/headlessRenderers.mjs +37 -32
  42. package/dist/{ref-DvWoULcy.d.mts → ref-Ckt5liZs.d.mts} +1 -1
  43. package/dist/{renderer-B3s8o2B8.d.mts → renderer-BAGoX4AK.d.mts} +20 -26
  44. package/dist/themes/mantine.d.mts +1 -1
  45. package/dist/themes/mantine.mjs +6 -4
  46. package/dist/themes/mui.d.mts +1 -1
  47. package/dist/themes/mui.mjs +7 -5
  48. package/dist/themes/radix.d.mts +1 -1
  49. package/dist/themes/radix.mjs +5 -4
  50. package/dist/themes/shadcn.d.mts +1 -1
  51. package/dist/themes/shadcn.mjs +8 -6
  52. package/dist/{typeInference-k7FXfTVO.d.mts → typeInference-5JiqIZ8t.d.mts} +57 -4
  53. package/package.json +1 -1
@@ -1,5 +1,5 @@
1
1
  import { T as SchemaMeta, m as JsonObject } from "../types-D_5ST7SS.mjs";
2
- import { i as DiagnosticsOptions } from "../diagnostics-DzbZmcLI.mjs";
2
+ import { i as DiagnosticsOptions } from "../diagnostics-BYk63jsC.mjs";
3
3
 
4
4
  //#region src/core/adapter.d.ts
5
5
  type SchemaInput = Record<string, unknown>;
@@ -1,5 +1,5 @@
1
1
  import { D as StringConstraints, f as FileConstraints, t as ArrayConstraints, x as ObjectConstraints, y as NumberConstraints } from "../types-D_5ST7SS.mjs";
2
- import { i as DiagnosticsOptions } from "../diagnostics-DzbZmcLI.mjs";
2
+ import { i as DiagnosticsOptions } from "../diagnostics-BYk63jsC.mjs";
3
3
 
4
4
  //#region src/core/constraints.d.ts
5
5
  declare function extractStringConstraints(schema: Record<string, unknown>, diagnostics?: DiagnosticsOptions, pointer?: string): StringConstraints;
@@ -1,2 +1,2 @@
1
- import { a as appendPointer, i as DiagnosticsOptions, n as DiagnosticCode, o as emitDiagnostic, r as DiagnosticSink, t as Diagnostic } from "../diagnostics-DzbZmcLI.mjs";
1
+ import { a as appendPointer, i as DiagnosticsOptions, n as DiagnosticCode, o as emitDiagnostic, r as DiagnosticSink, t as Diagnostic } from "../diagnostics-BYk63jsC.mjs";
2
2
  export { Diagnostic, DiagnosticCode, DiagnosticSink, DiagnosticsOptions, appendPointer, emitDiagnostic };
@@ -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 };
@@ -1,10 +1,6 @@
1
+ import { i as DiagnosticsOptions } from "../diagnostics-BYk63jsC.mjs";
2
+
1
3
  //#region src/core/merge.d.ts
2
- /**
3
- * Schema merging, nullable detection, and discriminated union detection.
4
- *
5
- * Used by the walker to handle `allOf`, `anyOf [T, null]`, and
6
- * `oneOf` with discriminator properties.
7
- */
8
4
  /**
9
5
  * Annotation keywords that can appear as siblings of `$ref` per
10
6
  * Draft 2020-12 / OpenAPI 3.1. Structural keywords (type, properties,
@@ -24,8 +20,13 @@ declare function mergeRefSiblings(referencer: Record<string, unknown>, resolvedM
24
20
  /**
25
21
  * Merge multiple JSON Schema objects from allOf into one.
26
22
  * Merges: properties, required, meta fields, and constraints.
23
+ *
24
+ * Semantics are first-write-wins for meta and constraint keywords.
25
+ * When a later branch redefines a keyword with a non-equal value the
26
+ * later value is silently dropped — an `allof-conflict` diagnostic is
27
+ * emitted so the loss is visible to consumers.
27
28
  */
28
- declare function mergeAllOf(schemas: unknown[]): Record<string, unknown>;
29
+ declare function mergeAllOf(schemas: unknown[], diagnostics?: DiagnosticsOptions, pointer?: string): Record<string, unknown>;
29
30
  interface NormalisedAnyOf {
30
31
  inner: Record<string, unknown>;
31
32
  isNullable: boolean;
@@ -42,7 +43,12 @@ interface Discriminated {
42
43
  /**
43
44
  * Detect oneOf where every option is an object with a property
44
45
  * that has a `const` value → discriminated union.
46
+ *
47
+ * When options carry inconsistent discriminator candidates (e.g. one
48
+ * uses `kind` while another uses `type`) detection fails and a
49
+ * `discriminator-inconsistent` diagnostic is emitted so callers can
50
+ * see why the union falls back to a generic oneOf.
45
51
  */
46
- declare function detectDiscriminated(options: unknown[]): Discriminated | undefined;
52
+ declare function detectDiscriminated(options: unknown[], diagnostics?: DiagnosticsOptions, pointer?: string): Discriminated | undefined;
47
53
  //#endregion
48
54
  export { ANNOTATION_SIBLINGS, Discriminated, NormalisedAnyOf, detectDiscriminated, mergeAllOf, mergeRefSiblings, normaliseAnyOf };
@@ -1,4 +1,5 @@
1
1
  import { isObject } from "./guards.mjs";
2
+ import { emitDiagnostic } from "./diagnostics.mjs";
2
3
  //#region src/core/merge.ts
3
4
  /**
4
5
  * Schema merging, nullable detection, and discriminated union detection.
@@ -19,6 +20,59 @@ function getObject(obj, key) {
19
20
  return isObject(value) ? value : void 0;
20
21
  }
21
22
  /**
23
+ * Structural equality for arbitrary JSON-like values. Used to decide
24
+ * whether a duplicated keyword across `allOf` branches genuinely
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.
34
+ */
35
+ function deepEqual(a, b) {
36
+ return deepEqualInner(a, b, /* @__PURE__ */ new WeakMap());
37
+ }
38
+ function deepEqualInner(a, b, seen) {
39
+ if (a === b) return true;
40
+ if (typeof a !== typeof b) return false;
41
+ if (Array.isArray(a)) {
42
+ if (!Array.isArray(b) || a.length !== b.length) 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;
46
+ return true;
47
+ }
48
+ if (isObject(a) && isObject(b)) {
49
+ const keysA = Object.keys(a);
50
+ const keysB = Object.keys(b);
51
+ if (keysA.length !== keysB.length) return false;
52
+ if (hasSeenPair(seen, a, b)) return true;
53
+ recordPair(seen, a, b);
54
+ for (const key of keysA) {
55
+ if (!Object.prototype.hasOwnProperty.call(b, key)) return false;
56
+ if (!deepEqualInner(a[key], b[key], seen)) return false;
57
+ }
58
+ return true;
59
+ }
60
+ return false;
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
+ }
75
+ /**
22
76
  * Annotation keywords that can appear as siblings of `$ref` per
23
77
  * Draft 2020-12 / OpenAPI 3.1. Structural keywords (type, properties,
24
78
  * etc.) are NOT annotation siblings and should not be merged.
@@ -50,8 +104,13 @@ function mergeRefSiblings(referencer, resolvedMeta) {
50
104
  /**
51
105
  * Merge multiple JSON Schema objects from allOf into one.
52
106
  * Merges: properties, required, meta fields, and constraints.
107
+ *
108
+ * Semantics are first-write-wins for meta and constraint keywords.
109
+ * When a later branch redefines a keyword with a non-equal value the
110
+ * later value is silently dropped — an `allof-conflict` diagnostic is
111
+ * emitted so the loss is visible to consumers.
53
112
  */
54
- function mergeAllOf(schemas) {
113
+ function mergeAllOf(schemas, diagnostics, pointer = "") {
55
114
  const merged = {};
56
115
  const properties = {};
57
116
  const required = [];
@@ -66,10 +125,30 @@ function mergeAllOf(schemas) {
66
125
  for (const [key, value] of Object.entries(entry)) {
67
126
  if (key === "properties" || key === "required" || key === "allOf" || key === "type") continue;
68
127
  if (!(key in merged)) merged[key] = value;
128
+ else if (!deepEqual(merged[key], value)) emitDiagnostic(diagnostics, {
129
+ code: "allof-conflict",
130
+ message: `allOf branches define conflicting values for "${key}"; keeping the first occurrence and discarding subsequent values`,
131
+ pointer,
132
+ detail: {
133
+ key,
134
+ kept: merged[key],
135
+ discarded: value
136
+ }
137
+ });
69
138
  }
70
- if (!("type" in merged)) {
71
- const type = getString(entry, "type");
72
- if (type !== void 0) merged.type = type;
139
+ const entryType = getString(entry, "type");
140
+ if (entryType !== void 0) {
141
+ if (!("type" in merged)) merged.type = entryType;
142
+ else if (!deepEqual(merged.type, entryType)) emitDiagnostic(diagnostics, {
143
+ code: "allof-conflict",
144
+ message: `allOf branches define conflicting values for "type"; keeping the first occurrence and discarding subsequent values`,
145
+ pointer,
146
+ detail: {
147
+ key: "type",
148
+ kept: merged.type,
149
+ discarded: entryType
150
+ }
151
+ });
73
152
  }
74
153
  }
75
154
  if (Object.keys(properties).length > 0) merged.properties = properties;
@@ -98,22 +177,40 @@ function normaliseAnyOf(options) {
98
177
  /**
99
178
  * Detect oneOf where every option is an object with a property
100
179
  * that has a `const` value → discriminated union.
180
+ *
181
+ * When options carry inconsistent discriminator candidates (e.g. one
182
+ * uses `kind` while another uses `type`) detection fails and a
183
+ * `discriminator-inconsistent` diagnostic is emitted so callers can
184
+ * see why the union falls back to a generic oneOf.
101
185
  */
102
- function detectDiscriminated(options) {
186
+ function detectDiscriminated(options, diagnostics, pointer = "") {
103
187
  if (options.length === 0) return void 0;
104
188
  let discriminator;
189
+ const perOptionKeys = [];
105
190
  for (const opt of options) {
106
191
  if (!isObject(opt)) return void 0;
107
192
  const props = getObject(opt, "properties");
108
193
  if (props === void 0) return void 0;
109
- let foundKey;
110
- for (const [key, value] of Object.entries(props)) if (isObject(value) && "const" in value) {
111
- foundKey = key;
112
- break;
194
+ const constKeys = [];
195
+ for (const [key, value] of Object.entries(props)) if (isObject(value) && "const" in value) constKeys.push(key);
196
+ if (constKeys.length === 0) {
197
+ perOptionKeys.push(void 0);
198
+ continue;
113
199
  }
114
- if (foundKey === void 0) return void 0;
115
- if (discriminator === void 0) discriminator = foundKey;
116
- else if (discriminator !== foundKey) return;
200
+ const foundKey = constKeys[0];
201
+ perOptionKeys.push(foundKey);
202
+ discriminator ??= foundKey;
203
+ }
204
+ if (perOptionKeys.some((k) => k === void 0)) return void 0;
205
+ const uniqueKeys = new Set(perOptionKeys);
206
+ if (uniqueKeys.size > 1) {
207
+ emitDiagnostic(diagnostics, {
208
+ code: "discriminator-inconsistent",
209
+ message: `oneOf options use inconsistent discriminator keys (${[...uniqueKeys].map((k) => `"${k ?? ""}"`).join(", ")}); rendering as a generic union`,
210
+ pointer,
211
+ detail: { candidates: perOptionKeys }
212
+ });
213
+ return;
117
214
  }
118
215
  if (discriminator === void 0) return void 0;
119
216
  return {
@@ -1,4 +1,4 @@
1
- import { i as DiagnosticsOptions } from "../diagnostics-DzbZmcLI.mjs";
1
+ import { i as DiagnosticsOptions } from "../diagnostics-BYk63jsC.mjs";
2
2
  import { n as JsonSchemaDraft, r as OpenApiVersionInfo } from "../version-B5NV-35j.mjs";
3
3
 
4
4
  //#region src/core/normalise.d.ts
@@ -1,2 +1,2 @@
1
- import { a as findAnchor, i as dereference, n as RefOptions, o as resolveRef, r as countDistinctRefs, t as ExternalResolver } from "../ref-DvWoULcy.mjs";
1
+ import { a as findAnchor, i as dereference, n as RefOptions, o as resolveRef, r as countDistinctRefs, t as ExternalResolver } from "../ref-Ckt5liZs.mjs";
2
2
  export { ExternalResolver, RefOptions, countDistinctRefs, dereference, findAnchor, resolveRef };
@@ -1,2 +1,2 @@
1
- import { a as HtmlRenderProps, c as RenderFunction, d as getRenderFunction, f as mergeHtmlResolvers, i as HtmlRenderFunction, l as RenderProps, m as typeToKey, n as BaseFieldProps, o as HtmlResolver, p as mergeResolvers, r as ComponentResolver, s as RESOLVER_KEYS, t as AllConstraints, u as getHtmlRenderFn } from "../renderer-B3s8o2B8.mjs";
2
- export { AllConstraints, BaseFieldProps, ComponentResolver, HtmlRenderFunction, HtmlRenderProps, HtmlResolver, RESOLVER_KEYS, RenderFunction, RenderProps, getHtmlRenderFn, getRenderFunction, mergeHtmlResolvers, mergeResolvers, typeToKey };
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
+ export { AllConstraints, BaseFieldProps, ComponentResolver, HtmlRenderFunction, HtmlRenderProps, HtmlResolver, RESOLVER_KEYS, RenderFunction, RenderProps, buildRenderProps, getHtmlRenderFn, getRenderFunction, mergeHtmlResolvers, mergeResolvers, typeToKey };
@@ -1,4 +1,34 @@
1
1
  //#region src/core/renderer.ts
2
+ /** No-op onChange used when callers render in read-only mode. */
3
+ function noopOnChange() {}
4
+ /**
5
+ * Build the `RenderProps` object handed to a resolver render function or a
6
+ * widget. Used by both the server-side `<SchemaView>` (which has no
7
+ * `onChange`) and the client-side `<SchemaComponent>` (which threads an
8
+ * `onChange` callback).
9
+ *
10
+ * When `onChange` is `undefined` the caller is rendering in read-only mode:
11
+ * a noop `onChange` is wired up, `readOnly` is forced to `true`, and
12
+ * `writeOnly` is forced to `false`. Otherwise the editability is taken
13
+ * from `tree.editability`.
14
+ */
15
+ function buildRenderProps(tree, value, onChange, renderChild, path) {
16
+ const isReadOnly = onChange === void 0 || tree.editability === "presentation";
17
+ const isWriteOnly = onChange !== void 0 && tree.editability === "input";
18
+ const props = {
19
+ value,
20
+ onChange: onChange ?? noopOnChange,
21
+ readOnly: isReadOnly,
22
+ writeOnly: isWriteOnly,
23
+ meta: tree.meta,
24
+ constraints: tree.constraints,
25
+ path,
26
+ tree,
27
+ renderChild
28
+ };
29
+ if (tree.examples !== void 0) props.examples = tree.examples;
30
+ return props;
31
+ }
2
32
  const RESOLVER_KEYS = [
3
33
  "string",
4
34
  "number",
@@ -77,4 +107,4 @@ function mergeHtmlResolvers(user, fallback) {
77
107
  return merged;
78
108
  }
79
109
  //#endregion
80
- export { RESOLVER_KEYS, getHtmlRenderFn, getRenderFunction, mergeHtmlResolvers, mergeResolvers, typeToKey };
110
+ export { RESOLVER_KEYS, buildRenderProps, getHtmlRenderFn, getRenderFunction, mergeHtmlResolvers, mergeResolvers, typeToKey };
@@ -1,4 +1,4 @@
1
- import { i as DiagnosticsOptions } from "../diagnostics-DzbZmcLI.mjs";
1
+ import { i as DiagnosticsOptions } from "../diagnostics-BYk63jsC.mjs";
2
2
  import { NodeTransform } from "./normalise.mjs";
3
3
 
4
4
  //#region src/core/swagger2.d.ts
@@ -1,2 +1,2 @@
1
- import { a as OpenAPIRequestBodyType, c as ResolveOpenAPIRef, d as __SchemaInferenceFellBack, i as InferResponseFields, l as TypeAtPath, n as InferParameterOverrides, o as OpenAPIResponseType, r as InferRequestBodyFields, s as PathOfType, t as FromJSONSchema, u as UnsafeFields } from "../typeInference-k7FXfTVO.mjs";
2
- export { FromJSONSchema, InferParameterOverrides, InferRequestBodyFields, InferResponseFields, OpenAPIRequestBodyType, OpenAPIResponseType, PathOfType, ResolveOpenAPIRef, TypeAtPath, UnsafeFields, __SchemaInferenceFellBack };
1
+ import { a as InferResponseFields, c as PathOfType, d as UnsafeFields, f as __SchemaInferenceFellBack, i as InferRequestBodyFields, l as ResolveOpenAPIRef, n as FromJSONSchema, o as OpenAPIRequestBodyType, r as InferParameterOverrides, s as OpenAPIResponseType, t as DEFAULT_MAX_DEPTH, u as TypeAtPath } from "../typeInference-5JiqIZ8t.mjs";
2
+ export { DEFAULT_MAX_DEPTH, FromJSONSchema, InferParameterOverrides, InferRequestBodyFields, InferResponseFields, OpenAPIRequestBodyType, OpenAPIResponseType, PathOfType, ResolveOpenAPIRef, TypeAtPath, UnsafeFields, __SchemaInferenceFellBack };
@@ -1,6 +1,6 @@
1
1
  import { M as WalkedField, O as StringField, T as SchemaMeta, b as NumberField, c as FieldBase, j as UnknownField, o as Editability, p as FileField, r as BooleanField, v as NullField } from "../types-D_5ST7SS.mjs";
2
- import { i as DiagnosticsOptions } from "../diagnostics-DzbZmcLI.mjs";
3
- import { t as ExternalResolver } from "../ref-DvWoULcy.mjs";
2
+ import { i as DiagnosticsOptions } from "../diagnostics-BYk63jsC.mjs";
3
+ import { t as ExternalResolver } from "../ref-Ckt5liZs.mjs";
4
4
 
5
5
  //#region src/core/walkBuilders.d.ts
6
6
  declare function getString(obj: Record<string, unknown>, key: string): string | undefined;
@@ -74,7 +74,7 @@ function walk(schema, options = {}) {
74
74
  }
75
75
  function walkNode(schema, ctx) {
76
76
  const allOf = getArray(schema, "allOf");
77
- if (allOf !== void 0 && allOf.length > 0) return walkNode(mergeAllOf(allOf), ctx);
77
+ if (allOf !== void 0 && allOf.length > 0) return walkNode(mergeAllOf(allOf, ctx.diagnostics, ctx.pointer), ctx);
78
78
  const anyOf = getArray(schema, "anyOf");
79
79
  if (anyOf !== void 0) {
80
80
  const nullable = normaliseAnyOf(anyOf);
@@ -86,7 +86,7 @@ function walkNode(schema, ctx) {
86
86
  }
87
87
  const oneOf = getArray(schema, "oneOf");
88
88
  if (oneOf !== void 0) {
89
- const discriminated = detectDiscriminated(oneOf);
89
+ const discriminated = detectDiscriminated(oneOf, ctx.diagnostics, ctx.pointer);
90
90
  if (discriminated !== void 0) return walkDiscriminatedUnion(discriminated, ctx);
91
91
  return walkUnion(oneOf, ctx);
92
92
  }
@@ -16,7 +16,7 @@
16
16
  * Machine-readable codes identifying each class of diagnostic.
17
17
  * Stable across releases — consumers can pattern-match on these.
18
18
  */
19
- type DiagnosticCode = "unresolved-ref" | "unknown-keyword" | "unknown-format" | "invalid-const" | "unsupported-type" | "dropped-swagger-feature" | "external-ref" | "type-negation-fallback" | "conditional-fallback" | "assumed-draft" | "depth-exceeded";
19
+ type DiagnosticCode = "unresolved-ref" | "unknown-keyword" | "unknown-format" | "invalid-const" | "unsupported-type" | "dropped-swagger-feature" | "external-ref" | "type-negation-fallback" | "conditional-fallback" | "assumed-draft" | "depth-exceeded" | "allof-conflict" | "discriminator-inconsistent";
20
20
  /**
21
21
  * A single diagnostic emitted during schema processing.
22
22
  */
@@ -1,8 +1,19 @@
1
1
  import { M as WalkedField } from "../types-D_5ST7SS.mjs";
2
- import { t as AllConstraints } from "../renderer-B3s8o2B8.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
6
+ /**
7
+ * Append a structural suffix to a parent path. Mirrors the canonical
8
+ * `joinPath` used by the React renderers: when the suffix is omitted
9
+ * (e.g. transparent wrappers like union options) the parent path is
10
+ * returned unchanged so the child inherits the parent's id.
11
+ *
12
+ * Suffixes are dot-joined except for bracketed array indices like `[0]`
13
+ * which append directly so `tags` + `[0]` becomes `tags[0]` rather than
14
+ * `tags.[0]`.
15
+ */
16
+ declare function joinPath(parent: string, suffix: string | undefined): string;
6
17
  /**
7
18
  * Build the input ID for a field at a given path.
8
19
  */
@@ -45,4 +56,4 @@ declare function buildHintElement(inputId: string, constraints: AllConstraints):
45
56
  */
46
57
  declare function requiredIndicator(field: WalkedField): HtmlNode;
47
58
  //#endregion
48
- export { ariaDescribedByAttrs, ariaLabelAttrs, ariaReadonlyAttrs, ariaRequiredAttrs, buildHintElement, buildHintId, buildInputId, constraintHint, requiredIndicator };
59
+ export { ariaDescribedByAttrs, ariaLabelAttrs, ariaReadonlyAttrs, ariaRequiredAttrs, buildHintElement, buildHintId, buildInputId, constraintHint, joinPath, requiredIndicator };
@@ -1,10 +1,34 @@
1
1
  import { h } from "./html.mjs";
2
2
  //#region src/html/a11y.ts
3
3
  /**
4
+ * Append a structural suffix to a parent path. Mirrors the canonical
5
+ * `joinPath` used by the React renderers: when the suffix is omitted
6
+ * (e.g. transparent wrappers like union options) the parent path is
7
+ * returned unchanged so the child inherits the parent's id.
8
+ *
9
+ * Suffixes are dot-joined except for bracketed array indices like `[0]`
10
+ * which append directly so `tags` + `[0]` becomes `tags[0]` rather than
11
+ * `tags.[0]`.
12
+ */
13
+ function joinPath(parent, suffix) {
14
+ if (suffix === void 0 || suffix.length === 0) return parent;
15
+ if (parent.length === 0) return suffix;
16
+ if (suffix.startsWith("[")) return `${parent}${suffix}`;
17
+ return `${parent}.${suffix}`;
18
+ }
19
+ /**
20
+ * Normalise a path into the id segment used after the `sc-` prefix.
21
+ * Dots (object nesting) and brackets (array indices) become hyphens so
22
+ * the id remains a valid CSS selector and matches test query semantics.
23
+ */
24
+ function normaliseIdSegment(value) {
25
+ return value.replace(/[.[\]]+/g, "-").replace(/-+$/g, "");
26
+ }
27
+ /**
4
28
  * Build the input ID for a field at a given path.
5
29
  */
6
30
  function buildInputId(path, key) {
7
- return `sc-${path ? `${path}-${key}` : key}`;
31
+ return `sc-${normaliseIdSegment(joinPath(path, key))}`;
8
32
  }
9
33
  /**
10
34
  * Derive the hint element ID from the input ID.
@@ -78,4 +102,4 @@ function requiredIndicator(field) {
78
102
  }, " *");
79
103
  }
80
104
  //#endregion
81
- export { ariaDescribedByAttrs, ariaLabelAttrs, ariaReadonlyAttrs, ariaRequiredAttrs, buildHintElement, buildHintId, buildInputId, constraintHint, requiredIndicator };
105
+ export { ariaDescribedByAttrs, ariaLabelAttrs, ariaReadonlyAttrs, ariaRequiredAttrs, buildHintElement, buildHintId, buildInputId, constraintHint, joinPath, requiredIndicator };
@@ -1,5 +1,5 @@
1
1
  import { T as SchemaMeta } from "../types-D_5ST7SS.mjs";
2
- import { o as HtmlResolver } from "../renderer-B3s8o2B8.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,7 @@
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";
4
+ import { joinPath } from "./a11y.mjs";
5
5
  import { defaultHtmlResolver } from "./renderers.mjs";
6
6
  //#region src/html/renderToHtml.ts
7
7
  /**
@@ -45,11 +45,12 @@ function renderToHtml(schema, options = {}) {
45
45
  });
46
46
  const resolver = options.resolver ?? defaultHtmlResolver;
47
47
  const MAX_HTML_DEPTH = 10;
48
- const makeRenderChild = (currentDepth) => (childTree, childValue, pathSuffix) => {
48
+ const makeRenderChild = (currentDepth, parentPath) => (childTree, childValue, pathSuffix) => {
49
49
  if (currentDepth >= MAX_HTML_DEPTH) return `<fieldset class="sc-recursive"><em>\u21bb ${typeof childTree.meta.description === "string" ? childTree.meta.description : "schema"} (recursive)</em></fieldset>`;
50
- return renderFieldHtml(childTree, childValue, resolver, pathSuffix ?? childTree.meta.description ?? "", makeRenderChild(currentDepth + 1));
50
+ const childPath = joinPath(parentPath, pathSuffix);
51
+ return renderFieldHtml(childTree, childValue, resolver, childPath, makeRenderChild(currentDepth + 1, childPath));
51
52
  };
52
- const renderChild = makeRenderChild(0);
53
+ const renderChild = makeRenderChild(0, "");
53
54
  return renderFieldHtml(tree, options.value ?? tree.defaultValue, resolver, "", renderChild);
54
55
  }
55
56
  function renderFieldHtml(tree, value, resolver, path, renderChild) {
@@ -57,35 +58,19 @@ function renderFieldHtml(tree, value, resolver, path, renderChild) {
57
58
  const effectiveValue = value ?? tree.defaultValue;
58
59
  const mergedResolver = mergeHtmlResolvers(resolver, defaultHtmlResolver);
59
60
  const renderFn = getHtmlRenderFn(tree.type, mergedResolver);
60
- if (renderFn !== void 0) {
61
- const props = {
62
- value: effectiveValue,
63
- readOnly: tree.editability === "presentation",
64
- writeOnly: tree.editability === "input",
65
- meta: tree.meta,
66
- constraints: tree.constraints,
67
- path,
68
- tree,
69
- renderChild
70
- };
71
- if (tree.type === "enum") props.enumValues = tree.enumValues;
72
- if (tree.type === "array" && tree.element !== void 0) props.element = tree.element;
73
- if (tree.type === "object") props.fields = tree.fields;
74
- if (tree.type === "union" || tree.type === "discriminatedUnion") props.options = tree.options;
75
- if (tree.type === "discriminatedUnion") props.discriminator = tree.discriminator;
76
- if (tree.type === "record") props.keyType = tree.keyType;
77
- if (tree.type === "record") props.valueType = tree.valueType;
78
- if (tree.type === "tuple") props.prefixItems = tree.prefixItems;
79
- if (tree.type === "conditional") props.ifClause = tree.ifClause;
80
- if (tree.type === "conditional" && tree.thenClause !== void 0) props.thenClause = tree.thenClause;
81
- if (tree.type === "conditional" && tree.elseClause !== void 0) props.elseClause = tree.elseClause;
82
- if (tree.type === "negation") props.negated = tree.negated;
83
- if (tree.type === "literal") props.literalValues = tree.literalValues;
84
- if (tree.examples !== void 0) props.examples = tree.examples;
85
- return renderFn(props);
86
- }
87
- if (effectiveValue === void 0 || effectiveValue === null) return serialize(h("span", { class: "sc-value sc-value--empty" }, "—"));
88
- 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);
89
74
  }
90
75
  //#endregion
91
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-B3s8o2B8.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,11 +1,12 @@
1
1
  import { M as WalkedField } from "../types-D_5ST7SS.mjs";
2
- import { o as HtmlResolver } from "../renderer-B3s8o2B8.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;
6
6
  /**
7
- * Normalise a dot-separated path into a valid, `sc-` prefixed HTML ID.
8
- * Dots in paths (from nested objects) become hyphens.
7
+ * Normalise a structural path into a valid, `sc-` prefixed HTML ID.
8
+ * Dots (object nesting) and brackets (array indices) become hyphens so
9
+ * the id remains a valid CSS selector and predictable in test queries.
9
10
  */
10
11
  declare function fieldId(path: string): string;
11
12
  declare function matchUnionOption(options: WalkedField[], value: unknown): WalkedField | undefined;