schema-components 1.29.0 → 2.0.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 (90) hide show
  1. package/README.md +38 -16
  2. package/dist/core/adapter.d.mts +213 -3
  3. package/dist/core/adapter.mjs +1 -1
  4. package/dist/core/constraintHint.d.mts +15 -0
  5. package/dist/core/constraintHint.mjs +24 -0
  6. package/dist/core/constraints.d.mts +2 -2
  7. package/dist/core/constraints.mjs +1 -1
  8. package/dist/core/diagnostics.d.mts +1 -1
  9. package/dist/core/errors.d.mts +1 -1
  10. package/dist/core/fieldOrder.d.mts +1 -1
  11. package/dist/core/formats.d.mts +1 -1
  12. package/dist/core/idPath.d.mts +35 -5
  13. package/dist/core/idPath.mjs +79 -7
  14. package/dist/core/inferValue.d.mts +2 -0
  15. package/dist/core/inferValue.mjs +1 -0
  16. package/dist/core/limits.d.mts +1 -1
  17. package/dist/core/limits.mjs +5 -0
  18. package/dist/core/merge.d.mts +12 -1
  19. package/dist/core/merge.mjs +66 -3
  20. package/dist/core/normalise.d.mts +1 -1
  21. package/dist/core/normalise.mjs +1 -1
  22. package/dist/core/openapi30.mjs +1 -1
  23. package/dist/core/ref.d.mts +1 -1
  24. package/dist/core/refChain.d.mts +3 -4
  25. package/dist/core/refChain.mjs +2 -3
  26. package/dist/core/renderer.d.mts +199 -2
  27. package/dist/core/swagger2.d.mts +1 -1
  28. package/dist/core/swagger2.mjs +1 -1
  29. package/dist/core/typeInference.d.mts +3 -3
  30. package/dist/core/types.d.mts +1 -1
  31. package/dist/core/unionMatch.d.mts +1 -1
  32. package/dist/core/uri.d.mts +12 -4
  33. package/dist/core/uri.mjs +30 -4
  34. package/dist/core/walkBuilders.d.mts +18 -6
  35. package/dist/core/walkBuilders.mjs +3 -1
  36. package/dist/core/walker.d.mts +1 -1
  37. package/dist/core/walker.mjs +5 -0
  38. package/dist/{diagnostics-ByEzkjrA.d.mts → diagnostics-BTrm3O6J.d.mts} +1 -1
  39. package/dist/{errors-D8JndRwI.d.mts → errors-Dki7tji4.d.mts} +1 -1
  40. package/dist/html/a11y.d.mts +3 -7
  41. package/dist/html/a11y.mjs +1 -16
  42. package/dist/html/renderToHtml.d.mts +22 -9
  43. package/dist/html/renderToHtml.mjs +2 -1
  44. package/dist/html/renderToHtmlStream.d.mts +24 -11
  45. package/dist/html/renderToHtmlStream.mjs +2 -1
  46. package/dist/html/renderers.d.mts +2 -33
  47. package/dist/html/renderers.mjs +39 -91
  48. package/dist/html/streamRenderers.d.mts +3 -3
  49. package/dist/html/streamRenderers.mjs +13 -8
  50. package/dist/inferValue-PPXWJpbN.d.mts +77 -0
  51. package/dist/{limits-DswmqWuy.d.mts → limits-x4OiyJxh.d.mts} +5 -0
  52. package/dist/{normalise-Db1xaxgx.mjs → normalise-DB-Xtjmn.mjs} +43 -2
  53. package/dist/openapi/ApiCallbacks.d.mts +1 -1
  54. package/dist/openapi/ApiLinks.d.mts +1 -1
  55. package/dist/openapi/ApiResponseHeaders.d.mts +1 -1
  56. package/dist/openapi/ApiSecurity.d.mts +1 -1
  57. package/dist/openapi/ApiSecurity.mjs +21 -8
  58. package/dist/openapi/bundle.d.mts +31 -0
  59. package/dist/openapi/components.d.mts +41 -10
  60. package/dist/openapi/components.mjs +19 -13
  61. package/dist/openapi/parser.d.mts +13 -13
  62. package/dist/openapi/parser.mjs +12 -12
  63. package/dist/openapi/resolve.d.mts +38 -49
  64. package/dist/openapi/resolve.mjs +62 -56
  65. package/dist/react/SchemaComponent.d.mts +19 -95
  66. package/dist/react/SchemaComponent.mjs +12 -1
  67. package/dist/react/SchemaView.d.mts +11 -7
  68. package/dist/react/SchemaView.mjs +3 -1
  69. package/dist/react/a11y.d.mts +74 -7
  70. package/dist/react/a11y.mjs +67 -6
  71. package/dist/react/fieldPath.d.mts +16 -1
  72. package/dist/react/fieldPath.mjs +25 -1
  73. package/dist/react/fieldShell.d.mts +49 -0
  74. package/dist/react/fieldShell.mjs +37 -0
  75. package/dist/react/headless.d.mts +1 -1
  76. package/dist/react/headlessRenderers.d.mts +2 -2
  77. package/dist/react/headlessRenderers.mjs +123 -54
  78. package/dist/{ref-CPh8rKQ3.d.mts → ref-DdsbekXX.d.mts} +33 -1
  79. package/dist/themes/mantine.d.mts +36 -20
  80. package/dist/themes/mantine.mjs +179 -150
  81. package/dist/themes/mui.d.mts +47 -21
  82. package/dist/themes/mui.mjs +259 -222
  83. package/dist/themes/radix.d.mts +38 -23
  84. package/dist/themes/radix.mjs +208 -180
  85. package/dist/themes/shadcn.d.mts +6 -3
  86. package/dist/themes/shadcn.mjs +93 -93
  87. package/dist/{types-C2Ay1FEh.d.mts → types-BrYbjC7_.d.mts} +7 -0
  88. package/package.json +5 -1
  89. package/dist/adapter-DcWi4XXn.d.mts +0 -223
  90. package/dist/renderer-OaOz-n6-.d.mts +0 -185
