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,5 +1,6 @@
1
1
  import { isObject } from "./guards.mjs";
2
- import { emitDiagnostic } from "./diagnostics.mjs";
2
+ import { appendPointer, emitDiagnostic } from "./diagnostics.mjs";
3
+ import { isPrototypePollutingKey } from "./uri.mjs";
3
4
  //#region src/core/merge.ts
4
5
  /**
5
6
  * Schema merging, nullable detection, and discriminated union detection.
@@ -137,7 +138,18 @@ function mergeAllOf(schemas, diagnostics, pointer = "") {
137
138
  if (entry === true) continue;
138
139
  if (!isObject(entry)) continue;
139
140
  const props = getObject(entry, "properties");
140
- if (props !== void 0) for (const [key, value] of Object.entries(props)) properties[key] = value;
141
+ if (props !== void 0) for (const [key, value] of Object.entries(props)) {
142
+ if (isPrototypePollutingKey(key)) {
143
+ emitDiagnostic(diagnostics, {
144
+ code: "prototype-polluting-property",
145
+ message: `Refusing to merge prototype-polluting property name from allOf branch: ${key}`,
146
+ pointer: appendPointer(pointer, `properties/${key}`),
147
+ detail: { propertyName: key }
148
+ });
149
+ continue;
150
+ }
151
+ properties[key] = value;
152
+ }
141
153
  const req = getArray(entry, "required");
142
154
  if (req !== void 0) {
143
155
  for (const r of req) if (typeof r === "string" && !required.includes(r)) required.push(r);
@@ -232,6 +244,17 @@ function normaliseAnyOf(options) {
232
244
  * uses `kind` while another uses `type`) detection fails and a
233
245
  * `discriminator-inconsistent` diagnostic is emitted so callers can
234
246
  * see why the union falls back to a generic oneOf.
247
+ *
248
+ * When two or more options share the same discriminator `const` value,
249
+ * the union is still treated as discriminated — the first-match
250
+ * behaviour in `resolveDiscriminatedActive` (in `core/unionMatch.ts`)
251
+ * resolves the active option — but a `discriminator-duplicate`
252
+ * diagnostic is emitted so the unreachable branch is visible to the
253
+ * consumer. Changing the behaviour to fall back to a generic union
254
+ * would be a silent regression for the much commoner case of two
255
+ * intentionally-identical discriminator values appearing in distinct
256
+ * sub-schemas (e.g. an `allOf`-driven hierarchy where the base option
257
+ * duplicates the leaf).
235
258
  */
236
259
  function detectDiscriminated(options, diagnostics, pointer = "") {
237
260
  if (options.length === 0) return void 0;
@@ -263,10 +286,50 @@ function detectDiscriminated(options, diagnostics, pointer = "") {
263
286
  return;
264
287
  }
265
288
  if (discriminator === void 0) return void 0;
289
+ const objectOptions = options.filter(isObject);
290
+ emitDuplicateDiscriminatorDiagnostic(objectOptions, discriminator, diagnostics, pointer);
266
291
  return {
267
- options: options.filter(isObject),
292
+ options: objectOptions,
268
293
  discriminator
269
294
  };
270
295
  }
296
+ /**
297
+ * Inspect the discriminator `const` value declared on each option and
298
+ * emit a `discriminator-duplicate` diagnostic when two or more options
299
+ * share the same value. The diagnostic detail carries the offending
300
+ * value plus the indices of the colliding options so consumers can
301
+ * point at them directly.
302
+ */
303
+ function emitDuplicateDiscriminatorDiagnostic(options, discriminator, diagnostics, pointer) {
304
+ const groups = /* @__PURE__ */ new Map();
305
+ for (const [index, option] of options.entries()) {
306
+ const props = getObject(option, "properties");
307
+ if (props === void 0) continue;
308
+ const discriminatorSchema = getObject(props, discriminator);
309
+ if (discriminatorSchema === void 0) continue;
310
+ if (!("const" in discriminatorSchema)) continue;
311
+ const constValue = discriminatorSchema.const;
312
+ const key = JSON.stringify(constValue);
313
+ const existing = groups.get(key);
314
+ if (existing === void 0) groups.set(key, {
315
+ value: constValue,
316
+ indices: [index]
317
+ });
318
+ else existing.indices.push(index);
319
+ }
320
+ for (const { value, indices } of groups.values()) {
321
+ if (indices.length < 2) continue;
322
+ emitDiagnostic(diagnostics, {
323
+ code: "discriminator-duplicate",
324
+ message: `oneOf options ${indices.join(", ")} share the same discriminator value for "${discriminator}" (${JSON.stringify(value)}); only the first option is reachable`,
325
+ pointer,
326
+ detail: {
327
+ discriminator,
328
+ value,
329
+ indices
330
+ }
331
+ });
332
+ }
333
+ }
271
334
  //#endregion
272
335
  export { ANNOTATION_SIBLINGS, detectDiscriminated, mergeAllOf, mergeRefSiblings, normaliseAnyOf };
@@ -1,4 +1,4 @@
1
- import { i as DiagnosticsOptions } from "../diagnostics-ByEzkjrA.mjs";
1
+ import { i as DiagnosticsOptions } from "../diagnostics-BTrm3O6J.mjs";
2
2
  import { i as OpenApiVersionInfo, r as JsonSchemaDraft } from "../version-DL8U5RuA.mjs";
