schema-components 1.18.1 → 1.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/dist/core/adapter.d.mts +2 -2
  2. package/dist/core/adapter.mjs +128 -15
  3. package/dist/core/constraints.d.mts +2 -2
  4. package/dist/core/diagnostics.d.mts +1 -1
  5. package/dist/core/errors.d.mts +1 -1
  6. package/dist/core/errors.mjs +15 -1
  7. package/dist/core/fieldOrder.d.mts +1 -1
  8. package/dist/core/formats.d.mts +21 -14
  9. package/dist/core/formats.mjs +96 -4
  10. package/dist/core/merge.d.mts +1 -1
  11. package/dist/core/normalise.d.mts +38 -5
  12. package/dist/core/normalise.mjs +2 -2
  13. package/dist/core/openapi30.d.mts +33 -4
  14. package/dist/core/openapi30.mjs +2 -2
  15. package/dist/core/ref.d.mts +1 -1
  16. package/dist/core/renderer.d.mts +1 -1
  17. package/dist/core/renderer.mjs +7 -2
  18. package/dist/core/swagger2.d.mts +1 -1
  19. package/dist/core/swagger2.mjs +1 -1
  20. package/dist/core/typeInference.d.mts +2 -2
  21. package/dist/core/types.d.mts +1 -1
  22. package/dist/core/uri.d.mts +41 -0
  23. package/dist/core/uri.mjs +76 -0
  24. package/dist/core/version.d.mts +2 -2
  25. package/dist/core/version.mjs +43 -9
  26. package/dist/core/walkBuilders.d.mts +3 -3
  27. package/dist/core/walker.d.mts +1 -1
  28. package/dist/core/walker.mjs +50 -3
  29. package/dist/{diagnostics-BYk63jsC.d.mts → diagnostics-CbBPsxSt.d.mts} +1 -1
  30. package/dist/{errors-C5zRC2PU.d.mts → errors-C2iABcn9.d.mts} +14 -2
  31. package/dist/html/a11y.d.mts +2 -2
  32. package/dist/html/renderToHtml.d.mts +2 -2
  33. package/dist/html/renderToHtmlStream.d.mts +2 -2
  34. package/dist/html/renderers.d.mts +2 -2
  35. package/dist/html/renderers.mjs +37 -2
  36. package/dist/html/streamRenderers.d.mts +2 -2
  37. package/dist/normalise-CMMEl4cd.mjs +1306 -0
  38. package/dist/openapi/ApiCallbacks.d.mts +1 -1
  39. package/dist/openapi/ApiLinks.d.mts +1 -1
  40. package/dist/openapi/ApiResponseHeaders.d.mts +1 -1
  41. package/dist/openapi/ApiSecurity.d.mts +1 -1
  42. package/dist/openapi/bundle.mjs +2 -0
  43. package/dist/openapi/components.d.mts +2 -2
  44. package/dist/openapi/components.mjs +20 -5
  45. package/dist/openapi/parser.d.mts +1 -1
  46. package/dist/openapi/parser.mjs +6 -1
  47. package/dist/openapi/resolve.d.mts +17 -6
  48. package/dist/openapi/resolve.mjs +45 -7
  49. package/dist/react/SchemaComponent.d.mts +21 -9
  50. package/dist/react/SchemaComponent.mjs +3 -13
  51. package/dist/react/SchemaView.d.mts +3 -3
  52. package/dist/react/SchemaView.mjs +1 -0
  53. package/dist/react/fieldPath.d.mts +1 -1
  54. package/dist/react/headless.d.mts +7 -1
  55. package/dist/react/headless.mjs +13 -1
  56. package/dist/react/headlessRenderers.d.mts +54 -3
  57. package/dist/react/headlessRenderers.mjs +153 -3
  58. package/dist/{ref-Ckt5liZs.d.mts → ref-C8JbwfiS.d.mts} +1 -1
  59. package/dist/{renderer-BAGoX4AK.d.mts → renderer-SOIbJBtk.d.mts} +9 -3
  60. package/dist/themes/mantine.d.mts +1 -1
  61. package/dist/themes/mui.d.mts +1 -1
  62. package/dist/themes/radix.d.mts +1 -1
  63. package/dist/themes/shadcn.d.mts +1 -1
  64. package/dist/{typeInference-5JiqIZ8t.d.mts → typeInference-CDoD_LZ_.d.mts} +187 -42
  65. package/dist/{types-D_5ST7SS.d.mts → types-C9zw9wbX.d.mts} +6 -0
  66. package/dist/{version-B5NV-35j.d.mts → version-D-u7aMfy.d.mts} +43 -1
  67. package/package.json +1 -1
  68. package/dist/normalise-tL9FckAk.mjs +0 -748
