schema-components 1.20.0 → 1.22.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 (77) hide show
  1. package/README.md +1 -1
  2. package/dist/core/adapter.d.mts +28 -4
  3. package/dist/core/adapter.mjs +408 -71
  4. package/dist/core/constraints.d.mts +2 -2
  5. package/dist/core/constraints.mjs +0 -2
  6. package/dist/core/diagnostics.d.mts +1 -1
  7. package/dist/core/errors.d.mts +1 -1
  8. package/dist/core/errors.mjs +9 -15
  9. package/dist/core/fieldOrder.d.mts +1 -1
  10. package/dist/core/formats.d.mts +22 -1
  11. package/dist/core/formats.mjs +21 -0
  12. package/dist/core/limits.d.mts +2 -0
  13. package/dist/core/limits.mjs +23 -0
  14. package/dist/core/merge.d.mts +11 -2
  15. package/dist/core/merge.mjs +11 -0
  16. package/dist/core/normalise.d.mts +36 -4
  17. package/dist/core/normalise.mjs +2 -2
  18. package/dist/core/openapi30.d.mts +24 -1
  19. package/dist/core/openapi30.mjs +2 -2
  20. package/dist/core/ref.d.mts +1 -1
  21. package/dist/core/ref.mjs +35 -9
  22. package/dist/core/renderer.d.mts +1 -1
  23. package/dist/core/renderer.mjs +0 -2
  24. package/dist/core/swagger2.d.mts +1 -1
  25. package/dist/core/swagger2.mjs +1 -1
  26. package/dist/core/typeInference.d.mts +2 -2
  27. package/dist/core/types.d.mts +2 -2
  28. package/dist/core/types.mjs +1 -4
  29. package/dist/core/version.d.mts +1 -1
  30. package/dist/core/walkBuilders.d.mts +13 -5
  31. package/dist/core/walkBuilders.mjs +11 -3
  32. package/dist/core/walker.d.mts +1 -1
  33. package/dist/core/walker.mjs +110 -26
  34. package/dist/{diagnostics-CbBPsxSt.d.mts → diagnostics-D0QCYGv0.d.mts} +1 -1
  35. package/dist/{errors-C2iABcn9.d.mts → errors-DpFwqs5C.d.mts} +7 -11
  36. package/dist/html/a11y.d.mts +2 -2
  37. package/dist/html/a11y.mjs +10 -3
  38. package/dist/html/renderToHtml.d.mts +10 -3
  39. package/dist/html/renderToHtml.mjs +13 -3
  40. package/dist/html/renderToHtmlStream.d.mts +2 -2
  41. package/dist/html/renderers.d.mts +2 -2
  42. package/dist/html/renderers.mjs +1 -6
  43. package/dist/html/streamRenderers.d.mts +5 -4
  44. package/dist/html/streamRenderers.mjs +91 -30
  45. package/dist/limits-Cw5QZND8.d.mts +29 -0
  46. package/dist/{normalise-CMMEl4cd.mjs → normalise-DVEJQmF7.mjs} +791 -141
  47. package/dist/openapi/ApiCallbacks.d.mts +1 -1
  48. package/dist/openapi/ApiLinks.d.mts +1 -1
  49. package/dist/openapi/ApiResponseHeaders.d.mts +1 -1
  50. package/dist/openapi/ApiSecurity.d.mts +1 -1
  51. package/dist/openapi/ApiSecurity.mjs +127 -7
  52. package/dist/openapi/components.d.mts +175 -21
  53. package/dist/openapi/components.mjs +145 -21
  54. package/dist/openapi/parser.d.mts +1 -1
  55. package/dist/openapi/parser.mjs +74 -7
  56. package/dist/openapi/resolve.d.mts +70 -12
  57. package/dist/openapi/resolve.mjs +265 -42
  58. package/dist/react/SchemaComponent.d.mts +100 -35
  59. package/dist/react/SchemaComponent.mjs +66 -24
  60. package/dist/react/SchemaView.d.mts +3 -3
  61. package/dist/react/SchemaView.mjs +2 -2
  62. package/dist/react/fieldPath.d.mts +1 -1
  63. package/dist/react/headless.d.mts +1 -1
  64. package/dist/react/headless.mjs +1 -2
  65. package/dist/react/headlessRenderers.d.mts +3 -4
  66. package/dist/react/headlessRenderers.mjs +11 -31
  67. package/dist/{ref-C8JbwfiS.d.mts → ref-D-_JBZkF.d.mts} +7 -2
  68. package/dist/{renderer-SOIbJBtk.d.mts → renderer-BaRlQIuN.d.mts} +2 -2
  69. package/dist/themes/mantine.d.mts +1 -1
  70. package/dist/themes/mui.d.mts +1 -1
  71. package/dist/themes/radix.d.mts +1 -1
  72. package/dist/themes/shadcn.d.mts +1 -1
  73. package/dist/typeInference-DkcUHfaM.d.mts +982 -0
  74. package/dist/{types-C9zw9wbX.d.mts → types-BrRMV0en.d.mts} +15 -12
  75. package/package.json +1 -3
  76. package/dist/typeInference-CDoD_LZ_.d.mts +0 -533
  77. /package/dist/{version-D-u7aMfy.d.mts → version-D2jfdX6E.d.mts} +0 -0
