schema-components 1.12.11 → 1.13.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 (63) hide show
  1. package/README.md +31 -0
  2. package/dist/core/adapter.d.mts +1 -1
  3. package/dist/core/adapter.mjs +37 -8
  4. package/dist/core/constraints.d.mts +16 -0
  5. package/dist/core/constraints.mjs +138 -0
  6. package/dist/core/merge.d.mts +32 -0
  7. package/dist/core/merge.mjs +96 -0
  8. package/dist/core/normalise.d.mts +40 -0
  9. package/dist/core/normalise.mjs +171 -0
  10. package/dist/core/openapi30.d.mts +38 -0
  11. package/dist/core/openapi30.mjs +223 -0
  12. package/dist/core/ref.d.mts +25 -0
  13. package/dist/core/ref.mjs +86 -0
  14. package/dist/core/renderer.d.mts +2 -2
  15. package/dist/core/renderer.mjs +8 -0
  16. package/dist/core/swagger2.d.mts +10 -0
  17. package/dist/core/swagger2.mjs +294 -0
  18. package/dist/core/typeInference.d.mts +2 -0
  19. package/dist/core/typeInference.mjs +1 -0
  20. package/dist/core/types.d.mts +2 -3
  21. package/dist/core/types.mjs +55 -2
  22. package/dist/core/version.d.mts +2 -0
  23. package/dist/core/version.mjs +79 -0
  24. package/dist/core/walkBuilders.d.mts +52 -0
  25. package/dist/core/walkBuilders.mjs +152 -0
  26. package/dist/core/walker.d.mts +3 -10
  27. package/dist/core/walker.mjs +143 -231
  28. package/dist/html/a11y.d.mts +5 -4
  29. package/dist/html/renderToHtml.d.mts +3 -3
  30. package/dist/html/renderToHtml.mjs +23 -379
  31. package/dist/html/renderToHtmlStream.d.mts +29 -47
  32. package/dist/html/renderToHtmlStream.mjs +33 -305
  33. package/dist/html/renderers.d.mts +14 -0
  34. package/dist/html/renderers.mjs +406 -0
  35. package/dist/html/streamRenderers.d.mts +13 -0
  36. package/dist/html/streamRenderers.mjs +243 -0
  37. package/dist/openapi/components.d.mts +2 -1
  38. package/dist/openapi/parser.d.mts +59 -2
  39. package/dist/openapi/parser.mjs +189 -8
  40. package/dist/react/SchemaComponent.d.mts +4 -2
  41. package/dist/react/SchemaComponent.mjs +39 -85
  42. package/dist/react/SchemaView.d.mts +2 -1
  43. package/dist/react/SchemaView.mjs +21 -9
  44. package/dist/react/fieldPath.d.mts +20 -0
  45. package/dist/react/fieldPath.mjs +81 -0
  46. package/dist/react/headless.d.mts +2 -4
  47. package/dist/react/headless.mjs +3 -492
  48. package/dist/react/headlessRenderers.d.mts +23 -0
  49. package/dist/react/headlessRenderers.mjs +507 -0
  50. package/dist/renderer-DseHeliw.d.mts +160 -0
  51. package/dist/themes/mantine.d.mts +1 -1
  52. package/dist/themes/mantine.mjs +2 -1
  53. package/dist/themes/mui.d.mts +1 -1
  54. package/dist/themes/mui.mjs +2 -1
  55. package/dist/themes/radix.d.mts +1 -1
  56. package/dist/themes/radix.mjs +2 -1
  57. package/dist/themes/shadcn.d.mts +1 -1
  58. package/dist/themes/shadcn.mjs +10 -6
  59. package/dist/typeInference-CRPqVwKu.d.mts +299 -0
  60. package/dist/types-ag2jYLqQ.d.mts +261 -0
  61. package/dist/version-CLchheaH.d.mts +40 -0
  62. package/package.json +1 -1
  63. package/dist/types-BJzEgJdX.d.mts +0 -335
package/README.md CHANGED
@@ -179,6 +179,37 @@ import { SchemaField } from "schema-components/react/SchemaComponent";
179
179
 
180
180
  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
181
 