3
3
 
4
4
  //#region src/core/normalise.d.ts
@@ -1,2 +1,2 @@
1
- import { a as normaliseJsonSchema, i as normaliseDraft04Node, n as deepNormaliseWithContext, o as normaliseOpenApiSchemas, r as documentContainsKeyword, s as selectDraftTransform, t as deepNormalise } from "../normalise-Db1xaxgx.mjs";
1
+ import { a as normaliseJsonSchema, i as normaliseDraft04Node, n as deepNormaliseWithContext, o as normaliseOpenApiSchemas, r as documentContainsKeyword, s as selectDraftTransform, t as deepNormalise } from "../normalise-DB-Xtjmn.mjs";
2
2
  export { deepNormalise, deepNormaliseWithContext, documentContainsKeyword, normaliseDraft04Node, normaliseJsonSchema, normaliseOpenApiSchemas, selectDraftTransform };
@@ -1,2 +1,2 @@
1
- import { d as deepNormaliseOpenApiDoc, f as liftExampleToExamples, h as normaliseOpenApi30Node, l as applyDiscriminatorAllOfPrepass, m as normaliseOpenApi30Discriminator, p as normaliseOpenApi30Combined, u as deepNormaliseOpenApi30Doc } from "../normalise-Db1xaxgx.mjs";
1
+ import { d as deepNormaliseOpenApiDoc, f as liftExampleToExamples, h as normaliseOpenApi30Node, l as applyDiscriminatorAllOfPrepass, m as normaliseOpenApi30Discriminator, p as normaliseOpenApi30Combined, u as deepNormaliseOpenApi30Doc } from "../normalise-DB-Xtjmn.mjs";
2
2
  export { applyDiscriminatorAllOfPrepass, deepNormaliseOpenApi30Doc, deepNormaliseOpenApiDoc, liftExampleToExamples, normaliseOpenApi30Combined, normaliseOpenApi30Discriminator, normaliseOpenApi30Node };
@@ -1,2 +1,2 @@
1
- import { a as dereference, i as countDistinctRefs, n as RECURSIVE_ANCHOR_SENTINEL, o as findAnchor, r as RefOptions, s as resolveRef, t as ExternalResolver } from "../ref-CPh8rKQ3.mjs";
1
+ import { a as dereference, i as countDistinctRefs, n as RECURSIVE_ANCHOR_SENTINEL, o as findAnchor, r as RefOptions, s as resolveRef, t as ExternalResolver } from "../ref-DdsbekXX.mjs";
2
2
  export { ExternalResolver, RECURSIVE_ANCHOR_SENTINEL, RefOptions, countDistinctRefs, dereference, findAnchor, resolveRef };
@@ -13,8 +13,6 @@
13
13
  * detect cycles, and tracking hop count against `maxHops`. The caller chooses
14
14
  * what to do on cycle or depth-cap via `onCycle` / `onDepthExceeded`.
15
15
  */
16
- /** Maximum number of `$ref` hops permitted by default. */
17
- declare const DEFAULT_REF_CHAIN_MAX_HOPS = 8;
18
16
  /**
19
17
  * Configuration for a single chain resolution.
20
18
  *
@@ -51,7 +49,8 @@ interface ResolveRefChainOptions<T> {
51
49
  readonly onDepthExceeded?: (ref: string) => T | undefined;
52
50
  /**
53
51
  * Maximum number of `$ref` hops permitted before `onDepthExceeded` fires.
54
- * Defaults to `DEFAULT_REF_CHAIN_MAX_HOPS`.
52
+ * Defaults to `MAX_PATH_ITEM_REF_HOPS` from `core/limits.ts`, the
53
+ * canonical cap shared with the OpenAPI Path Item ref walker.
55
54
  */
56
55
  readonly maxHops?: number;
57
56
  /**
@@ -67,4 +66,4 @@ interface ResolveRefChainOptions<T> {
67
66
  */
68
67
  declare function resolveRefChain<T>(initial: T, options: ResolveRefChainOptions<T>): T | undefined;
69
68
  //#endregion
70
- export { DEFAULT_REF_CHAIN_MAX_HOPS, ResolveRefChainOptions, resolveRefChain };
69
+ export { ResolveRefChainOptions, resolveRefChain };
@@ -1,3 +1,4 @@
1
+ import "./limits.mjs";
1
2
  //#region src/core/refChain.ts
2
3
  /**
3
4
  * Generic single-pass `$ref` chain resolver.
@@ -13,8 +14,6 @@
13
14
  * detect cycles, and tracking hop count against `maxHops`. The caller chooses
14
15
  * what to do on cycle or depth-cap via `onCycle` / `onDepthExceeded`.
15
16
  */
16
- /** Maximum number of `$ref` hops permitted by default. */
17
- const DEFAULT_REF_CHAIN_MAX_HOPS = 8;
18
17
  function defaultExtractRef(node) {
19
18
  if (typeof node !== "object" || node === null) return void 0;
20
19
  if (!("$ref" in node)) return void 0;
@@ -41,4 +40,4 @@ function resolveRefChain(initial, options) {
41
40
  }
42
41
  }
43
42
  //#endregion