@@ -1,4 +1,4 @@
1
- import { i as DiagnosticsOptions } from "../diagnostics-BYk63jsC.mjs";
1
+ import { i as DiagnosticsOptions } from "../diagnostics-CbBPsxSt.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 normaliseSwagger2Document } from "../normalise-tL9FckAk.mjs";
1
+ import { o as normaliseSwagger2Document } from "../normalise-CMMEl4cd.mjs";
2
2
  export { normaliseSwagger2Document };
@@ -1,2 +1,2 @@
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
+ import { a as InferResponseFields, c as PathOfType, d as TypeAtPath, f as UnrepresentableZodSchemaError, h as __SchemaInferenceFellBack, i as InferRequestBodyFields, l as RejectUnrepresentableZod, m as UnsafeFields, n as FromJSONSchema, o as OpenAPIRequestBodyType, p as UnrepresentableZodType, r as InferParameterOverrides, s as OpenAPIResponseType, t as DEFAULT_MAX_DEPTH, u as ResolveOpenAPIRef } from "../typeInference-CDoD_LZ_.mjs";
2
+ export { DEFAULT_MAX_DEPTH, FromJSONSchema, InferParameterOverrides, InferRequestBodyFields, InferResponseFields, OpenAPIRequestBodyType, OpenAPIResponseType, PathOfType, RejectUnrepresentableZod, ResolveOpenAPIRef, TypeAtPath, UnrepresentableZodSchemaError, UnrepresentableZodType, UnsafeFields, __SchemaInferenceFellBack };
@@ -1,2 +1,2 @@
1
- import { A as UnionField, B as isNegationField, C as RecordField, D as StringConstraints, E as SchemaType, F as isConditionalField, G as isRecordField, H as isNullField, I as isDiscriminatedUnionField, J as isTupleField, K as isRecursiveField, L as isEnumField, M as WalkedField, N as isArrayField, O as StringField, P as isBooleanField, R as isFileField, S as ObjectField, T as SchemaMeta, U as isNumberField, V as isNeverField, W as isObjectField, X as isUnknownField, Y as isUnionField, Z 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 UnknownField, k as TupleField, l as FieldConstraints, m as JsonObject, n as ArrayField, o as Editability, p as FileField, q as isStringField, r as BooleanField, s as EnumField, t as ArrayConstraints, u as FieldOverride, v as NullField, w as RecursiveField, x as ObjectConstraints, y as NumberConstraints, z as isLiteralField } from "../types-D_5ST7SS.mjs";
1
+ import { A as UnionField, B as isNegationField, C as RecordField, D as StringConstraints, E as SchemaType, F as isConditionalField, G as isRecordField, H as isNullField, I as isDiscriminatedUnionField, J as isTupleField, K as isRecursiveField, L as isEnumField, M as WalkedField, N as isArrayField, O as StringField, P as isBooleanField, R as isFileField, S as ObjectField, T as SchemaMeta, U as isNumberField, V as isNeverField, W as isObjectField, X as isUnknownField, Y as isUnionField, Z 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 UnknownField, k as TupleField, l as FieldConstraints, m as JsonObject, n as ArrayField, o as Editability, p as FileField, q as isStringField, r as BooleanField, s as EnumField, t as ArrayConstraints, u as FieldOverride, v as NullField, w as RecursiveField, x as ObjectConstraints, y as NumberConstraints, z as isLiteralField } from "../types-C9zw9wbX.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, RecursiveField, SchemaMeta, SchemaType, StringConstraints, StringField, TupleField, UnionField, UnknownField, WalkedField, isArrayField, isBooleanField, isConditionalField, isDiscriminatedUnionField, isEnumField, isFileField, isLiteralField, isNegationField, isNeverField, isNullField, isNumberField, isObjectField, isRecordField, isRecursiveField, isStringField, isTupleField, isUnionField, isUnknownField, resolveEditability };
@@ -0,0 +1,41 @@
1
+ //#region src/core/uri.d.ts
2
+ /**
3
+ * URI safety helpers shared by HTML and React renderers.
4
+ *
5
+ * User-supplied URI values flow into anchor `href` attributes. Without
6
+ * scheme validation a value such as `javascript:alert(1)` becomes a
7
+ * clickable XSS sink — HTML escaping does not help, since the dangerous
8
+ * payload sits inside the scheme rather than the body of the attribute.
9
+ *
10
+ * These helpers exist so the HTML renderer (`html/renderers.ts`) and the
11
+ * React renderer (`react/headlessRenderers.tsx`) apply identical rules
12
+ * when deciding whether a string is safe to render as an `href`.
13
+ */
14
+ /**
15
+ * Decide whether `value` is safe to use as an anchor `href`.
16
+ *
17
+ * Returns `true` when the value is either a relative reference (no scheme
18
+ * component) or an absolute URI using `http`/`https`. Returns `false`
19
+ * for any other scheme, including dangerous ones like `javascript:` and
20
+ * `data:`.
21
+ */
22
+ declare function isSafeHyperlink(value: string): boolean;
23
+ /**
24
+ * Decide whether `value` is safe to interpolate into a `mailto:` URI.
25
+ *
26
+ * 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
+ */
31
+ declare function isSafeMailtoAddress(value: string): boolean;
32
+ /**
33
+ * Decide whether `key` is one of the prototype-polluting property names
34
+ * (`__proto__`, `constructor`, `prototype`).
35
+ *
36
+ * Used by JSON Pointer resolvers and by the JSON Schema `properties`
37
+ * walker to refuse traversal into these names.
38
+ */
39
+ declare function isPrototypePollutingKey(key: string): boolean;
40
+ //#endregion
41
+ export { isPrototypePollutingKey, isSafeHyperlink, isSafeMailtoAddress };
@@ -0,0 +1,76 @@
1
+ import { EMAIL_FORMAT_PATTERN } from "./formats.mjs";
2
+ //#region src/core/uri.ts
3
+ /**
4
+ * URI safety helpers shared by HTML and React renderers.
5
+ *
6
+ * User-supplied URI values flow into anchor `href` attributes. Without
7
+ * scheme validation a value such as `javascript:alert(1)` becomes a
8
+ * clickable XSS sink — HTML escaping does not help, since the dangerous
9
+ * payload sits inside the scheme rather than the body of the attribute.
10
+ *
11
+ * These helpers exist so the HTML renderer (`html/renderers.ts`) and the
12
+ * React renderer (`react/headlessRenderers.tsx`) apply identical rules
13
+ * when deciding whether a string is safe to render as an `href`.
14
+ */
15
+ /**
16
+ * Match the scheme portion of an absolute URI (RFC 3986 production
17
+ * `scheme ":"`). Leading ASCII whitespace is tolerated because browsers
18
+ * strip it before parsing the scheme; any other prefix (including raw
19
+ * control characters) keeps the value out of the safe-scheme branch.
20
+ */
21
+ const ABSOLUTE_URI_SCHEME = /^\s*([a-z][a-z0-9+\-.]*):/i;
22
+ /**
23
+ * Schemes safe to emit unmodified into an `href` attribute. Anything
24
+ * outside this set — most importantly `javascript:`, `data:`, `vbscript:`
25
+ * and `file:` — is rejected and rendered as text.
26
+ */
27
+ const SAFE_HYPERLINK_SCHEMES = new Set(["http", "https"]);
28
+ /**
29
+ * Decide whether `value` is safe to use as an anchor `href`.
30
+ *
31
+ * Returns `true` when the value is either a relative reference (no scheme
32
+ * component) or an absolute URI using `http`/`https`. Returns `false`
33
+ * for any other scheme, including dangerous ones like `javascript:` and
34
+ * `data:`.
35
+ */
36
+ function isSafeHyperlink(value) {
37
+ const match = ABSOLUTE_URI_SCHEME.exec(value);
38
+ if (match === null) return true;
39
+ const scheme = match[1];
40
+ if (scheme === void 0) return false;
41
+ return SAFE_HYPERLINK_SCHEMES.has(scheme.toLowerCase());
42
+ }
43
+ /**
44
+ * Decide whether `value` is safe to interpolate into a `mailto:` URI.
45
+ *
46
+ * 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.
50
+ */
51
+ function isSafeMailtoAddress(value) {
52
+ return EMAIL_FORMAT_PATTERN.test(value);
53
+ }
54
+ /**
55
+ * Property names that must never be traversed via dynamic indexing on an
56
+ * untrusted object. Walking into any of these returns `Object.prototype`
57
+ * (or similar) and lets an attacker plant fields visible to every plain
58
+ * object in the runtime.
59
+ */
60
+ const PROTOTYPE_POLLUTING_KEYS = new Set([
61
+ "__proto__",
62
+ "constructor",
63
+ "prototype"
64
+ ]);
65
+ /**
66
+ * Decide whether `key` is one of the prototype-polluting property names
67
+ * (`__proto__`, `constructor`, `prototype`).
68
+ *
69
+ * Used by JSON Pointer resolvers and by the JSON Schema `properties`
70
+ * walker to refuse traversal into these names.
71
+ */
72
+ function isPrototypePollutingKey(key) {
73
+ return PROTOTYPE_POLLUTING_KEYS.has(key);
74
+ }
75
+ //#endregion
76
+ export { isPrototypePollutingKey, isSafeHyperlink, isSafeMailtoAddress };
@@ -1,2 +1,2 @@
1
- import { a as detectOpenApiVersion, c as isOpenApi30, i as detectJsonSchemaDraft, l as isOpenApi31, n as JsonSchemaDraft, o as inferJsonSchemaDraft, r as OpenApiVersionInfo, s as inferJsonSchemaDraftWithReason, t as InferredDraft, u as isSwagger2 } from "../version-B5NV-35j.mjs";
2
- export { InferredDraft, JsonSchemaDraft, OpenApiVersionInfo, detectJsonSchemaDraft, detectOpenApiVersion, inferJsonSchemaDraft, inferJsonSchemaDraftWithReason, isOpenApi30, isOpenApi31, isSwagger2 };
1
+ import { a as detectJsonSchemaDraft, c as inferJsonSchemaDraftWithReason, d as isSwagger2, f as matchJsonSchemaDraftUri, i as OpenApiVersionInfo, l as isOpenApi30, n as JsonSchemaDialectInfo, o as detectOpenApiVersion, p as readJsonSchemaDialect, r as JsonSchemaDraft, s as inferJsonSchemaDraft, t as InferredDraft, u as isOpenApi31 } from "../version-D-u7aMfy.mjs";
2
+ export { InferredDraft, JsonSchemaDialectInfo, JsonSchemaDraft, OpenApiVersionInfo, detectJsonSchemaDraft, detectOpenApiVersion, inferJsonSchemaDraft, inferJsonSchemaDraftWithReason, isOpenApi30, isOpenApi31, isSwagger2, matchJsonSchemaDraftUri, readJsonSchemaDialect };
@@ -15,6 +15,22 @@ const DRAFT_URIS = new Map([
15
15
  ["https://json-schema.org/draft-04/schema#", "draft-04"]
16
16
  ]);