@@ -1,3 +1,4 @@
1
+ import { constraintHint } from "../core/constraintHint.mjs";
1
2
  import { fieldDomId, hintIdFor } from "../core/idPath.mjs";
2
3
  import { h } from "./html.mjs";
3
4
  //#region src/html/a11y.ts
@@ -35,22 +36,6 @@ function buildHintId(inputId) {
35
36
  return hintIdFor(inputId);
36
37
  }
37
38
  /**
38
- * Build a human-readable constraint description string.
39
- * Returns undefined if no constraints are present.
40
- */
41
- function constraintHint(c) {
42
- const parts = [];
43
- if (c.minLength !== void 0) parts.push(`Minimum ${String(c.minLength)} characters`);
44
- if (c.maxLength !== void 0) parts.push(`Maximum ${String(c.maxLength)} characters`);
45
- if (c.minimum !== void 0) parts.push(`Minimum ${String(c.minimum)}`);
46
- if (c.maximum !== void 0) parts.push(`Maximum ${String(c.maximum)}`);
47
- if (c.pattern !== void 0 && c.format === void 0) parts.push("Must match pattern");
48
- if (c.minItems !== void 0) parts.push(`Minimum ${String(c.minItems)} items`);
49
- if (c.maxItems !== void 0) parts.push(`Maximum ${String(c.maxItems)} items`);
50
- if (parts.length === 0) return void 0;
51
- return parts.join(". ");
52
- }
53
- /**
54
39
  * Build `aria-required` attribute for required fields.
55
40
  * Returns an object to spread into `h()` attributes, or empty object.
56
41
  */
@@ -1,5 +1,8 @@
1
- import { w as SchemaMeta } from "../types-C2Ay1FEh.mjs";
2
- import { o as HtmlResolver } from "../renderer-OaOz-n6-.mjs";
1
+ import { w as SchemaMeta } from "../types-BrYbjC7_.mjs";
2
+ import { SchemaIoSide } from "../core/adapter.mjs";
3
+ import { HtmlResolver } from "../core/renderer.mjs";
4
+ import { RejectUnrepresentableZod } from "../core/typeInference.mjs";
5
+ import { a as InferredValue, t as InferFields } from "../inferValue-PPXWJpbN.mjs";
3
6
 
4
7
  //#region src/html/renderToHtml.d.ts
5
8
  /**
@@ -15,15 +18,25 @@ declare function recursionSentinelHtml(label: string): string;
15
18
  /**
16
19
  * Options accepted by {@link renderToHtml}.
17
20
  *
21
+ * The generic parameters mirror `<SchemaComponent>` so a typed
22
+ * `schema` argument drives typed `value`, `ref`, and `fields` options.
23
+ *
18
24
  * @group HTML
19
25
  */