182
+ ## Spec support
183
+
184
+ The walker reads canonical Draft 2020-12 JSON Schema. Older drafts and OpenAPI documents are normalised to that form transparently.
185
+
186
+ | Spec | Detection | Normalisation | Notes |
187
+ |---|---|---|---|
188
+ | JSON Schema Draft 04 | `http://json-schema.org/draft-04/schema#` | `exclusiveMinimum`/`Maximum` boolean → number; `id` left in place | |
189
+ | JSON Schema Draft 06 | `http://json-schema.org/draft-06/schema#` | Pass-through (canonical for `const`, `examples[]`, `$id`, `propertyNames`, `contains`) | |
190
+ | JSON Schema Draft 07 | `http://json-schema.org/draft-07/schema#` | Pass-through (adds `if`/`then`/`else`, `contentEncoding`, `contentMediaType`) | |
191
+ | JSON Schema Draft 2019-09 | `https://json-schema.org/draft/2019-09/schema` | `$recursiveRef` → `$ref`, `$recursiveAnchor` → `$anchor` | Adds `unevaluatedProperties`/`Items`, `dependentSchemas`/`Required` |
192
+ | JSON Schema Draft 2020-12 | `https://json-schema.org/draft/2020-12/schema` (default) | `$dynamicRef` → `$ref`, `$dynamicAnchor` → `$anchor`; tuple form via `prefixItems` | |
193
+ | 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` | |
194
+ | OpenAPI 3.0.x | `openapi: "3.0.x"` | `nullable` → `anyOf [T, null]`; `discriminator` mapping → injected `const`; `example` → `examples[]` | Callbacks, links, security schemes preserved |
195
+ | OpenAPI 3.1.x | `openapi: "3.1.x"` | Direct (already Draft 2020-12) | Webhooks, `components/pathItems`, JSON Schema `type` arrays, `examples[]` |
196
+
197
+ ### Documented type-level fallbacks
198
+
199
+ 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`):
200
+
201
+ | Keyword | Type-level result | Why |
202
+ |---|---|---|
203
+ | `not` | `unknown` | TypeScript has no type negation |
204
+ | `if` / `then` / `else` | Base schema (conditions ignored) | Requires value-dependent conditional evaluation |
205
+ | `propertyNames` | Ignored | Cannot constrain object key *shape* |
206
+ | `dependentSchemas` / `dependentRequired` | Ignored | Cross-field runtime conditionals |
207
+ | `unevaluatedProperties` / `unevaluatedItems` | Ignored | Requires whole-tree evaluation context |
208
+ | `contains` / `minContains` / `maxContains` | Element type unchanged | Constraint metadata only |
209
+ | `$recursiveRef` | `unknown` | Recursive types not expressible |
210
+
211
+ Runtime rendering and validation handle each of these correctly; only the static type widens.
212
+
182
213
  ## OpenAPI components
183
214
 
184
215
  Render API operations with type-safe field overrides:
@@ -1,4 +1,4 @@
1
- import { l as JsonObject, m as SchemaMeta } from "../types-BJzEgJdX.mjs";
1
+ import { m as JsonObject, w as SchemaMeta } from "../types-ag2jYLqQ.mjs";
2
2
 
3
3
  //#region src/core/adapter.d.ts
4
4
  type SchemaInput = Record<string, unknown>;
@@ -1,4 +1,7 @@
1
1
  import { getProperty, hasProperty, isObject } from "./guards.mjs";
2
+ import { dereference } from "./ref.mjs";
3
+ import { detectJsonSchemaDraft, detectOpenApiVersion, isSwagger2 } from "./version.mjs";
4
+ import { normaliseJsonSchema as normaliseJsonSchema$1, normaliseOpenApiSchemas } from "./normalise.mjs";
2
5
  import { z } from "zod";
3
6
  //#region src/core/adapter.ts
4
7
  /**
@@ -16,7 +19,7 @@ const schemaCache = /* @__PURE__ */ new WeakMap();
16
19
  function detectSchemaKind(input) {
17
20
  if (hasProperty(input, "_zod")) return "zod4";
18
21
  if (hasProperty(input, "_def") && !hasProperty(input, "_zod")) return "zod3";
19
- if (hasProperty(input, "openapi")) return "openapi";
22
+ if (hasProperty(input, "openapi") || hasProperty(input, "swagger")) return "openapi";
20
23
  return "jsonSchema";
21
24
  }