17
17
  /**
18
+ * Match a `$schema` URI string to a known draft. Returns `undefined`
19
+ * when the URI matches none of the documented Draft 04 – Draft 2020-12
20
+ * schema URIs (including the known prefix patterns) — callers can use
21
+ * this to distinguish an authoritative match from a silent fallback.
22
+ */
23
+ function matchJsonSchemaDraftUri(uri) {
24
+ const exact = DRAFT_URIS.get(uri);
25
+ if (exact !== void 0) return exact;
26
+ for (const [draftUri, draft] of DRAFT_URIS) if (uri.startsWith(draftUri) || uri === draftUri) return draft;
27
+ if (uri.includes("/draft/2020-12/")) return "draft-2020-12";
28
+ if (uri.includes("/draft/2019-09/")) return "draft-2019-09";
29
+ if (uri.includes("/draft-07")) return "draft-07";
30
+ if (uri.includes("/draft-06")) return "draft-06";
31
+ if (uri.includes("/draft-04")) return "draft-04";
32
+ }
33
+ /**
18
34
  * Detect the JSON Schema draft version from a schema's `$schema` URI.
19
35
  * When `$schema` is absent, uses heuristic keyword detection via
20
36
  * `inferJsonSchemaDraft` to guess the draft version.
@@ -24,14 +40,8 @@ const DRAFT_URIS = new Map([
24
40
  function detectJsonSchemaDraft(schema) {
25
41
  const $schema = schema.$schema;
26
42
  if (typeof $schema === "string") {
27
- const exact = DRAFT_URIS.get($schema);
28
- if (exact !== void 0) return exact;
29
- for (const [uri, draft] of DRAFT_URIS) if ($schema.startsWith(uri) || $schema === uri) return draft;
30
- if ($schema.includes("/draft/2020-12/")) return "draft-2020-12";
31
- if ($schema.includes("/draft/2019-09/")) return "draft-2019-09";
32
- if ($schema.includes("/draft-07")) return "draft-07";
33
- if ($schema.includes("/draft-06")) return "draft-06";
34
- if ($schema.includes("/draft-04")) return "draft-04";
43
+ const matched = matchJsonSchemaDraftUri($schema);
44
+ if (matched !== void 0) return matched;
35
45
  return "draft-2020-12";
36
46
  }
37
47
  return inferJsonSchemaDraft(schema);
@@ -147,5 +157,29 @@ function isOpenApi31(version) {
147
157
  function isSwagger2(version) {
148
158
  return version.major === 2;
149
159
  }
160
+ /**
161
+ * Inspect an OpenAPI 3.1 document for a `jsonSchemaDialect` declaration.
162
+ *
163
+ * Per the OpenAPI 3.1 spec, an OpenAPI document may declare the default
164
+ * JSON Schema dialect for its Schema Objects via the top-level
165
+ * `jsonSchemaDialect` URI. Real-world 3.1 documents overwhelmingly omit
166
+ * the keyword and rely on the spec-defined Draft 2020-12 default — this
167
+ * helper surfaces the declaration so the normaliser can either honour a
168
+ * known dialect or emit a diagnostic for an unknown one.
169
+ */
170
+ function readJsonSchemaDialect(doc) {
171
+ const value = doc.jsonSchemaDialect;
172
+ if (typeof value !== "string") return { kind: "absent" };
173
+ const draft = matchJsonSchemaDraftUri(value);
174
+ if (draft === void 0) return {
175
+ kind: "unknown",
176
+ uri: value
177
+ };
178
+ return {
179
+ kind: "known",
180
+ uri: value,
181
+ draft
182
+ };
183
+ }
150
184
  //#endregion