44
- export { DEFAULT_REF_CHAIN_MAX_HOPS, resolveRefChain };
43
+ export { resolveRefChain };
@@ -1,2 +1,199 @@
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-OaOz-n6-.mjs";
2
- export { AllConstraints, BaseFieldProps, ComponentResolver, HtmlRenderFunction, HtmlRenderProps, HtmlResolver, RESOLVER_KEYS, RenderFunction, RenderProps, buildRenderProps, getHtmlRenderFn, getRenderFunction, mergeHtmlResolvers, mergeResolvers, typeToKey };
1
+ import { E as StringConstraints, f as FileConstraints, j as WalkedField, t as ArrayConstraints, w as SchemaMeta, x as ObjectConstraints, y as NumberConstraints } from "../types-BrYbjC7_.mjs";
2
+
3
+ //#region src/core/renderer.d.ts
4
+ /**
5
+ * Flat intersection of all constraint types.
6
+ * Used in renderer props where the render function receives the union
7
+ * but knows (by resolver key) which subset applies.
8
+ *
9
+ * The walker's discriminated WalkedField enforces type-correct constraints
10
+ * at construction time; the renderer consumes them as this flat type.
11
+ */
12
+ type AllConstraints = StringConstraints & NumberConstraints & ArrayConstraints & ObjectConstraints & FileConstraints;
13
+ /**
14
+ * Properties available on every schema field, regardless of rendering target.
15
+ * Both React and HTML renderers receive these.
16
+ *
17
+ * Per-type schema data — enum values, object fields, array element schema,
18
+ * union options, record key/value types, tuple `prefixItems`, conditional
19
+ * if/then/else clauses, negation `negated`, recursive `refTarget`, literal
20
+ * values — lives on the discriminated `tree`. Renderers narrow on
21
+ * `tree.type` and read from the matching variant; there are no duplicate
22
+ * sibling fields on these props.
23
+ */
24
+ interface BaseFieldProps {
25
+ /** Current field value. */
26
+ value: unknown;
27
+ /** Whether to render as read-only display. */
28
+ readOnly: boolean;
29
+ /** Whether to render as an empty input. */
30
+ writeOnly: boolean;
31
+ /** Schema metadata for this field. */
32
+ meta: SchemaMeta;
33
+ /** Constraints from schema checks. */
34
+ constraints: AllConstraints;
35
+ /** Dot-separated path from root (e.g. "address.city"). */
36
+ path: string;
37
+ /** Example values from the schema's `examples` keyword. */
38
+ examples?: unknown[];
39
+ /** Walked field tree for recursive rendering. */
40
+ tree: WalkedField;
41
+ }
42
+ /**
43
+ * Props for React render functions. Extends BaseFieldProps with:
44
+ * - `onChange` — callback to propagate value changes back to state
45
+ * - `renderChild` — recursively renders a child field, threading onChange
46
+ */
47
+ interface RenderProps extends BaseFieldProps {
48
+ /** Callback to update the field value. */
49
+ onChange: (value: unknown) => void;
50
+ /**
51
+ * Render a child field. Theme adapters call this to recursively render
52
+ * nested structures (object fields, array elements, union options).
53
+ * The resolver and rendering context are already wired in.
54
+ *
55
+ * @param tree - The walked field tree for the child
56
+ * @param value - The child's current value
57
+ * @param onChange - Callback receiving the child's next value
58
+ * @param pathSuffix - Path segment from the parent (e.g. "city",
59
+ * "[0]"). Joined to the parent's path with a dot, or substituted
60
+ * when the parent acts as a transparent wrapper (union options).
61
+ * Required for every container — without it children inherit no
62
+ * path and `inputId()` will throw.
63
+ */
64
+ renderChild: (tree: WalkedField, value: unknown, onChange: (v: unknown) => void, pathSuffix?: string) => unknown;
65
+ }
66
+ /**
67
+ * Props for HTML render functions. Extends BaseFieldProps with:
68
+ * - `renderChild` — recursively renders a child field to HTML string
69
+ *
70
+ * No `onChange` — HTML rendering is pure output with no event handling.
71
+ */
72
+ interface HtmlRenderProps extends BaseFieldProps {
73
+ /**
74
+ * Render a child field to an HTML string. Theme adapters call this
75
+ * to recursively render nested structures.
76
+ *
77
+ * @param tree - The walked field tree for the child
78
+ * @param value - The child's current value
79
+ * @param pathSuffix - Path segment from the parent (e.g. "city",
80
+ * "[0]"). When omitted, the child's description is used as fallback.
81
+ */
82
+ renderChild: (tree: WalkedField, value: unknown, pathSuffix?: string) => string;
83
+ }
84
+ /**
85
+ * Build the `RenderProps` object handed to a resolver render function or a
86
+ * widget. Used by both the server-side `<SchemaView>` (which has no
87
+ * `onChange`) and the client-side `<SchemaComponent>` (which threads an
88
+ * `onChange` callback).
89
+ *
90
+ * When `onChange` is `undefined` the caller is rendering in read-only mode:
91
+ * a noop `onChange` is wired up, `readOnly` is forced to `true`, and
92
+ * `writeOnly` is forced to `false`. Otherwise the editability is taken
93
+ * from `tree.editability`.
94
+ */
95
+ declare function buildRenderProps(tree: WalkedField, value: unknown, onChange: ((next: unknown) => void) | undefined, renderChild: RenderProps["renderChild"], path: string): RenderProps;
96
+ /**
97
+ * Signature for a React render function attached to a
98
+ * {@link ComponentResolver}. Receives the per-field {@link RenderProps}
99
+ * built by the walker and returns any ReactNode-compatible value.
100
+ */
101
+ type RenderFunction = (props: RenderProps) => unknown;
102
+ /**
103
+ * Widget map — maps component hints (from `.meta({ component })`) to render
104
+ * functions. A per-render bag consumed by every renderer surface that
105
+ * dispatches widget overrides; conceptually parallel to
106
+ * {@link ComponentResolver} but keyed by user-supplied hint names rather
107
+ * than schema types.
108
+ *
109
+ * Scoped at three levels in the React renderer:
110
+ *
111
+ * 1. **Per-instance** — `widgets` prop on `<SchemaComponent>`
112
+ * 2. **Context-scoped** — `widgets` prop on `<SchemaProvider>`
113
+ * 3. **Global** — `registerWidget()` (app-wide defaults)
114
+ *
115
+ * Resolution order: instance → context → global → resolver → headless.
116
+ */
117
+ type WidgetMap = ReadonlyMap<string, RenderFunction>;
118
+ /**
119
+ * Theme adapter — maps every schema field type to its React renderer.
120
+ * Unset keys fall back to the headless resolver. Pass to
121
+ * `SchemaProvider` (or `SchemaView.resolver`) to drive every
122
+ * schema-driven render with a specific theme.
123
+ */
124
+ interface ComponentResolver {
125
+ string?: RenderFunction;
126
+ number?: RenderFunction;
127
+ boolean?: RenderFunction;
128
+ null?: RenderFunction;
129
+ enum?: RenderFunction;
130
+ object?: RenderFunction;
131
+ array?: RenderFunction;
132
+ tuple?: RenderFunction;
133
+ record?: RenderFunction;
134
+ union?: RenderFunction;
135
+ discriminatedUnion?: RenderFunction;
136
+ conditional?: RenderFunction;
137
+ negation?: RenderFunction;
138
+ literal?: RenderFunction;
139
+ file?: RenderFunction;
140
+ never?: RenderFunction;
141
+ unknown?: RenderFunction;
142
+ }
143
+ /** An HTML render function returns a string. */
144
+ type HtmlRenderFunction = (props: HtmlRenderProps) => string;
145
+ /**
146
+ * HTML resolver — maps schema types to HTML string renderers.
147
+ * Structurally mirrors ComponentResolver but produces strings.
148
+ */
149
+ interface HtmlResolver {
150
+ string?: HtmlRenderFunction;
151
+ number?: HtmlRenderFunction;
152
+ boolean?: HtmlRenderFunction;
153
+ null?: HtmlRenderFunction;
154
+ enum?: HtmlRenderFunction;
155
+ object?: HtmlRenderFunction;
156
+ array?: HtmlRenderFunction;
157
+ tuple?: HtmlRenderFunction;
158
+ record?: HtmlRenderFunction;
159
+ union?: HtmlRenderFunction;
160
+ discriminatedUnion?: HtmlRenderFunction;
161
+ conditional?: HtmlRenderFunction;
162
+ negation?: HtmlRenderFunction;
163
+ literal?: HtmlRenderFunction;
164
+ file?: HtmlRenderFunction;
165
+ never?: HtmlRenderFunction;
166
+ unknown?: HtmlRenderFunction;
167
+ }
168
+ /**
169
+ * Canonical list of resolver keys, one per {@link WalkedField} variant.
170
+ * Iterated by the resolver merge helpers so adding a new key here is the
171
+ * single point of change when a new field variant is introduced.
172
+ */
173
+ declare const RESOLVER_KEYS: readonly ["string", "number", "boolean", "null", "enum", "object", "array", "tuple", "record", "union", "discriminatedUnion", "conditional", "negation", "literal", "file", "never", "unknown"];
174
+ type ResolverKey = (typeof RESOLVER_KEYS)[number];
175
+ /**
176
+ * Map a schema type to the resolver key that handles it.
177
+ * Every WalkedField variant has a direct resolver key — exhaustive switch
178
+ * ensures new variants surface as a type error rather than silently
179
+ * falling through to "unknown".
180
+ */
181
+ declare function typeToKey(type: WalkedField["type"]): ResolverKey;
182
+ /**
183
+ * Look up the render function for a schema type in a ComponentResolver.
184
+ */
185
+ declare function getRenderFunction(type: WalkedField["type"], resolver: ComponentResolver): RenderFunction | undefined;
186
+ /**
187
+ * Look up the render function for a schema type in an HtmlResolver.
188
+ */
189
+ declare function getHtmlRenderFn(type: WalkedField["type"], resolver: HtmlResolver): HtmlRenderFunction | undefined;
190
+ /**
191
+ * Merge two ComponentResolvers — user values take priority, fallback fills gaps.
192
+ */
193
+ declare function mergeResolvers(user: ComponentResolver, fallback: ComponentResolver): ComponentResolver;
194
+ /**
195
+ * Merge two HtmlResolvers — user values take priority, fallback fills gaps.
196
+ */
197
+ declare function mergeHtmlResolvers(user: HtmlResolver, fallback: HtmlResolver): HtmlResolver;
198
+ //#endregion
199
+ export { AllConstraints, BaseFieldProps, ComponentResolver, HtmlRenderFunction, HtmlRenderProps, HtmlResolver, RESOLVER_KEYS, RenderFunction, RenderProps, WidgetMap, buildRenderProps, getHtmlRenderFn, getRenderFunction, mergeHtmlResolvers, mergeResolvers, typeToKey };
@@ -1,4 +1,4 @@
1
- import { i as DiagnosticsOptions } from "../diagnostics-ByEzkjrA.mjs";
1
+ import { i as DiagnosticsOptions } from "../diagnostics-BTrm3O6J.mjs";
2
2
  import { NodeTransform } from "./normalise.mjs";