@@ -61,8 +61,6 @@ function extractArrayConstraints(schema) {
61
61
  const maxItems = getNumber(schema, "maxItems");
62
62
  if (maxItems !== void 0) c.maxItems = maxItems;
63
63
  if (schema.uniqueItems === true) c.uniqueItems = true;
64
- const contains = getObject(schema, "contains");
65
- if (contains !== void 0) c.contains = contains;
66
64
  const minContains = getNumber(schema, "minContains");
67
65
  if (minContains !== void 0) c.minContains = minContains;
68
66
  const maxContains = getNumber(schema, "maxContains");
@@ -1,2 +1,2 @@
1
- import { a as appendPointer, i as DiagnosticsOptions, n as DiagnosticCode, o as emitDiagnostic, r as DiagnosticSink, t as Diagnostic } from "../diagnostics-CbBPsxSt.mjs";
1
+ import { a as appendPointer, i as DiagnosticsOptions, n as DiagnosticCode, o as emitDiagnostic, r as DiagnosticSink, t as Diagnostic } from "../diagnostics-D0QCYGv0.mjs";
2
2
  export { Diagnostic, DiagnosticCode, DiagnosticSink, DiagnosticsOptions, appendPointer, emitDiagnostic };
@@ -1,2 +1,2 @@
1
- import { i as SchemaRenderError, n as SchemaFieldError, r as SchemaNormalisationError, t as SchemaError } from "../errors-C2iABcn9.mjs";
1
+ import { i as SchemaRenderError, n as SchemaFieldError, r as SchemaNormalisationError, t as SchemaError } from "../errors-DpFwqs5C.mjs";
2
2
  export { SchemaError, SchemaFieldError, SchemaNormalisationError, SchemaRenderError };
@@ -14,12 +14,17 @@
14
14
  /**
15
15
  * Base class for all schema-components errors.
16
16
  * Catch this to handle any library error uniformly.
17
+ *
18
+ * Forwards the optional `cause` to the native ES2022 `Error` constructor so
19
+ * `error.cause` is wired up by the runtime and rendered correctly by
20
+ * `util.inspect` ("Caused by: ..."). Subclasses that need a typed `cause`
21
+ * field still get it via the platform's own `Error.cause` getter.
17
22
  */
18
23
  var SchemaError = class extends Error {
19
24
  /** The schema input that caused the error. */
20
25
  schema;
21
- constructor(message, schema) {
22
- super(message);
26
+ constructor(message, schema, cause) {
27
+ super(message, cause !== void 0 ? { cause } : void 0);
23
28
  this.name = "SchemaError";
24
29
  this.schema = schema;
25
30
  }
@@ -37,19 +42,11 @@ var SchemaNormalisationError = class extends SchemaError {
37
42
  * (e.g. "bigint", "date", "map", "set"). `undefined` for other kinds.
38
43
  */
39
44
  zodType;
40
- /**
41
- * The original underlying error, when this normalisation error wraps
42
- * another exception (typically the error thrown by `z.toJSONSchema()`).
43
- * Preserves the source stack trace so the root cause is not lost when
44
- * the classifier translates the message.
45
- */
46
- cause;
47
45
  constructor(message, schema, kind, zodType, cause) {
48
- super(message, schema);
46
+ super(message, schema, cause);
49
47
  this.name = "SchemaNormalisationError";
50
48
  this.kind = kind;
51
49
  this.zodType = zodType;
52
- this.cause = cause;
53
50
  }
54
51
  };
55
52
  /**
@@ -60,13 +57,10 @@ var SchemaNormalisationError = class extends SchemaError {
60
57
  var SchemaRenderError = class extends SchemaError {
61
58
  /** The schema type being rendered when the error occurred. */
62
59
  schemaType;
63
- /** The original error from the render function. */
64
- cause;
65
60
  constructor(message, schema, schemaType, cause) {
66
- super(message, schema);
61
+ super(message, schema, cause);
67
62
  this.name = "SchemaRenderError";
68
63
  this.schemaType = schemaType;
69
- this.cause = cause;
70
64
  }
71
65
  };
72
66
  /**
@@ -1,4 +1,4 @@
1
- import { M as WalkedField } from "../types-C9zw9wbX.mjs";
1
+ import { j as WalkedField } from "../types-BrRMV0en.mjs";
2
2
 
3
3
  //#region src/core/fieldOrder.d.ts
4
4
  /**
@@ -1,4 +1,4 @@
1
- import { i as DiagnosticsOptions } from "../diagnostics-CbBPsxSt.mjs";
1
+ import { i as DiagnosticsOptions } from "../diagnostics-D0QCYGv0.mjs";
2
2
 
3
3
  //#region src/core/formats.d.ts
4
4
  /**
@@ -16,6 +16,27 @@ type FormatValidator = RegExp | ((value: string) => boolean);
16
16
  /**
17
17
  * Recognised JSON Schema formats with their validation patterns.
18
18
  * Unknown formats emit an `unknown-format` diagnostic and skip derivation.
19
+ *
20
+ * Draft origin reference — formats first standardised by each draft:
21
+ *
22
+ * - Draft 04: `date-time`, `email`, `hostname`, `ipv4`, `ipv6`, `uri`
23
+ * - Draft 06: `uri-reference`, `uri-template`, `json-pointer`
24
+ * - Draft 07: `date`, `time`, `idn-email`, `idn-hostname`, `iri`,
25
+ * `iri-reference`, `regex`, `relative-json-pointer`
26
+ * - Draft 2019-09: `duration`, `uuid`
27
+ * - Vocabulary extensions / non-standard: `binary` (OpenAPI), and the
28
+ * Zod-emitted formats `cuid`, `cuid2`, `nanoid`, `cidrv4`, `cidrv6`,
29
+ * `base64`, `base64url`, `e164`, `emoji`, `ulid`, `xid`, `ksuid`,
30
+ * `lowercase`, `uppercase`, `jwt`, `json-string`
31
+ *
32
+ * Policy: schema-components accepts ALL formats in ALL drafts. We do
33
+ * not reject (e.g.) `uri-reference` on a Draft 04 schema or `uuid` on
34
+ * a Draft 06 schema, even though the spec did not standardise those
35
+ * names until a later draft. This matches the behaviour of every
36
+ * mainstream JSON Schema validator (Ajv, jsonschema, etc.) and avoids
37
+ * spurious failures on legitimate real-world schemas that pre-date or
38
+ * post-date the dialect they declare. Authors who want strict draft-
39
+ * locked behaviour should validate with a dedicated meta-schema tool.
19
40
  */
20
41
  /**
21
42
  * Email format pattern, exported as a named const so callers that need a
@@ -11,6 +11,27 @@ const MAX_REGEX_PATTERN_LENGTH = 500;
11
11
  /**
12
12
  * Recognised JSON Schema formats with their validation patterns.
13
13
  * Unknown formats emit an `unknown-format` diagnostic and skip derivation.
14
+ *
15
+ * Draft origin reference — formats first standardised by each draft:
16
+ *
17
+ * - Draft 04: `date-time`, `email`, `hostname`, `ipv4`, `ipv6`, `uri`
18
+ * - Draft 06: `uri-reference`, `uri-template`, `json-pointer`
19
+ * - Draft 07: `date`, `time`, `idn-email`, `idn-hostname`, `iri`,
20
+ * `iri-reference`, `regex`, `relative-json-pointer`
21
+ * - Draft 2019-09: `duration`, `uuid`
22
+ * - Vocabulary extensions / non-standard: `binary` (OpenAPI), and the
23
+ * Zod-emitted formats `cuid`, `cuid2`, `nanoid`, `cidrv4`, `cidrv6`,
24
+ * `base64`, `base64url`, `e164`, `emoji`, `ulid`, `xid`, `ksuid`,
25
+ * `lowercase`, `uppercase`, `jwt`, `json-string`
26
+ *
27
+ * Policy: schema-components accepts ALL formats in ALL drafts. We do
28
+ * not reject (e.g.) `uri-reference` on a Draft 04 schema or `uuid` on
29
+ * a Draft 06 schema, even though the spec did not standardise those
30
+ * names until a later draft. This matches the behaviour of every
31
+ * mainstream JSON Schema validator (Ajv, jsonschema, etc.) and avoids
32
+ * spurious failures on legitimate real-world schemas that pre-date or
33
+ * post-date the dialect they declare. Authors who want strict draft-
34
+ * locked behaviour should validate with a dedicated meta-schema tool.
14
35
  */
15
36
  /**
16
37
  * Email format pattern, exported as a named const so callers that need a
@@ -0,0 +1,2 @@
1
+ import { i as MaxRefDepth, n as MAX_REF_DEPTH, r as MAX_RENDER_DEPTH, t as MAX_PATH_ITEM_REF_HOPS } from "../limits-Cw5QZND8.mjs";
2
+ export { MAX_PATH_ITEM_REF_HOPS, MAX_REF_DEPTH, MAX_RENDER_DEPTH, MaxRefDepth };
@@ -0,0 +1,23 @@
1
+ //#region src/core/limits.ts
2
+ /**
3
+ * Shared depth caps and hop counts used to bound recursion across
4
+ * schema-components. All numeric limits live here so the renderer, the
5
+ * ref resolver, the OpenAPI parser, and the type-level inference engine
6
+ * agree on the same constants.
7
+ */
8
+ /**
9
+ * Maximum recursion depth for the schema walker, the React renderers,
10
+ * the streaming HTML renderer, and the server-side renderer. Beyond
11
+ * this depth a recursion sentinel is emitted instead of further descent
12
+ * — the only safe response to a cyclic walked-field graph.
13
+ */
14
+ const MAX_RENDER_DEPTH = 10;
15
+ const MAX_REF_DEPTH = 64;
16
+ /**
17
+ * Maximum number of `$ref` hops permitted when walking a chain of
18
+ * OpenAPI Path Item Object references. Beyond this a
19
+ * `path-item-ref-too-deep` diagnostic is emitted and resolution stops.
20
+ */
21
+ const MAX_PATH_ITEM_REF_HOPS = 8;
22
+ //#endregion
23
+ export { MAX_PATH_ITEM_REF_HOPS, MAX_REF_DEPTH, MAX_RENDER_DEPTH };
@@ -1,4 +1,4 @@
1
- import { i as DiagnosticsOptions } from "../diagnostics-CbBPsxSt.mjs";
1
+ import { i as DiagnosticsOptions } from "../diagnostics-D0QCYGv0.mjs";
2
2
 
3
3
  //#region src/core/merge.d.ts
4
4
  /**
@@ -25,8 +25,17 @@ declare function mergeRefSiblings(referencer: Record<string, unknown>, resolvedM
25
25
  * When a later branch redefines a keyword with a non-equal value the
26
26
  * later value is silently dropped — an `allof-conflict` diagnostic is
27
27
  * emitted so the loss is visible to consumers.
28
+ *
29
+ * Boolean branches (valid per Draft 06+) collapse the composite:
30
+ * - `false` makes the entire \`allOf\` unsatisfiable — return \`false\`,
31
+ * which the walker turns into a \`NeverField\`.
32
+ * - \`true\` is the always-valid schema and contributes no constraints —
33
+ * skip silently.
34
+ *
35
+ * Non-boolean, non-object entries (e.g. arrays, numbers) are malformed
36
+ * inputs that cannot represent a schema; skip them as before.
28
37
  */
29
- declare function mergeAllOf(schemas: unknown[], diagnostics?: DiagnosticsOptions, pointer?: string): Record<string, unknown>;
38
+ declare function mergeAllOf(schemas: unknown[], diagnostics?: DiagnosticsOptions, pointer?: string): Record<string, unknown> | false;
30
39
  interface NormalisedAnyOf {
31
40
  inner: Record<string, unknown>;
32
41
  isNullable: boolean;
@@ -109,12 +109,23 @@ function mergeRefSiblings(referencer, resolvedMeta) {
109
109
  * When a later branch redefines a keyword with a non-equal value the
110
110
  * later value is silently dropped — an `allof-conflict` diagnostic is
111
111
  * emitted so the loss is visible to consumers.
112
+ *
113
+ * Boolean branches (valid per Draft 06+) collapse the composite:
114
+ * - `false` makes the entire \`allOf\` unsatisfiable — return \`false\`,
115
+ * which the walker turns into a \`NeverField\`.
116
+ * - \`true\` is the always-valid schema and contributes no constraints —
117
+ * skip silently.
118
+ *
119
+ * Non-boolean, non-object entries (e.g. arrays, numbers) are malformed
120
+ * inputs that cannot represent a schema; skip them as before.
112
121
  */
113
122
  function mergeAllOf(schemas, diagnostics, pointer = "") {
114
123
  const merged = {};
115
124
  const properties = {};
116
125
  const required = [];
117
126
  for (const entry of schemas) {
127
+ if (entry === false) return false;
128
+ if (entry === true) continue;
118
129
  if (!isObject(entry)) continue;
119
130
  const props = getObject(entry, "properties");
120
131
  if (props !== void 0) for (const [key, value] of Object.entries(props)) properties[key] = value;
@@ -1,23 +1,47 @@
1
- import { i as DiagnosticsOptions } from "../diagnostics-CbBPsxSt.mjs";
2
- import { i as OpenApiVersionInfo, r as JsonSchemaDraft } from "../version-D-u7aMfy.mjs";
1
+ import { i as DiagnosticsOptions } from "../diagnostics-D0QCYGv0.mjs";
2
+ import { i as OpenApiVersionInfo, r as JsonSchemaDraft } from "../version-D2jfdX6E.mjs";
3
3
 
4
4
  //#region src/core/normalise.d.ts
5
5
  type NodeTransform = (node: Record<string, unknown>) => Record<string, unknown>;
6
6
  /**
7
7
  * Deep-normalise a JSON Schema object by applying a per-node transform
8
8
  * and recursing into every sub-schema location.
9
+ *
10
+ * The optional `visited` set guards against shared object references and
11
+ * cycles introduced upstream (e.g. by the OpenAPI bundler's
12
+ * `structuredClone`-based inlining of external refs). The walk skips
13
+ * already-seen nodes by returning the original reference rather than
14
+ * recursing forever.
9
15
  */
10
- declare function deepNormalise(schema: Record<string, unknown>, transform: NodeTransform): Record<string, unknown>;
16
+ declare function deepNormalise(schema: Record<string, unknown>, transform: NodeTransform, visited?: WeakSet<object>): Record<string, unknown>;
11
17
  /**
12
18
  * Per-node context threaded through `deepNormaliseWithContext`.
13
19
  *
14
20
  * Carries the diagnostics sink and the JSON Pointer to the current
15
21
  * node so per-node transforms can emit pointer-accurate diagnostics
16
22
  * when they translate or reject legacy constructs.
23
+ *
24
+ * `documentHasDynamicAnchor` is set once at the entry point by scanning
25
+ * the input for any `$dynamicAnchor`/`$recursiveAnchor` keyword. Per-
26
+ * node transforms read it to decide whether a `$dynamicRef`/`$recursiveRef`
27
+ * rewrite needs a `dynamic-ref-degraded` diagnostic — a ref pointing at
28
+ * an anchor in the document body cannot be statically resolved without
29
+ * losing dynamic-scope semantics, while a document with no dynamic
30
+ * anchors at all has no semantics to lose.
31
+ *
32
+ * `documentHasRecursiveAnchor` is the same idea for Draft 2019-09's
33
+ * `$recursiveAnchor` keyword.
34
+ *
35
+ * `declaredDraft` carries the draft the normaliser is operating under
36
+ * (when known) so per-node transforms can emit `keyword-out-of-draft`
37
+ * diagnostics for keywords introduced in a later draft.
17
38
  */
18
39
  interface NodeContext {
19
40
  diagnostics: DiagnosticsOptions | undefined;
20
41
  pointer: string;
42
+ documentHasDynamicAnchor: boolean;
43
+ documentHasRecursiveAnchor: boolean;
44
+ declaredDraft: JsonSchemaDraft | undefined;
21
45
  }
22
46
  type NodeTransformWithContext = (node: Record<string, unknown>, ctx: NodeContext) => Record<string, unknown>;
23
47
  /**
@@ -52,6 +76,14 @@ declare function deepNormaliseWithContext(schema: Record<string, unknown>, trans
52
76
  * {@link deepNormaliseWithContext} to thread diagnostics.
53
77
  */
54
78
  declare function normaliseDraft04Node(node: Record<string, unknown>): Record<string, unknown>;
79
+ /**
80
+ * Pick the per-node transform that normalises a single Schema Object to
81
+ * canonical Draft 2020-12 form for the supplied draft. Exposed so the
82
+ * OpenAPI 3.1 path can honour a non-default `jsonSchemaDialect`
83
+ * declaration by routing each Schema Object through the matching
84
+ * transform without re-implementing the dispatch.
85
+ */
86
+ declare function selectDraftTransform(draft: JsonSchemaDraft): NodeTransformWithContext;
55
87
  /**
56
88
  * Normalise a JSON Schema to canonical Draft 2020-12 form.
57
89
  * Deep-clones the input — the original is never mutated.
@@ -71,4 +103,4 @@ declare function normaliseJsonSchema(schema: Record<string, unknown>, draft: Jso
71
103
  */
72
104
  declare function normaliseOpenApiSchemas(doc: Record<string, unknown>, version: OpenApiVersionInfo, diagnostics?: DiagnosticsOptions): Record<string, unknown>;
73
105
  //#endregion
74
- export { NodeContext, NodeTransform, NodeTransformWithContext, deepNormalise, deepNormaliseWithContext, normaliseDraft04Node, normaliseJsonSchema, normaliseOpenApiSchemas };
106
+ export { NodeContext, NodeTransform, NodeTransformWithContext, deepNormalise, deepNormaliseWithContext, normaliseDraft04Node, normaliseJsonSchema, normaliseOpenApiSchemas, selectDraftTransform };
@@ -1,2 +1,2 @@
1
- import { a as normaliseOpenApiSchemas, i as normaliseJsonSchema, n as deepNormaliseWithContext, r as normaliseDraft04Node, t as deepNormalise } from "../normalise-CMMEl4cd.mjs";
2
- export { deepNormalise, deepNormaliseWithContext, normaliseDraft04Node, normaliseJsonSchema, normaliseOpenApiSchemas };
1
+ import { a as normaliseOpenApiSchemas, i as normaliseJsonSchema, n as deepNormaliseWithContext, o as selectDraftTransform, r as normaliseDraft04Node, t as deepNormalise } from "../normalise-DVEJQmF7.mjs";
2
+ export { deepNormalise, deepNormaliseWithContext, normaliseDraft04Node, normaliseJsonSchema, normaliseOpenApiSchemas, selectDraftTransform };
@@ -23,6 +23,29 @@ declare function normaliseOpenApi30Node(node: Record<string, unknown>): Record<s
23
23
  * `mapping` or infers them from `$ref` fragment names.
24
24
  */
25
25
  declare function normaliseOpenApi30Discriminator(node: Record<string, unknown>): Record<string, unknown>;
26
+ /**
27
+ * Document-level pre-pass for OpenAPI discriminators that are declared
28
+ * on a base schema and inherited by subtypes via `allOf`.
29
+ *
30
+ * The per-node {@link normaliseOpenApi30Discriminator} only handles
31
+ * discriminators that already sit alongside `oneOf`/`anyOf`. For the
32
+ * canonical "Cat extends Pet" pattern — where `Pet` carries the
33
+ * discriminator and `Cat`/`Dog` reference `Pet` via `allOf` — the
34
+ * discriminator is silently lost. This pre-pass:
35
+ *
36
+ * 1. Injects the discriminator `const` on each subtype's local
37
+ * `properties` (so a direct render of the subtype validates the
38
+ * discriminator value correctly).
39
+ * 2. Synthesises a `oneOf` on the base whenever it lacks one, listing
40
+ * each subtype as `{ $ref, properties: { propertyName: { const } } }`.
41
+ * The per-node discriminator transform then sees `oneOf` and clears
42
+ * the `discriminator` keyword, and the walker's
43
+ * `detectDiscriminated` finds the per-option `const`s.
44
+ *
45
+ * Mutates a shallow clone of `components/schemas` — the input document
46
+ * is never modified.
47
+ */
48
+ declare function applyDiscriminatorAllOfPrepass(doc: Record<string, unknown>): Record<string, unknown>;
26
49
  /**
27
50
  * Combined OpenAPI 3.0.x node transform: Draft 04 + nullable + discriminator.
28
51
  * Applied to every schema node in an OpenAPI 3.0 document.
@@ -67,4 +90,4 @@ declare function deepNormaliseOpenApiDoc(doc: Record<string, unknown>, normalise
67
90
  */
68
91
  declare function deepNormaliseOpenApi30Doc(doc: Record<string, unknown>, deepNormalise: (schema: Record<string, unknown>, transform: NodeTransform) => Record<string, unknown>): Record<string, unknown>;
69
92
  //#endregion
70
- export { deepNormaliseOpenApi30Doc, deepNormaliseOpenApiDoc, normaliseOpenApi30Combined, normaliseOpenApi30Discriminator, normaliseOpenApi30Node };
93
+ export { applyDiscriminatorAllOfPrepass, deepNormaliseOpenApi30Doc, deepNormaliseOpenApiDoc, normaliseOpenApi30Combined, normaliseOpenApi30Discriminator, normaliseOpenApi30Node };
@@ -1,2 +1,2 @@
1
- import { c as deepNormaliseOpenApiDoc, d as normaliseOpenApi30Node, l as normaliseOpenApi30Combined, s as deepNormaliseOpenApi30Doc, u as normaliseOpenApi30Discriminator } from "../normalise-CMMEl4cd.mjs";
2
- export { deepNormaliseOpenApi30Doc, deepNormaliseOpenApiDoc, normaliseOpenApi30Combined, normaliseOpenApi30Discriminator, normaliseOpenApi30Node };
1
+ import { c as applyDiscriminatorAllOfPrepass, d as normaliseOpenApi30Combined, f as normaliseOpenApi30Discriminator, l as deepNormaliseOpenApi30Doc, p as normaliseOpenApi30Node, u as deepNormaliseOpenApiDoc } from "../normalise-DVEJQmF7.mjs";
2
+ export { applyDiscriminatorAllOfPrepass, deepNormaliseOpenApi30Doc, deepNormaliseOpenApiDoc, normaliseOpenApi30Combined, normaliseOpenApi30Discriminator, normaliseOpenApi30Node };
@@ -1,2 +1,2 @@
1
- import { a as findAnchor, i as dereference, n as RefOptions, o as resolveRef, r as countDistinctRefs, t as ExternalResolver } from "../ref-C8JbwfiS.mjs";
1
+ import { a as findAnchor, i as dereference, n as RefOptions, o as resolveRef, r as countDistinctRefs, t as ExternalResolver } from "../ref-D-_JBZkF.mjs";
2
2
  export { ExternalResolver, RefOptions, countDistinctRefs, dereference, findAnchor, resolveRef };
package/dist/core/ref.mjs CHANGED
@@ -1,5 +1,7 @@
1
1
  import { isObject } from "./guards.mjs";
2
+ import "./limits.mjs";
2
3
  import { emitDiagnostic } from "./diagnostics.mjs";
4
+ import { isPrototypePollutingKey } from "./uri.mjs";
3
5
  //#region src/core/ref.ts
4
6
  /**
5
7
  * $ref resolution for JSON Schema.
@@ -18,15 +20,27 @@ function getString(obj, key) {
18
20
  */
19
21
  function countDistinctRefs(root) {
20
22
  const refs = /* @__PURE__ */ new Set();
21
- collectRefs(root, refs);
23
+ collectRefs(root, refs, /* @__PURE__ */ new WeakSet());
22
24
  return Math.max(refs.size, 1);
23
25
  }
24
- function collectRefs(node, refs) {
26
+ /**
27
+ * The OpenAPI bundler (`bundleOpenApiDoc`) inlines external refs via
28
+ * `structuredClone`, which preserves shared object references and cycles.
29
+ * Without the `visited` set this walk would recurse forever on cyclic or
30
+ * diamond-shaped input. The set is a no-op for tree-shaped documents.
31
+ */
32
+ function collectRefs(node, refs, visited) {
25
33
  if (!isObject(node)) return;
34
+ if (visited.has(node)) return;
35
+ visited.add(node);
26
36
  const ref = node.$ref;
27
37
  if (typeof ref === "string") refs.add(ref);
28
- for (const value of Object.values(node)) if (isObject(value)) collectRefs(value, refs);
29
- else if (Array.isArray(value)) for (const item of value) collectRefs(item, refs);
38
+ for (const value of Object.values(node)) if (isObject(value)) collectRefs(value, refs, visited);
39
+ else if (Array.isArray(value)) {
40
+ if (visited.has(value)) continue;
41
+ visited.add(value);
42
+ for (const item of value) collectRefs(item, refs, visited);
43
+ }
30
44
  }
31
45
  /**
32
46
  * Resolve a `$ref` in a schema against a root document.
@@ -134,6 +148,7 @@ function dereference(ref, root) {
134
148
  for (const part of parts) {
135
149
  if (!isObject(current)) return void 0;
136
150
  const decoded = part.replace(/~1/g, "/").replace(/~0/g, "~");
151
+ if (isPrototypePollutingKey(decoded)) return void 0;
137
152
  current = current[decoded];
138
153
  }
139
154
  return isObject(current) ? current : void 0;
@@ -146,18 +161,29 @@ function dereference(ref, root) {
146
161
  /**
147
162
  * Recursively scan a schema document for a `$anchor` matching the given name.
148
163
  * Returns the schema object containing the anchor, or undefined.
164
+ *
165
+ * The optional `visited` set guards against shared object references and
166
+ * cycles introduced by the OpenAPI bundler's `structuredClone`-based
167
+ * inlining of external refs. Without it a recursive document would stack
168
+ * overflow before reaching the matching anchor.
149
169
  */
150
- function findAnchor(node, anchorName) {
170
+ function findAnchor(node, anchorName, visited = /* @__PURE__ */ new WeakSet()) {
151
171
  if (!isObject(node)) return void 0;
172
+ if (visited.has(node)) return void 0;
173
+ visited.add(node);
152
174
  if (node.$anchor === anchorName) return node;
153
175
  for (const value of Object.values(node)) {
154
176
  if (isObject(value)) {
155
- const found = findAnchor(value, anchorName);
177
+ const found = findAnchor(value, anchorName, visited);
156
178
  if (found !== void 0) return found;
157
179
  }
158
- if (Array.isArray(value)) for (const item of value) {
159
- const found = findAnchor(item, anchorName);
160
- if (found !== void 0) return found;
180
+ if (Array.isArray(value)) {
181
+ if (visited.has(value)) continue;
182
+ visited.add(value);
183
+ for (const item of value) {
184
+ const found = findAnchor(item, anchorName, visited);
185
+ if (found !== void 0) return found;
186
+ }
161
187
  }
162
188
  }
163
189
  }
@@ -1,2 +1,2 @@
1
- import { a as HtmlRenderProps, c as RenderFunction, d as getHtmlRenderFn, f as getRenderFunction, h as typeToKey, i as HtmlRenderFunction, l as RenderProps, m as mergeResolvers, n as BaseFieldProps, o as HtmlResolver, p as mergeHtmlResolvers, r as ComponentResolver, s as RESOLVER_KEYS, t as AllConstraints, u as buildRenderProps } from "../renderer-SOIbJBtk.mjs";
1
+ import { a as HtmlRenderProps, c as RenderFunction, d as getHtmlRenderFn, f as getRenderFunction, h as typeToKey, i as HtmlRenderFunction, l as RenderProps, m as mergeResolvers, n as BaseFieldProps, o as HtmlResolver, p as mergeHtmlResolvers, r as ComponentResolver, s as RESOLVER_KEYS, t as AllConstraints, u as buildRenderProps } from "../renderer-BaRlQIuN.mjs";
2
2
  export { AllConstraints, BaseFieldProps, ComponentResolver, HtmlRenderFunction, HtmlRenderProps, HtmlResolver, RESOLVER_KEYS, RenderFunction, RenderProps, buildRenderProps, getHtmlRenderFn, getRenderFunction, mergeHtmlResolvers, mergeResolvers, typeToKey };
@@ -43,7 +43,6 @@ const RESOLVER_KEYS = [
43
43
  "discriminatedUnion",
44
44
  "conditional",
45
45
  "negation",
46
- "recursive",
47
46
  "literal",
48
47
  "file",
49
48
  "never",
@@ -70,7 +69,6 @@ function typeToKey(type) {
70
69
  case "discriminatedUnion":
71
70
  case "conditional":
72
71
  case "negation":
73
- case "recursive":
74
72
  case "literal":
75
73
  case "file":
76
74
  case "never":
@@ -1,4 +1,4 @@
1
- import { i as DiagnosticsOptions } from "../diagnostics-CbBPsxSt.mjs";
1
+ import { i as DiagnosticsOptions } from "../diagnostics-D0QCYGv0.mjs";
2
2
  import { NodeTransform } from "./normalise.mjs";
3
3
 
4
4
  //#region src/core/swagger2.d.ts
@@ -1,2 +1,2 @@
1
- import { o as normaliseSwagger2Document } from "../normalise-CMMEl4cd.mjs";
1
+ import { s as normaliseSwagger2Document } from "../normalise-DVEJQmF7.mjs";
2
2
  export { normaliseSwagger2Document };
@@ -1,2 +1,2 @@
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
+ import { _ as __SchemaInferenceFellBack, a as InferParameterOverrides, c as OpenAPIRequestBodyType, d as RejectUnrepresentableZod, f as ResolveOpenAPIRef, g as UnsafeFields, h as UnrepresentableZodType, i as FromJSONSchemaMode, l as OpenAPIResponseType, m as UnrepresentableZodSchemaError, n as DEFAULT_OPENAPI_CONTENT_TYPE, o as InferRequestBodyFields, p as TypeAtPath, r as FromJSONSchema, s as InferResponseFields, t as DEFAULT_MAX_DEPTH, u as PathOfType } from "../typeInference-DkcUHfaM.mjs";
2
+ export { DEFAULT_MAX_DEPTH, DEFAULT_OPENAPI_CONTENT_TYPE, FromJSONSchema, FromJSONSchemaMode, 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-C9zw9wbX.mjs";
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 };
1
+ import { A as UnknownField, B as isNeverField, C as RecordField, D as StringField, E as StringConstraints, F as isDiscriminatedUnionField, G as isStringField, H as isNumberField, I as isEnumField, J as isUnknownField, K as isTupleField, L as isFileField, M as isArrayField, N as isBooleanField, O as TupleField, P as isConditionalField, R as isLiteralField, S as ObjectField, T as SchemaType, U as isObjectField, V as isNullField, W as isRecordField, Y as resolveEditability, _ as NeverField, a as DiscriminatedUnionField, b as NumberField, c as FieldBase, d as FieldOverrides, f as FileConstraints, g as NegationField, h as LiteralField, i as ConditionalField, j as WalkedField, k as UnionField, l as FieldConstraints, m as JsonObject, n as ArrayField, o as Editability, p as FileField, q as isUnionField, r as BooleanField, s as EnumField, t as ArrayConstraints, u as FieldOverride, v as NullField, w as SchemaMeta, x as ObjectConstraints, y as NumberConstraints, z as isNegationField } from "../types-BrRMV0en.mjs";
2
+ export { ArrayConstraints, ArrayField, BooleanField, ConditionalField, DiscriminatedUnionField, Editability, EnumField, FieldBase, FieldConstraints, FieldOverride, FieldOverrides, FileConstraints, FileField, JsonObject, LiteralField, NegationField, NeverField, NullField, NumberConstraints, NumberField, ObjectConstraints, ObjectField, RecordField, SchemaMeta, SchemaType, StringConstraints, StringField, TupleField, UnionField, UnknownField, WalkedField, isArrayField, isBooleanField, isConditionalField, isDiscriminatedUnionField, isEnumField, isFileField, isLiteralField, isNegationField, isNeverField, isNullField, isNumberField, isObjectField, isRecordField, isStringField, isTupleField, isUnionField, isUnknownField, resolveEditability };
@@ -83,9 +83,6 @@ function isNegationField(field) {
83
83
  function isFileField(field) {
84
84
  return isField(field, "file");
85
85
  }
86
- function isRecursiveField(field) {
87
- return isField(field, "recursive");
88
- }
89
86
  function isNeverField(field) {
90
87
  return isField(field, "never");
91
88
  }
@@ -93,4 +90,4 @@ function isUnknownField(field) {
93
90
  return isField(field, "unknown");
94
91
  }
95
92
  //#endregion
96
- export { isArrayField, isBooleanField, isConditionalField, isDiscriminatedUnionField, isEnumField, isFileField, isLiteralField, isNegationField, isNeverField, isNullField, isNumberField, isObjectField, isRecordField, isRecursiveField, isStringField, isTupleField, isUnionField, isUnknownField, resolveEditability };
93
+ export { isArrayField, isBooleanField, isConditionalField, isDiscriminatedUnionField, isEnumField, isFileField, isLiteralField, isNegationField, isNeverField, isNullField, isNumberField, isObjectField, isRecordField, isStringField, isTupleField, isUnionField, isUnknownField, resolveEditability };
@@ -1,2 +1,2 @@
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";
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-D2jfdX6E.mjs";
2
2
  export { InferredDraft, JsonSchemaDialectInfo, JsonSchemaDraft, OpenApiVersionInfo, 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-C9zw9wbX.mjs";
2
- import { i as DiagnosticsOptions } from "../diagnostics-CbBPsxSt.mjs";
3
- import { t as ExternalResolver } from "../ref-C8JbwfiS.mjs";
1
+ import { A as UnknownField, D as StringField, b as NumberField, c as FieldBase, j as WalkedField, o as Editability, p as FileField, r as BooleanField, v as NullField, w as SchemaMeta } from "../types-BrRMV0en.mjs";
2
+ import { i as DiagnosticsOptions } from "../diagnostics-D0QCYGv0.mjs";
3
+ import { t as ExternalResolver } from "../ref-D-_JBZkF.mjs";
4
4
 
5
5
  //#region src/core/walkBuilders.d.ts
6
6
  declare function getString(obj: Record<string, unknown>, key: string): string | undefined;
@@ -52,8 +52,16 @@ declare function buildBooleanField(schema: Record<string, unknown>, ctx: WalkCon
52
52
  declare function buildNullField(schema: Record<string, unknown>, ctx: WalkContext): NullField;
53
53
  declare function buildUnknownField(schema: Record<string, unknown>, ctx: WalkContext): UnknownField;
54
54
  declare function buildFileField(schema: Record<string, unknown>, ctx: WalkContext): FileField;
55
- /** Walk a map of sub-schemas (patternProperties, dependentSchemas). */
56
- declare function walkSubSchemaMap<T>(map: Record<string, unknown>, walkNode: (schema: Record<string, unknown>, ctx: WalkContext) => T, ctx: WalkContext): Record<string, T>;
55
+ /**
56
+ * Walk a map of sub-schemas (patternProperties, dependentSchemas, $defs).
57
+ *
58
+ * The callback receives each value as `unknown` so the caller can route
59
+ * boolean schemas (`true`/`false`, valid per Draft 06+) through the
60
+ * walker's boolean dispatch alongside object schemas. Non-schema values
61
+ * (numbers, strings, arrays, undefined) are silently skipped — they
62
+ * cannot represent a JSON Schema and have no walk-time meaning.
63
+ */
64
+ declare function walkSubSchemaMap<T>(map: Record<string, unknown>, walkSubSchema: (schema: unknown, ctx: WalkContext) => T, ctx: WalkContext): Record<string, T>;
57
65
  /** Walk a dependentRequired map (Record<string, string[]>). */
58
66
  declare function walkDependentRequiredMap(map: Record<string, unknown>): Record<string, string[]>;
59
67
  /**
@@ -124,10 +124,18 @@ function buildFileField(schema, ctx) {
124
124
  constraints: extractFileConstraints(schema)
125
125
  };
126
126
  }
127
- /** Walk a map of sub-schemas (patternProperties, dependentSchemas). */
128
- function walkSubSchemaMap(map, walkNode, ctx) {
127
+ /**
128
+ * Walk a map of sub-schemas (patternProperties, dependentSchemas, $defs).
129
+ *
130
+ * The callback receives each value as `unknown` so the caller can route
131
+ * boolean schemas (`true`/`false`, valid per Draft 06+) through the
132
+ * walker's boolean dispatch alongside object schemas. Non-schema values
133
+ * (numbers, strings, arrays, undefined) are silently skipped — they
134
+ * cannot represent a JSON Schema and have no walk-time meaning.
135
+ */
136
+ function walkSubSchemaMap(map, walkSubSchema, ctx) {
129
137
  const result = {};
130
- for (const [key, value] of Object.entries(map)) if (isObject(value)) result[key] = walkNode(value, ctx);
138
+ for (const [key, value] of Object.entries(map)) if (isObject(value) || typeof value === "boolean") result[key] = walkSubSchema(value, ctx);
131
139
  return result;
132
140
  }
133
141
  /** Walk a dependentRequired map (Record<string, string[]>). */
@@ -1,4 +1,4 @@
1
- import { M as WalkedField } from "../types-C9zw9wbX.mjs";
1
+ import { j as WalkedField } from "../types-BrRMV0en.mjs";
2
2
  import { WalkOptions } from "./walkBuilders.mjs";
3
3
 
4
4
  //#region src/core/walker.d.ts