20
- interface RenderToHtmlOptions {
21
- /** The data value to render. */
22
- value?: unknown;
26
+ interface RenderToHtmlOptions<T = unknown, Ref extends string | undefined = undefined, Mode extends SchemaIoSide = "output"> {
27
+ /**
28
+ * The data value to render. Typed against `InferredValue<T, Ref, undefined, Mode>`
29
+ * so a typed `schema` argument drives the rendered value's shape.
30
+ */
31
+ value?: InferredValue<T, Ref, undefined, Mode>;
23
32
  /** For OpenAPI: a ref string like "#/components/schemas/User". */
24
- ref?: string;
25
- /** Per-field meta overrides. */
26
- fields?: Record<string, unknown>;
33
+ ref?: Ref;
34
+ /**
35
+ * Per-field meta overrides — nested object mirroring schema shape.
36
+ * Typed against {@link InferFields} so a typed `schema` argument
37
+ * drives autocomplete on the override map.
38
+ */
39
+ fields?: InferFields<T, Ref>;
27
40
  /** Meta overrides applied to the root schema. */
28
41
  meta?: SchemaMeta;
29
42
  /** Force all fields read-only. */
@@ -54,6 +67,6 @@ interface RenderToHtmlOptions {
54
67
  * const html = renderToHtml(userSchema, { value: user, readOnly: true });
55
68
  * ```
56
69
  */
57
- declare function renderToHtml(schema: unknown, options?: RenderToHtmlOptions): string;
70
+ declare function renderToHtml<T = unknown, Ref extends string | undefined = undefined, Mode extends SchemaIoSide = "output">(schema: RejectUnrepresentableZod<T>, options?: RenderToHtmlOptions<T, Ref, Mode>): string;
58
71
  //#endregion
59
72
  export { RenderToHtmlOptions, recursionSentinelHtml, renderToHtml };
@@ -1,3 +1,4 @@
1
+ import { toRecordOrUndefined } from "../core/guards.mjs";
1
2
  import "../core/limits.mjs";
2
3
  import { normaliseSchema } from "../core/adapter.mjs";
3
4
  import { getHtmlRenderFn, mergeHtmlResolvers } from "../core/renderer.mjs";
@@ -66,7 +67,7 @@ function renderToHtml(schema, options = {}) {
66
67
  const tree = walk(jsonSchema, {
67
68
  componentMeta: mergedMeta,
68
69
  rootMeta,
69
- fieldOverrides: options.fields,
70
+ fieldOverrides: toRecordOrUndefined(options.fields),
70
71
  rootDocument
71
72
  });
72
73
  const resolver = options.resolver ?? defaultHtmlResolver;
@@ -1,5 +1,8 @@
1
- import { w as SchemaMeta } from "../types-C2Ay1FEh.mjs";
2
- import { o as HtmlResolver } from "../renderer-OaOz-n6-.mjs";
1
+ import { w as SchemaMeta } from "../types-BrYbjC7_.mjs";
2
+ import { SchemaIoSide } from "../core/adapter.mjs";
3
+ import { HtmlResolver } from "../core/renderer.mjs";
4
+ import { RejectUnrepresentableZod } from "../core/typeInference.mjs";
5
+ import { a as InferredValue, t as InferFields } from "../inferValue-PPXWJpbN.mjs";
3
6
 
4
7
  //#region src/html/renderToHtmlStream.d.ts
5
8
  /**
@@ -7,15 +10,25 @@ import { o as HtmlResolver } from "../renderer-OaOz-n6-.mjs";
7
10
  * ({@link renderToHtmlChunks}, {@link renderToHtmlStream},
8
11
  * {@link renderToHtmlReadable}).
9
12
  *
13
+ * The generic parameters mirror `<SchemaComponent>` so a typed
14
+ * `schema` argument drives typed `value`, `ref`, and `fields` options.
15
+ *
10
16
  * @group HTML
11
17
  */
12
- interface StreamRenderOptions {
13
- /** The data value to render. */
14
- value?: unknown;
18
+ interface StreamRenderOptions<T = unknown, Ref extends string | undefined = undefined, Mode extends SchemaIoSide = "output"> {
19
+ /**
20
+ * The data value to render. Typed against `InferredValue<T, Ref, undefined, Mode>`
21
+ * so a typed `schema` argument drives the rendered value's shape.
22
+ */
23
+ value?: InferredValue<T, Ref, undefined, Mode>;
15
24
  /** For OpenAPI: a ref string like "#/components/schemas/User". */
16
- ref?: string;
17
- /** Per-field meta overrides. */
18
- fields?: Record<string, unknown>;
25
+ ref?: Ref;
26
+ /**
27
+ * Per-field meta overrides — nested object mirroring schema shape.
28
+ * Typed against {@link InferFields} so a typed `schema` argument
29
+ * drives autocomplete on the override map.
30
+ */
31
+ fields?: InferFields<T, Ref>;
19
32
  /** Meta overrides applied to the root schema. */
20
33
  meta?: SchemaMeta;
21
34
  /** Force all fields read-only. */
@@ -43,7 +56,7 @@ interface StreamRenderOptions {
43
56
  * }
44
57
  * ```
45
58
  */
46
- declare function renderToHtmlChunks(schema: unknown, options?: StreamRenderOptions): Iterable<string>;
59
+ declare function renderToHtmlChunks<T = unknown, Ref extends string | undefined = undefined, Mode extends SchemaIoSide = "output">(schema: RejectUnrepresentableZod<T>, options?: StreamRenderOptions<T, Ref, Mode>): Iterable<string>;
47
60
  /**
48
61
  * Render a schema as an async iterable of HTML string chunks. Yields
49
62
  * back to the event loop between chunks via a microtask so other tasks
@@ -51,7 +64,7 @@ declare function renderToHtmlChunks(schema: unknown, options?: StreamRenderOptio
51
64
  *
52
65
  * @group HTML
53
66
  */
54
- declare function renderToHtmlStream(schema: unknown, options?: StreamRenderOptions): AsyncIterable<string>;
67
+ declare function renderToHtmlStream<T = unknown, Ref extends string | undefined = undefined, Mode extends SchemaIoSide = "output">(schema: RejectUnrepresentableZod<T>, options?: StreamRenderOptions<T, Ref, Mode>): AsyncIterable<string>;
55
68
  /**
56
69
  * Render a schema as a web `ReadableStream<string>` so it can be piped
57
70
  * into a `Response` body. Pulls chunks lazily from the synchronous
@@ -67,6 +80,6 @@ declare function renderToHtmlStream(schema: unknown, options?: StreamRenderOptio
67
80
  * });
68
81
  * ```
69
82
  */
70
- declare function renderToHtmlReadable(schema: unknown, options?: StreamRenderOptions): ReadableStream<string>;
83
+ declare function renderToHtmlReadable<T = unknown, Ref extends string | undefined = undefined, Mode extends SchemaIoSide = "output">(schema: RejectUnrepresentableZod<T>, options?: StreamRenderOptions<T, Ref, Mode>): ReadableStream<string>;
71
84
  //#endregion
72
85
  export { StreamRenderOptions, renderToHtmlChunks, renderToHtmlReadable, renderToHtmlStream };
@@ -1,3 +1,4 @@
1
+ import { toRecordOrUndefined } from "../core/guards.mjs";
1
2
  import { normaliseSchema } from "../core/adapter.mjs";
2
3
  import { mergeHtmlResolvers } from "../core/renderer.mjs";
3
4
  import { walk } from "../core/walker.mjs";
@@ -88,7 +89,7 @@ function prepareTree(schema, options) {
88
89
  tree: walk(jsonSchema, {
89
90
  componentMeta: mergedMeta,
90
91
  rootMeta,
91
- fieldOverrides: options.fields,
92
+ fieldOverrides: toRecordOrUndefined(options.fields),
92
93
  rootDocument
93
94
  }),
94
95
  resolver: options.resolver ?? defaultHtmlResolver
@@ -1,39 +1,8 @@
1
+ import { HtmlResolver } from "../core/renderer.mjs";
1
2
  import { dateInputType } from "../core/formats.mjs";
2
- import { o as HtmlResolver } from "../renderer-OaOz-n6-.mjs";
3
3
  import { matchUnionOption } from "../core/unionMatch.mjs";
4
4
 
5
5
  //#region src/html/renderers.d.ts
6
- /**
7
- * Thin wrapper over `fieldDomId` from `core/idPath.ts`. Every render
8
- * pipeline must derive ids from the same canonical normaliser so that
9
- * `aria-controls`, `aria-labelledby`, and `htmlFor` references resolve
10
- * consistently across the React, sync-HTML, and streaming-HTML outputs.
11
- *
12
- * The wrapper tolerates an empty path here (returning `sc-`) so that
13
- * a leaf renderer at the schema root — `renderToHtml(z.string())` — has
14
- * a usable id without throwing. Container renderers always thread a
15
- * non-empty path through `renderChild`, so the empty-id fallback can
16
- * never produce sibling collisions inside a structured form.
17
- */
18
- declare function fieldId(path: string): string;
19
- /**
20
- * Tab-panel id for a discriminated union at `path`. Delegates to the
21
- * canonical `panelIdFor` from `core/idPath.ts` for the normal case so
22
- * the sync, streaming, and React renderers all emit identical ids; falls
23
- * back to a structurally-equivalent string when the renderer is invoked
24
- * with an empty root path (a discriminated union at the schema root —
25
- * see the `fieldId` doc comment for the wider context).
26
- *
27
- * Exported because `streamRenderers.ts` needs to derive identical ids
28
- * — the panel id on the `<div role="tabpanel">` must match the
29
- * `aria-controls` on every tab regardless of which pipeline rendered it.
30
- */
31
- declare function panelId(path: string): string;
32
- /**
33
- * Tab id for tab `i` within a discriminated union at `path`. Mirror of
34
- * `panelId` above — see its comment.
35
- */
36
- declare function tabId(path: string, i: number): string;
37
6
  /**
38
7
  * Default HTML resolver used by `renderToHtml` and the streaming
39
8
  * renderers when the consumer does not pass a custom resolver. Maps
@@ -42,4 +11,4 @@ declare function tabId(path: string, i: number): string;
42
11
  */
43
12
  declare const defaultHtmlResolver: HtmlResolver;
44
13
  //#endregion
45
- export { dateInputType, defaultHtmlResolver, fieldId, matchUnionOption, panelId, tabId };
14
+ export { dateInputType, defaultHtmlResolver, matchUnionOption };
@@ -9,56 +9,13 @@ import { displayJsonValue } from "../core/walkBuilders.mjs";
9
9
  import { h, raw, serialize } from "./html.mjs";
10
10
  import { ariaDescribedByAttrs, ariaLabelAttrs, ariaReadonlyAttrs, ariaRequiredAttrs, buildHintElement, buildInputId, requiredIndicator } from "./a11y.mjs";
11
11
  //#region src/html/renderers.ts
12
- /**
13
- * Thin wrapper over `fieldDomId` from `core/idPath.ts`. Every render
14
- * pipeline must derive ids from the same canonical normaliser so that
15
- * `aria-controls`, `aria-labelledby`, and `htmlFor` references resolve
16
- * consistently across the React, sync-HTML, and streaming-HTML outputs.
17
- *
18
- * The wrapper tolerates an empty path here (returning `sc-`) so that
19
- * a leaf renderer at the schema root — `renderToHtml(z.string())` — has
20
- * a usable id without throwing. Container renderers always thread a
21
- * non-empty path through `renderChild`, so the empty-id fallback can
22
- * never produce sibling collisions inside a structured form.
23
- */
24
- function fieldId(path) {
25
- if (path.length === 0) return "sc-";
26
- return fieldDomId(path);
27
- }
28
- /**
29
- * Tab-panel id for a discriminated union at `path`. Delegates to the
30
- * canonical `panelIdFor` from `core/idPath.ts` for the normal case so
31
- * the sync, streaming, and React renderers all emit identical ids; falls
32
- * back to a structurally-equivalent string when the renderer is invoked
33
- * with an empty root path (a discriminated union at the schema root —
34
- * see the `fieldId` doc comment for the wider context).
35
- *
36
- * Exported because `streamRenderers.ts` needs to derive identical ids
37
- * — the panel id on the `<div role="tabpanel">` must match the
38
- * `aria-controls` on every tab regardless of which pipeline rendered it.
39
- */
40
- function panelId(path) {
41
- if (path.length === 0) return `${fieldId(path)}-panel`;
42
- return panelIdFor(path);
43
- }
44
- /**
45
- * Tab id for tab `i` within a discriminated union at `path`. Mirror of
46
- * `panelId` above — see its comment.
47
- */
48
- function tabId(path, i) {
49
- if (path.length === 0) return `${fieldId(path)}-tab-${String(i)}`;
50
- return tabIdFor(path, i);
51
- }
52
12
  function renderStringHtml(props) {
53
13
  if (props.readOnly) return serialize(renderStringReadOnly(props));
54
14
  return serialize(renderStringEditable(props));
55
15
  }
56
16
  function renderStringReadOnly(props) {
57
17
  const strValue = typeof props.value === "string" ? props.value : void 0;
58
- if (strValue === void 0 || strValue.length === 0) return h("span", {
59
- class: SC_CLASSES.valueEmpty,
60
- ...ariaReadonlyAttrs()
61
- }, "—");
18
+ if (strValue === void 0 || strValue.length === 0) return h("span", { class: SC_CLASSES.valueEmpty }, "—");
62
19
  const format = props.constraints.format;
63
20
  if (format === "email" && isSafeMailtoAddress(strValue)) return h("a", {
64
21
  class: SC_CLASSES.value,
@@ -70,22 +27,22 @@ function renderStringReadOnly(props) {
70
27
  href: strValue,
71
28
  ...ariaReadonlyAttrs()
72
29
  }, strValue);
73
- return h("span", {
74
- class: SC_CLASSES.value,
75
- ...ariaReadonlyAttrs()
76
- }, strValue);
30
+ return h("span", { class: SC_CLASSES.value }, strValue);
77
31
  }
78
32
  function renderStringEditable(props) {
79
33
  const strValue = typeof props.value === "string" ? props.value : "";
80
34
  const format = props.constraints.format;
81
- const inputType = dateInputType(format) ?? (format === "email" ? "email" : format === "uri" ? "url" : "text");
82
- const id = fieldId(props.path);
35
+ const dateType = dateInputType(format);
36
+ const isCredential = props.writeOnly && format === "password";
37
+ const inputType = dateType ?? (isCredential ? "password" : format === "email" ? "email" : format === "uri" ? "url" : "text");
38
+ const id = fieldDomId(props.path);
83
39
  const attrs = {
84
40
  class: SC_CLASSES.input,
85
41
  id,
86
42
  type: inputType,
87
43
  name: id
88
44
  };
45
+ if (isCredential) attrs.autocomplete = strValue.length > 0 ? "current-password" : "new-password";
89
46
  if (!props.writeOnly) attrs.value = strValue;
90
47
  if (typeof props.meta.description === "string") attrs.placeholder = props.meta.description;
91
48
  if (props.constraints.minLength !== void 0) attrs.minlength = String(props.constraints.minLength);
@@ -99,24 +56,24 @@ function renderNumberHtml(props) {
99
56
  return serialize(renderNumberEditable(props));
100
57
  }
101
58
  function renderNumberReadOnly(props) {
102
- if (typeof props.value !== "number") return h("span", {
103
- class: SC_CLASSES.valueEmpty,
104
- ...ariaReadonlyAttrs()
105
- }, "—");
106
- return h("span", {
107
- class: SC_CLASSES.value,
108
- ...ariaReadonlyAttrs()
109
- }, props.value.toLocaleString());
59
+ if (typeof props.value !== "number") return h("span", { class: SC_CLASSES.valueEmpty }, "—");
60
+ return h("span", { class: SC_CLASSES.value }, props.value.toLocaleString());
110
61
  }
111
62
  function renderNumberEditable(props) {
112
63
  const numValue = typeof props.value === "number" ? String(props.value) : "";
113
- const id = fieldId(props.path);
64
+ const id = fieldDomId(props.path);
65
+ const isInteger = props.tree.type === "number" ? props.tree.isInteger : false;
66
+ const inputMode = isInteger ? "numeric" : "decimal";
67
+ const multipleOf = props.constraints.multipleOf;
114
68
  const attrs = {
115
69
  class: SC_CLASSES.input,
116
70
  id,
117
71
  type: "number",
118
- name: id
72
+ name: id,
73
+ inputmode: inputMode
119
74
  };
75
+ if (multipleOf !== void 0) attrs.step = String(multipleOf);
76
+ else if (isInteger) attrs.step = "1";
120
77
  if (!props.writeOnly) attrs.value = numValue;
121
78
  if (props.constraints.minimum !== void 0) attrs.min = String(props.constraints.minimum);
122
79
  if (props.constraints.maximum !== void 0) attrs.max = String(props.constraints.maximum);
@@ -129,17 +86,11 @@ function renderBooleanHtml(props) {
129
86
  return serialize(renderBooleanEditable(props));
130
87
  }
131
88
  function renderBooleanReadOnly(props) {
132
- if (typeof props.value !== "boolean") return h("span", {
133
- class: SC_CLASSES.valueEmpty,
134
- ...ariaReadonlyAttrs()
135
- }, "—");
136
- return h("span", {
137
- class: "sc-value sc-value--boolean",
138
- ...ariaReadonlyAttrs()
139
- }, props.value ? "Yes" : "No");
89
+ if (typeof props.value !== "boolean") return h("span", { class: SC_CLASSES.valueEmpty }, "—");
90
+ return h("span", { class: "sc-value sc-value--boolean" }, props.value ? "Yes" : "No");
140
91
  }
141
92
  function renderBooleanEditable(props) {
142
- const id = fieldId(props.path);
93
+ const id = fieldDomId(props.path);
143
94
  const attrs = {
144
95
  class: SC_CLASSES.input,
145
96
  id,
@@ -157,18 +108,12 @@ function renderEnumHtml(props) {
157
108
  }
158
109
  function renderEnumReadOnly(props) {
159
110
  const enumValue = typeof props.value === "string" ? props.value : "";
160
- if (enumValue.length === 0) return h("span", {
161
- class: SC_CLASSES.valueEmpty,
162
- ...ariaReadonlyAttrs()
163
- }, "—");
164
- return h("span", {
165
- class: SC_CLASSES.value,
166
- ...ariaReadonlyAttrs()
167
- }, enumValue);
111
+ if (enumValue.length === 0) return h("span", { class: SC_CLASSES.valueEmpty }, "—");
112
+ return h("span", { class: SC_CLASSES.value }, enumValue);
168
113
  }
169
114
  function renderEnumEditable(props) {
170
115
  const enumValue = typeof props.value === "string" ? props.value : "";
171
- const id = fieldId(props.path);
116
+ const id = fieldDomId(props.path);
172
117
  const selectedValue = props.writeOnly ? "" : enumValue;
173
118
  const enumValues = props.tree.type === "enum" ? props.tree.enumValues : [];
174
119
  const optionNodes = [h("option", { value: "" }, `Select…`), ...enumValues.map((v) => {
@@ -268,8 +213,12 @@ function renderRecordNode(props) {
268
213
  }
269
214
  const children = [];
270
215
  for (const [key, val] of Object.entries(obj)) {
216
+ const childInputId = buildInputId(props.path, key);
271
217
  const childHtml = props.renderChild(valueType, val, key);
272
- children.push(h("div", { class: SC_CLASSES.field }, h("label", { class: SC_CLASSES.label }, key), raw(childHtml)));
218
+ children.push(h("div", { class: SC_CLASSES.field }, h("label", {
219
+ class: SC_CLASSES.label,
220
+ for: childInputId
221
+ }, key), raw(childHtml)));
273
222
  }
274
223
  return h("div", attrs, ...children);
275
224
  }
@@ -307,14 +256,14 @@ function renderDiscriminatedUnionHtml(props) {
307
256
  if (activeOption !== void 0) return props.renderChild(activeOption, props.value);
308
257
  return serialize(h("span", { class: SC_CLASSES.valueEmpty }, "—"));
309
258
  }
310
- const tabPanelId = panelId(props.path);
259
+ const tabPanelId = panelIdFor(props.path);
311
260
  const tabButtons = options.map((_opt, i) => {
312
261
  return h("button", {
313
262
  type: "button",
314
263
  role: "tab",
315
264
  class: i === activeIndex ? SC_CLASSES.tabActive : SC_CLASSES.tab,
316
- id: tabId(props.path, i),
317
- "aria-selected": i === activeIndex ? "true" : void 0,
265
+ id: tabIdFor(props.path, i),
266
+ "aria-selected": i === activeIndex ? "true" : "false",
318
267
  "aria-controls": tabPanelId,
319
268
  tabindex: i === activeIndex ? "0" : "-1"
320
269
  }, optionLabels[i]);
@@ -322,25 +271,25 @@ function renderDiscriminatedUnionHtml(props) {
322
271
  const children = [h("div", {
323
272
  role: "tablist",
324
273
  class: SC_CLASSES.tabs,
325
- "aria-label": "Select variant"
274
+ "aria-label": "Select variant",
275
+ "aria-orientation": "horizontal"
326
276
  }, ...tabButtons)];
327
277
  if (activeOption !== void 0) {
328
278
  const childHtml = props.renderChild(activeOption, props.value);
329
279
  children.push(h("div", {
330
280
  role: "tabpanel",
331
281
  id: tabPanelId,
332
- "aria-labelledby": tabId(props.path, activeIndex)
282
+ "aria-labelledby": tabIdFor(props.path, activeIndex)
333
283
  }, raw(childHtml)));
334
284
  }
335
285
  return serialize(h("div", { class: SC_CLASSES.discriminatedUnion }, ...children));
336
286
  }
337
287
  function renderFileHtml(props) {
338
- const id = fieldId(props.path);
288
+ const id = fieldDomId(props.path);
339
289
  const accept = props.constraints.mimeTypes?.join(",");
340
290
  if (props.readOnly) return serialize(h("span", {
341
291
  class: SC_CLASSES.value,
342
- id,
343
- ...ariaReadonlyAttrs()
292
+ id
344
293
  }, "File field"));
345
294
  const attrs = {
346
295
  class: SC_CLASSES.input,
@@ -407,7 +356,7 @@ function renderNegationHtml(props) {
407
356
  * The only valid value is `null`, so render an em-dash placeholder.
408
357
  */
409
358
  function renderNullHtml(props) {
410
- const id = fieldId(props.path);
359
+ const id = fieldDomId(props.path);
411
360
  return serialize(h("span", {
412
361
  class: SC_CLASSES.valueEmpty,
413
362
  id,
@@ -424,8 +373,7 @@ function renderNullHtml(props) {
424
373
  function renderNeverHtml(props) {
425
374
  return serialize(h("span", {
426
375
  class: "sc-value sc-never",
427
- id: fieldId(props.path),
428
- ...ariaReadonlyAttrs()
376
+ id: fieldDomId(props.path)
429
377
  }, h("em", {}, "never matches")));
430
378
  }
431
379
  /**
@@ -454,4 +402,4 @@ const defaultHtmlResolver = {
454
402
  unknown: renderUnknownHtml
455
403
  };
456
404
  //#endregion
457
- export { dateInputType, defaultHtmlResolver, fieldId, matchUnionOption, panelId, tabId };
405
+ export { dateInputType, defaultHtmlResolver, matchUnionOption };
@@ -1,6 +1,6 @@
1
- import { j as WalkedField } from "../types-C2Ay1FEh.mjs";
2
- import { i as DiagnosticsOptions } from "../diagnostics-ByEzkjrA.mjs";
3
- import { o as HtmlResolver } from "../renderer-OaOz-n6-.mjs";
1
+ import { j as WalkedField } from "../types-BrYbjC7_.mjs";
2
+ import { i as DiagnosticsOptions } from "../diagnostics-BTrm3O6J.mjs";
3
+ import { HtmlResolver } from "../core/renderer.mjs";
4
4
  import { HtmlElement } from "./html.mjs";
5
5
 
6
6
  //#region src/html/streamRenderers.d.ts
@@ -2,11 +2,11 @@ import { isObject } from "../core/guards.mjs";
2
2
  import "../core/limits.mjs";
3
3
  import { emitDiagnostic } from "../core/diagnostics.mjs";
4
4
  import { SC_CLASSES } from "../core/cssClasses.mjs";
5
+ import { panelIdFor, tabIdFor } from "../core/idPath.mjs";
5
6
  import { getHtmlRenderFn } from "../core/renderer.mjs";
6
7
  import { matchUnionOption, resolveDiscriminatedActive } from "../core/unionMatch.mjs";
7
8
  import { VOID_ELEMENTS, h, raw, serialize, serializeAttributes } from "./html.mjs";
8
9
  import { ariaLabelAttrs, buildHintElement, buildInputId, joinPath, requiredIndicator } from "./a11y.mjs";
9
- import { panelId, tabId } from "./renderers.mjs";
10
10
  import { recursionSentinelHtml } from "./renderToHtml.mjs";
11
11
  //#region src/html/streamRenderers.ts
12
12
  /**
@@ -164,7 +164,7 @@ function* streamObject(tree, value, mergedResolver, path, rawResolver, currentDe
164
164
  class: SC_CLASSES.label,
165
165
  for: fieldId
166
166
  }, ...labelContent), raw(childChunks)];
167
- const hint = buildHintElement(key, field.constraints);
167
+ const hint = buildHintElement(fieldId, field.constraints);
168
168
  if (hint !== void 0) fieldChildren.push(hint);
169
169
  yield serialize(h("div", { class: SC_CLASSES.field }, ...fieldChildren));
170
170
  }
@@ -237,8 +237,12 @@ function* streamRecord(tree, value, mergedResolver, path, rawResolver, currentDe
237
237
  const container = h("div", attrs);
238
238
  yield yieldOpen(container);
239
239
  for (const [key, val] of Object.entries(obj)) {
240
+ const childInputId = buildInputId(path, key);
240
241
  const childHtml = renderFieldSync(valueType, val, mergedResolver, joinPath(path, key), rawResolver, currentDepth + 1, diagnostics);
241
- yield serialize(h("div", { class: SC_CLASSES.field }, h("label", { class: SC_CLASSES.label }, key), raw(childHtml)));
242
+ yield serialize(h("div", { class: SC_CLASSES.field }, h("label", {
243
+ class: SC_CLASSES.label,
244
+ for: childInputId
245
+ }, key), raw(childHtml)));
242
246
  }
243
247
  yield yieldClose(container);
244
248
  }
@@ -267,7 +271,7 @@ function* streamDiscriminatedUnion(tree, value, mergedResolver, path, rawResolve
267
271
  if (activeOption !== void 0) yield* streamField(activeOption, value, mergedResolver, path, rawResolver, currentDepth + 1, diagnostics);
268
272
  return;
269
273
  }
270
- const tabPanelId = panelId(path);
274
+ const tabPanelId = panelIdFor(path);
271
275
  const wrapper = h("div", { class: SC_CLASSES.discriminatedUnion });
272
276
  yield yieldOpen(wrapper);
273
277
  const tabButtons = options.map((_opt, i) => {
@@ -275,8 +279,8 @@ function* streamDiscriminatedUnion(tree, value, mergedResolver, path, rawResolve
275
279
  type: "button",
276
280
  role: "tab",
277
281
  class: i === activeIndex ? SC_CLASSES.tabActive : SC_CLASSES.tab,
278
- id: tabId(path, i),
279
- "aria-selected": i === activeIndex ? "true" : void 0,
282
+ id: tabIdFor(path, i),
283
+ "aria-selected": i === activeIndex ? "true" : "false",
280
284
  "aria-controls": tabPanelId,
281
285
  tabindex: i === activeIndex ? "0" : "-1"
282
286
  }, optionLabels[i]);
@@ -284,12 +288,13 @@ function* streamDiscriminatedUnion(tree, value, mergedResolver, path, rawResolve
284
288
  yield serialize(h("div", {
285
289
  role: "tablist",
286
290
  class: SC_CLASSES.tabs,
287
- "aria-label": "Select variant"
291
+ "aria-label": "Select variant",
292
+ "aria-orientation": "horizontal"
288
293
  }, ...tabButtons));
289
294
  const panelOpen = h("div", {
290
295
  role: "tabpanel",
291
296
  id: tabPanelId,
292
- "aria-labelledby": tabId(path, activeIndex)
297
+ "aria-labelledby": tabIdFor(path, activeIndex)
293
298
  });
294
299
  yield yieldOpen(panelOpen);
295
300
  if (activeOption !== void 0) yield* streamField(activeOption, value, mergedResolver, path, rawResolver, currentDepth + 1, diagnostics);
@@ -0,0 +1,77 @@
1
+ import { d as FieldOverrides, u as FieldOverride } from "./types-BrYbjC7_.mjs";
2
+ import { SchemaIoSide } from "./core/adapter.mjs";
3
+ import { FromJSONSchema, FromJSONSchemaMode, IsSwagger2Doc, ResolveOpenAPIRef, TypeAtPath, __SchemaInferenceFellBack } from "./core/typeInference.mjs";
4
+ import { z } from "zod";
5
+
6
+ //#region src/core/inferValue.d.ts
7
+ /**
8
+ * Recursive mapped type that mirrors a schema's shape for per-field
9
+ * overrides. Dispatches on the schema kind in the same order as
10
+ * {@link InferSchemaValue} so the inferred override map tracks the
11
+ * inferred value shape.
12
+ *
13
+ * Exported so `<SchemaView>` and other consumers can type their
14
+ * `fields` prop against the same machinery `<SchemaComponent>` uses.
15
+ *
16
+ * @group Components
17
+ */
18
+ type InferFields<T, Ref extends string | undefined> = IsSwagger2Doc<T> extends true ? __SchemaInferenceFellBack : T extends z.ZodType ? FieldOverrides<z.infer<T>> : T extends {
19
+ openapi: unknown;
20
+ } ? Ref extends string ? FieldOverrides<ResolveOpenAPIRef<T & Record<string, unknown>, Ref>> : Record<string, FieldOverride> : T extends object ? unknown extends FromJSONSchema<T> ? Record<string, FieldOverride> : FieldOverrides<FromJSONSchema<T>> : Record<string, FieldOverride>;
21
+ /**
22
+ * Infer the data type carried by the schema input.
23
+ *
24
+ * Mirrors {@link InferFields}'s dispatch order: Zod schema → `z.infer`,
25
+ * OpenAPI doc + ref → `ResolveOpenAPIRef`, plain JSON Schema object →
26
+ * `FromJSONSchema`, everything else → `unknown`. The `Mode` parameter
27
+ * is plumbed through to `FromJSONSchema` / `ResolveOpenAPIRef` so
28
+ * `readOnly` / `writeOnly` keywords participate in the inferred
29
+ * object shape — `"output"` for the rendered value, `"input"` for the
30
+ * `onChange` argument.
31
+ *
32
+ * When the schema's value type cannot be statically determined (e.g.
33
+ * a runtime `Record<string, unknown>` JSON Schema, or an OpenAPI doc
34
+ * without a ref), the result falls back to `unknown` so callers can
35
+ * still supply arbitrary values.
36
+ */
37
+ type InferSchemaValue<T, Ref extends string | undefined, Mode extends FromJSONSchemaMode> = IsSwagger2Doc<T> extends true ? __SchemaInferenceFellBack : T extends z.ZodType ? Mode extends "input" ? z.input<T> : z.output<T> : T extends {
38
+ openapi: unknown;
39
+ } ? Ref extends string ? ResolveOpenAPIRef<T & Record<string, unknown>, Ref, [], Mode> : unknown : T extends object ? FromJSONSchema<T, Record<string, never>, [], Mode> | (unknown extends FromJSONSchema<T> ? unknown : never) extends infer V ? V : unknown : unknown;
40
+ /**
41
+ * Narrow an inferred value type to the sub-shape at `P`, or return
42
+ * the original value type when `P` is `undefined` (no path supplied).
43
+ */
44
+ type NarrowAtPath<V, P extends string | undefined> = P extends string ? TypeAtPath<V, P> : V;
45
+ /**
46
+ * Public alias mapping a schema input to the rendered value type.
47
+ *
48
+ * Picks the OUTPUT side (server → client) of every transform / pipe /
49
+ * codec. For an `<SchemaComponent io="output">` or `<SchemaView
50
+ * io="output">` (both defaults), this is the inferred shape of
51
+ * `value` and the parameter of `onChange`.
52
+ */
53
+ type InferredOutputValue<T, Ref extends string | undefined = undefined, P extends string | undefined = undefined> = NarrowAtPath<InferSchemaValue<T, Ref, "output">, P>;
54
+ /**
55
+ * Companion to {@link InferredOutputValue} for `"input"`-mode shapes.
56
+ *
57
+ * Picks the INPUT side (client → server) of every transform / pipe /
58
+ * codec. Surfaces as the inferred shape of `value` / `onChange` when
59
+ * a consumer renders `<SchemaComponent io="input">`. For JSON Schema
60
+ * inputs with `readOnly`/`writeOnly` annotations, the INPUT mode
61
+ * omits properties marked `readOnly: true`.
62
+ */
63
+ type InferredInputValue<T, Ref extends string | undefined = undefined, P extends string | undefined = undefined> = NarrowAtPath<InferSchemaValue<T, Ref, "input">, P>;
64
+ /**
65
+ * Resolve the schema-driven value type for either I/O direction.
66
+ *
67
+ * Thin convenience over {@link InferredOutputValue} /
68
+ * {@link InferredInputValue} so consumers that decide between the
69
+ * two at the type level (e.g. a generic wrapper component) can pass
70
+ * the chosen direction as a type argument rather than branch on it
71
+ * with conditional types. Falls back to `unknown` when the schema's
72
+ * value type cannot be statically inferred, identical to the
73
+ * underlying helpers.
74
+ */
75
+ type InferredValue<T, Ref extends string | undefined = undefined, P extends string | undefined = undefined, Mode extends SchemaIoSide = "output"> = NarrowAtPath<InferSchemaValue<T, Ref, Mode>, P>;
76
+ //#endregion
77
+ export { InferredValue as a, InferredOutputValue as i, InferSchemaValue as n, InferredInputValue as r, InferFields as t };
@@ -24,6 +24,11 @@ declare const MAX_REF_DEPTH: MaxRefDepth;
24
24
  * Maximum number of `$ref` hops permitted when walking a chain of
25
25
  * OpenAPI Path Item Object references. Beyond this a
26
26
  * `path-item-ref-too-deep` diagnostic is emitted and resolution stops.
27
+ *
28
+ * Also the default `maxHops` for the generic `resolveRefChain` helper
29
+ * in `core/refChain.ts`, so every ref-chain walker — Path Item refs,
30
+ * Parameter / Response refs, Reference Object chains, etc. — shares
31
+ * the same hop cap.
27
32
  */
28
33
  declare const MAX_PATH_ITEM_REF_HOPS = 8;
29
34
  //#endregion