22
25
  /**
@@ -70,21 +73,41 @@ function normaliseZod4(input) {
70
73
  };
71
74
  }
72
75
  function normaliseJsonSchema(jsonSchema) {
76
+ const normalised = normaliseJsonSchema$1(jsonSchema, detectJsonSchemaDraft(jsonSchema));
73
77
  return {
74
- jsonSchema,
75
- rootMeta: extractRootMetaFromJson(jsonSchema),
76
- rootDocument: jsonSchema
78
+ jsonSchema: normalised,
79
+ rootMeta: extractRootMetaFromJson(normalised),
80
+ rootDocument: normalised
77
81
  };
78
82
  }
79
83
  function normaliseZod3() {
80
84
  throw new Error("Zod 3 schemas are not yet supported. Convert to Zod 4 or provide JSON Schema directly.");
81
85
  }
86
+ /**
87
+ * Mapping of Swagger 2.0 $ref prefixes to their OpenAPI 3.x equivalents.
88
+ * Used by the adapter to rewrite user-provided ref strings so they
89
+ * resolve correctly against the normalised document.
90
+ */
91
+ const REF_REWRITES_ADAPTER = [
92
+ ["#/definitions/", "#/components/schemas/"],
93
+ ["#/parameters/", "#/components/parameters/"],
94
+ ["#/responses/", "#/components/responses/"]
95
+ ];
82
96
  function normaliseOpenApi(doc, ref) {
83
- const resolved = resolveOpenApiRef(doc, ref);
97
+ const version = detectOpenApiVersion(doc);
98
+ const normalisedDoc = version !== void 0 ? normaliseOpenApiSchemas(doc, version) : doc;
99
+ let rewrittenRef = ref;
100
+ if (rewrittenRef !== void 0 && version !== void 0 && isSwagger2(version)) {
101
+ for (const [from, to] of REF_REWRITES_ADAPTER) if (rewrittenRef.startsWith(from)) {
102
+ rewrittenRef = to + rewrittenRef.slice(from.length);
103
+ break;
104
+ }
105
+ }
106
+ const resolved = resolveOpenApiRef(normalisedDoc, rewrittenRef);
84
107
  return {
85
108
  jsonSchema: resolved,
86
109
  rootMeta: extractRootMetaFromJson(resolved),
87
- rootDocument: doc
110
+ rootDocument: normalisedDoc
88
111
  };
89
112
  }
90
113
  function resolveOpenApiRef(doc, ref) {
@@ -120,11 +143,17 @@ function resolveOpenApiRef(doc, ref) {
120
143
  const content = getProperty(requestBody, "content");
121
144
  if (!isObject(content)) throw new Error(`No content for ${ref}`);
122
145
  const json = getProperty(content, "application/json");
123
- if (!isObject(json)) throw new Error(`No application/json for ${ref}`);
124
- const schema = getProperty(json, "schema");
146
+ const multipart = getProperty(content, "multipart/form-data");
147
+ const mediaType = isObject(json) ? json : isObject(multipart) ? multipart : void 0;
148
+ if (mediaType === void 0) throw new Error(`No content for ${ref}`);
149
+ const schema = getProperty(mediaType, "schema");
125
150
  if (!isObject(schema)) throw new Error(`Could not resolve request body schema for ${ref}`);
126
151
  return schema;
127
152
  }
153
+ if (ref.startsWith("#/")) {
154
+ const resolved = dereference(ref, doc);
155
+ if (resolved !== void 0) return resolved;
156
+ }
128
157
  throw new Error(`Unsupported OpenAPI ref format: ${ref}`);
129
158
  }
