schema-components 1.12.11 → 1.14.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 (83) hide show
  1. package/README.md +35 -0
  2. package/dist/core/adapter.d.mts +8 -3
  3. package/dist/core/adapter.mjs +58 -14
  4. package/dist/core/constraints.d.mts +17 -0
  5. package/dist/core/constraints.mjs +150 -0
  6. package/dist/core/diagnostics.d.mts +2 -0
  7. package/dist/core/diagnostics.mjs +33 -0
  8. package/dist/core/errors.d.mts +1 -1
  9. package/dist/core/formats.d.mts +33 -0
  10. package/dist/core/formats.mjs +67 -0
  11. package/dist/core/merge.d.mts +48 -0
  12. package/dist/core/merge.mjs +125 -0
  13. package/dist/core/normalise.d.mts +41 -0
  14. package/dist/core/normalise.mjs +2 -0
  15. package/dist/core/openapi30.d.mts +41 -0
  16. package/dist/core/openapi30.mjs +2 -0
  17. package/dist/core/ref.d.mts +2 -0
  18. package/dist/core/ref.mjs +165 -0
  19. package/dist/core/renderer.d.mts +2 -2
  20. package/dist/core/renderer.mjs +8 -0
  21. package/dist/core/swagger2.d.mts +11 -0
  22. package/dist/core/swagger2.mjs +2 -0
  23. package/dist/core/typeInference.d.mts +2 -0
  24. package/dist/core/typeInference.mjs +1 -0
  25. package/dist/core/types.d.mts +2 -3
  26. package/dist/core/types.mjs +58 -2
  27. package/dist/core/version.d.mts +2 -0
  28. package/dist/core/version.mjs +151 -0
  29. package/dist/core/walkBuilders.d.mts +66 -0
  30. package/dist/core/walkBuilders.mjs +152 -0
  31. package/dist/core/walker.d.mts +3 -10
  32. package/dist/core/walker.mjs +245 -233
  33. package/dist/diagnostics-DzbZmcLI.d.mts +64 -0
  34. package/dist/html/a11y.d.mts +5 -4
  35. package/dist/html/renderToHtml.d.mts +3 -3
  36. package/dist/html/renderToHtml.mjs +23 -379
  37. package/dist/html/renderToHtmlStream.d.mts +29 -47
  38. package/dist/html/renderToHtmlStream.mjs +33 -305
  39. package/dist/html/renderers.d.mts +14 -0
  40. package/dist/html/renderers.mjs +406 -0
  41. package/dist/html/streamRenderers.d.mts +13 -0
  42. package/dist/html/streamRenderers.mjs +243 -0
  43. package/dist/normalise-tL9FckAk.mjs +748 -0
  44. package/dist/openapi/ApiCallbacks.d.mts +16 -0
  45. package/dist/openapi/ApiCallbacks.mjs +34 -0
  46. package/dist/openapi/ApiLinks.d.mts +16 -0
  47. package/dist/openapi/ApiLinks.mjs +42 -0
  48. package/dist/openapi/ApiResponseHeaders.d.mts +16 -0
  49. package/dist/openapi/ApiResponseHeaders.mjs +35 -0
  50. package/dist/openapi/ApiSecurity.d.mts +19 -0
  51. package/dist/openapi/ApiSecurity.mjs +33 -0
  52. package/dist/openapi/bundle.d.mts +47 -0
  53. package/dist/openapi/bundle.mjs +95 -0
  54. package/dist/openapi/components.d.mts +7 -1
  55. package/dist/openapi/components.mjs +30 -6
  56. package/dist/openapi/parser.d.mts +59 -2
  57. package/dist/openapi/parser.mjs +189 -8
  58. package/dist/react/SchemaComponent.d.mts +13 -4
  59. package/dist/react/SchemaComponent.mjs +51 -91
  60. package/dist/react/SchemaView.d.mts +10 -2
  61. package/dist/react/SchemaView.mjs +33 -15
  62. package/dist/react/fieldPath.d.mts +20 -0
  63. package/dist/react/fieldPath.mjs +81 -0
  64. package/dist/react/headless.d.mts +2 -4
  65. package/dist/react/headless.mjs +3 -492
  66. package/dist/react/headlessRenderers.d.mts +23 -0
  67. package/dist/react/headlessRenderers.mjs +507 -0
  68. package/dist/ref-DvWoULcy.d.mts +44 -0
  69. package/dist/renderer-BdSqllx5.d.mts +160 -0
  70. package/dist/themes/mantine.d.mts +1 -1
  71. package/dist/themes/mantine.mjs +2 -1
  72. package/dist/themes/mui.d.mts +1 -1
  73. package/dist/themes/mui.mjs +3 -2
  74. package/dist/themes/radix.d.mts +1 -1
  75. package/dist/themes/radix.mjs +2 -1
  76. package/dist/themes/shadcn.d.mts +1 -1
  77. package/dist/themes/shadcn.mjs +10 -6
  78. package/dist/typeInference-k7FXfTVO.d.mts +335 -0
  79. package/dist/types-D_5ST7SS.d.mts +269 -0
  80. package/dist/version-B5NV-35j.d.mts +69 -0
  81. package/package.json +1 -1
  82. package/dist/types-BJzEgJdX.d.mts +0 -335
  83. /package/dist/{errors-DIKI2C78.d.mts → errors-C5zRC2PU.d.mts} +0 -0