151
- export { detectJsonSchemaDraft, detectOpenApiVersion, inferJsonSchemaDraft, inferJsonSchemaDraftWithReason, isOpenApi30, isOpenApi31, isSwagger2 };
185
+ export { detectJsonSchemaDraft, detectOpenApiVersion, inferJsonSchemaDraft, inferJsonSchemaDraftWithReason, isOpenApi30, isOpenApi31, isSwagger2, matchJsonSchemaDraftUri, readJsonSchemaDialect };
@@ -1,6 +1,6 @@
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-BYk63jsC.mjs";
3
- import { t as ExternalResolver } from "../ref-Ckt5liZs.mjs";
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-C9zw9wbX.mjs";
2
+ import { i as DiagnosticsOptions } from "../diagnostics-CbBPsxSt.mjs";
3
+ import { t as ExternalResolver } from "../ref-C8JbwfiS.mjs";
4
4
 
5
5
  //#region src/core/walkBuilders.d.ts
6
6
  declare function getString(obj: Record<string, unknown>, key: string): string | undefined;
@@ -1,4 +1,4 @@
1
- import { M as WalkedField } from "../types-D_5ST7SS.mjs";
1
+ import { M as WalkedField } from "../types-C9zw9wbX.mjs";
2
2
  import { WalkOptions } from "./walkBuilders.mjs";