130
159
  function extractRootMetaFromJson(jsonSchema) {
@@ -0,0 +1,16 @@
1
+ import { E as StringConstraints, b as ObjectConstraints, f as FileConstraints, t as ArrayConstraints, v as NumberConstraints } from "../types-ag2jYLqQ.mjs";
2
+
3
+ //#region src/core/constraints.d.ts
4
+ declare function extractStringConstraints(schema: Record<string, unknown>): StringConstraints;
5
+ declare function extractNumberConstraints(schema: Record<string, unknown>): NumberConstraints;
6
+ declare function extractArrayConstraints(schema: Record<string, unknown>): ArrayConstraints;
7
+ declare function extractObjectConstraints(schema: Record<string, unknown>): ObjectConstraints;
8
+ declare function extractFileConstraints(schema: Record<string, unknown>): FileConstraints;
9
+ /**
10
+ * Return a copy of the schema with constraint keywords that don't apply
11
+ * to the given type removed. Meta keywords (description, title, etc.)
12
+ * and composition keywords are always preserved.
13
+ */
14
+ declare function stripInapplicableConstraints(schema: Record<string, unknown>, targetType: string): Record<string, unknown>;
15
+ //#endregion
16
+ export { extractArrayConstraints, extractFileConstraints, extractNumberConstraints, extractObjectConstraints, extractStringConstraints, stripInapplicableConstraints };
@@ -0,0 +1,138 @@
1
+ import { isObject } from "./guards.mjs";
2
+ //#region src/core/constraints.ts
3
+ function getString(obj, key) {
4
+ const value = obj[key];
5
+ return typeof value === "string" ? value : void 0;
6
+ }
7
+ function getNumber(obj, key) {
8
+ const value = obj[key];
9
+ return typeof value === "number" ? value : void 0;
10
+ }
11
+ function getObject(obj, key) {
12
+ const value = obj[key];
13
+ return isObject(value) ? value : void 0;
14
+ }
15
+ function extractStringConstraints(schema) {
16
+ const c = {};
17
+ const minLength = getNumber(schema, "minLength");
18
+ if (minLength !== void 0) c.minLength = minLength;
19
+ const maxLength = getNumber(schema, "maxLength");
20
+ if (maxLength !== void 0) c.maxLength = maxLength;
21
+ const pattern = getString(schema, "pattern");
22
+ if (pattern !== void 0) c.pattern = pattern;
23
+ const format = getString(schema, "format");
24
+ if (format !== void 0) c.format = format;
25
+ const contentEncoding = getString(schema, "contentEncoding");
26
+ if (contentEncoding !== void 0) c.contentEncoding = contentEncoding;
27
+ const contentMediaType = getString(schema, "contentMediaType");
28
+ if (contentMediaType !== void 0) c.contentMediaType = contentMediaType;
29
+ return c;
30
+ }
31
+ function extractNumberConstraints(schema) {
32
+ const c = {};
33
+ const minimum = getNumber(schema, "minimum");
34
+ if (minimum !== void 0) c.minimum = minimum;
35
+ const maximum = getNumber(schema, "maximum");
36
+ if (maximum !== void 0) c.maximum = maximum;
37
+ const exclusiveMinimum = getNumber(schema, "exclusiveMinimum");
38
+ if (exclusiveMinimum !== void 0) c.exclusiveMinimum = exclusiveMinimum;
39
+ const exclusiveMaximum = getNumber(schema, "exclusiveMaximum");
40
+ if (exclusiveMaximum !== void 0) c.exclusiveMaximum = exclusiveMaximum;
41
+ const multipleOf = getNumber(schema, "multipleOf");
42
+ if (multipleOf !== void 0) c.multipleOf = multipleOf;
43
+ return c;
44
+ }
45
+ function extractArrayConstraints(schema) {
46
+ const c = {};
47
+ const minItems = getNumber(schema, "minItems");
48
+ if (minItems !== void 0) c.minItems = minItems;
49
+ const maxItems = getNumber(schema, "maxItems");
50
+ if (maxItems !== void 0) c.maxItems = maxItems;
51
+ if (schema.uniqueItems === true) c.uniqueItems = true;
52
+ const contains = getObject(schema, "contains");
53
+ if (contains !== void 0) c.contains = contains;
54
+ const minContains = getNumber(schema, "minContains");
55
+ if (minContains !== void 0) c.minContains = minContains;
56
+ const maxContains = getNumber(schema, "maxContains");
57
+ if (maxContains !== void 0) c.maxContains = maxContains;
58
+ const unevaluatedItems = getObject(schema, "unevaluatedItems");
59
+ if (unevaluatedItems !== void 0) c.unevaluatedItems = unevaluatedItems;
60
+ return c;
61
+ }
62
+ function extractObjectConstraints(schema) {
63
+ const c = {};
64
+ const minProperties = getNumber(schema, "minProperties");
65
+ if (minProperties !== void 0) c.minProperties = minProperties;
66
+ const maxProperties = getNumber(schema, "maxProperties");
67
+ if (maxProperties !== void 0) c.maxProperties = maxProperties;
68
+ return c;
69
+ }
70
+ function extractFileConstraints(schema) {
71
+ const c = {};
72
+ const contentMediaType = getString(schema, "contentMediaType");
73
+ if (contentMediaType !== void 0) c.mimeTypes = [contentMediaType];
74
+ return c;
75
+ }
76
+ /**
77
+ * Constraint keywords that apply only to specific types.
78
+ * Used to strip inapplicable constraints when expanding type arrays.
79
+ */
80
+ const STRING_CONSTRAINTS = new Set([
81
+ "minLength",
82
+ "maxLength",
83
+ "pattern"
84
+ ]);
85
+ const NUMBER_CONSTRAINTS = new Set([
86
+ "minimum",
87
+ "maximum",
88
+ "exclusiveMinimum",
89
+ "exclusiveMaximum",
90
+ "multipleOf"
91
+ ]);
92
+ const ARRAY_CONSTRAINTS = new Set([
93
+ "minItems",
94
+ "maxItems",
95
+ "uniqueItems",
96
+ "contains",
97
+ "minContains",
98
+ "maxContains"
99
+ ]);
100
+ const OBJECT_CONSTRAINTS = new Set(["minProperties", "maxProperties"]);
101
+ const ALL_CONSTRAINTS = new Set([
102
+ ...STRING_CONSTRAINTS,
103
+ ...NUMBER_CONSTRAINTS,
104
+ ...ARRAY_CONSTRAINTS,
105
+ ...OBJECT_CONSTRAINTS
106
+ ]);
107
+ /**
108
+ * Return a copy of the schema with constraint keywords that don't apply
109
+ * to the given type removed. Meta keywords (description, title, etc.)
110
+ * and composition keywords are always preserved.
111
+ */
112
+ function stripInapplicableConstraints(schema, targetType) {
113
+ let keepForType;
114
+ switch (targetType) {
115
+ case "string":
116
+ keepForType = STRING_CONSTRAINTS;
117
+ break;
118
+ case "number":
119
+ case "integer":
120
+ keepForType = NUMBER_CONSTRAINTS;
121
+ break;
122
+ case "array":
123
+ keepForType = ARRAY_CONSTRAINTS;
124
+ break;
125
+ case "object":
126
+ keepForType = OBJECT_CONSTRAINTS;
127
+ break;
128
+ default: keepForType = /* @__PURE__ */ new Set();
129
+ }
130
+ const result = {};
131
+ for (const [key, value] of Object.entries(schema)) {
132
+ if (ALL_CONSTRAINTS.has(key) && !keepForType.has(key)) continue;
133
+ result[key] = value;
134
+ }
135
+ return result;
136
+ }
137
+ //#endregion
138
+ export { extractArrayConstraints, extractFileConstraints, extractNumberConstraints, extractObjectConstraints, extractStringConstraints, stripInapplicableConstraints };
@@ -0,0 +1,32 @@
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
+ * Merge multiple JSON Schema objects from allOf into one.
10
+ * Merges: properties, required, meta fields, and constraints.
11
+ */
12
+ declare function mergeAllOf(schemas: unknown[]): Record<string, unknown>;
13
+ interface NormalisedAnyOf {
14
+ inner: Record<string, unknown>;
15
+ isNullable: boolean;
16
+ }
17
+ /**
18
+ * Detect `anyOf: [T, { type: "null" }]` → nullable T.
19
+ * Returns the non-null schema and a nullable flag.
20
+ */
21
+ declare function normaliseAnyOf(options: unknown[]): NormalisedAnyOf | undefined;
22
+ interface Discriminated {
23
+ options: Record<string, unknown>[];
24
+ discriminator: string;
25
+ }
26
+ /**
27
+ * Detect oneOf where every option is an object with a property
28
+ * that has a `const` value → discriminated union.
29
+ */
30
+ declare function detectDiscriminated(options: unknown[]): Discriminated | undefined;
31
+ //#endregion
32
+ export { Discriminated, NormalisedAnyOf, detectDiscriminated, mergeAllOf, normaliseAnyOf };
@@ -0,0 +1,96 @@
1
+ import { isObject } from "./guards.mjs";
2
+ //#region src/core/merge.ts
3
+ /**
4
+ * Schema merging, nullable detection, and discriminated union detection.
5
+ *
6
+ * Used by the walker to handle `allOf`, `anyOf [T, null]`, and
7
+ * `oneOf` with discriminator properties.
8
+ */
9
+ function getString(obj, key) {
10
+ const value = obj[key];
11
+ return typeof value === "string" ? value : void 0;
12
+ }
13
+ function getArray(obj, key) {
14
+ const value = obj[key];
15
+ return Array.isArray(value) ? value : void 0;
16
+ }
17
+ function getObject(obj, key) {
18
+ const value = obj[key];
19
+ return isObject(value) ? value : void 0;
20
+ }
21
+ /**
22
+ * Merge multiple JSON Schema objects from allOf into one.
23
+ * Merges: properties, required, meta fields, and constraints.
24
+ */
25
+ function mergeAllOf(schemas) {
26
+ const merged = {};
27
+ const properties = {};
28
+ const required = [];
29
+ for (const entry of schemas) {
30
+ if (!isObject(entry)) continue;
31
+ const props = getObject(entry, "properties");
32
+ if (props !== void 0) for (const [key, value] of Object.entries(props)) properties[key] = value;
33
+ const req = getArray(entry, "required");
34
+ if (req !== void 0) {
35
+ for (const r of req) if (typeof r === "string" && !required.includes(r)) required.push(r);
36
+ }
37
+ for (const [key, value] of Object.entries(entry)) {
38
+ if (key === "properties" || key === "required" || key === "allOf" || key === "type") continue;
39
+ if (!(key in merged)) merged[key] = value;
40
+ }
41
+ if (!("type" in merged)) {
42
+ const type = getString(entry, "type");
43
+ if (type !== void 0) merged.type = type;
44
+ }
45
+ }
46
+ if (Object.keys(properties).length > 0) merged.properties = properties;
47
+ if (required.length > 0) merged.required = required;
48
+ return merged;
49
+ }
50
+ /**
51
+ * Detect `anyOf: [T, { type: "null" }]` → nullable T.
52
+ * Returns the non-null schema and a nullable flag.
53
+ */
54
+ function normaliseAnyOf(options) {
55
+ if (options.length !== 2) return void 0;
56
+ let inner;
57
+ let hasNull = false;
58
+ for (const opt of options) {
59
+ if (!isObject(opt)) return void 0;
60
+ if (opt.type === "null") hasNull = true;
61
+ else inner = opt;
62
+ }
63
+ if (!hasNull || inner === void 0) return void 0;
64
+ return {
65
+ inner,
66
+ isNullable: true
67
+ };
68
+ }
69
+ /**
70
+ * Detect oneOf where every option is an object with a property
71
+ * that has a `const` value → discriminated union.
72
+ */
73
+ function detectDiscriminated(options) {
74
+ if (options.length === 0) return void 0;
75
+ let discriminator;
76
+ for (const opt of options) {
77
+ if (!isObject(opt)) return void 0;
78
+ const props = getObject(opt, "properties");
79
+ if (props === void 0) return void 0;
80
+ let foundKey;
81
+ for (const [key, value] of Object.entries(props)) if (isObject(value) && "const" in value) {
82
+ foundKey = key;
83
+ break;
84
+ }
85
+ if (foundKey === void 0) return void 0;
86
+ if (discriminator === void 0) discriminator = foundKey;
87
+ else if (discriminator !== foundKey) return;
88
+ }
89
+ if (discriminator === void 0) return void 0;
90
+ return {
91
+ options: options.filter(isObject),
92
+ discriminator
93
+ };
94
+ }
95
+ //#endregion
96
+ export { detectDiscriminated, mergeAllOf, normaliseAnyOf };
@@ -0,0 +1,40 @@
1
+ import { n as OpenApiVersionInfo, t as JsonSchemaDraft } from "../version-CLchheaH.mjs";
2
+
3
+ //#region src/core/normalise.d.ts
4
+ type NodeTransform = (node: Record<string, unknown>) => Record<string, unknown>;
5
+ /**
6
+ * Deep-normalise a JSON Schema object by applying a per-node transform
7
+ * and recursing into every sub-schema location.
8
+ */
9
+ declare function deepNormalise(schema: Record<string, unknown>, transform: NodeTransform): Record<string, unknown>;
10
+ /**
11
+ * Normalise Draft 04 `exclusiveMinimum`/`exclusiveMaximum` from boolean
12
+ * to number form.
13
+ *
14
+ * In Draft 04:
15
+ * - `exclusiveMinimum: true` + `minimum: 5` → value must be > 5
16
+ * - `exclusiveMinimum: false` (or absent) + `minimum: 5` → value must be >= 5
17
+ *
18
+ * In Draft 06+:
19
+ * - `exclusiveMinimum: 5` → value must be > 5 (no separate `minimum`)
20
+ * - `minimum: 5` → value must be >= 5
21
+ *
22
+ * The transform converts boolean form to number form so the walker can
23
+ * treat `exclusiveMinimum`/`exclusiveMaximum` uniformly as numbers.
24
+ */
25
+ declare function normaliseDraft04Node(node: Record<string, unknown>): Record<string, unknown>;
26
+ /**
27
+ * Normalise a JSON Schema to canonical Draft 2020-12 form.
28
+ * Deep-clones the input — the original is never mutated.
29
+ */
30
+ declare function normaliseJsonSchema(schema: Record<string, unknown>, draft: JsonSchemaDraft): Record<string, unknown>;
31
+ /**
32
+ * Normalise an OpenAPI document's schemas for walker consumption.
33
+ * Handles version-specific keyword transformations.
34
+ *
35
+ * Returns the same object reference if no normalisation is needed
36
+ * (OpenAPI 3.1.x), or a deep-cloned normalised copy otherwise.
37
+ */
38
+ declare function normaliseOpenApiSchemas(doc: Record<string, unknown>, version: OpenApiVersionInfo): Record<string, unknown>;
39
+ //#endregion
40
+ export { NodeTransform, deepNormalise, normaliseDraft04Node, normaliseJsonSchema, normaliseOpenApiSchemas };
@@ -0,0 +1,171 @@
1
+ import { isObject } from "./guards.mjs";
2
+ import { isOpenApi30, isSwagger2 } from "./version.mjs";
3
+ import { deepNormaliseOpenApi30Doc } from "./openapi30.mjs";
4
+ import { normaliseSwagger2Document } from "./swagger2.mjs";
5
+ //#region src/core/normalise.ts
6
+ /**
7
+ * Keys whose values are `Record<string, SubSchema>` — objects where each
8
+ * property is a sub-schema.
9
+ */
10
+ const OBJECT_SUBSCHEMA_KEYS = new Set([
11
+ "properties",
12
+ "patternProperties",
13
+ "$defs",
14
+ "definitions",
15
+ "dependentSchemas"
16
+ ]);
17
+ /**
18
+ * Keys whose values are `SubSchema[]` — arrays of sub-schemas.
19
+ */
20
+ const ARRAY_SUBSCHEMA_KEYS = new Set([
21
+ "allOf",
22
+ "anyOf",
23
+ "oneOf",
24
+ "prefixItems"
25
+ ]);
26
+ /**
27
+ * Keys whose values are a single sub-schema object.
28
+ */
29
+ const SINGLE_SUBSCHEMA_KEYS = new Set([
30
+ "additionalProperties",
31
+ "not",
32
+ "contains",
33
+ "propertyNames",
34
+ "if",
35
+ "then",
36
+ "else",
37
+ "unevaluatedProperties",
38
+ "unevaluatedItems"
39
+ ]);
40
+ /**
41
+ * Normalise each element of an unknown array by applying deepNormalise
42
+ * to object elements and passing others through unchanged.
43
+ */
44
+ function normaliseArray(items, transform) {
45
+ const result = [];
46
+ for (const item of items) result.push(isObject(item) ? deepNormalise(item, transform) : item);
47
+ return result;
48
+ }
49
+ /**
50
+ * Normalise each value of a sub-schema map (e.g. properties, $defs).
51
+ */
52
+ function normaliseSubSchemaMap(map, transform) {
53
+ const result = {};
54
+ for (const [k, v] of Object.entries(map)) result[k] = isObject(v) ? deepNormalise(v, transform) : v;
55
+ return result;
56
+ }
57
+ /**
58
+ * Deep-normalise a JSON Schema object by applying a per-node transform
59
+ * and recursing into every sub-schema location.
60
+ */
61
+ function deepNormalise(schema, transform) {
62
+ const node = transform({ ...schema });
63
+ const result = {};
64
+ for (const [key, value] of Object.entries(node)) if (isObject(value) && OBJECT_SUBSCHEMA_KEYS.has(key)) result[key] = normaliseSubSchemaMap(value, transform);
65
+ else if (Array.isArray(value) && ARRAY_SUBSCHEMA_KEYS.has(key)) result[key] = normaliseArray(value, transform);
66
+ else if (isObject(value) && SINGLE_SUBSCHEMA_KEYS.has(key)) result[key] = deepNormalise(value, transform);
67
+ else if (key === "items") if (Array.isArray(value)) result[key] = normaliseArray(value, transform);
68
+ else if (isObject(value)) result[key] = deepNormalise(value, transform);
69
+ else result[key] = value;
70
+ else result[key] = value;
71
+ return result;
72
+ }
73
+ /**
74
+ * Normalise Draft 04 `exclusiveMinimum`/`exclusiveMaximum` from boolean
75
+ * to number form.
76
+ *
77
+ * In Draft 04:
78
+ * - `exclusiveMinimum: true` + `minimum: 5` → value must be > 5
79
+ * - `exclusiveMinimum: false` (or absent) + `minimum: 5` → value must be >= 5
80
+ *
81
+ * In Draft 06+:
82
+ * - `exclusiveMinimum: 5` → value must be > 5 (no separate `minimum`)
83
+ * - `minimum: 5` → value must be >= 5
84
+ *
85
+ * The transform converts boolean form to number form so the walker can
86
+ * treat `exclusiveMinimum`/`exclusiveMaximum` uniformly as numbers.
87
+ */
88
+ function normaliseDraft04Node(node) {
89
+ if (node.exclusiveMinimum === true && typeof node.minimum === "number") {
90
+ node.exclusiveMinimum = node.minimum;
91
+ delete node.minimum;
92
+ } else if (node.exclusiveMinimum === false) delete node.exclusiveMinimum;
93
+ if (node.exclusiveMaximum === true && typeof node.maximum === "number") {
94
+ node.exclusiveMaximum = node.maximum;
95
+ delete node.maximum;
96
+ } else if (node.exclusiveMaximum === false) delete node.exclusiveMaximum;
97
+ if (typeof node.id === "string" && !("$id" in node)) {
98
+ node.$id = node.id;
99
+ delete node.id;
100
+ }
101
+ if (Array.isArray(node.items) && !("prefixItems" in node)) {
102
+ node.prefixItems = node.items;
103
+ delete node.items;
104
+ if ("additionalItems" in node) {
105
+ node.items = node.additionalItems;
106
+ delete node.additionalItems;
107
+ }
108
+ }
109
+ return node;
110
+ }
111
+ /**
112
+ * Normalise Draft 2019-09 `$recursiveRef` to `$ref`.
113
+ *
114
+ * `$recursiveRef` resolves to the nearest `$recursiveAnchor` in the
115
+ * dynamic scope. For rendering, the common pattern is a root
116
+ * `$recursiveAnchor: true` — the normaliser converts
117
+ * `$recursiveRef: "#"` to `$ref: "#"` pointing to the root.
118
+ *
119
+ * If a `$recursiveAnchor` name is given (non-empty string), the ref
120
+ * is converted to `$ref: "#<anchor>"` so the existing $anchor
121
+ * resolution in ref.ts can find it.
122
+ */
123
+ function normaliseDraft201909Node(node) {
124
+ if (typeof node.$recursiveRef === "string") {
125
+ node.$ref = "#";
126
+ delete node.$recursiveRef;
127
+ }
128
+ if (node.$recursiveAnchor === true) {
129
+ if (typeof node.$anchor !== "string") node.$anchor = "__recursive__";
130
+ delete node.$recursiveAnchor;
131
+ }
132
+ return node;
133
+ }
134
+ function normaliseDynamicRefNode(node) {
135
+ if (typeof node.$dynamicRef === "string") {
136
+ node.$ref = node.$dynamicRef;
137
+ delete node.$dynamicRef;
138
+ }
139
+ if (typeof node.$dynamicAnchor === "string") {
140
+ if (typeof node.$anchor !== "string") node.$anchor = node.$dynamicAnchor;
141
+ delete node.$dynamicAnchor;
142
+ }
143
+ return node;
144
+ }
145
+ /**
146
+ * Normalise a JSON Schema to canonical Draft 2020-12 form.
147
+ * Deep-clones the input — the original is never mutated.
148
+ */
149
+ function normaliseJsonSchema(schema, draft) {
150
+ switch (draft) {
151
+ case "draft-04": return deepNormalise(schema, normaliseDraft04Node);
152
+ case "draft-2019-09": return deepNormalise(schema, normaliseDraft201909Node);
153
+ case "draft-2020-12": return deepNormalise(schema, normaliseDynamicRefNode);
154
+ case "draft-06":
155
+ case "draft-07": return schema;
156
+ }
157
+ }
158
+ /**
159
+ * Normalise an OpenAPI document's schemas for walker consumption.
160
+ * Handles version-specific keyword transformations.
161
+ *
162
+ * Returns the same object reference if no normalisation is needed
163
+ * (OpenAPI 3.1.x), or a deep-cloned normalised copy otherwise.
164
+ */
165
+ function normaliseOpenApiSchemas(doc, version) {
166
+ if (isSwagger2(version)) return normaliseSwagger2Document(doc, deepNormalise, normaliseDraft04Node);
167
+ if (isOpenApi30(version)) return deepNormaliseOpenApi30Doc(doc, deepNormalise);
168
+ return doc;
169
+ }
170
+ //#endregion
171
+ export { deepNormalise, normaliseDraft04Node, normaliseJsonSchema, normaliseOpenApiSchemas };
@@ -0,0 +1,38 @@
1
+ import { NodeTransform } from "./normalise.mjs";
2
+
3
+ //#region src/core/openapi30.d.ts
4
+ /**
5
+ * Normalise OpenAPI 3.0.x `nullable` keyword to `anyOf [T, null]`.
6
+ *
7
+ * OpenAPI 3.0 uses `nullable: true` instead of the JSON Schema standard
8
+ * `anyOf: [T, { type: "null" }]`. The walker understands the latter form
9
+ * natively, so this normaliser converts `nullable` to `anyOf`.
10
+ *
11
+ * Only applied when `nullable` is explicitly `true`. `nullable: false` or
12
+ * absent is the default and requires no transformation.
13
+ */
14
+ declare function normaliseOpenApi30Node(node: Record<string, unknown>): Record<string, unknown>;
15
+ /**
16
+ * Normalise OpenAPI 3.0.x `discriminator` keyword by injecting `const`
17
+ * values into each `oneOf`/`anyOf` option's discriminator property.
18
+ *
19
+ * In OpenAPI 3.0, `discriminator` is a sibling of `oneOf`/`anyOf`:
20
+ * discriminator: { propertyName: "type" }
21
+ * The walker detects discriminated unions from `oneOf` + `const` on a
22
+ * property, so this normaliser injects the `const` values from the
23
+ * `mapping` or infers them from `$ref` fragment names.
24
+ */
25
+ declare function normaliseOpenApi30Discriminator(node: Record<string, unknown>): Record<string, unknown>;
26
+ /**
27
+ * Combined OpenAPI 3.0.x node transform: nullable + discriminator.
28
+ * Applied to every schema node in an OpenAPI 3.0 document.
29
+ */
30
+ declare function normaliseOpenApi30Combined(node: Record<string, unknown>): Record<string, unknown>;
31
+ /**
32
+ * Deep-normalise all schemas in an OpenAPI 3.0.x document.
33
+ * Walks components/schemas, path operations, parameters, request bodies,
34
+ * and responses — applying `nullable` normalisation to each schema.
35
+ */
36
+ declare function deepNormaliseOpenApi30Doc(doc: Record<string, unknown>, deepNormalise: (schema: Record<string, unknown>, transform: NodeTransform) => Record<string, unknown>): Record<string, unknown>;
37
+ //#endregion
38
+ export { deepNormaliseOpenApi30Doc, normaliseOpenApi30Combined, normaliseOpenApi30Discriminator, normaliseOpenApi30Node };