package/README.md CHANGED
@@ -14,6 +14,10 @@ npm install schema-components
14
14
 
15
15
  Peer dependencies: `zod@^4.0.0`, `react@^18.0.0 || ^19.0.0`.
16
16
 
17
+ ### Zod version requirement
18
+
19
+ schema-components requires **Zod 4**. If you are on Zod 3, see the [Zod 4 migration guide](https://zod.dev/v4/migration). The library detects Zod 3 inputs and throws a descriptive error rather than silently misbehaving.
20
+
17
21
  ## `SchemaComponent`
18
22
 
19
23
  The single entry point. Accepts Zod schemas, JSON Schema objects, or OpenAPI documents:
@@ -179,6 +183,37 @@ import { SchemaField } from "schema-components/react/SchemaComponent";
179
183
 
180
184
  When the schema is a Zod schema or typed `as const`, only valid dot-paths like `"address.city"` are accepted. Invalid paths trigger TypeScript errors. Runtime schemas accept any string.
181
185
 
186
+ ## Spec support
187
+
188
+ The walker reads canonical Draft 2020-12 JSON Schema. Older drafts and OpenAPI documents are normalised to that form transparently.
189
+
190
+ | Spec | Detection | Normalisation | Notes |
191
+ |---|---|---|---|
192
+ | JSON Schema Draft 04 | `http://json-schema.org/draft-04/schema#` | `exclusiveMinimum`/`Maximum` boolean → number; `id` left in place | |
193
+ | JSON Schema Draft 06 | `http://json-schema.org/draft-06/schema#` | Pass-through (canonical for `const`, `examples[]`, `$id`, `propertyNames`, `contains`) | |
194
+ | JSON Schema Draft 07 | `http://json-schema.org/draft-07/schema#` | Pass-through (adds `if`/`then`/`else`, `contentEncoding`, `contentMediaType`) | |
195
+ | JSON Schema Draft 2019-09 | `https://json-schema.org/draft/2019-09/schema` | `$recursiveRef` → `$ref`, `$recursiveAnchor` → `$anchor` | Adds `unevaluatedProperties`/`Items`, `dependentSchemas`/`Required` |
196
+ | JSON Schema Draft 2020-12 | `https://json-schema.org/draft/2020-12/schema` (default) | `$dynamicRef` → `$ref`, `$dynamicAnchor` → `$anchor`; tuple form via `prefixItems` | |
197
+ | OpenAPI 2.0 (Swagger) | `swagger: "2.0"` | Full document restructure → OpenAPI 3.1; `definitions` → `components/schemas`; body params → `requestBody`; `formData` → `multipart/form-data`; `collectionFormat` → `style`/`explode` | |
198
+ | OpenAPI 3.0.x | `openapi: "3.0.x"` | `nullable` → `anyOf [T, null]`; `discriminator` mapping → injected `const`; `example` → `examples[]` | Callbacks, links, security schemes preserved |
199
+ | OpenAPI 3.1.x | `openapi: "3.1.x"` | Direct (already Draft 2020-12) | Webhooks, `components/pathItems`, JSON Schema `type` arrays, `examples[]` |
200
+
201
+ ### Documented type-level fallbacks
202
+
203
+ A few JSON Schema keywords can't be expressed in TypeScript's type system. They are handled at runtime by the walker, but `FromJSONSchema<S>` falls back as follows (each pinned by `tests/type-inference-advanced.test.ts`):
204
+
205
+ | Keyword | Type-level result | Why |
206
+ |---|---|---|
207
+ | `not` | `unknown` | TypeScript has no type negation |
208
+ | `if` / `then` / `else` | Base schema (conditions ignored) | Requires value-dependent conditional evaluation |
209
+ | `propertyNames` | Ignored | Cannot constrain object key *shape* |
210
+ | `dependentSchemas` / `dependentRequired` | Ignored | Cross-field runtime conditionals |
211
+ | `unevaluatedProperties` / `unevaluatedItems` | Ignored | Requires whole-tree evaluation context |
212
+ | `contains` / `minContains` / `maxContains` | Element type unchanged | Constraint metadata only |
213
+ | `$recursiveRef` | `unknown` | Recursive types not expressible |
214
+
215
+ Runtime rendering and validation handle each of these correctly; only the static type widens.
216
+
182
217
  ## OpenAPI components
183
218
 
184
219
  Render API operations with type-safe field overrides:
@@ -1,4 +1,5 @@
1
- import { l as JsonObject, m as SchemaMeta } from "../types-BJzEgJdX.mjs";
1
+ import { T as SchemaMeta, m as JsonObject } from "../types-D_5ST7SS.mjs";
2
+ import { i as DiagnosticsOptions } from "../diagnostics-DzbZmcLI.mjs";
2
3
 
3
4
  //#region src/core/adapter.d.ts
4
5
  type SchemaInput = Record<string, unknown>;
@@ -14,6 +15,10 @@ interface NormalisedSchema {
14
15
  /** The root document for $ref resolution. */
15
16
  rootDocument: JsonObject;
16
17
  }
17
- declare function normaliseSchema(input: unknown, ref?: string): NormalisedSchema;
18
+ interface NormaliseOptions {
19
+ /** Diagnostics channel for surfacing silent fallbacks. */
20
+ diagnostics?: DiagnosticsOptions;
21
+ }
22
+ declare function normaliseSchema(input: unknown, ref?: string, options?: NormaliseOptions): NormalisedSchema;
18
23
  //#endregion
19
- export { type JsonObject, NormalisedSchema, SchemaInput, SchemaKind, type SchemaMeta, detectSchemaKind, normaliseSchema };
24
+ export { type JsonObject, NormaliseOptions, NormalisedSchema, SchemaInput, SchemaKind, type SchemaMeta, detectSchemaKind, normaliseSchema };
@@ -1,4 +1,8 @@
1
1
  import { getProperty, hasProperty, isObject } from "./guards.mjs";
2
+ import { emitDiagnostic } from "./diagnostics.mjs";
3
+ import { dereference } from "./ref.mjs";
4
+ import { detectJsonSchemaDraft, detectOpenApiVersion, inferJsonSchemaDraftWithReason, isSwagger2 } from "./version.mjs";
5
+ import { i as normaliseOpenApiSchemas, r as normaliseJsonSchema$1 } from "../normalise-tL9FckAk.mjs";
2
6
  import { z } from "zod";
3
7
  //#region src/core/adapter.ts
4
8
  /**
@@ -16,7 +20,7 @@ const schemaCache = /* @__PURE__ */ new WeakMap();
16
20
  function detectSchemaKind(input) {
17
21
  if (hasProperty(input, "_zod")) return "zod4";
18
22
  if (hasProperty(input, "_def") && !hasProperty(input, "_zod")) return "zod3";
19
- if (hasProperty(input, "openapi")) return "openapi";
23
+ if (hasProperty(input, "openapi") || hasProperty(input, "swagger")) return "openapi";
20
24
  return "jsonSchema";
21
25
  }
22
26
  /**
@@ -30,7 +34,7 @@ function detectSchemaKind(input) {
30
34
  function callToJsonSchema(schema) {
31
35
  return z.toJSONSchema(schema);
32
36
  }
33
- function normaliseSchema(input, ref) {
37
+ function normaliseSchema(input, ref, options) {
34
38
  if (ref === void 0 && isObject(input)) {
35
39
  const cached = schemaCache.get(input);
36
40
  if (cached !== void 0) return cached;
@@ -46,11 +50,11 @@ function normaliseSchema(input, ref) {
46
50
  break;
47
51
  case "openapi":
48
52
  if (!isObject(input)) throw new Error("Invalid OpenAPI document");
49
- result = normaliseOpenApi(input, ref);
53
+ result = normaliseOpenApi(input, ref, options);
50
54
  break;
51
55
  case "jsonSchema":
52
56
  if (!isObject(input)) throw new Error("Invalid JSON Schema");
53
- result = normaliseJsonSchema(input);
57
+ result = normaliseJsonSchema(input, options?.diagnostics);
54
58
  break;
55
59
  }
56
60
  if (ref === void 0 && isObject(input)) schemaCache.set(input, result);
@@ -69,22 +73,56 @@ function normaliseZod4(input) {
69
73
  rootDocument: jsonSchema
70
74
  };
71
75
  }
72
- function normaliseJsonSchema(jsonSchema) {
76
+ function normaliseJsonSchema(jsonSchema, diagnostics) {
77
+ let draft;
78
+ if (typeof jsonSchema.$schema !== "string") {
79
+ const inferred = inferJsonSchemaDraftWithReason(jsonSchema);
80
+ draft = inferred.draft;
81
+ emitDiagnostic(diagnostics, {
82
+ code: "assumed-draft",
83
+ message: `No $schema present; inferred ${inferred.draft} from keywords (${inferred.inferredFrom})`,
84
+ pointer: "",
85
+ detail: {
86
+ inferredFrom: inferred.inferredFrom,
87
+ draft: inferred.draft
88
+ }
89
+ });
90
+ } else draft = detectJsonSchemaDraft(jsonSchema);
91
+ const normalised = normaliseJsonSchema$1(jsonSchema, draft);
73
92
  return {
74
- jsonSchema,
75
- rootMeta: extractRootMetaFromJson(jsonSchema),
76
- rootDocument: jsonSchema
93
+ jsonSchema: normalised,
94
+ rootMeta: extractRootMetaFromJson(normalised),
95
+ rootDocument: normalised
77
96
  };
78
97
  }
79
98
  function normaliseZod3() {
80
- throw new Error("Zod 3 schemas are not yet supported. Convert to Zod 4 or provide JSON Schema directly.");
99
+ throw new Error("Zod 3 schemas are not supported. schema-components requires Zod 4. Detected: Zod 3 (has _def without _zod). See the Zod 4 migration guide at https://zod.dev/v4/migration or run: pnpm add zod@^4");
81
100
  }
82
- function normaliseOpenApi(doc, ref) {
83
- const resolved = resolveOpenApiRef(doc, ref);
101
+ /**
102
+ * Mapping of Swagger 2.0 $ref prefixes to their OpenAPI 3.x equivalents.
103
+ * Used by the adapter to rewrite user-provided ref strings so they
104
+ * resolve correctly against the normalised document.
105
+ */
106
+ const REF_REWRITES_ADAPTER = [
107
+ ["#/definitions/", "#/components/schemas/"],
108
+ ["#/parameters/", "#/components/parameters/"],
109
+ ["#/responses/", "#/components/responses/"]
110
+ ];
111
+ function normaliseOpenApi(doc, ref, options) {
112
+ const version = detectOpenApiVersion(doc);
113
+ const normalisedDoc = version !== void 0 ? normaliseOpenApiSchemas(doc, version, options?.diagnostics) : doc;
114
+ let rewrittenRef = ref;
115
+ if (rewrittenRef !== void 0 && version !== void 0 && isSwagger2(version)) {
116
+ for (const [from, to] of REF_REWRITES_ADAPTER) if (rewrittenRef.startsWith(from)) {
117
+ rewrittenRef = to + rewrittenRef.slice(from.length);
118
+ break;
119
+ }
120
+ }
121
+ const resolved = resolveOpenApiRef(normalisedDoc, rewrittenRef);
84
122
  return {
85
123
  jsonSchema: resolved,
86
124
  rootMeta: extractRootMetaFromJson(resolved),
87
- rootDocument: doc
125
+ rootDocument: normalisedDoc
88
126
  };
89
127
  }
90
128
  function resolveOpenApiRef(doc, ref) {
@@ -120,11 +158,17 @@ function resolveOpenApiRef(doc, ref) {
120
158
  const content = getProperty(requestBody, "content");
121
159
  if (!isObject(content)) throw new Error(`No content for ${ref}`);
122
160
  const json = getProperty(content, "application/json");
123
- if (!isObject(json)) throw new Error(`No application/json for ${ref}`);
124
- const schema = getProperty(json, "schema");
161
+ const multipart = getProperty(content, "multipart/form-data");
162
+ const mediaType = isObject(json) ? json : isObject(multipart) ? multipart : void 0;
163
+ if (mediaType === void 0) throw new Error(`No content for ${ref}`);
164
+ const schema = getProperty(mediaType, "schema");
125
165
  if (!isObject(schema)) throw new Error(`Could not resolve request body schema for ${ref}`);
126
166
  return schema;
127
167
  }
168
+ if (ref.startsWith("#/")) {
169
+ const resolved = dereference(ref, doc);
170
+ if (resolved !== void 0) return resolved;
171
+ }
128
172
  throw new Error(`Unsupported OpenAPI ref format: ${ref}`);
129
173
  }
130
174
  function extractRootMetaFromJson(jsonSchema) {
@@ -0,0 +1,17 @@
1
+ import { D as StringConstraints, f as FileConstraints, t as ArrayConstraints, x as ObjectConstraints, y as NumberConstraints } from "../types-D_5ST7SS.mjs";
2
+ import { i as DiagnosticsOptions } from "../diagnostics-DzbZmcLI.mjs";
3
+
4
+ //#region src/core/constraints.d.ts
5
+ declare function extractStringConstraints(schema: Record<string, unknown>, diagnostics?: DiagnosticsOptions, pointer?: string): StringConstraints;
6
+ declare function extractNumberConstraints(schema: Record<string, unknown>): NumberConstraints;
7
+ declare function extractArrayConstraints(schema: Record<string, unknown>): ArrayConstraints;
8
+ declare function extractObjectConstraints(schema: Record<string, unknown>): ObjectConstraints;
9
+ declare function extractFileConstraints(schema: Record<string, unknown>): FileConstraints;
10
+ /**
11
+ * Return a copy of the schema with constraint keywords that don't apply
12
+ * to the given type removed. Meta keywords (description, title, etc.)
13
+ * and composition keywords are always preserved.
14
+ */
15
+ declare function stripInapplicableConstraints(schema: Record<string, unknown>, targetType: string): Record<string, unknown>;
16
+ //#endregion
17
+ export { extractArrayConstraints, extractFileConstraints, extractNumberConstraints, extractObjectConstraints, extractStringConstraints, stripInapplicableConstraints };
@@ -0,0 +1,150 @@
1
+ import { isObject } from "./guards.mjs";
2
+ import { emitDiagnostic } from "./diagnostics.mjs";
3
+ import { FORMAT_PATTERNS } from "./formats.mjs";
4
+ //#region src/core/constraints.ts
5
+ function getString(obj, key) {
6
+ const value = obj[key];
7
+ return typeof value === "string" ? value : void 0;
8
+ }
9
+ function getNumber(obj, key) {
10
+ const value = obj[key];
11
+ return typeof value === "number" ? value : void 0;
12
+ }
13
+ function getObject(obj, key) {
14
+ const value = obj[key];
15
+ return isObject(value) ? value : void 0;
16
+ }
17
+ function extractStringConstraints(schema, diagnostics, pointer = "") {
18
+ const c = {};
19
+ const minLength = getNumber(schema, "minLength");
20
+ if (minLength !== void 0) c.minLength = minLength;
21
+ const maxLength = getNumber(schema, "maxLength");
22
+ if (maxLength !== void 0) c.maxLength = maxLength;
23
+ const pattern = getString(schema, "pattern");
24
+ if (pattern !== void 0) c.pattern = pattern;
25
+ const format = getString(schema, "format");
26
+ if (format !== void 0) {
27
+ c.format = format;
28
+ const pattern = FORMAT_PATTERNS[format];
29
+ if (pattern !== void 0) c.formatPattern = pattern;
30
+ else if (format !== "binary") emitDiagnostic(diagnostics, {
31
+ code: "unknown-format",
32
+ message: `Unknown format: ${format}`,
33
+ pointer,
34
+ detail: { format }
35
+ });
36
+ }
37
+ const contentEncoding = getString(schema, "contentEncoding");
38
+ if (contentEncoding !== void 0) c.contentEncoding = contentEncoding;
39
+ const contentMediaType = getString(schema, "contentMediaType");
40
+ if (contentMediaType !== void 0) c.contentMediaType = contentMediaType;
41
+ return c;
42
+ }
43
+ function extractNumberConstraints(schema) {
44
+ const c = {};
45
+ const minimum = getNumber(schema, "minimum");
46
+ if (minimum !== void 0) c.minimum = minimum;
47
+ const maximum = getNumber(schema, "maximum");
48
+ if (maximum !== void 0) c.maximum = maximum;
49
+ const exclusiveMinimum = getNumber(schema, "exclusiveMinimum");
50
+ if (exclusiveMinimum !== void 0) c.exclusiveMinimum = exclusiveMinimum;
51
+ const exclusiveMaximum = getNumber(schema, "exclusiveMaximum");
52
+ if (exclusiveMaximum !== void 0) c.exclusiveMaximum = exclusiveMaximum;
53
+ const multipleOf = getNumber(schema, "multipleOf");
54
+ if (multipleOf !== void 0) c.multipleOf = multipleOf;
55
+ return c;
56
+ }
57
+ function extractArrayConstraints(schema) {
58
+ const c = {};
59
+ const minItems = getNumber(schema, "minItems");
60
+ if (minItems !== void 0) c.minItems = minItems;
61
+ const maxItems = getNumber(schema, "maxItems");
62
+ if (maxItems !== void 0) c.maxItems = maxItems;
63
+ if (schema.uniqueItems === true) c.uniqueItems = true;
64
+ const contains = getObject(schema, "contains");
65
+ if (contains !== void 0) c.contains = contains;
66
+ const minContains = getNumber(schema, "minContains");
67
+ if (minContains !== void 0) c.minContains = minContains;
68
+ const maxContains = getNumber(schema, "maxContains");
69
+ if (maxContains !== void 0) c.maxContains = maxContains;
70
+ const unevaluatedItems = getObject(schema, "unevaluatedItems");
71
+ if (unevaluatedItems !== void 0) c.unevaluatedItems = unevaluatedItems;
72
+ return c;
73
+ }
74
+ function extractObjectConstraints(schema) {
75
+ const c = {};
76
+ const minProperties = getNumber(schema, "minProperties");
77
+ if (minProperties !== void 0) c.minProperties = minProperties;
78
+ const maxProperties = getNumber(schema, "maxProperties");
79
+ if (maxProperties !== void 0) c.maxProperties = maxProperties;
80
+ return c;
81
+ }
82
+ function extractFileConstraints(schema) {
83
+ const c = {};
84
+ const contentMediaType = getString(schema, "contentMediaType");
85
+ if (contentMediaType !== void 0) c.mimeTypes = [contentMediaType];
86
+ return c;
87
+ }
88
+ /**
89
+ * Constraint keywords that apply only to specific types.
90
+ * Used to strip inapplicable constraints when expanding type arrays.
91
+ */
92
+ const STRING_CONSTRAINTS = new Set([
93
+ "minLength",
94
+ "maxLength",
95
+ "pattern"
96
+ ]);
97
+ const NUMBER_CONSTRAINTS = new Set([
98
+ "minimum",
99
+ "maximum",
100
+ "exclusiveMinimum",
101
+ "exclusiveMaximum",
102
+ "multipleOf"
103
+ ]);
104
+ const ARRAY_CONSTRAINTS = new Set([
105
+ "minItems",
106
+ "maxItems",
107
+ "uniqueItems",
108
+ "contains",
109
+ "minContains",
110
+ "maxContains"
111
+ ]);
112
+ const OBJECT_CONSTRAINTS = new Set(["minProperties", "maxProperties"]);
113
+ const ALL_CONSTRAINTS = new Set([
114
+ ...STRING_CONSTRAINTS,
115
+ ...NUMBER_CONSTRAINTS,
116
+ ...ARRAY_CONSTRAINTS,
117
+ ...OBJECT_CONSTRAINTS
118
+ ]);
119
+ /**
120
+ * Return a copy of the schema with constraint keywords that don't apply
121
+ * to the given type removed. Meta keywords (description, title, etc.)
122
+ * and composition keywords are always preserved.
123
+ */
124
+ function stripInapplicableConstraints(schema, targetType) {
125
+ let keepForType;
126
+ switch (targetType) {
127
+ case "string":
128
+ keepForType = STRING_CONSTRAINTS;
129
+ break;
130
+ case "number":
131
+ case "integer":
132
+ keepForType = NUMBER_CONSTRAINTS;
133
+ break;
134
+ case "array":
135
+ keepForType = ARRAY_CONSTRAINTS;
136
+ break;
137
+ case "object":
138
+ keepForType = OBJECT_CONSTRAINTS;
139
+ break;
140
+ default: keepForType = /* @__PURE__ */ new Set();
141
+ }
142
+ const result = {};
143
+ for (const [key, value] of Object.entries(schema)) {
144
+ if (ALL_CONSTRAINTS.has(key) && !keepForType.has(key)) continue;
145
+ result[key] = value;
146
+ }
147
+ return result;
148
+ }
149
+ //#endregion
150
+ export { extractArrayConstraints, extractFileConstraints, extractNumberConstraints, extractObjectConstraints, extractStringConstraints, stripInapplicableConstraints };
@@ -0,0 +1,2 @@
1
+ import { a as appendPointer, i as DiagnosticsOptions, n as DiagnosticCode, o as emitDiagnostic, r as DiagnosticSink, t as Diagnostic } from "../diagnostics-DzbZmcLI.mjs";
2
+ export { Diagnostic, DiagnosticCode, DiagnosticSink, DiagnosticsOptions, appendPointer, emitDiagnostic };
@@ -0,0 +1,33 @@
1
+ import { SchemaNormalisationError } from "./errors.mjs";
2
+ //#region src/core/diagnostics.ts
3
+ /**
4
+ * Diagnostics channel for schema-components.
5
+ *
6
+ * Provides a structured way to surface silent fallbacks — unresolved `$ref`,
7
+ * unknown keywords, unknown `format` values, invalid `const` values,
8
+ * unsupported `type` entries, dropped Swagger 2.0 features, external
9
+ * `$ref`, type-negation fallbacks, and conditional fallbacks.
10
+ *
11
+ * Consumers pass a `DiagnosticSink` callback to receive diagnostics
12
+ * as they occur. By default, diagnostics are silently discarded.
13
+ * Setting `strict: true` converts any diagnostic into a thrown
14
+ * `SchemaCompatibilityError`.
15
+ */
16
+ /**
17
+ * Emit a diagnostic through the configured sink.
18
+ * When `strict` is enabled, throws a `SchemaCompatibilityError` instead.
19
+ */
20
+ function emitDiagnostic(opts, diagnostic) {
21
+ if (opts?.strict === true) throw new SchemaNormalisationError(`[${diagnostic.code}] ${diagnostic.message} (at ${diagnostic.pointer})`, diagnostic.detail, "unknown");
22
+ if (opts?.diagnostics !== void 0) opts.diagnostics(diagnostic);
23
+ }
24
+ /**
25
+ * Append a segment to a JSON Pointer.
26
+ * Encodes `/` and `~` per RFC 6901.
27
+ */
28
+ function appendPointer(base, segment) {
29
+ const escaped = segment.replace(/~/g, "~0").replace(/\//g, "~1");
30
+ return base === "" ? `/${escaped}` : `${base}/${escaped}`;
31
+ }
32
+ //#endregion
33
+ export { appendPointer, emitDiagnostic };
@@ -1,2 +1,2 @@
1
- import { i as SchemaRenderError, n as SchemaFieldError, r as SchemaNormalisationError, t as SchemaError } from "../errors-DIKI2C78.mjs";
1
+ import { i as SchemaRenderError, n as SchemaFieldError, r as SchemaNormalisationError, t as SchemaError } from "../errors-C5zRC2PU.mjs";
2
2
  export { SchemaError, SchemaFieldError, SchemaNormalisationError, SchemaRenderError };
@@ -0,0 +1,33 @@
1
+ //#region src/core/formats.d.ts
2
+ /**
3
+ * Built-in format patterns for JSON Schema string validation.
4
+ *
5
+ * Maps standard JSON Schema/OpenAPI `format` values to RegExp patterns
6
+ * or predicate validators. Used by the constraint extractor to derive
7
+ * `formatPattern` alongside the existing `format` string, and by the
8
+ * `validate` helper for runtime format checking.
9
+ *
10
+ * Formats that cannot be validated by regex (iri, regex) use predicate
11
+ * functions instead. `validateFormat` dispatches to whichever is available.
12
+ *
13
+ * The user's explicit `pattern` constraint always takes precedence —
14
+ * `formatPattern` is exposed as a separate field for renderers.
15
+ */
16
+ /**
17
+ * A format validator: either a RegExp pattern or a predicate function.
18
+ */
19
+ type FormatValidator = RegExp | ((value: string) => boolean);
20
+ /**
21
+ * Recognised JSON Schema formats with their validation patterns.
22
+ * Unknown formats emit an `unknown-format` diagnostic and skip derivation.
23
+ */
24
+ declare const FORMAT_PATTERNS: Readonly<Record<string, RegExp>>;
25
+ /**
26
+ * Validate a string value against format constraints.
27
+ * Returns `true` when the value matches the format,
28
+ * `false` when it does not, and `undefined` when the format
29
+ * is not recognised (no validator available).
30
+ */
31
+ declare function validateFormat(value: string, format: string): boolean | undefined;
32
+ //#endregion
33
+ export { FORMAT_PATTERNS, FormatValidator, validateFormat };
@@ -0,0 +1,67 @@
1
+ //#region src/core/formats.ts
2
+ /**
3
+ * Recognised JSON Schema formats with their validation patterns.
4
+ * Unknown formats emit an `unknown-format` diagnostic and skip derivation.
5
+ */
6
+ const FORMAT_PATTERNS = {
7
+ uuid: /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,
8
+ email: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
9
+ "date-time": /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:\d{2})$/,
10
+ date: /^\d{4}-\d{2}-\d{2}$/,
11
+ time: /^\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:\d{2})?$/,
12
+ ipv4: /^(\d{1,3}\.){3}\d{1,3}$/,
13
+ ipv6: /^([0-9a-f]{0,4}:){2,7}[0-9a-f]{0,4}$/i,
14
+ uri: /^[a-z][a-z0-9+\-.]*:/i,
15
+ hostname: /^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?(\.[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?)*$/i,
16
+ "uri-reference": /^(|[a-z][a-z0-9+\-.]*:|[?#][^\s]*)/i,
17
+ "uri-template": /^([^{}]|[{][+#/.;?&=,!@|]([a-zA-Z0-9_%.]+)?(:[1-9][0-9]*)?(,[a-zA-Z0-9_%.]+)*[}])*$/,
18
+ "json-pointer": /^(([/]([^/~]|~0|~1)*)*|)$/,
19
+ "relative-json-pointer": /^(0|[1-9][0-9]*)(#?([/]([^/~]|~0|~1)*)*)?$/,
20
+ duration: /^P(?!$)(\d+Y)?(\d+M)?(\d+W)?(\d+D)?(T(?=\d)(\d+H)?(\d+M)?(\d+(\.\d+)?S)?)?$/,
21
+ "idn-email": /^[^\s@]+@[^\s@]+\.[^\s@]+$/u,
22
+ "idn-hostname": /^[a-z0-9\u00a1-\uffff]([a-z0-9\u00a1-\uffff-]{0,61}[a-z0-9\u00a1-\uffff])?(\.[a-z0-9\u00a1-\uffff]([a-z0-9\u00a1-\uffff-]{0,61}[a-z0-9\u00a1-\uffff])?)*$/iu
23
+ };
24
+ /**
25
+ * Format validators that use predicate functions instead of regex.
26
+ * These are checked in `validateFormat` when no regex pattern exists.
27
+ */
28
+ const PREDICATE_VALIDATORS = {
29
+ iri: (value) => {
30
+ try {
31
+ return new URL(value).protocol.length > 0;
32
+ } catch {
33
+ return false;
34
+ }
35
+ },
36
+ "iri-reference": (value) => {
37
+ if (value === "") return true;
38
+ try {
39
+ new URL(value);
40
+ return true;
41
+ } catch {
42
+ return !/\s/.test(value);
43
+ }
44
+ },
45
+ regex: (value) => {
46
+ try {
47
+ new RegExp(value);
48
+ return true;
49
+ } catch {
50
+ return false;
51
+ }
52
+ }
53
+ };
54
+ /**
55
+ * Validate a string value against format constraints.
56
+ * Returns `true` when the value matches the format,
57
+ * `false` when it does not, and `undefined` when the format
58
+ * is not recognised (no validator available).
59
+ */
60
+ function validateFormat(value, format) {
61
+ const pattern = FORMAT_PATTERNS[format];
62
+ if (pattern !== void 0) return pattern.test(value);
63
+ const predicate = PREDICATE_VALIDATORS[format];
64
+ if (predicate !== void 0) return predicate(value);
65
+ }
66
+ //#endregion
67
+ export { FORMAT_PATTERNS, validateFormat };
@@ -0,0 +1,48 @@
1
+ //#region src/core/merge.d.ts
2
+ /**
3
+ * Schema merging, nullable detection, and discriminated union detection.
4
+ *
5
+ * Used by the walker to handle `allOf`, `anyOf [T, null]`, and
6
+ * `oneOf` with discriminator properties.
7
+ */
8
+ /**
9
+ * Annotation keywords that can appear as siblings of `$ref` per
10
+ * Draft 2020-12 / OpenAPI 3.1. Structural keywords (type, properties,
11
+ * etc.) are NOT annotation siblings and should not be merged.
12
+ */
13
+ declare const ANNOTATION_SIBLINGS: ReadonlySet<string>;
14
+ /**
15
+ * Merge annotation siblings from the referencing node onto the
16
+ * resolved target's annotations. The referencer wins for annotations.
17
+ *
18
+ * Structural keywords on the referencer are NOT merged — per spec,
19
+ * `$ref` with structural siblings was invalid pre-2019-09.
20
+ *
21
+ * Returns a new meta object with the merged annotations.
22
+ */
23
+ declare function mergeRefSiblings(referencer: Record<string, unknown>, resolvedMeta: Record<string, unknown>): Record<string, unknown>;
24
+ /**
25
+ * Merge multiple JSON Schema objects from allOf into one.
26
+ * Merges: properties, required, meta fields, and constraints.
27
+ */
28
+ declare function mergeAllOf(schemas: unknown[]): Record<string, unknown>;
29
+ interface NormalisedAnyOf {
30
+ inner: Record<string, unknown>;
31
+ isNullable: boolean;
32
+ }
33
+ /**
34
+ * Detect `anyOf: [T, { type: "null" }]` → nullable T.
35
+ * Returns the non-null schema and a nullable flag.
36
+ */
37
+ declare function normaliseAnyOf(options: unknown[]): NormalisedAnyOf | undefined;
38
+ interface Discriminated {
39
+ options: Record<string, unknown>[];
40
+ discriminator: string;
41
+ }
42
+ /**
43
+ * Detect oneOf where every option is an object with a property
44
+ * that has a `const` value → discriminated union.
45
+ */
46
+ declare function detectDiscriminated(options: unknown[]): Discriminated | undefined;
47
+ //#endregion
48
+ export { ANNOTATION_SIBLINGS, Discriminated, NormalisedAnyOf, detectDiscriminated, mergeAllOf, mergeRefSiblings, normaliseAnyOf };