3
3
 
4
4
  //#region src/core/walker.d.ts
@@ -3,6 +3,7 @@ import { appendPointer, emitDiagnostic } from "./diagnostics.mjs";
3
3
  import { countDistinctRefs, resolveRef } from "./ref.mjs";
4
4
  import { extractArrayConstraints, extractObjectConstraints, stripInapplicableConstraints } from "./constraints.mjs";
5
5
  import { ANNOTATION_SIBLINGS, detectDiscriminated, mergeAllOf, mergeRefSiblings, normaliseAnyOf } from "./merge.mjs";
6
+ import { isPrototypePollutingKey } from "./uri.mjs";
6
7
  import { buildBase, buildBooleanField, buildFileField, buildNullField, buildNumberField, buildStringField, buildUnknownField, extractChildOverride, extractSchemaMetaFields, getArray, getObject, getString, isPrimitive, walkDependentRequiredMap, walkSubSchemaMap, withoutKeys } from "./walkBuilders.mjs";
7
8
  //#region src/core/walker.ts
8
9
  /**
@@ -236,11 +237,28 @@ function walkBoolean(schema, ctx) {
236
237
  return buildBooleanField(schema, ctx);
237
238
  }
238
239
  function walkEnum(schema, enumValues, ctx) {
240
+ const accepted = [];
241
+ for (let i = 0; i < enumValues.length; i++) {
242
+ const v = enumValues[i];
243
+ if (isPrimitive(v)) {
244
+ accepted.push(v);
245
+ continue;
246
+ }
247
+ emitDiagnostic(ctx.diagnostics, {
248
+ code: "enum-value-filtered",
249
+ message: `enum value at index ${String(i)} is not a primitive (${v === void 0 ? "undefined" : typeof v}); dropping the entry`,
250
+ pointer: appendPointer(ctx.pointer, `enum/${String(i)}`),
251
+ detail: {
252
+ index: i,
253
+ value: v
254
+ }
255
+ });
256
+ }
239
257
  return {
240
258
  ...buildBase(schema, ctx),
241
259
  type: "enum",
242
260
  constraints: {},
243
- enumValues: enumValues.filter((v) => typeof v === "string" || typeof v === "number" || typeof v === "boolean" || v === null)
261
+ enumValues: accepted
244
262
  };
245
263
  }
246
264
  function walkLiteral(schema, ctx) {
@@ -254,9 +272,35 @@ function walkLiteral(schema, ctx) {
254
272
  };
255
273
  }
256
274
  function walkObject(schema, properties, ctx) {
257
- const requiredFields = getArray(schema, "required")?.filter((r) => typeof r === "string") ?? [];
275
+ const required = getArray(schema, "required");
276
+ const requiredFields = [];
277
+ if (required !== void 0) for (let i = 0; i < required.length; i++) {
278
+ const r = required[i];
279
+ if (typeof r === "string") {
280
+ requiredFields.push(r);
281
+ continue;
282
+ }
283
+ emitDiagnostic(ctx.diagnostics, {
284
+ code: "required-non-string",
285
+ message: `required[${String(i)}] is not a string (${r === null ? "null" : typeof r}); dropping the entry`,
286
+ pointer: appendPointer(ctx.pointer, `required/${String(i)}`),
287
+ detail: {
288
+ index: i,
289
+ value: r
290
+ }
291
+ });
292
+ }
258
293
  const fields = {};
259
294
  for (const [key, propSchema] of Object.entries(properties)) {
295
+ if (isPrototypePollutingKey(key)) {
296
+ emitDiagnostic(ctx.diagnostics, {
297
+ code: "prototype-polluting-property",
298
+ message: `Refusing to register prototype-polluting property name: ${key}`,
299
+ pointer: appendPointer(ctx.pointer, key),
300
+ detail: { propertyName: key }
301
+ });
302
+ continue;
303
+ }
260
304
  const childOverride = extractChildOverride(ctx.fieldOverrides, key);
261
305
  const isRequired = requiredFields.includes(key);
262
306
  const childCtx = {
@@ -343,11 +387,14 @@ function walkArray(schema, ctx) {
343
387
  const prefixItems = getArray(schema, "prefixItems");
344
388
  if (prefixItems !== void 0) {
345
389
  const walkedItems = prefixItems.filter(isObject).map((item) => walkNode(item, ctx));
390
+ const restSchema = getObject(schema, "items");
391
+ const restItems = restSchema !== void 0 ? walkNode(restSchema, ctx) : void 0;
346
392
  return {
347
393
  ...buildBase(schema, ctx),
348
394
  type: "tuple",
349
395
  constraints: extractArrayConstraints(schema),
350
- prefixItems: walkedItems
396
+ prefixItems: walkedItems,
397
+ ...restItems !== void 0 ? { restItems } : {}
351
398
  };
352
399
  }
353
400
  const unevaluatedItemsSchema = getObject(schema, "unevaluatedItems");
@@ -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" | "allof-conflict" | "discriminator-inconsistent";
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" | "divisible-by-conflict" | "legacy-dependencies-split" | "dependent-required-invalid" | "unknown-json-schema-dialect" | "relative-ref-resolved" | "bare-exclusive-bound" | "enum-value-filtered" | "required-non-string" | "pattern-invalid" | "duplicate-body-parameter" | "prototype-polluting-property";
20
20
  /**
21
21
  * A single diagnostic emitted during schema processing.
22
22
  */