3
3
 
4
4
  //#region src/core/swagger2.d.ts
@@ -1,2 +1,2 @@
1
- import { c as normaliseSwagger2Document } from "../normalise-Db1xaxgx.mjs";
1
+ import { c as normaliseSwagger2Document } from "../normalise-DB-Xtjmn.mjs";
2
2
  export { normaliseSwagger2Document };
@@ -1,5 +1,5 @@
1
- import { d as FieldOverrides, u as FieldOverride } from "../types-C2Ay1FEh.mjs";
2
- import { i as MaxRefDepth } from "../limits-DswmqWuy.mjs";
1
+ import { d as FieldOverrides, u as FieldOverride } from "../types-BrYbjC7_.mjs";
2
+ import { i as MaxRefDepth } from "../limits-x4OiyJxh.mjs";
3
3
  import { z } from "zod";
4
4
 
5
5
  //#region src/core/typeInference.d.ts
@@ -790,7 +790,7 @@ type RequestBodySchemaOf<Op, ContentType extends string = DEFAULT_OPENAPI_CONTEN
790
790
  * Without this fallback, querying a concrete status against a document
791
791
  * that declares only `"2XX"` or `"default"` would silently produce
792
792
  * `unknown`. The runtime resolver applies the same fall-through
793
- * behaviour in `resolveResponseFromParsed`.
793
+ * behaviour in `resolveResponse`.
794
794
  */