@@ -27,8 +27,20 @@ declare class SchemaError extends Error {
27
27
  * JSON Schema, missing OpenAPI ref, unsupported ref format.
28
28
  */
29
29
  declare class SchemaNormalisationError extends SchemaError {
30
- readonly kind: "invalid-zod" | "zod3-unsupported" | "invalid-json-schema" | "openapi-missing-ref" | "openapi-invalid" | "unknown";
31
- constructor(message: string, schema: unknown, kind: SchemaNormalisationError["kind"]);
30
+ readonly kind: "invalid-zod" | "zod3-unsupported" | "zod-transform-unsupported" | "zod-type-unrepresentable" | "zod-conversion-failed" | "invalid-json-schema" | "openapi-missing-ref" | "openapi-invalid" | "unknown";
31
+ /**
32
+ * For `zod-type-unrepresentable`, the offending Zod type name
33
+ * (e.g. "bigint", "date", "map", "set"). `undefined` for other kinds.
34
+ */
35
+ readonly zodType: string | undefined;
36
+ /**
37
+ * The original underlying error, when this normalisation error wraps
38
+ * another exception (typically the error thrown by `z.toJSONSchema()`).
39
+ * Preserves the source stack trace so the root cause is not lost when
40
+ * the classifier translates the message.
41
+ */
42
+ readonly cause: unknown;
43
+ constructor(message: string, schema: unknown, kind: SchemaNormalisationError["kind"], zodType?: string, cause?: unknown);
32
44
  }
33
45
  /**
34
46
  * A theme adapter's render function threw during rendering.
@@ -1,5 +1,5 @@
1
- import { M as WalkedField } from "../types-D_5ST7SS.mjs";
2
- import { t as AllConstraints } from "../renderer-BAGoX4AK.mjs";
1
+ import { M as WalkedField } from "../types-C9zw9wbX.mjs";
2
+ import { t as AllConstraints } from "../renderer-SOIbJBtk.mjs";
3
3
  import { HtmlAttributes, HtmlNode } from "./html.mjs";
4
4
 
5
5
  //#region src/html/a11y.d.ts
@@ -1,5 +1,5 @@
1
- import { T as SchemaMeta } from "../types-D_5ST7SS.mjs";
2
- import { o as HtmlResolver } from "../renderer-BAGoX4AK.mjs";
1
+ import { T as SchemaMeta } from "../types-C9zw9wbX.mjs";
2
+ import { o as HtmlResolver } from "../renderer-SOIbJBtk.mjs";
3
3
 
4
4
  //#region src/html/renderToHtml.d.ts
5
5
  interface RenderToHtmlOptions {
@@ -1,5 +1,5 @@
1
- import { T as SchemaMeta } from "../types-D_5ST7SS.mjs";
2
- import { o as HtmlResolver } from "../renderer-BAGoX4AK.mjs";
1
+ import { T as SchemaMeta } from "../types-C9zw9wbX.mjs";
2
+ import { o as HtmlResolver } from "../renderer-SOIbJBtk.mjs";
3
3
 
4
4
  //#region src/html/renderToHtmlStream.d.ts
5
5
  interface StreamRenderOptions {
@@ -1,5 +1,5 @@
1
- import { M as WalkedField } from "../types-D_5ST7SS.mjs";
2
- import { o as HtmlResolver } from "../renderer-BAGoX4AK.mjs";
1
+ import { M as WalkedField } from "../types-C9zw9wbX.mjs";
2
+ import { o as HtmlResolver } from "../renderer-SOIbJBtk.mjs";
3
3
 
4
4
  //#region src/html/renderers.d.ts
5
5
  declare function dateInputType(format: string | undefined): string | undefined;
@@ -1,4 +1,5 @@
1
1
  import { sortFieldsByOrder } from "../core/fieldOrder.mjs";
2
+ import { isSafeHyperlink, isSafeMailtoAddress } from "../core/uri.mjs";
2
3
  import { h, raw, serialize } from "./html.mjs";
3
4
  import { ariaDescribedByAttrs, ariaLabelAttrs, ariaReadonlyAttrs, ariaRequiredAttrs, buildHintElement, buildInputId, requiredIndicator } from "./a11y.mjs";
4
5
  //#region src/html/renderers.ts
@@ -26,12 +27,12 @@ function renderStringReadOnly(props) {
26
27
  ...ariaReadonlyAttrs()
27
28
  }, "—");
28
29
  const format = props.constraints.format;
29
- if (format === "email") return h("a", {
30
+ if (format === "email" && isSafeMailtoAddress(strValue)) return h("a", {
30
31
  class: "sc-value",
31
32
  href: `mailto:${strValue}`,
32
33
  ...ariaReadonlyAttrs()
33
34
  }, strValue);
34
- if (format === "uri" || format === "url") return h("a", {
35
+ if ((format === "uri" || format === "url") && isSafeHyperlink(strValue)) return h("a", {
35
36
  class: "sc-value",
36
37
  href: strValue,
37
38
  ...ariaReadonlyAttrs()
@@ -356,6 +357,7 @@ function renderTupleHtml(props) {
356
357
  if (props.tree.type !== "tuple") return renderUnknownHtml(props);
357
358
  const arr = Array.isArray(props.value) ? props.value : [];
358
359
  const prefixItems = props.tree.prefixItems;
360
+ const restItems = props.tree.restItems;
359
361
  const children = [];
360
362
  for (let i = 0; i < prefixItems.length; i++) {
361
363
  const itemValue = arr[i];
@@ -364,6 +366,11 @@ function renderTupleHtml(props) {
364
366
  const childHtml = props.renderChild(element, itemValue, `[${String(i)}]`);
365
367
  children.push(h("div", { class: "sc-tuple-item" }, h("span", { class: "sc-tuple-index" }, String(i)), raw(childHtml)));
366
368
  }
369
+ if (restItems !== void 0) for (let i = prefixItems.length; i < arr.length; i++) {
370
+ const itemValue = arr[i];
371
+ const childHtml = props.renderChild(restItems, itemValue, `[${String(i)}]`);
372
+ children.push(h("div", { class: "sc-tuple-item sc-tuple-rest" }, h("span", { class: "sc-tuple-index" }, String(i)), raw(childHtml)));
373
+ }
367
374
  return serialize(h("div", { class: "sc-tuple" }, ...children));
368
375
  }
369
376
  function renderConditionalHtml(props) {
@@ -378,6 +385,32 @@ function renderConditionalHtml(props) {
378
385
  function renderNegationHtml(props) {
379
386
  return serialize(h("div", { class: "sc-negation" }, raw("not: ...")));
380
387
  }
388
+ /**
389
+ * Render a null field — `z.null()` or `{ type: "null" }`.
390
+ *
391
+ * The only valid value is `null`, so render an em-dash placeholder.
392
+ */
393
+ function renderNullHtml(props) {
394
+ return serialize(h("span", {
395
+ class: "sc-value sc-value--empty",
396
+ id: fieldId(props.path),
397
+ ...ariaReadonlyAttrs()
398
+ }, "—"));
399
+ }
400
+ /**
401
+ * Render a never field — `z.never()` or a `false` schema.
402
+ *
403
+ * `never` indicates a position that cannot hold any value. Render a
404
+ * visible placeholder rather than throwing because some valid schemas
405
+ * intentionally contain `never` branches.
406
+ */
407
+ function renderNeverHtml(props) {
408
+ return serialize(h("span", {
409
+ class: "sc-value sc-never",
410
+ id: fieldId(props.path),
411
+ ...ariaReadonlyAttrs()
412
+ }, h("em", {}, "never matches")));
413
+ }
381
414
  function matchUnionOption(options, value) {
382
415
  if (typeof value === "string") return options.find((o) => o.type === "string" || o.type === "enum");
383
416
  if (typeof value === "number") return options.find((o) => o.type === "number");
@@ -389,6 +422,7 @@ const defaultHtmlResolver = {
389
422
  string: renderStringHtml,
390
423
  number: renderNumberHtml,
391
424
  boolean: renderBooleanHtml,
425
+ null: renderNullHtml,
392
426
  enum: renderEnumHtml,
393
427
  object: renderObjectHtml,
394
428
  array: renderArrayHtml,
@@ -401,6 +435,7 @@ const defaultHtmlResolver = {
401
435
  negation: renderNegationHtml,
402
436
  recursive: renderRecursiveHtml,
403
437
  file: renderFileHtml,
438
+ never: renderNeverHtml,
404
439
  unknown: renderUnknownHtml
405
440
  };
406
441
  //#endregion
@@ -1,5 +1,5 @@
1
- import { M as WalkedField } from "../types-D_5ST7SS.mjs";
2
- import { o as HtmlResolver } from "../renderer-BAGoX4AK.mjs";
1
+ import { M as WalkedField } from "../types-C9zw9wbX.mjs";
2
+ import { o as HtmlResolver } from "../renderer-SOIbJBtk.mjs";
3
3
  import { HtmlElement } from "./html.mjs";
4
4
 
5
5
  //#region src/html/streamRenderers.d.ts