795
795
  type ResponseSchemaOf<Op, Status extends string, ContentType extends string = DEFAULT_OPENAPI_CONTENT_TYPE> = Op extends {
796
796
  responses: infer Rs extends Record<string, unknown>;
@@ -1,2 +1,2 @@
1
- import { A as UnknownField, B as isNeverField, C as RecordField, D as StringField, E as StringConstraints, F as isDiscriminatedUnionField, G as isStringField, H as isNumberField, I as isEnumField, J as isUnknownField, K as isTupleField, L as isFileField, M as isArrayField, N as isBooleanField, O as TupleField, P as isConditionalField, R as isLiteralField, S as ObjectField, T as SchemaType, U as isObjectField, V as isNullField, W as isRecordField, Y as resolveEditability, _ as NeverField, a as DiscriminatedUnionField, b as NumberField, c as FieldBase, d as FieldOverrides, f as FileConstraints, g as NegationField, h as LiteralField, i as ConditionalField, j as WalkedField, k as UnionField, l as FieldConstraints, m as JsonObject, n as ArrayField, o as Editability, p as FileField, q as isUnionField, r as BooleanField, s as EnumField, t as ArrayConstraints, u as FieldOverride, v as NullField, w as SchemaMeta, x as ObjectConstraints, y as NumberConstraints, z as isNegationField } from "../types-C2Ay1FEh.mjs";
1
+ import { A as UnknownField, B as isNeverField, C as RecordField, D as StringField, E as StringConstraints, F as isDiscriminatedUnionField, G as isStringField, H as isNumberField, I as isEnumField, J as isUnknownField, K as isTupleField, L as isFileField, M as isArrayField, N as isBooleanField, O as TupleField, P as isConditionalField, R as isLiteralField, S as ObjectField, T as SchemaType, U as isObjectField, V as isNullField, W as isRecordField, Y as resolveEditability, _ as NeverField, a as DiscriminatedUnionField, b as NumberField, c as FieldBase, d as FieldOverrides, f as FileConstraints, g as NegationField, h as LiteralField, i as ConditionalField, j as WalkedField, k as UnionField, l as FieldConstraints, m as JsonObject, n as ArrayField, o as Editability, p as FileField, q as isUnionField, r as BooleanField, s as EnumField, t as ArrayConstraints, u as FieldOverride, v as NullField, w as SchemaMeta, x as ObjectConstraints, y as NumberConstraints, z as isNegationField } from "../types-BrYbjC7_.mjs";
2
2
  export { ArrayConstraints, ArrayField, BooleanField, ConditionalField, DiscriminatedUnionField, Editability, EnumField, FieldBase, FieldConstraints, FieldOverride, FieldOverrides, FileConstraints, FileField, JsonObject, LiteralField, NegationField, NeverField, NullField, NumberConstraints, NumberField, ObjectConstraints, ObjectField, RecordField, SchemaMeta, SchemaType, StringConstraints, StringField, TupleField, UnionField, UnknownField, WalkedField, isArrayField, isBooleanField, isConditionalField, isDiscriminatedUnionField, isEnumField, isFileField, isLiteralField, isNegationField, isNeverField, isNullField, isNumberField, isObjectField, isRecordField, isStringField, isTupleField, isUnionField, isUnknownField, resolveEditability };
@@ -1,4 +1,4 @@
1
- import { j as WalkedField } from "../types-C2Ay1FEh.mjs";
1
+ import { j as WalkedField } from "../types-BrYbjC7_.mjs";
2
2
 
3
3
  //#region src/core/unionMatch.d.ts
4
4
  /**
@@ -17,16 +17,24 @@
17
17
  * Returns `true` when the value is either a relative reference (no scheme
18
18
  * component) or an absolute URI using `http`/`https`. Returns `false`
19
19
  * for any other scheme, including dangerous ones like `javascript:` and
20
- * `data:`.
20
+ * `data:`, and for any value that splices ASCII tab/newline/NUL bytes
21
+ * into its scheme — the WHATWG URL parser strips those before scheme
22
+ * detection, so accepting them would let `"java\tscript:alert(1)"`
23
+ * resolve to `javascript:alert(1)` in a browser.
21
24
  */
22
25
  declare function isSafeHyperlink(value: string): boolean;
23
26
  /**
24
27
  * Decide whether `value` is safe to interpolate into a `mailto:` URI.
25
28
  *
26
29
  * The check rejects values that do not match the standard email format
27
- * pattern. The format pattern excludes whitespace, which means a CRLF
28
- * sequence (or its percent-encoded form embedded by the caller) cannot
29
- * pass eliminating the SMTP/`mailto` header-injection vector.
30
+ * pattern. The format pattern excludes whitespace, but it does permit
31
+ * `%`, and a browser decodes percent-escapes at click time so a value
32
+ * such as `"foo%0Abcc:victim@bar.com"` would inject a `Bcc:` header into
33
+ * the resulting `mailto:` URI. Refuse any value containing `%` to close
34
+ * that header-injection vector. The plain email-format regex stays a
35
+ * pure email-syntax check; the additional `%` filter lives here so other
36
+ * callers of the format pattern (form validators, JSON Schema `format:
37
+ * email` checks) are not affected.
30
38
  */
31
39
  declare function isSafeMailtoAddress(value: string): boolean;
32
40
  /**
package/dist/core/uri.mjs CHANGED
@@ -20,6 +20,22 @@ import { EMAIL_FORMAT_PATTERN } from "./formats.mjs";
20
20
  */
21
21
  const ABSOLUTE_URI_SCHEME = /^\s*([a-z][a-z0-9+\-.]*):/i;
22
22
  /**
23
+ * ASCII control characters that the WHATWG URL parser strips before it
24
+ * detects a scheme. A value such as `"java\tscript:alert(1)"` therefore
25
+ * resolves to `javascript:alert(1)` in a browser, even though the literal
26
+ * scheme regex would not match. Splicing any of these characters into a
27
+ * URI is unambiguously hostile, so the safe-scheme check refuses such
28
+ * values outright.
29
+ *
30
+ * Source: WHATWG URL Living Standard §4.4 "URL parsing" — tab and newline
31
+ * (`\t`, `\n`, `\r`) are removed prior to state-machine entry. NUL bytes
32
+ * (`\0`) are likewise stripped by some user agents and never legitimate
33
+ * inside a URI.
34
+ *
35
+ * https://web.archive.org/web/20251101000000*\/https://url.spec.whatwg.org/#concept-basic-url-parser
36
+ */
37
+ const URL_CONTROL_CHARACTERS = /[\t\n\r\0]/;
38
+ /**
23
39
  * Schemes safe to emit unmodified into an `href` attribute. Anything
24
40
  * outside this set — most importantly `javascript:`, `data:`, `vbscript:`
25
41
  * and `file:` — is rejected and rendered as text.
@@ -31,9 +47,13 @@ const SAFE_HYPERLINK_SCHEMES = new Set(["http", "https"]);
31
47
  * Returns `true` when the value is either a relative reference (no scheme
32
48
  * component) or an absolute URI using `http`/`https`. Returns `false`
33
49
  * for any other scheme, including dangerous ones like `javascript:` and
34
- * `data:`.
50
+ * `data:`, and for any value that splices ASCII tab/newline/NUL bytes
51
+ * into its scheme — the WHATWG URL parser strips those before scheme
52
+ * detection, so accepting them would let `"java\tscript:alert(1)"`
53
+ * resolve to `javascript:alert(1)` in a browser.
35
54
  */
36
55
  function isSafeHyperlink(value) {
56
+ if (URL_CONTROL_CHARACTERS.test(value)) return false;
37
57
  const match = ABSOLUTE_URI_SCHEME.exec(value);
38
58
  if (match === null) return true;
39
59
  const scheme = match[1];
@@ -44,11 +64,17 @@ function isSafeHyperlink(value) {
44
64
  * Decide whether `value` is safe to interpolate into a `mailto:` URI.
45
65
  *
46
66
  * The check rejects values that do not match the standard email format
47
- * pattern. The format pattern excludes whitespace, which means a CRLF
48
- * sequence (or its percent-encoded form embedded by the caller) cannot
49
- * pass eliminating the SMTP/`mailto` header-injection vector.
67
+ * pattern. The format pattern excludes whitespace, but it does permit
68
+ * `%`, and a browser decodes percent-escapes at click time so a value
69
+ * such as `"foo%0Abcc:victim@bar.com"` would inject a `Bcc:` header into
70
+ * the resulting `mailto:` URI. Refuse any value containing `%` to close
71
+ * that header-injection vector. The plain email-format regex stays a
72
+ * pure email-syntax check; the additional `%` filter lives here so other
73
+ * callers of the format pattern (form validators, JSON Schema `format:
74
+ * email` checks) are not affected.
50
75
  */
51
76
  function isSafeMailtoAddress(value) {
77
+ if (value.includes("%")) return false;
52
78
  return EMAIL_FORMAT_PATTERN.test(value);
53
79
  }
54
80
  /**
@@ -1,6 +1,6 @@
1
- import { A as UnknownField, D as StringField, b as NumberField, c as FieldBase, j as WalkedField, o as Editability, p as FileField, r as BooleanField, v as NullField, w as SchemaMeta } from "../types-C2Ay1FEh.mjs";
2
- import { i as DiagnosticsOptions } from "../diagnostics-ByEzkjrA.mjs";
3
- import { t as ExternalResolver } from "../ref-CPh8rKQ3.mjs";
1
+ import { A as UnknownField, D as StringField, b as NumberField, c as FieldBase, d as FieldOverrides, j as WalkedField, o as Editability, p as FileField, r as BooleanField, v as NullField, w as SchemaMeta } from "../types-BrYbjC7_.mjs";
2
+ import { i as DiagnosticsOptions } from "../diagnostics-BTrm3O6J.mjs";
3
+ import { t as ExternalResolver } from "../ref-DdsbekXX.mjs";
4
4
 
5
5
  //#region src/core/walkBuilders.d.ts
6
6
  /** Read a key from a JSON object, returning the value when it is a string and `undefined` otherwise. */
@@ -14,13 +14,25 @@ declare function getObject(obj: Record<string, unknown>, key: string): Record<st
14
14
  * overrides, the root document for cross-document `$ref` resolution, a
15
15
  * diagnostics sink, and an external `$ref` resolver.
16
16
  *
17
+ * `WalkOptions` is generic in the schema's value type so callers that
18
+ * walk a typed schema can carry `FieldOverrides<T>` through. The
19
+ * default `T = unknown` preserves the loose runtime record shape for
20
+ * existing non-generic callers — `Record<string, unknown>`.
21
+ *
17
22
  * @group Walkers
18
23
  */
19
- interface WalkOptions {
24
+ interface WalkOptions<T = unknown> {
20
25
  componentMeta?: SchemaMeta | undefined;
21
26
  rootMeta?: SchemaMeta | undefined;
22
- /** Nested field overrides — same shape as the schema. */
23
- fieldOverrides?: Record<string, unknown> | undefined;
27
+ /**
28
+ * Nested field overrides — same shape as the schema.
29
+ *
30
+ * Typed against `FieldOverrides<T>` when a schema value type is
31
+ * supplied; falls back to `Record<string, unknown>` for the
32
+ * default `T = unknown` so the loose runtime shape continues to
33
+ * compile.
34
+ */
35
+ fieldOverrides?: unknown extends T ? Record<string, unknown> | undefined : FieldOverrides<T> | undefined;
24
36
  /** The root document for $ref resolution. */
25
37
  rootDocument?: Record<string, unknown> | undefined;
26
38
  /** Diagnostics channel for surfacing silent fallbacks. */
@@ -107,10 +107,12 @@ function buildStringField(schema, ctx) {
107
107
  }
108
108
  /** Build a walked `NumberField` from a JSON Schema node. */
109
109
  function buildNumberField(schema, ctx) {
110
+ const isInteger = schema.type === "integer";
110
111
  return {
111
112
  ...buildBase(schema, ctx),
112
113
  type: "number",
113
- constraints: extractNumberConstraints(schema)
114
+ constraints: extractNumberConstraints(schema),
115
+ isInteger
114
116
  };
115
117
  }
116
118
  /** Build a walked `BooleanField` from a JSON Schema node. */
@@ -1,4 +1,4 @@
1
- import { j as WalkedField } from "../types-C2Ay1FEh.mjs";
1
+ import { j as WalkedField } from "../types-BrYbjC7_.mjs";
2
2
  import { WalkOptions } from "./walkBuilders.mjs";
3
3
 
4
4
  //#region src/core/walker.d.ts
@@ -323,6 +323,11 @@ function walkBoolean(schema, ctx) {
323
323
  return buildBooleanField(schema, ctx);
324
324
  }
325
325
  function walkEnum(schema, enumValues, ctx) {
326
+ if (enumValues.length === 0) emitDiagnostic(ctx.diagnostics, {
327
+ code: "enum-empty",
328
+ message: "`enum` array is empty; per Draft 2020-12 §6.1.2 it must be non-empty. The schema describes an unsatisfiable value set.",
329
+ pointer: ctx.pointer
330
+ });
326
331
  return {
327
332
  ...buildBase(schema, ctx),
328
333
  type: "enum",
@@ -18,7 +18,7 @@
18
18
  *
19
19
  * @group Diagnostics
20
20
  */
21
- type DiagnosticCode = "allof-conflict" | "assumed-draft" | "bare-exclusive-bound" | "conditional-fallback" | "cross-schema-relative-ref-unsupported" | "cyclic-header-ref" | "cyclic-link-ref" | "cyclic-parameter-ref" | "cyclic-path-item-ref" | "dependencies-conflict" | "dependent-required-invalid" | "depth-exceeded" | "discriminator-inconsistent" | "divisible-by-conflict" | "doc-not-object" | "dropped-swagger-feature" | "header-ref-too-deep" | "duplicate-body-parameter" | "duplicate-operation-id" | "dynamic-ref-degraded" | "enum-value-filtered" | "external-ref" | "invalid-const" | "invalid-id-fragment" | "keyword-out-of-draft" | "link-ref-too-deep" | "legacy-dependencies-split" | "legacy-dependencies-split-2019" | "non-json-media-type-fallback" | "parameter-missing-schema" | "parameter-ref-too-deep" | "path-item-ref-too-deep" | "path-webhook-name-collision" | "pattern-invalid" | "prototype-polluting-property" | "recursive-anchor-collision" | "relative-ref-resolved" | "required-non-string" | "schema-allof-incompatible" | "swagger-collection-format-dropped" | "swagger-cyclic-parameter-ref" | "swagger-invalid-file-parameter" | "swagger-malformed-oauth-flow" | "swagger-missing-consumes" | "swagger-missing-host" | "type-mismatch" | "type-negation-fallback" | "unknown-format" | "unknown-json-schema-dialect" | "unknown-keyword" | "unknown-openapi-version" | "unknown-parameter-location" | "unknown-security-scheme-type" | "unresolved-ref" | "unsupported-type" | "zod-codec-nested-output-only" | "zod-codec-output-only" | "zod-preprocess-output-only" | "zod-promise-nested-unwrap";
21
+ type DiagnosticCode = "allof-conflict" | "assumed-draft" | "bare-exclusive-bound" | "conditional-fallback" | "cross-schema-relative-ref-unsupported" | "cyclic-header-ref" | "cyclic-link-ref" | "cyclic-parameter-ref" | "cyclic-path-item-ref" | "dependencies-conflict" | "dependent-required-invalid" | "depth-exceeded" | "discriminator-duplicate" | "discriminator-inconsistent" | "divisible-by-conflict" | "doc-not-object" | "dropped-swagger-feature" | "header-ref-too-deep" | "duplicate-body-parameter" | "duplicate-operation-id" | "dynamic-ref-degraded" | "enum-empty" | "enum-value-filtered" | "external-ref" | "invalid-const" | "invalid-id-fragment" | "keyword-out-of-draft" | "link-ref-too-deep" | "legacy-dependencies-split" | "legacy-dependencies-split-2019" | "non-json-media-type-fallback" | "parameter-missing-schema" | "parameter-ref-too-deep" | "path-item-ref-too-deep" | "path-webhook-name-collision" | "pattern-invalid" | "prototype-polluting-property" | "recursive-anchor-collision" | "relative-ref-resolved" | "required-non-string" | "schema-allof-incompatible" | "swagger-collection-format-dropped" | "swagger-cyclic-parameter-ref" | "swagger-invalid-file-parameter" | "swagger-malformed-oauth-flow" | "swagger-missing-consumes" | "swagger-missing-host" | "type-mismatch" | "type-negation-fallback" | "unknown-format" | "unknown-json-schema-dialect" | "unknown-keyword" | "unknown-openapi-version" | "unknown-parameter-location" | "unknown-security-scheme-type" | "unresolved-ref" | "unsupported-type" | "zod-codec-nested-output-only" | "zod-codec-output-only" | "zod-preprocess-output-only" | "zod-promise-nested-unwrap";
22
22
  /**
23
23
  * A single diagnostic emitted during schema processing.
24
24
  *
@@ -1,4 +1,4 @@
1
- import { T as SchemaType } from "./types-C2Ay1FEh.mjs";
1
+ import { T as SchemaType } from "./types-BrYbjC7_.mjs";
2
2
 
3
3
  //#region src/core/errors.d.ts
4
4
  /**
@@ -1,5 +1,6 @@
1
- import { j as WalkedField } from "../types-C2Ay1FEh.mjs";
2
- import { t as AllConstraints } from "../renderer-OaOz-n6-.mjs";
1
+ import { j as WalkedField } from "../types-BrYbjC7_.mjs";
2
+ import { AllConstraints } from "../core/renderer.mjs";
3
+ import { constraintHint } from "../core/constraintHint.mjs";
3
4
  import { HtmlAttributes, HtmlNode } from "./html.mjs";
4
5
 
5
6
  //#region src/html/a11y.d.ts
@@ -27,11 +28,6 @@ declare function buildInputId(path: string, key: string): string;
27
28
  * the HTML renderers.
28
29
  */
29
30
  declare function buildHintId(inputId: string): string;
30
- /**
31
- * Build a human-readable constraint description string.
32
- * Returns undefined if no constraints are present.
33
- */
34
- declare function constraintHint(c: AllConstraints): string | undefined;
35
31
  /**
36
32
  * Build `aria-required` attribute for required fields.
37
33
  * Returns an object to spread into `h()` attributes, or empty object.