schema-components 2.0.2 → 2.1.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.
- package/README.md +98 -1
- package/dist/SchemaComponent-B__6-5-E.d.mts +277 -0
- package/dist/SchemaComponent-BxzzsHsK.mjs +668 -0
- package/dist/adapter-ktQaheWB.d.mts +213 -0
- package/dist/constructorTypes-BdCiMS6e.d.mts +30 -0
- package/dist/core/adapter.d.mts +3 -213
- package/dist/core/constraintHint.d.mts +1 -1
- package/dist/core/constraints.d.mts +2 -2
- package/dist/core/contexts.d.mts +71 -0
- package/dist/core/contexts.mjs +1 -0
- package/dist/core/diagnostics.d.mts +1 -1
- package/dist/core/errors.d.mts +1 -1
- package/dist/core/fieldOrder.d.mts +1 -1
- package/dist/{react → core}/fieldPath.d.mts +2 -2
- package/dist/{react → core}/fieldPath.mjs +3 -3
- package/dist/core/formats.d.mts +1 -1
- package/dist/core/inferValue.d.mts +1 -1
- package/dist/core/limits.d.mts +1 -1
- package/dist/core/merge.d.mts +1 -1
- package/dist/core/normalise.d.mts +2 -2
- package/dist/core/ref.d.mts +1 -1
- package/dist/core/renderField.d.mts +147 -0
- package/dist/core/renderField.mjs +81 -0
- package/dist/core/renderer.d.mts +2 -199
- package/dist/core/swagger2.d.mts +1 -1
- package/dist/core/typeInference.d.mts +1 -982
- package/dist/core/types.d.mts +1 -1
- package/dist/core/unionMatch.d.mts +1 -1
- package/dist/core/version.d.mts +1 -1
- package/dist/core/walkBuilders.d.mts +3 -3
- package/dist/core/walker.d.mts +1 -1
- package/dist/{errors-Dki7tji4.d.mts → errors-DbaI04x2.d.mts} +1 -1
- package/dist/html/a11y.d.mts +2 -2
- package/dist/html/renderToHtml.d.mts +5 -5
- package/dist/html/renderToHtml.mjs +33 -18
- package/dist/html/renderToHtmlStream.d.mts +5 -5
- package/dist/html/renderers.d.mts +1 -1
- package/dist/html/streamRenderers.d.mts +3 -3
- package/dist/{inferValue-Ce-PviSD.d.mts → inferValue-eAnh50EM.d.mts} +3 -3
- package/dist/lit/SchemaComponent.d.mts +125 -0
- package/dist/lit/SchemaComponent.mjs +2 -0
- package/dist/lit/SchemaField.d.mts +65 -0
- package/dist/lit/SchemaField.mjs +2 -0
- package/dist/lit/SchemaView.d.mts +14 -0
- package/dist/lit/SchemaView.mjs +2 -0
- package/dist/lit/constructorTypes.d.mts +2 -0
- package/dist/lit/constructorTypes.mjs +1 -0
- package/dist/lit/contexts.d.mts +78 -0
- package/dist/lit/contexts.mjs +238 -0
- package/dist/lit/defaultResolver.d.mts +33 -0
- package/dist/lit/defaultResolver.mjs +2 -0
- package/dist/lit/registry.d.mts +66 -0
- package/dist/lit/registry.mjs +2 -0
- package/dist/lit/renderers/baseElement.d.mts +131 -0
- package/dist/lit/renderers/baseElement.mjs +109 -0
- package/dist/lit/renderers/recordHelpers.d.mts +25 -0
- package/dist/lit/renderers/recordHelpers.mjs +55 -0
- package/dist/lit/renderers/scArray.d.mts +14 -0
- package/dist/lit/renderers/scArray.mjs +86 -0
- package/dist/lit/renderers/scBoolean.d.mts +15 -0
- package/dist/lit/renderers/scBoolean.mjs +47 -0
- package/dist/lit/renderers/scConditional.d.mts +23 -0
- package/dist/lit/renderers/scConditional.mjs +65 -0
- package/dist/lit/renderers/scDiscriminated.d.mts +23 -0
- package/dist/lit/renderers/scDiscriminated.mjs +138 -0
- package/dist/lit/renderers/scEnum.d.mts +16 -0
- package/dist/lit/renderers/scEnum.mjs +66 -0
- package/dist/lit/renderers/scFile.d.mts +15 -0
- package/dist/lit/renderers/scFile.mjs +53 -0
- package/dist/lit/renderers/scLiteralNullNever.d.mts +30 -0
- package/dist/lit/renderers/scLiteralNullNever.mjs +57 -0
- package/dist/lit/renderers/scNumber.d.mts +15 -0
- package/dist/lit/renderers/scNumber.mjs +64 -0
- package/dist/lit/renderers/scObject.d.mts +14 -0
- package/dist/lit/renderers/scObject.mjs +57 -0
- package/dist/lit/renderers/scRecord.d.mts +14 -0
- package/dist/lit/renderers/scRecord.mjs +112 -0
- package/dist/lit/renderers/scString.d.mts +19 -0
- package/dist/lit/renderers/scString.mjs +165 -0
- package/dist/lit/renderers/scTuple.d.mts +14 -0
- package/dist/lit/renderers/scTuple.mjs +58 -0
- package/dist/lit/renderers/scUnion.d.mts +14 -0
- package/dist/lit/renderers/scUnion.mjs +44 -0
- package/dist/lit/renderers/scUnknown.d.mts +15 -0
- package/dist/lit/renderers/scUnknown.mjs +45 -0
- package/dist/lit/ssr.d.mts +37 -0
- package/dist/lit/ssr.mjs +9565 -0
- package/dist/lit/types.d.mts +2 -0
- package/dist/lit/types.mjs +1 -0
- package/dist/lit/widget.d.mts +71 -0
- package/dist/lit/widget.mjs +87 -0
- package/dist/openapi/ApiCallbacks.d.mts +1 -1
- package/dist/openapi/ApiLinks.d.mts +1 -1
- package/dist/openapi/ApiResponseHeaders.d.mts +1 -1
- package/dist/openapi/ApiSecurity.d.mts +1 -1
- package/dist/openapi/components.d.mts +4 -4
- package/dist/openapi/parser.d.mts +2 -2
- package/dist/openapi/resolve.d.mts +1 -1
- package/dist/preact/SchemaComponent.d.mts +3 -0
- package/dist/preact/SchemaComponent.mjs +26 -0
- package/dist/preact/SchemaErrorBoundary.d.mts +2 -0
- package/dist/preact/SchemaErrorBoundary.mjs +20 -0
- package/dist/preact/SchemaView.d.mts +2 -0
- package/dist/preact/SchemaView.mjs +22 -0
- package/dist/preact/headless.d.mts +2 -0
- package/dist/preact/headless.mjs +18 -0
- package/dist/react/SchemaComponent.d.mts +3 -270
- package/dist/react/SchemaComponent.mjs +41 -32
- package/dist/react/SchemaView.d.mts +6 -6
- package/dist/react/SchemaView.mjs +32 -29
- package/dist/react/a11y.d.mts +2 -2
- package/dist/react/fieldShell.d.mts +1 -1
- package/dist/react/headless.d.mts +1 -1
- package/dist/react/headlessRenderers.d.mts +2 -2
- package/dist/{ref-DdsbekXX.d.mts → ref-DWrQG1Er.d.mts} +1 -1
- package/dist/renderer-ab9E52Bp.d.mts +245 -0
- package/dist/solid/SchemaComponent.d.mts +136 -0
- package/dist/solid/SchemaComponent.mjs +391 -0
- package/dist/solid/SchemaErrorBoundary.d.mts +38 -0
- package/dist/solid/SchemaErrorBoundary.mjs +57 -0
- package/dist/solid/SchemaField.d.mts +40 -0
- package/dist/solid/SchemaField.mjs +113 -0
- package/dist/solid/SchemaView.d.mts +54 -0
- package/dist/solid/SchemaView.mjs +168 -0
- package/dist/solid/a11y.d.mts +70 -0
- package/dist/solid/a11y.mjs +71 -0
- package/dist/solid/contexts.d.mts +37 -0
- package/dist/solid/contexts.mjs +66 -0
- package/dist/solid/headless.d.mts +10 -0
- package/dist/solid/headless.mjs +27 -0
- package/dist/solid/renderers.d.mts +79 -0
- package/dist/solid/renderers.mjs +840 -0
- package/dist/solid/types.d.mts +90 -0
- package/dist/solid/types.mjs +1 -0
- package/dist/solid/widget.d.mts +29 -0
- package/dist/solid/widget.mjs +35 -0
- package/dist/themes/mantine.d.mts +1 -1
- package/dist/themes/mui.d.mts +1 -1
- package/dist/themes/radix.d.mts +1 -1
- package/dist/themes/shadcn.d.mts +1 -1
- package/dist/typeInference-Y8tNEQJk.d.mts +983 -0
- package/dist/types-BCy7K3nk.d.mts +125 -0
- package/package.json +71 -1
- package/src/svelte/SchemaComponent.svelte +427 -0
- package/src/svelte/SchemaErrorBoundary.svelte +66 -0
- package/src/svelte/SchemaField.svelte +216 -0
- package/src/svelte/SchemaProvider.svelte +46 -0
- package/src/svelte/SchemaView.svelte +244 -0
- package/src/svelte/a11y.ts +112 -0
- package/src/svelte/contexts.ts +79 -0
- package/src/svelte/dispatch.ts +267 -0
- package/src/svelte/headless.ts +73 -0
- package/src/svelte/headlessFns.ts +124 -0
- package/src/svelte/renderers/Array.svelte +98 -0
- package/src/svelte/renderers/Boolean.svelte +43 -0
- package/src/svelte/renderers/Conditional.svelte +67 -0
- package/src/svelte/renderers/DiscriminatedUnion.svelte +197 -0
- package/src/svelte/renderers/Enum.svelte +53 -0
- package/src/svelte/renderers/Fallback.svelte +24 -0
- package/src/svelte/renderers/File.svelte +46 -0
- package/src/svelte/renderers/Literal.svelte +29 -0
- package/src/svelte/renderers/Mount.svelte +24 -0
- package/src/svelte/renderers/Negation.svelte +35 -0
- package/src/svelte/renderers/Never.svelte +24 -0
- package/src/svelte/renderers/Null.svelte +19 -0
- package/src/svelte/renderers/Number.svelte +68 -0
- package/src/svelte/renderers/Object.svelte +74 -0
- package/src/svelte/renderers/Record.svelte +134 -0
- package/src/svelte/renderers/RecursionSentinel.svelte +27 -0
- package/src/svelte/renderers/String.svelte +152 -0
- package/src/svelte/renderers/Tuple.svelte +84 -0
- package/src/svelte/renderers/Union.svelte +49 -0
- package/src/svelte/renderers/Unknown.svelte +42 -0
- package/src/svelte/svelte-modules.d.ts +25 -0
- package/src/svelte/types.ts +238 -0
- package/src/svelte/widget.ts +62 -0
- /package/dist/{diagnostics-BTrm3O6J.d.mts → diagnostics-mftUZI7c.d.mts} +0 -0
- /package/dist/{limits-x4OiyJxh.d.mts → limits-Vv9hUbI_.d.mts} +0 -0
- /package/dist/{types-BrYbjC7_.d.mts → types-BBQaEPfE.d.mts} +0 -0
- /package/dist/{version-DL8U5RuA.d.mts → version-BEBx10ND.d.mts} +0 -0
|
@@ -1,983 +1,2 @@
|
|
|
1
|
-
import { d as
|
|
2
|
-
import { i as MaxRefDepth } from "../limits-x4OiyJxh.mjs";
|
|
3
|
-
import { z } from "zod";
|
|
4
|
-
|
|
5
|
-
//#region src/core/typeInference.d.ts
|
|
6
|
-
/**
|
|
7
|
-
* Zod 4 types that have no useful JSON Schema representation and so
|
|
8
|
-
* cannot meaningfully be rendered by schema-components. The runtime
|
|
9
|
-
* adapter (`packages/core/src/core/adapter.ts`, see the
|
|
10
|
-
* `zod-type-unrepresentable` classifier rules) catches the thrown
|
|
11
|
-
* error and surfaces it as a `SchemaNormalisationError` — but for
|
|
12
|
-
* the runtime-throwing variants the failure only happens on first
|
|
13
|
-
* render. Statically rejecting these types at the props boundary
|
|
14
|
-
* gives the same diagnostic at compile time.
|
|
15
|
-
*
|
|
16
|
-
* Two categories are listed:
|
|
17
|
-
*
|
|
18
|
-
* 1. **Runtime-throwing.** `z.toJSONSchema()` itself throws when it
|
|
19
|
-
* encounters one of these — bigint, date, map, set, symbol,
|
|
20
|
-
* function, custom, undefined, void, nan, codec. Source-of-truth
|
|
21
|
-
* is the classifier in `adapter.ts` (search for
|
|
22
|
-
* `zod-type-unrepresentable`).
|
|
23
|
-
* 2. **Statically rejected by schema-components.** `z.toJSONSchema()`
|
|
24
|
-
* accepts these without throwing, but the resulting JSON Schema
|
|
25
|
-
* is either degenerate (`ZodNever` becomes `{ not: {} }`,
|
|
26
|
-
* contributing nothing renderable) or the async/Promise dimension
|
|
27
|
-
* is dropped silently (`ZodPromise` is unwrapped to its inner
|
|
28
|
-
* type with no signal to the consumer). Both surface here so the
|
|
29
|
-
* rejection is explicit at the type level even though the runtime
|
|
30
|
-
* is permissive.
|
|
31
|
-
*
|
|
32
|
-
* Names mirror the Zod 4 classic interface exports in
|
|
33
|
-
* `node_modules/zod/v4/classic/schemas.d.cts`.
|
|
34
|
-
*/
|
|
35
|
-
type UnrepresentableZodType = z.ZodBigInt | z.ZodDate | z.ZodMap | z.ZodSet | z.ZodSymbol | z.ZodFunction | z.ZodUndefined | z.ZodVoid | z.ZodNaN | z.ZodCodec | z.ZodCustom | z.ZodNever | z.ZodPromise;
|
|
36
|
-
/**
|
|
37
|
-
* Brand returned in place of a rejected Zod input. The descriptive
|
|
38
|
-
* literal is what TypeScript displays when the rejection fires, so
|
|
39
|
-
* developers see why their schema is incompatible.
|
|
40
|
-
*/
|
|
41
|
-
interface UnrepresentableZodSchemaError {
|
|
42
|
-
readonly __schemaComponentsError: "Zod 4 type has no JSON Schema representation. See SchemaNormalisationError code 'zod-type-unrepresentable'.";
|
|
43
|
-
}
|
|
44
|
-
/**
|
|
45
|
-
* Recursively unwrap Zod 4 wrappers that hold an inner schema —
|
|
46
|
-
* `ZodOptional`, `ZodNullable`, `ZodReadonly`, `ZodLazy`, and `ZodPipe`.
|
|
47
|
-
*
|
|
48
|
-
* The runtime conversion still throws for `z.optional(z.bigint())`,
|
|
49
|
-
* `z.lazy(() => z.bigint())`, `z.nullable(z.bigint())`,
|
|
50
|
-
* `z.bigint().readonly()`, and `z.bigint().pipe(z.bigint())` because
|
|
51
|
-
* `z.toJSONSchema()` walks into the wrapped schema before reporting the
|
|
52
|
-
* unrepresentable type. The compile-time rejection must therefore peel
|
|
53
|
-
* those wrappers off before checking against the rejection list, or the
|
|
54
|
-
* brand would never surface for wrapped inputs.
|
|
55
|
-
*
|
|
56
|
-
* `ZodCodec` is short-circuited at the top because it is itself listed
|
|
57
|
-
* in {@link UnrepresentableZodType}. It extends `ZodPipe` structurally,
|
|
58
|
-
* so without the early exit the `ZodPipe` branch would unwrap a bare
|
|
59
|
-
* codec into its two sides and the rejection would no longer fire for
|
|
60
|
-
* `z.codec(...)`.
|
|
61
|
-
*
|
|
62
|
-
* `ZodPipe` (non-codec) has two slots (`in` and `out`); both are
|
|
63
|
-
* unwrapped into a union so a single unrepresentable side surfaces via
|
|
64
|
-
* {@link AnyMemberIsUnrepresentable}.
|
|
65
|
-
*/
|
|
66
|
-
type UnwrapZodWrapper<T> = T extends z.ZodCodec ? T : T extends z.ZodOptional<infer Inner> ? UnwrapZodWrapper<Inner> : T extends z.ZodNullable<infer Inner> ? UnwrapZodWrapper<Inner> : T extends z.ZodReadonly<infer Inner> ? UnwrapZodWrapper<Inner> : T extends z.ZodLazy<infer Inner> ? UnwrapZodWrapper<Inner> : T extends z.ZodPipe<infer InnerIn, infer InnerOut> ? UnwrapZodWrapper<InnerIn> | UnwrapZodWrapper<InnerOut> : T;
|
|
67
|
-
/**
|
|
68
|
-
* True when any member of the (possibly unioned) input extends one of
|
|
69
|
-
* the unrepresentable Zod types. Distribution over the union ensures a
|
|
70
|
-
* single unrepresentable member triggers true — matching runtime
|
|
71
|
-
* semantics for `ZodPipe`, whose two sides expand to a union via
|
|
72
|
-
* {@link UnwrapZodWrapper}.
|
|
73
|
-
*/
|
|
74
|
-
type AnyMemberIsUnrepresentable<T> = (T extends UnrepresentableZodType ? true : false) extends false ? false : true;
|
|
75
|
-
/**
|
|
76
|
-
* Reject Zod 4 inputs whose runtime conversion is known to throw.
|
|
77
|
-
*
|
|
78
|
-
* - When `T` (or any inner schema wrapped by `ZodOptional`,
|
|
79
|
-
* `ZodNullable`, `ZodReadonly`, `ZodLazy`, or `ZodPipe`) is one of
|
|
80
|
-
* the {@link UnrepresentableZodType} variants, the resolved type is
|
|
81
|
-
* {@link UnrepresentableZodSchemaError}, which is not assignable from
|
|
82
|
-
* any legitimate Zod / JSON Schema / OpenAPI input — so the prop
|
|
83
|
-
* fails to typecheck.
|
|
84
|
-
* - Anything else (Zod 4 schemas that DO convert, JSON Schema literals,
|
|
85
|
-
* OpenAPI documents, `unknown` for runtime inputs) passes through
|
|
86
|
-
* unchanged.
|
|
87
|
-
*/
|
|
88
|
-
type RejectUnrepresentableZod<T> = AnyMemberIsUnrepresentable<UnwrapZodWrapper<T>> extends true ? UnrepresentableZodSchemaError : T;
|
|
89
|
-
/**
|
|
90
|
-
* Convert a readonly tuple/array of values to a union type.
|
|
91
|
-
* Handles both `as const` readonly tuples and mutable arrays.
|
|
92
|
-
*/
|
|
93
|
-
type ArrayToUnion<A> = A extends readonly unknown[] ? A[number] : never;
|
|
94
|
-
/**
|
|
95
|
-
* Direction of inference for `FromJSONSchema`.
|
|
96
|
-
*
|
|
97
|
-
* JSON Schema's `readOnly` / `writeOnly` keywords carry directional
|
|
98
|
-
* semantics: a `readOnly` property must not appear in client → server
|
|
99
|
-
* payloads, and a `writeOnly` property must not appear in server →
|
|
100
|
-
* client payloads. Mapping a schema to a TypeScript type therefore
|
|
101
|
-
* requires knowing which direction the value travels.
|
|
102
|
-
*
|
|
103
|
-
* - `"both"` — return every property regardless of `readOnly` /
|
|
104
|
-
* `writeOnly`. Default, preserves the prior behaviour for callers
|
|
105
|
-
* that do not care about the distinction.
|
|
106
|
-
* - `"input"` — omit properties marked `readOnly: true`. Use for the
|
|
107
|
-
* shape consumers may supply (e.g. `onChange` arguments, POST
|
|
108
|
-
* bodies).
|
|
109
|
-
* - `"output"` — omit properties marked `writeOnly: true`. Use for
|
|
110
|
-
* the shape the server returns (e.g. rendered `value` props, GET
|
|
111
|
-
* responses).
|
|
112
|
-
*/
|
|
113
|
-
type FromJSONSchemaMode = "input" | "output" | "both";
|
|
114
|
-
/**
|
|
115
|
-
* Maps a JSON Schema structure to a TypeScript type.
|
|
116
|
-
* Works with `as const` literals -- provides full autocomplete for `fields`.
|
|
117
|
-
*
|
|
118
|
-
* Supports all JSON Schema draft versions (04-2020-12) and OpenAPI 3.x:
|
|
119
|
-
* - Primitive types: string, number, integer, boolean, null
|
|
120
|
-
* - type as array: `["string", "null"]` -\> `string | null` (nullable)
|
|
121
|
-
* - enum -\> union of literal types
|
|
122
|
-
* - const -\> literal type
|
|
123
|
-
* - object with properties/required -\> specific object type
|
|
124
|
-
* - object with properties + additionalProperties -\> object & `Record<string, V>`
|
|
125
|
-
* - object with additionalProperties only -\> `Record<string, T>`
|
|
126
|
-
* - array with items -\> `T[]`
|
|
127
|
-
* - array with prefixItems -\> tuple type
|
|
128
|
-
* - allOf -\> intersection type
|
|
129
|
-
* - anyOf -\> union type
|
|
130
|
-
* - oneOf -\> union type (plain union, or tagged union when `discriminator` is set)
|
|
131
|
-
* - $ref -\> resolved via $defs/definitions/$anchor context
|
|
132
|
-
* - $dynamicRef -\> resolved via $dynamicAnchor in definitions
|
|
133
|
-
* - $recursiveRef -\> unknown (recursive types not expressible in TS)
|
|
134
|
-
* - if/then/else -\> base schema (conditionals not expressible in TS)
|
|
135
|
-
* - not -\> unknown (negation not expressible in TS)
|
|
136
|
-
* - patternProperties -\> merged into loose index signature
|
|
137
|
-
*
|
|
138
|
-
* The `Mode` parameter controls how `readOnly` / `writeOnly` keywords
|
|
139
|
-
* influence inferred object properties — see {@link FromJSONSchemaMode}.
|
|
140
|
-
*/
|
|
141
|
-
type FromJSONSchema<S, Defs extends Record<string, unknown> = Record<string, never>, Depth extends readonly unknown[] = [], Mode extends FromJSONSchemaMode = "both"> = MergeRootDefs<S, Defs> extends infer MergedDefs extends Record<string, unknown> ? S extends {
|
|
142
|
-
nullable: true;
|
|
143
|
-
} ?
|
|
144
|
-
/**
|
|
145
|
-
* OpenAPI 3.0 `nullable: true` — surface the keyword wherever it
|
|
146
|
-
* appears (not just inside `ResolveMaybeRef`). The runtime path
|
|
147
|
-
* rewrites this to `anyOf: [T, { type: "null" }]` via
|
|
148
|
-
* `normaliseOpenApi30Node` (`openapi30.ts`). Mirroring at the
|
|
149
|
-
* `FromJSONSchema` level means nested fields inside refs preserve
|
|
150
|
-
* nullability when resolved.
|
|
151
|
-
*/
|
|
152
|
-
FromJSONSchema<Omit<S, "nullable">, MergedDefs, Depth, Mode> | null : S extends {
|
|
153
|
-
$ref: infer R extends string;
|
|
154
|
-
} ? ResolveSchemaRef<R, MergedDefs, Depth, Mode> : S extends {
|
|
155
|
-
$recursiveRef: string;
|
|
156
|
-
} ? unknown : S extends {
|
|
157
|
-
$dynamicRef: infer R extends string;
|
|
158
|
-
} ? ResolveSchemaRef<R, MergedDefs, Depth, Mode> : S extends {
|
|
159
|
-
allOf: infer A;
|
|
160
|
-
} ? AllOfToType<A, MergedDefs, Depth, Mode> : S extends {
|
|
161
|
-
anyOf: infer A;
|
|
162
|
-
} ? UnionOfMembers<A, MergedDefs, Depth, Mode> : S extends {
|
|
163
|
-
oneOf: infer A;
|
|
164
|
-
discriminator: {
|
|
165
|
-
propertyName: infer DP extends string;
|
|
166
|
-
};
|
|
167
|
-
} ? DiscriminatedOneOfToUnion<A, DP, GetDiscriminatorMapping<S>, MergedDefs, Depth, Mode> : S extends {
|
|
168
|
-
oneOf: infer A;
|
|
169
|
-
} ? UnionOfMembers<A, MergedDefs, Depth, Mode> : S extends {
|
|
170
|
-
if: unknown;
|
|
171
|
-
} ? FromJSONSchema<Omit<S, "if" | "then" | "else">, MergedDefs, Depth, Mode> : S extends {
|
|
172
|
-
not: unknown;
|
|
173
|
-
} ? unknown : S extends {
|
|
174
|
-
const: infer V;
|
|
175
|
-
} ? V : S extends {
|
|
176
|
-
enum: infer E;
|
|
177
|
-
} ? ArrayToUnion<E> : S extends {
|
|
178
|
-
type: infer T;
|
|
179
|
-
} ? TypeToTs<T, S, MergedDefs, Depth, Mode> : S extends readonly (infer E)[] ? E : unknown : unknown;
|
|
180
|
-
/**
|
|
181
|
-
* Extract the `discriminator.mapping` map if present, otherwise the
|
|
182
|
-
* empty sentinel. Used by {@link DiscriminatedOneOfToUnion} to inject
|
|
183
|
-
* the discriminator literal into members referenced via $ref.
|
|
184
|
-
*/
|
|
185
|
-
type GetDiscriminatorMapping<S> = S extends {
|
|
186
|
-
discriminator: {
|
|
187
|
-
mapping: infer M extends Record<string, string>;
|
|
188
|
-
};
|
|
189
|
-
} ? M : Record<string, never>;
|
|
190
|
-
/**
|
|
191
|
-
* Merge `$defs` / `definitions` declared at the current schema position with
|
|
192
|
-
* the caller-supplied `Defs` map BEFORE the ref/allOf/anyOf/oneOf dispatch.
|
|
193
|
-
*
|
|
194
|
-
* SOURCE-OF-TRUTH: parity with the runtime walker, which uses `rootDocument`
|
|
195
|
-
* (see `packages/core/src/core/ref.ts` line 91) to resolve any `$ref` against
|
|
196
|
-
* the full document — including sibling definitions colocated with the
|
|
197
|
-
* reference. Without this merge, a legal schema like
|
|
198
|
-
* `{ $ref: "#/definitions/Foo", definitions: { Foo: {...} } }` would lose
|
|
199
|
-
* its sibling defs because the ref branch fires before `ExtractDefs` runs
|
|
200
|
-
* inside `ObjectSchemaToTs`.
|
|
201
|
-
*
|
|
202
|
-
* Merge semantics (per-key resolution via `CollisionSafeMerge`):
|
|
203
|
-
* - Parent-only keys: parent value wins
|
|
204
|
-
* - Local-only keys: local value wins
|
|
205
|
-
* - Shared keys: parent value wins (caller / inherited context takes
|
|
206
|
-
* precedence over a deeper redeclaration)
|
|
207
|
-
*
|
|
208
|
-
* When the current schema declares no local defs (`HasLocalDefs<S>` is
|
|
209
|
-
* `false`), `ParentDefs` is returned unchanged so the inherited context
|
|
210
|
-
* is never poisoned by the empty index-signature sentinel.
|
|
211
|
-
*/
|
|
212
|
-
type MergeRootDefs<S, ParentDefs extends Record<string, unknown>> = HasLocalDefs<S> extends true ? ExtractRawDefs<S> extends infer RawDefs extends Record<string, unknown> ? CollisionSafeMerge<CollisionSafeMerge<RawDefs, ExtractAnchors<RawDefs>>, ParentDefs> : ParentDefs : ParentDefs;
|
|
213
|
-
/**
|
|
214
|
-
* Merge two record-shaped types where keys present in `B` always take
|
|
215
|
-
* precedence over the same key in `A`.
|
|
216
|
-
*
|
|
217
|
-
* The empty default `Record<string, never>` is detected explicitly via
|
|
218
|
-
* {@link IsEmptyDefs}: when either side is the sentinel, the other side
|
|
219
|
-
* is returned unchanged. This avoids two pitfalls of the naive
|
|
220
|
-
* `Omit<A, keyof B> & B` approach:
|
|
221
|
-
*
|
|
222
|
-
* 1. `keyof Record<string, never>` is the entire `string` type, so
|
|
223
|
-
* `Omit<A, string>` would strip every key from `A`.
|
|
224
|
-
* 2. Iterating a mapped type over `keyof A | keyof B` where either side
|
|
225
|
-
* contributes `string` collapses every entry to the index-signature
|
|
226
|
-
* value (`never` in the sentinel), wiping the literal keys from the
|
|
227
|
-
* other side.
|
|
228
|
-
*
|
|
229
|
-
* Only when both sides hold concrete literal keys does the per-key
|
|
230
|
-
* mapped merge run.
|
|
231
|
-
*/
|
|
232
|
-
type CollisionSafeMerge<A, B> = IsEmptyDefs<A> extends true ? B : IsEmptyDefs<B> extends true ? A : { [K in keyof A | keyof B]: K extends keyof B ? B[K] : K extends keyof A ? A[K] : never };
|
|
233
|
-
/**
|
|
234
|
-
* True for the empty-default `Record<string, never>` sentinel used as the
|
|
235
|
-
* initial `Defs` map — i.e. an open index signature `[string]: never`
|
|
236
|
-
* with no literal keys. Distinguished from a record with at least one
|
|
237
|
-
* literal key by checking that `string extends keyof T`.
|
|
238
|
-
*/
|
|
239
|
-
type IsEmptyDefs<T> = [keyof T] extends [never] ? true : string extends keyof T ? true : false;
|
|
240
|
-
/**
|
|
241
|
-
* True when the schema declares `$defs`, `definitions`, or
|
|
242
|
-
* `components.schemas` as an object, false otherwise. Used by
|
|
243
|
-
* `MergeRootDefs` and `ExtractDefs` to avoid intersecting
|
|
244
|
-
* the parent context with an empty index-signature sentinel.
|
|
245
|
-
*
|
|
246
|
-
* `components.schemas` is recognised so OpenAPI documents whose root
|
|
247
|
-
* `oneOf`/`anyOf`/`$ref` schemas point into the components tree resolve
|
|
248
|
-
* via the same `Defs` map as `$defs`/`definitions` entries.
|
|
249
|
-
*/
|
|
250
|
-
type HasLocalDefs<S> = S extends {
|
|
251
|
-
$defs: Record<string, unknown>;
|
|
252
|
-
} ? true : S extends {
|
|
253
|
-
definitions: Record<string, unknown>;
|
|
254
|
-
} ? true : S extends {
|
|
255
|
-
components: {
|
|
256
|
-
schemas: Record<string, unknown>;
|
|
257
|
-
};
|
|
258
|
-
} ? true : false;
|
|
259
|
-
/**
|
|
260
|
-
* Marker type emitted when OpenAPI $ref resolution hits the type-level
|
|
261
|
-
* recursion depth limit. Instead of silently falling back to
|
|
262
|
-
* `Record<string, FieldOverride>`, produces this branded type so
|
|
263
|
-
* consumers can detect it via conditional types.
|
|
264
|
-
*
|
|
265
|
-
* Usage:
|
|
266
|
-
* ```ts
|
|
267
|
-
* type Fields = InferRequestBodyFields<Doc, "/users", "post">;
|
|
268
|
-
* type IsFallback = Fields extends __SchemaInferenceFellBack ? true : false;
|
|
269
|
-
* ```
|
|
270
|
-
*/
|
|
271
|
-
interface __SchemaInferenceFellBack {
|
|
272
|
-
readonly __schemaInferenceFallback: unique symbol;
|
|
273
|
-
}
|
|
274
|
-
/**
|
|
275
|
-
* Escape hatch for recursive schemas where type-level inference
|
|
276
|
-
* cannot proceed. Typed as a map from field name (any string except
|
|
277
|
-
* the brand key) to `FieldOverride`, branded with a **required**
|
|
278
|
-
* discriminator so the unsafe path is an explicit opt-in rather than
|
|
279
|
-
* a silent default.
|
|
280
|
-
*
|
|
281
|
-
* Earlier revisions made the brand optional (`__unsafe?: true`).
|
|
282
|
-
* That defeated the brand's purpose: any plain
|
|
283
|
-
* `Record<string, FieldOverride>` literal silently satisfied the type
|
|
284
|
-
* and the "unsafe" intent was invisible to readers and reviewers.
|
|
285
|
-
* Marking the brand required forces callers to write
|
|
286
|
-
* `{ __unsafe: true, ... }`, making the escape-hatch use visible at
|
|
287
|
-
* the call site.
|
|
288
|
-
*
|
|
289
|
-
* The brand key is carved out of the field-name index signature so
|
|
290
|
-
* `__unsafe: true` does not collide with the `FieldOverride` value
|
|
291
|
-
* constraint — an index signature `[string]: FieldOverride` would
|
|
292
|
-
* otherwise reject the boolean literal.
|
|
293
|
-
*
|
|
294
|
-
* JSDoc trade-off note: This bypasses field-level type safety.
|
|
295
|
-
* Prefer restructuring the schema to avoid deep $ref chains
|
|
296
|
-
* when possible.
|
|
297
|
-
*/
|
|
298
|
-
interface UnsafeFields {
|
|
299
|
-
/**
|
|
300
|
-
* Required marker — set to `true` to acknowledge that callers
|
|
301
|
-
* are deliberately bypassing field-level inference. The literal
|
|
302
|
-
* value is not used at runtime.
|
|
303
|
-
*/
|
|
304
|
-
readonly __unsafe: true;
|
|
305
|
-
/**
|
|
306
|
-
* Field overrides keyed by name. The recursive `Record` exclusion
|
|
307
|
-
* prevents the brand from being assigned through the index
|
|
308
|
-
* signature.
|
|
309
|
-
*/
|
|
310
|
-
readonly [field: string]: FieldOverride | true;
|
|
311
|
-
}
|
|
312
|
-
/**
|
|
313
|
-
* Convert a `FromJSONSchema` result to `unknown` when recursion is detected.
|
|
314
|
-
* Returns the original type when the schema is non-recursive.
|
|
315
|
-
*/
|
|
316
|
-
type DetectRecursiveFallback<T> = unknown extends T ? __SchemaInferenceFellBack : T;
|
|
317
|
-
/**
|
|
318
|
-
* Type-level recursion bound for $ref resolution.
|
|
319
|
-
*
|
|
320
|
-
* The TypeScript type system imposes its own recursion limit; without an
|
|
321
|
-
* explicit bound a cyclic schema graph would exhaust it and degrade to
|
|
322
|
-
* `any`/`unknown` silently. This number is the runtime walker's parallel
|
|
323
|
-
* — see `resolveRef` in `packages/core/src/core/ref.ts` (line 119), whose
|
|
324
|
-
* default `maxDepth` is `64`. Matching the runtime bound here means a
|
|
325
|
-
* schema that the runtime resolves successfully is never silently dropped
|
|
326
|
-
* to `__SchemaInferenceFellBack` at compile time purely because the
|
|
327
|
-
* type-level limit was lower.
|
|
328
|
-
*
|
|
329
|
-
* A fixed bound is used here rather than a derived one because the type
|
|
330
|
-
* system has no way to count distinct strings across a recursive `Defs`
|
|
331
|
-
* map without itself recursing — which is the problem the bound exists
|
|
332
|
-
* to solve. Real-world OpenAPI documents (Stripe, GitHub, AWS) routinely
|
|
333
|
-
* contain 30-100+ distinct `$ref` strings, so a low ceiling would mask
|
|
334
|
-
* legitimate references. Deeper graphs surface as
|
|
335
|
-
* `__SchemaInferenceFellBack` so consumers can detect the limit
|
|
336
|
-
* explicitly.
|
|
337
|
-
*/
|
|
338
|
-
type DEFAULT_MAX_DEPTH = MaxRefDepth;
|
|
339
|
-
/**
|
|
340
|
-
* Resolve a $ref against the local definitions context.
|
|
341
|
-
*
|
|
342
|
-
* SOURCE-OF-TRUTH: mirrors runtime `resolveRef` in
|
|
343
|
-
* `packages/core/src/core/ref.ts` (line 90). Any change to the runtime
|
|
344
|
-
* ref-resolution rules (new ref forms, different cycle handling) must be
|
|
345
|
-
* reflected here and pinned in
|
|
346
|
-
* `packages/core/tests/typeInference-walker-parity.test.ts`.
|
|
347
|
-
*
|
|
348
|
-
* Supports:
|
|
349
|
-
* - `#` (root)
|
|
350
|
-
* - `#/$defs/Name` and `#/definitions/Name` (named definitions)
|
|
351
|
-
* - `#/components/schemas/Name` (OpenAPI 3.x component schemas)
|
|
352
|
-
* - `#SomeName` ($anchor, $dynamicAnchor resolved from definitions)
|
|
353
|
-
*
|
|
354
|
-
* `#/components/schemas/` is resolved here for parity with the runtime's
|
|
355
|
-
* `dereference` (`ref.ts` line 217), which walks any `#/...` JSON Pointer
|
|
356
|
-
* uniformly. When the runtime walker encounters an inline `$ref` inside
|
|
357
|
-
* a Zod-converted or hand-written JSON Schema that points into the
|
|
358
|
-
* OpenAPI component tree, this branch produces the corresponding type.
|
|
359
|
-
*/
|
|
360
|
-
type ResolveSchemaRef<R extends string, Defs extends Record<string, unknown>, Depth extends readonly unknown[] = [], Mode extends FromJSONSchemaMode = "both"> = Depth["length"] extends DEFAULT_MAX_DEPTH ? __SchemaInferenceFellBack : R extends "#" ? unknown : R extends `#/$defs/${infer Name}` ? Name extends keyof Defs ? DetectRecursiveFallback<FromJSONSchema<Defs[Name], Defs, [unknown, ...Depth], Mode>> : unknown : R extends `#/definitions/${infer Name}` ? Name extends keyof Defs ? DetectRecursiveFallback<FromJSONSchema<Defs[Name], Defs, [unknown, ...Depth], Mode>> : unknown : R extends `#/components/schemas/${infer Name}` ? Name extends keyof Defs ? DetectRecursiveFallback<FromJSONSchema<Defs[Name], Defs, [unknown, ...Depth], Mode>> : unknown : R extends `#${infer AnchorName}` ? AnchorName extends keyof Defs ? DetectRecursiveFallback<FromJSONSchema<Defs[AnchorName], Defs, [unknown, ...Depth], Mode>> : unknown : unknown;
|
|
361
|
-
/**
|
|
362
|
-
* Merge an allOf array into an intersection type.
|
|
363
|
-
*
|
|
364
|
-
* KNOWN LIMITATION: when any member of the `allOf` array is itself a
|
|
365
|
-
* union (e.g. produced by an `anyOf` inside one member), distribution
|
|
366
|
-
* across the intersection does not always behave as a hand-written
|
|
367
|
-
* intersection would. `(A | B) & C` distributes to `(A & C) | (B & C)`,
|
|
368
|
-
* but TypeScript's mapped-type machinery — combined with the conditional
|
|
369
|
-
* dispatch above — does not always recover that distribution when the
|
|
370
|
-
* union arises from a `FromJSONSchema` expansion. The pinned regression
|
|
371
|
-
* test "allOf of unions is treated as the intersection of the union
|
|
372
|
-
* members" in `tests/type-inference-issue-fixes.test.ts` documents the
|
|
373
|
-
* current behaviour so future refactors do not silently make it worse.
|
|
374
|
-
* There is no known way to express "distribute every member-side union
|
|
375
|
-
* across the intersection" in TypeScript today without losing the
|
|
376
|
-
* non-union members' information.
|
|
377
|
-
*/
|
|
378
|
-
type AllOfToType<A, Defs extends Record<string, unknown>, Depth extends readonly unknown[] = [], Mode extends FromJSONSchemaMode = "both"> = A extends readonly unknown[] ? UnionToIntersection<FromJSONSchema<A[number], Defs, Depth, Mode>> : unknown;
|
|
379
|
-
/**
|
|
380
|
-
* Convert an anyOf/oneOf array into a union type.
|
|
381
|
-
*
|
|
382
|
-
* SOURCE-OF-TRUTH: mirrors runtime `walkUnion` (and the
|
|
383
|
-
* `walkDiscriminatedUnion` fast path) in
|
|
384
|
-
* `packages/core/src/core/walker.ts` (lines 723-752), together with
|
|
385
|
-
* `detectDiscriminated` and `normaliseAnyOf` in
|
|
386
|
-
* `packages/core/src/core/merge.ts` (lines 190-260).
|
|
387
|
-
*
|
|
388
|
-
* Deliberate divergence: the walker collapses qualifying `oneOf` members
|
|
389
|
-
* into a `discriminatedUnion` field at runtime. The type-level helper
|
|
390
|
-
* produces a plain TypeScript union because a discriminated union and a
|
|
391
|
-
* plain union over the same members are structurally indistinguishable
|
|
392
|
-
* at the type level. Parity is pinned in
|
|
393
|
-
* `packages/core/tests/typeInference-walker-parity.test.ts`.
|
|
394
|
-
*
|
|
395
|
-
* Filters out `{ type: "null" }` members and instead makes the result
|
|
396
|
-
* nullable when at least one null member is present — mirrors the
|
|
397
|
-
* walker's `normaliseAnyOf`.
|
|
398
|
-
*/
|
|
399
|
-
type UnionOfMembers<A, Defs extends Record<string, unknown>, Depth extends readonly unknown[] = [], Mode extends FromJSONSchemaMode = "both"> = A extends readonly unknown[] ? HasNullMember<A> extends true ? Exclude<FromJSONSchema<A[number], Defs, Depth, Mode>, null> | null : FromJSONSchema<A[number], Defs, Depth, Mode> : unknown;
|
|
400
|
-
/**
|
|
401
|
-
* Convert an OpenAPI 3.x `oneOf` + `discriminator` schema into a true
|
|
402
|
-
* tagged union by injecting the discriminator literal value into each
|
|
403
|
-
* member at the property named `PropertyName`.
|
|
404
|
-
*
|
|
405
|
-
* Resolution rules per OpenAPI 3.x §4.7.25 (Discriminator Object):
|
|
406
|
-
*
|
|
407
|
-
* - When the member is a `$ref` and `Mapping` contains an entry whose
|
|
408
|
-
* value equals the ref string, the entry key is used as the
|
|
409
|
-
* discriminator literal — this is the explicit mapping form. When
|
|
410
|
-
* no mapping entry matches, the ref's terminal name (the segment
|
|
411
|
-
* after the last `/`) is used per the implicit-mapping rule.
|
|
412
|
-
* - When the member is an inline schema and already declares a
|
|
413
|
-
* `const` value at `PropertyName`, that const is the discriminator
|
|
414
|
-
* value; the member's parsed type already carries the literal so
|
|
415
|
-
* no injection is required.
|
|
416
|
-
* - When the member is an inline schema without a `const` at
|
|
417
|
-
* `PropertyName`, the discriminator value cannot be inferred at
|
|
418
|
-
* the type level — fall through to the plain union.
|
|
419
|
-
*
|
|
420
|
-
* KNOWN LIMITATION: discriminator mappings whose values are external
|
|
421
|
-
* `$ref`s (e.g. `"#/components/schemas/SomeType"` defined in a
|
|
422
|
-
* different document) cannot be resolved at the type level because
|
|
423
|
-
* `FromJSONSchema` only sees the local `Defs` map. For external
|
|
424
|
-
* mappings the result falls back to the plain union of members.
|
|
425
|
-
*/
|
|
426
|
-
type DiscriminatedOneOfToUnion<A, PropertyName extends string, Mapping extends Record<string, string>, Defs extends Record<string, unknown>, Depth extends readonly unknown[], Mode extends FromJSONSchemaMode> = A extends readonly unknown[] ? HasNullMember<A> extends true ? Exclude<DistributeDiscriminator<A[number], PropertyName, Mapping, Defs, Depth, Mode>, null> | null : DistributeDiscriminator<A[number], PropertyName, Mapping, Defs, Depth, Mode> : unknown;
|
|
427
|
-
/**
|
|
428
|
-
* For each member of the discriminated `oneOf`, parse the member via
|
|
429
|
-
* `FromJSONSchema` and intersect with the discriminator literal when
|
|
430
|
-
* one can be derived from the mapping table or the implicit ref name.
|
|
431
|
-
*/
|
|
432
|
-
type DistributeDiscriminator<M, PropertyName extends string, Mapping extends Record<string, string>, Defs extends Record<string, unknown>, Depth extends readonly unknown[], Mode extends FromJSONSchemaMode> = M extends {
|
|
433
|
-
$ref: infer R extends string;
|
|
434
|
-
} ? FromJSONSchema<M, Defs, Depth, Mode> & Record<PropertyName, DiscriminatorLiteralFor<R, Mapping>> : FromJSONSchema<M, Defs, Depth, Mode>;
|
|
435
|
-
/**
|
|
436
|
-
* Resolve the literal discriminator value for a `$ref` string. First
|
|
437
|
-
* checks the explicit `Mapping` for any entry whose value equals the
|
|
438
|
-
* ref; falls back to the trailing path segment if no mapping match is
|
|
439
|
-
* found. `[K] extends [never]` short-circuits the empty-mapping case
|
|
440
|
-
* without colliding with the `K = never` distribution rules.
|
|
441
|
-
*/
|
|
442
|
-
type DiscriminatorLiteralFor<R extends string, Mapping extends Record<string, string>> = [LookupMappingKey<R, Mapping>] extends [never] ? RefTerminalName<R> : LookupMappingKey<R, Mapping>;
|
|
443
|
-
/**
|
|
444
|
-
* Find the key in `Mapping` whose value equals `R`. Returns the union
|
|
445
|
-
* of matching keys, or `never` when no key matches.
|
|
446
|
-
*/
|
|
447
|
-
type LookupMappingKey<R extends string, Mapping extends Record<string, string>> = { [K in keyof Mapping]: Mapping[K] extends R ? K : never }[keyof Mapping];
|
|
448
|
-
/** Last `/`-delimited segment of a ref string, or the ref itself. */
|
|
449
|
-
type RefTerminalName<R extends string> = R extends `${string}/${infer Rest}` ? RefTerminalName<Rest> : R;
|
|
450
|
-
/**
|
|
451
|
-
* Check whether an anyOf/oneOf array contains a `{ type: "null" }` member.
|
|
452
|
-
*
|
|
453
|
-
* SOURCE-OF-TRUTH: mirrors runtime `normaliseAnyOf` in
|
|
454
|
-
* `packages/core/src/core/merge.ts` (lines 190-209). Both implementations
|
|
455
|
-
* only recognise schema-shaped null members (`{ type: "null" }`); a bare
|
|
456
|
-
* `null` literal in the array is treated as non-nullable. Parity is
|
|
457
|
-
* pinned in `packages/core/tests/typeInference-walker-parity.test.ts`.
|
|
458
|
-
*/
|
|
459
|
-
type HasNullMember<A> = A extends readonly unknown[] ? null extends A[number] ? false : {
|
|
460
|
-
type: "null";
|
|
461
|
-
} extends A[number] ? true : false : false;
|
|
462
|
-
/**
|
|
463
|
-
* Dispatch on a `type` value -- handles single types, type arrays,
|
|
464
|
-
* and delegates to the appropriate type-specific resolver.
|
|
465
|
-
*/
|
|
466
|
-
type TypeToTs<T, S, Defs extends Record<string, unknown>, Depth extends readonly unknown[] = [], Mode extends FromJSONSchemaMode = "both"> = T extends "string" ? string : T extends "number" | "integer" ? number : T extends "boolean" ? boolean : T extends "null" ? null : T extends "array" ? ArraySchemaToTs<S, Defs, Depth, Mode> : T extends "object" ? ObjectSchemaToTs<S, Defs, Depth, Mode> : T extends readonly (infer E)[] ? TypeArrayToTs<E, S, Defs, Depth, Mode> : unknown;
|
|
467
|
-
/**
|
|
468
|
-
* Handle `type` as an array (Draft 04-07): `["string", "null"]`.
|
|
469
|
-
* Filters out "null" and makes the result nullable.
|
|
470
|
-
*
|
|
471
|
-
* Earlier revisions ran the array / object branches through
|
|
472
|
-
* `OmitArrayHelpers` to strip `prefixItems`, `items`, and
|
|
473
|
-
* `additionalProperties` before re-parsing. That was a bug: the strip
|
|
474
|
-
* stopped the type-array form from carrying any element / property
|
|
475
|
-
* information, so `{ type: ["array", "null"], items: { type: "string" } }`
|
|
476
|
-
* collapsed to `unknown[] | null` instead of `string[] | null`. The
|
|
477
|
-
* helper still parses the same schema, only without the unnecessary
|
|
478
|
-
* key removal. The regression is pinned in
|
|
479
|
-
* `tests/type-inference-issue-fixes.test.ts` via the
|
|
480
|
-
* `type: ["array", "null"] preserves items` case.
|
|
481
|
-
*/
|
|
482
|
-
type TypeArrayToTs<E, S, Defs extends Record<string, unknown>, Depth extends readonly unknown[] = [], Mode extends FromJSONSchemaMode = "both"> = E extends "null" ? null : E extends "string" ? NullableResult<string, S> : E extends "number" | "integer" ? NullableResult<number, S> : E extends "boolean" ? NullableResult<boolean, S> : E extends "array" ? NullableResult<ArraySchemaToTs<S, Defs, Depth, Mode>, S> : E extends "object" ? NullableResult<ObjectSchemaToTs<S, Defs, Depth, Mode>, S> : unknown;
|
|
483
|
-
/**
|
|
484
|
-
* Make a type nullable if the original schema `type` array includes "null".
|
|
485
|
-
* Detects nullable from the type array directly.
|
|
486
|
-
*/
|
|
487
|
-
type NullableResult<Base, S> = S extends {
|
|
488
|
-
type: readonly (infer T)[];
|
|
489
|
-
} ? "null" extends T ? Base | null : Base : Base;
|
|
490
|
-
/**
|
|
491
|
-
* Parse an array schema: prefixItems -\> tuple, items -\> `T[]`, or `unknown[]`.
|
|
492
|
-
*
|
|
493
|
-
* Draft 04 used tuple-form `items` (an array of schemas) for tuple typing;
|
|
494
|
-
* Draft 2020-12 renamed this to `prefixItems`. The runtime normaliser in
|
|
495
|
-
* `packages/core/src/core/normalise.ts` (lines 526-534) rewrites the legacy
|
|
496
|
-
* form to `prefixItems` before the walker sees it. We mirror that rewrite
|
|
497
|
-
* here so `as const` literals using the legacy form infer the same tuple
|
|
498
|
-
* type at compile time.
|
|
499
|
-
*
|
|
500
|
-
* `contains` / `minContains` / `maxContains` constrain elements at runtime
|
|
501
|
-
* but don't change the compile-time array element type.
|
|
502
|
-
*/
|
|
503
|
-
type ArraySchemaToTs<S, Defs extends Record<string, unknown>, Depth extends readonly unknown[] = [], Mode extends FromJSONSchemaMode = "both"> = S extends {
|
|
504
|
-
prefixItems: infer P;
|
|
505
|
-
} ? PrefixItemsToTuple<P, Defs, Depth, Mode> : S extends {
|
|
506
|
-
items: infer I extends readonly unknown[];
|
|
507
|
-
} ? PrefixItemsToTuple<I, Defs, Depth, Mode> : S extends {
|
|
508
|
-
items: infer I;
|
|
509
|
-
} ? FromJSONSchema<I, Defs, Depth, Mode>[] : unknown[];
|
|
510
|
-
/**
|
|
511
|
-
* Convert a prefixItems array to a TypeScript tuple type.
|
|
512
|
-
*/
|
|
513
|
-
type PrefixItemsToTuple<P, Defs extends Record<string, unknown>, Depth extends readonly unknown[] = [], Mode extends FromJSONSchemaMode = "both"> = P extends readonly [infer First, ...infer Rest] ? [FromJSONSchema<First, Defs, Depth, Mode>, ...PrefixItemsToTuple<Rest, Defs, Depth, Mode>] : [];
|
|
514
|
-
/**
|
|
515
|
-
* Parse an object schema: properties + required -\> specific object,
|
|
516
|
-
* additionalProperties -\> Record, or empty object.
|
|
517
|
-
*
|
|
518
|
-
* Handles:
|
|
519
|
-
* - `properties` + `required` -\> specific object type with required/optional keys
|
|
520
|
-
* - `additionalProperties` as schema -\> `Record<string, T>`
|
|
521
|
-
* - `properties` + `additionalProperties` -\> base object intersected with
|
|
522
|
-
* `Record<string, V>`, preserving the typed value shape of the extra props
|
|
523
|
-
* - `patternProperties` -\> merged into a loose index signature alongside specific props
|
|
524
|
-
* (TypeScript cannot express regex-keyed properties)
|
|
525
|
-
* - `propertyNames` -\> ignored at type level (TS cannot validate key shapes)
|
|
526
|
-
* - `dependentSchemas` / `dependentRequired` -\> ignored (runtime-only conditionals)
|
|
527
|
-
* - `unevaluatedProperties` -\> ignored (runtime-only)
|
|
528
|
-
*
|
|
529
|
-
* Properties marked `readOnly: true` are omitted when `Mode` is
|
|
530
|
-
* `"input"`; properties marked `writeOnly: true` are omitted when
|
|
531
|
-
* `Mode` is `"output"`. `Mode = "both"` (the default) ignores both
|
|
532
|
-
* keywords and preserves prior behaviour.
|
|
533
|
-
*/
|
|
534
|
-
type ObjectSchemaToTs<S, Defs extends Record<string, unknown>, Depth extends readonly unknown[] = [], Mode extends FromJSONSchemaMode = "both"> = S extends {
|
|
535
|
-
type: "object";
|
|
536
|
-
properties: infer P;
|
|
537
|
-
} ? ExtractDefs<S, Defs> extends infer D extends Record<string, unknown> ? MergePatternProps<MergeAdditionalProperties<{ [K in keyof P as K extends RequiredKeysOf<S> ? IsPropertyHidden<P[K], Mode> extends true ? never : K : never]: FromJSONSchema<P[K], D, Depth, Mode> } & { [K in keyof P as K extends RequiredKeysOf<S> ? never : IsPropertyHidden<P[K], Mode> extends true ? never : K]?: FromJSONSchema<P[K], D, Depth, Mode> }, S, D, Depth, Mode>, S, D, Depth, Mode> : never : S extends {
|
|
538
|
-
additionalProperties: infer V;
|
|
539
|
-
} ? Record<string, FromJSONSchema<V, Defs, Depth, Mode>> : Record<string, unknown>;
|
|
540
|
-
/**
|
|
541
|
-
* Decide whether a property should be omitted from the inferred type
|
|
542
|
-
* for the supplied `Mode`. `readOnly: true` properties are excluded
|
|
543
|
-
* from `"input"` mode (POST bodies, `onChange` arguments) and
|
|
544
|
-
* `writeOnly: true` properties are excluded from `"output"` mode
|
|
545
|
-
* (rendered values, GET responses). `"both"` is the permissive
|
|
546
|
-
* default and never hides anything.
|
|
547
|
-
*/
|
|
548
|
-
type IsPropertyHidden<P, Mode extends FromJSONSchemaMode> = Mode extends "input" ? P extends {
|
|
549
|
-
readOnly: true;
|
|
550
|
-
} ? true : false : Mode extends "output" ? P extends {
|
|
551
|
-
writeOnly: true;
|
|
552
|
-
} ? true : false : false;
|
|
553
|
-
/**
|
|
554
|
-
* Intersect the base object type with `Record<string, V>` when the
|
|
555
|
-
* schema declares `additionalProperties` as a schema in addition to
|
|
556
|
-
* `properties`. Without this branch the inferred type silently dropped
|
|
557
|
-
* the value-type information for the extra props, leaving consumers
|
|
558
|
-
* with no way to type un-named keys that the schema explicitly permits.
|
|
559
|
-
*
|
|
560
|
-
* `additionalProperties: false` keeps the base unchanged (no extra
|
|
561
|
-
* keys are allowed); `additionalProperties: true` widens the value
|
|
562
|
-
* type to `unknown`; an inline schema produces a typed index
|
|
563
|
-
* signature.
|
|
564
|
-
*/
|
|
565
|
-
type MergeAdditionalProperties<Base, S, Defs extends Record<string, unknown>, Depth extends readonly unknown[], Mode extends FromJSONSchemaMode> = S extends {
|
|
566
|
-
additionalProperties: false;
|
|
567
|
-
} ? Base : S extends {
|
|
568
|
-
additionalProperties: true;
|
|
569
|
-
} ? Base & Record<string, unknown> : S extends {
|
|
570
|
-
additionalProperties: infer V;
|
|
571
|
-
} ? Base & Record<string, FromJSONSchema<V, Defs, Depth, Mode>> : Base;
|
|
572
|
-
/**
|
|
573
|
-
* If the schema has `patternProperties`, intersect the base object type
|
|
574
|
-
* with a `Record<string, T>` index signature covering all pattern values.
|
|
575
|
-
* If no `patternProperties`, return the base type unchanged.
|
|
576
|
-
*/
|
|
577
|
-
type MergePatternProps<Base, S, Defs extends Record<string, unknown>, Depth extends readonly unknown[] = [], Mode extends FromJSONSchemaMode = "both"> = S extends {
|
|
578
|
-
patternProperties: infer PP;
|
|
579
|
-
} ? PP extends Record<string, unknown> ? Base & Record<string, UnionOfPatternValues<PP, Defs, Depth, Mode>> : Base : Base;
|
|
580
|
-
/**
|
|
581
|
-
* Extract the union of all pattern property value types.
|
|
582
|
-
*/
|
|
583
|
-
type UnionOfPatternValues<PP extends Record<string, unknown>, Defs extends Record<string, unknown>, Depth extends readonly unknown[] = [], Mode extends FromJSONSchemaMode = "both"> = { [K in keyof PP]: FromJSONSchema<PP[K], Defs, Depth, Mode> }[keyof PP];
|
|
584
|
-
/**
|
|
585
|
-
* Extract the `required` array from a schema as a union of string literals.
|
|
586
|
-
*
|
|
587
|
-
* Accepts both the `as const` readonly tuple form and a plain mutable
|
|
588
|
-
* `string[]` literal. The mutable form arises when a hand-written
|
|
589
|
-
* schema literal omits `as const` on the `required` field — without
|
|
590
|
-
* the second branch the conditional silently resolves to `never` and
|
|
591
|
-
* every property is marked optional, which is a silent footgun. See
|
|
592
|
-
* the regression test `RequiredKeysOf accepts widened string[] arrays`
|
|
593
|
-
* in `tests/type-inference-issue-fixes.test.ts`.
|
|
594
|
-
*/
|
|
595
|
-
type RequiredKeysOf<S> = S extends {
|
|
596
|
-
required: infer R;
|
|
597
|
-
} ? R extends readonly string[] ? R[number] : R extends string[] ? R[number] : never : never;
|
|
598
|
-
/**
|
|
599
|
-
* Extract $defs / definitions from a schema for $ref resolution context.
|
|
600
|
-
* Also indexes schemas with `$anchor` or `$dynamicAnchor` by their anchor
|
|
601
|
-
* name, enabling `#SomeName` ref resolution.
|
|
602
|
-
*
|
|
603
|
-
* Shares merge semantics with `MergeRootDefs`: caller-supplied
|
|
604
|
-
* (`ParentDefs`) entries win on key collision, the empty-default
|
|
605
|
-
* sentinel is detected so it does not poison the parent context, and the
|
|
606
|
-
* `HasLocalDefs` guard short-circuits when the current node declares no
|
|
607
|
-
* defs of its own.
|
|
608
|
-
*/
|
|
609
|
-
type ExtractDefs<S, ParentDefs extends Record<string, unknown>> = HasLocalDefs<S> extends true ? ExtractRawDefs<S> extends infer RawDefs extends Record<string, unknown> ? CollisionSafeMerge<CollisionSafeMerge<RawDefs, ExtractAnchors<RawDefs>>, ParentDefs> : ParentDefs : ParentDefs;
|
|
610
|
-
/**
|
|
611
|
-
* Extract raw `$defs` / `definitions` / `components.schemas` maps.
|
|
612
|
-
*
|
|
613
|
-
* Multiple keys may be present in a single schema (e.g. an OpenAPI
|
|
614
|
-
* document that also declares `$defs` for a Zod-converted subtree).
|
|
615
|
-
* `CollisionSafeMerge` resolves overlaps so the first non-empty source
|
|
616
|
-
* wins, mirroring the runtime's preference for `$defs` over the legacy
|
|
617
|
-
* `definitions` / `components.schemas` locations.
|
|
618
|
-
*/
|
|
619
|
-
type ExtractRawDefs<S> = CollisionSafeMerge<CollisionSafeMerge<RawDefsOf<S>, RawDefinitionsOf<S>>, RawComponentSchemasOf<S>>;
|
|
620
|
-
type RawDefsOf<S> = S extends {
|
|
621
|
-
$defs: infer D;
|
|
622
|
-
} ? D extends Record<string, unknown> ? D : Record<string, never> : Record<string, never>;
|
|
623
|
-
type RawDefinitionsOf<S> = S extends {
|
|
624
|
-
definitions: infer D;
|
|
625
|
-
} ? D extends Record<string, unknown> ? D : Record<string, never> : Record<string, never>;
|
|
626
|
-
type RawComponentSchemasOf<S> = S extends {
|
|
627
|
-
components: {
|
|
628
|
-
schemas: infer D;
|
|
629
|
-
};
|
|
630
|
-
} ? D extends Record<string, unknown> ? D : Record<string, never> : Record<string, never>;
|
|
631
|
-
/**
|
|
632
|
-
* Build a map of `$anchor` name -\> schema from a definitions block.
|
|
633
|
-
* Scans each definition value for `$anchor`, `$dynamicAnchor`, or the
|
|
634
|
-
* Draft 2019-09 `$recursiveAnchor` keyword and creates entries like
|
|
635
|
-
* `{ Tree: <schema-with-$anchor-Tree> }`.
|
|
636
|
-
*
|
|
637
|
-
* SOURCE-OF-TRUTH: mirrors `normaliseDraft201909NodeWithContext` in
|
|
638
|
-
* `packages/core/src/core/normalise.ts` (lines 638-650), which rewrites
|
|
639
|
-
* `$recursiveAnchor: true` to `$anchor: "__recursive__"` and a string
|
|
640
|
-
* `$recursiveAnchor: "name"` to `$anchor: "name"`. The corresponding
|
|
641
|
-
* `$recursiveRef: "#"` therefore resolves through the same `Defs` map
|
|
642
|
-
* as a modern `$ref: "#__recursive__"`.
|
|
643
|
-
*/
|
|
644
|
-
type ExtractAnchors<D extends Record<string, unknown>> = { [K in keyof D as D[K] extends {
|
|
645
|
-
$anchor: infer A extends string;
|
|
646
|
-
} ? A : D[K] extends {
|
|
647
|
-
$dynamicAnchor: infer A extends string;
|
|
648
|
-
} ? A : D[K] extends {
|
|
649
|
-
$recursiveAnchor: infer A extends string;
|
|
650
|
-
} ? A : D[K] extends {
|
|
651
|
-
$recursiveAnchor: true;
|
|
652
|
-
} ? "__recursive__" : never]: D[K] };
|
|
653
|
-
/**
|
|
654
|
-
* Convert a union to an intersection.
|
|
655
|
-
* `A | B` -\> `A & B`. Used for allOf merging.
|
|
656
|
-
*/
|
|
657
|
-
type UnionToIntersection<U> = (U extends unknown ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never;
|
|
658
|
-
/**
|
|
659
|
-
* Resolves an OpenAPI `ref` string to its JSON Schema, then parses it.
|
|
660
|
-
*
|
|
661
|
-
* SOURCE-OF-TRUTH: mirrors runtime `resolveRef` in
|
|
662
|
-
* `packages/core/src/core/ref.ts` (line 90), which is invoked by the
|
|
663
|
-
* walker entry point in `packages/core/src/core/walker.ts` (lines
|
|
664
|
-
* 144-154) for OpenAPI documents. Any change to the runtime ref-resolution
|
|
665
|
-
* rules (new ref forms, different cycle handling, JSON Pointer decoding)
|
|
666
|
-
* must be reflected here and pinned in
|
|
667
|
-
* `packages/core/tests/typeInference-walker-parity.test.ts`.
|
|
668
|
-
*
|
|
669
|
-
* Handles:
|
|
670
|
-
* - `#/components/schemas/Name` (OpenAPI 3.x)
|
|
671
|
-
* - `#/definitions/Name` (Swagger 2.0)
|
|
672
|
-
* - `#/paths/...` (path-based refs, navigating the document tree)
|
|
673
|
-
*
|
|
674
|
-
* When the resolved schema itself contains nested `$ref`s, the helper
|
|
675
|
-
* seeds {@link FromJSONSchema}'s `Defs` parameter with the document's
|
|
676
|
-
* `components.schemas` (OAS 3.x) and `definitions` (Swagger 2.0) maps
|
|
677
|
-
* so the nested refs resolve correctly. `Depth` is threaded too so the
|
|
678
|
-
* recursion budget is shared with the calling context. Earlier
|
|
679
|
-
* revisions called `FromJSONSchema<...>` with the empty defaults and
|
|
680
|
-
* silently produced `unknown` for any nested ref.
|
|
681
|
-
*/
|
|
682
|
-
type ResolveOpenAPIRef<Spec extends Record<string, unknown>, Ref extends string, Depth extends readonly unknown[] = [], Mode extends FromJSONSchemaMode = "both"> = SpecDefs<Spec> extends infer Defs extends Record<string, unknown> ? Ref extends `#/components/schemas/${infer Name}` ? Spec["components"] extends Record<string, unknown> ? Spec["components"]["schemas"] extends Record<string, unknown> ? Name extends keyof Spec["components"]["schemas"] ? FromJSONSchema<Spec["components"]["schemas"][Name], Defs, Depth, Mode> : unknown : unknown : unknown : Ref extends `#/definitions/${infer Name}` ? Spec["definitions"] extends Record<string, unknown> ? Name extends keyof Spec["definitions"] ? FromJSONSchema<Spec["definitions"][Name], Defs, Depth, Mode> : unknown : unknown : Ref extends `#/paths/${infer PathRest}` ? ResolvePathBasedRef<Spec, PathRest> : unknown : unknown;
|
|
683
|
-
/**
|
|
684
|
-
* Build the `Defs` map for an OpenAPI document by combining
|
|
685
|
-
* `components.schemas` (OAS 3.x) and `definitions` (Swagger 2.0).
|
|
686
|
-
*
|
|
687
|
-
* Both keys are accepted so a single helper handles both versions of
|
|
688
|
-
* the spec. {@link CollisionSafeMerge} ensures the OAS-3.x entries
|
|
689
|
-
* take precedence when both are present, matching the runtime
|
|
690
|
-
* preference for components/schemas over the legacy definitions field.
|
|
691
|
-
*/
|
|
692
|
-
type SpecDefs<Spec> = ExtractComponentsSchemas<Spec> extends infer C extends Record<string, unknown> ? ExtractDefinitions<Spec> extends infer D extends Record<string, unknown> ? CollisionSafeMerge<D, C> : C : Record<string, never>;
|
|
693
|
-
/** Extract `components.schemas` from a spec, or the empty sentinel. */
|
|
694
|
-
type ExtractComponentsSchemas<Spec> = Spec extends {
|
|
695
|
-
components: {
|
|
696
|
-
schemas: infer S;
|
|
697
|
-
};
|
|
698
|
-
} ? S extends Record<string, unknown> ? S : Record<string, never> : Record<string, never>;
|
|
699
|
-
/** Extract `definitions` from a spec, or the empty sentinel. */
|
|
700
|
-
type ExtractDefinitions<Spec> = Spec extends {
|
|
701
|
-
definitions: infer D;
|
|
702
|
-
} ? D extends Record<string, unknown> ? D : Record<string, never> : Record<string, never>;
|
|
703
|
-
/**
|
|
704
|
-
* Resolve a path-based $ref after the `#/paths/` prefix.
|
|
705
|
-
* Splits on `/` and navigates the document tree, decoding JSON Pointer
|
|
706
|
-
* tilde escapes (`~1` -\> `/`, `~0` -\> `~`) on every segment.
|
|
707
|
-
*
|
|
708
|
-
* SOURCE-OF-TRUTH: mirrors runtime `dereference` in
|
|
709
|
-
* `packages/core/src/core/ref.ts` (line 226), which applies the same
|
|
710
|
-
* `~1` -\> `/`, `~0` -\> `~` substitutions per RFC 6901 §4. The runtime
|
|
711
|
-
* uses ordered string replacement; the type-level mirror does the same
|
|
712
|
-
* via {@link DecodeJsonPointerSegment}.
|
|
713
|
-
*/
|
|
714
|
-
type ResolvePathBasedRef<Spec extends Record<string, unknown>, PathRest extends string> = Spec["paths"] extends Record<string, unknown> ? ResolvePathSegments<Spec["paths"], SplitPath<PathRest>> : unknown;
|
|
715
|
-
/**
|
|
716
|
-
* Replace every occurrence of `From` with `To` inside `S`.
|
|
717
|
-
*
|
|
718
|
-
* Pure type-level alternative to `String.prototype.replaceAll` used for
|
|
719
|
-
* JSON Pointer escape decoding. Terminates when no further match is
|
|
720
|
-
* found in the tail.
|
|
721
|
-
*/
|
|
722
|
-
type ReplaceAll<S extends string, From extends string, To extends string> = S extends `${infer Head}${From}${infer Tail}` ? `${Head}${To}${ReplaceAll<Tail, From, To>}` : S;
|
|
723
|
-
/**
|
|
724
|
-
* Decode a single JSON Pointer reference token per RFC 6901 §4:
|
|
725
|
-
* apply `~1` -\> `/` first, then `~0` -\> `~`. The order matters — an
|
|
726
|
-
* encoded `~` containing a literal `1` (e.g. `~01`) must remain `~1`
|
|
727
|
-
* after decoding, which only works when `~1` is processed first.
|
|
728
|
-
*/
|
|
729
|
-
type DecodeJsonPointerSegment<S extends string> = ReplaceAll<ReplaceAll<S, "~1", "/">, "~0", "~">;
|
|
730
|
-
/**
|
|
731
|
-
* Split a path string on `/` into a tuple of segments.
|
|
732
|
-
* The first segment is the path key (may be empty for `/pets` -\> `""` / `"pets"`).
|
|
733
|
-
*/
|
|
734
|
-
type SplitPath<S extends string> = S extends `${infer Head}/${infer Tail}` ? [Head, ...SplitPath<Tail>] : [S];
|
|
735
|
-
/**
|
|
736
|
-
* Recursively navigate into a document object by path segments. Each
|
|
737
|
-
* segment is JSON-Pointer-decoded before indexing so encoded forms such
|
|
738
|
-
* as `~1pets` correctly resolve to the `"/pets"` key.
|
|
739
|
-
*/
|
|
740
|
-
type ResolvePathSegments<Doc, Segs extends string[]> = Segs extends [infer Head extends string, ...infer Rest extends string[]] ? Doc extends Record<string, unknown> ? DecodeJsonPointerSegment<Head> extends infer Decoded extends string ? Rest extends [] ? Doc[Decoded] : ResolvePathSegments<Doc[Decoded], Rest> : unknown : unknown : unknown;
|
|
741
|
-
/** Navigate to a path item in an OpenAPI document. */
|
|
742
|
-
type PathItemOf<Doc, Path extends string> = Doc extends {
|
|
743
|
-
paths: Record<string, unknown>;
|
|
744
|
-
} ? Path extends keyof Doc["paths"] ? Doc["paths"][Path] : unknown : unknown;
|
|
745
|
-
/** Navigate to an operation within a path item. */
|
|
746
|
-
type OperationOf<PathItem, Method extends string> = PathItem extends Record<string, unknown> ? Method extends keyof PathItem ? PathItem[Method] : unknown : unknown;
|
|
747
|
-
/**
|
|
748
|
-
* Default content type preferred when callers do not specify one.
|
|
749
|
-
*
|
|
750
|
-
* `"application/json"` matches the most common OpenAPI convention and
|
|
751
|
-
* keeps prior `FromJSONSchema` behaviour for the common case. When the
|
|
752
|
-
* default content type is absent from an operation, the helpers below
|
|
753
|
-
* fall back to the first declared media type — matching the runtime
|
|
754
|
-
* resolver's first-match semantics.
|
|
755
|
-
*/
|
|
756
|
-
type DEFAULT_OPENAPI_CONTENT_TYPE = "application/json";
|
|
757
|
-
/**
|
|
758
|
-
* Pick the content type to use for a request/response when the caller
|
|
759
|
-
* supplies one explicitly: when `ContentType` is present in `Content`
|
|
760
|
-
* use it verbatim, otherwise fall through to the default content type
|
|
761
|
-
* if present, otherwise pick the first key.
|
|
762
|
-
*/
|
|
763
|
-
type PickContentType<Content, ContentType extends string> = Content extends Record<string, unknown> ? ContentType extends keyof Content ? ContentType : DEFAULT_OPENAPI_CONTENT_TYPE extends keyof Content ? DEFAULT_OPENAPI_CONTENT_TYPE : FirstKey<Content> : never;
|
|
764
|
-
/** First string key of an object type, or `never` for an empty map. */
|
|
765
|
-
type FirstKey<O> = keyof O extends infer K ? K extends string ? K : never : never;
|
|
766
|
-
/**
|
|
767
|
-
* Extract the schema for a specific content type from a request body
|
|
768
|
-
* Content map.
|
|
769
|
-
*
|
|
770
|
-
* The caller's `ContentType` selects the media type: it falls back to
|
|
771
|
-
* `DEFAULT_OPENAPI_CONTENT_TYPE` and then to the first declared media
|
|
772
|
-
* type when the requested one is absent.
|
|
773
|
-
*/
|
|
774
|
-
type RequestBodySchemaOf<Op, ContentType extends string = DEFAULT_OPENAPI_CONTENT_TYPE> = Op extends {
|
|
775
|
-
requestBody: {
|
|
776
|
-
content: infer C;
|
|
777
|
-
};
|
|
778
|
-
} ? PickContentType<C, ContentType> extends infer K extends string ? C extends Record<string, unknown> ? K extends keyof C ? C[K] extends {
|
|
779
|
-
schema: infer S;
|
|
780
|
-
} ? S : unknown : unknown : unknown : unknown : unknown;
|
|
781
|
-
/**
|
|
782
|
-
* Extract the schema for a specific status code and content type from
|
|
783
|
-
* a response map.
|
|
784
|
-
*
|
|
785
|
-
* Status-code resolution mirrors the OpenAPI 3.x §4.7.10 priority order:
|
|
786
|
-
*
|
|
787
|
-
* 1. The literal status (e.g. `"200"`) — exact match.
|
|
788
|
-
* 2. The class wildcard (e.g. `"2XX"`) derived from the leading digit.
|
|
789
|
-
* 3. The `"default"` key.
|
|
790
|
-
*
|
|
791
|
-
* Without this fallback, querying a concrete status against a document
|
|
792
|
-
* that declares only `"2XX"` or `"default"` would silently produce
|
|
793
|
-
* `unknown`. The runtime resolver applies the same fall-through
|
|
794
|
-
* behaviour in `resolveResponse`.
|
|
795
|
-
*/
|
|
796
|
-
type ResponseSchemaOf<Op, Status extends string, ContentType extends string = DEFAULT_OPENAPI_CONTENT_TYPE> = Op extends {
|
|
797
|
-
responses: infer Rs extends Record<string, unknown>;
|
|
798
|
-
} ? PickResponse<Rs, Status> extends infer Resp ? Resp extends {
|
|
799
|
-
content: infer C;
|
|
800
|
-
} ? PickContentType<C, ContentType> extends infer K extends string ? C extends Record<string, unknown> ? K extends keyof C ? C[K] extends {
|
|
801
|
-
schema: infer S;
|
|
802
|
-
} ? S : unknown : unknown : unknown : unknown : unknown : unknown : unknown;
|
|
803
|
-
/**
|
|
804
|
-
* Resolve a response entry from a status code following the OpenAPI
|
|
805
|
-
* priority order: concrete \> class wildcard \> `default`. When none of
|
|
806
|
-
* the three matches, the result is `never` and the caller's outer
|
|
807
|
-
* conditional falls through to `unknown`.
|
|
808
|
-
*/
|
|
809
|
-
type PickResponse<Rs, Status extends string> = Status extends keyof Rs ? Rs[Status] : StatusClassWildcard<Status> extends infer Wildcard extends string ? Wildcard extends keyof Rs ? Rs[Wildcard] : "default" extends keyof Rs ? Rs["default"] : never : "default" extends keyof Rs ? Rs["default"] : never;
|
|
810
|
-
/**
|
|
811
|
-
* Derive the class wildcard (`"2XX"`, `"4XX"`, etc.) for a numeric
|
|
812
|
-
* status code, or `never` for non-numeric inputs. Only the first digit
|
|
813
|
-
* of the status code participates so `"200"`, `"201"`, `"204"` all map
|
|
814
|
-
* to `"2XX"`.
|
|
815
|
-
*/
|
|
816
|
-
type StatusClassWildcard<Status extends string> = Status extends `${infer D extends "1" | "2" | "3" | "4" | "5"}${string}` ? `${D}XX` : never;
|
|
817
|
-
/**
|
|
818
|
-
* Resolve a schema that may be a `$ref` pointer.
|
|
819
|
-
*
|
|
820
|
-
* The `nullable: true` handling lives inside `FromJSONSchema` so it
|
|
821
|
-
* applies uniformly to direct schemas, refs, and nested fields. This
|
|
822
|
-
* helper only dispatches between ref-resolution and plain inference.
|
|
823
|
-
*
|
|
824
|
-
* Threads `Defs`/`Depth`/`Mode` into both `ResolveOpenAPIRef` and
|
|
825
|
-
* `FromJSONSchema` so a nested `$ref` inside an inline schema is
|
|
826
|
-
* resolved against the same component-schemas context the parent
|
|
827
|
-
* document supplies. Without the propagation, nested refs degraded
|
|
828
|
-
* silently to `unknown`.
|
|
829
|
-
*/
|
|
830
|
-
type ResolveMaybeRef<Doc, S, Depth extends readonly unknown[] = [], Mode extends FromJSONSchemaMode = "both"> = S extends {
|
|
831
|
-
$ref: infer R extends string;
|
|
832
|
-
} ? ResolveOpenAPIRef<Doc & Record<string, unknown>, R, Depth, Mode> : S extends Record<string, unknown> ? FromJSONSchema<S, SpecDefs<Doc>, Depth, Mode> : unknown;
|
|
833
|
-
/** Extract parameter names from an operation. */
|
|
834
|
-
type ParameterNamesOf<Doc, Path extends string, Method extends string> = OperationOf<PathItemOf<Doc, Path>, Method> extends {
|
|
835
|
-
parameters: readonly unknown[];
|
|
836
|
-
} ? OperationOf<PathItemOf<Doc, Path>, Method>["parameters"][number] extends {
|
|
837
|
-
name: infer N;
|
|
838
|
-
} ? N extends string ? N : never : never : never;
|
|
839
|
-
/**
|
|
840
|
-
* Detect whether a document is Swagger 2.0 (OpenAPI 2.0).
|
|
841
|
-
*
|
|
842
|
-
* SOURCE-OF-TRUTH: mirrors runtime `isSwagger2` in
|
|
843
|
-
* `packages/core/src/core/version.ts` (line 305), which parses the
|
|
844
|
-
* `swagger` field via `detectOpenApiVersion` (line 264) and returns true
|
|
845
|
-
* for any document whose major version is `2`. Runtime therefore accepts
|
|
846
|
-
* `"2.0"`, `"2.0.0"`, `"2.1"`, any other `2.x` form — and the numeric
|
|
847
|
-
* literals `2` and `2.0`. The type-level detector must mirror every
|
|
848
|
-
* shape the runtime accepts, otherwise a numeric-versioned document
|
|
849
|
-
* silently bypasses the fallback and produces `unknown` instead of the
|
|
850
|
-
* `__SchemaInferenceFellBack` brand consumers expect.
|
|
851
|
-
*
|
|
852
|
-
* Type-level Swagger 2.0 documents cannot be fully normalised at compile
|
|
853
|
-
* time — the rewrite reorders the document tree (definitions →
|
|
854
|
-
* components/schemas, body parameters → requestBody, etc.) in ways
|
|
855
|
-
* TypeScript's mapped-type machinery cannot express. Detecting the
|
|
856
|
-
* version is tractable, so we surface `__SchemaInferenceFellBack`
|
|
857
|
-
* deliberately rather than silently producing `unknown`.
|
|
858
|
-
*
|
|
859
|
-
* Accepted shapes:
|
|
860
|
-
* - `{ swagger: "2.<anything>" }` — the on-the-wire string form
|
|
861
|
-
* - `{ swagger: 2 }` / `{ swagger: 2.0 }` — numeric on-the-wire form
|
|
862
|
-
* (some YAML serialisers emit a number rather than a string)
|
|
863
|
-
* - `{ swagger: { major: 2, ... } }` — the parsed `OpenApiVersionInfo`
|
|
864
|
-
* object form, mirroring the runtime's tolerance for pre-parsed
|
|
865
|
-
* version metadata
|
|
866
|
-
*/
|
|
867
|
-
type IsSwagger2Doc<Doc> = Doc extends {
|
|
868
|
-
swagger: `2.${string}`;
|
|
869
|
-
} ? true : Doc extends {
|
|
870
|
-
swagger: 2;
|
|
871
|
-
} ? true : Doc extends {
|
|
872
|
-
swagger: 2.0;
|
|
873
|
-
} ? true : Doc extends {
|
|
874
|
-
swagger: {
|
|
875
|
-
major: 2;
|
|
876
|
-
};
|
|
877
|
-
} ? true : false;
|
|
878
|
-
/**
|
|
879
|
-
* Infer the TypeScript type of an OpenAPI operation's request body.
|
|
880
|
-
*
|
|
881
|
-
* `ContentType` selects which media type's schema to infer; defaults
|
|
882
|
-
* to {@link DEFAULT_OPENAPI_CONTENT_TYPE} and falls back to the first
|
|
883
|
-
* declared content type when the default is absent (see
|
|
884
|
-
* {@link PickContentType}).
|
|
885
|
-
*
|
|
886
|
-
* Swagger 2.0 documents are not normalised at the type level. When the
|
|
887
|
-
* input is Swagger 2.0, this returns `__SchemaInferenceFellBack` so
|
|
888
|
-
* callers can detect the fallback explicitly via a conditional type.
|
|
889
|
-
*/
|
|
890
|
-
type OpenAPIRequestBodyType<Doc, Path extends string, Method extends string, ContentType extends string = DEFAULT_OPENAPI_CONTENT_TYPE> = IsSwagger2Doc<Doc> extends true ? __SchemaInferenceFellBack : ResolveMaybeRef<Doc, RequestBodySchemaOf<OperationOf<PathItemOf<Doc, Path>, Method>, ContentType>>;
|
|
891
|
-
/**
|
|
892
|
-
* Infer the TypeScript type of an OpenAPI operation's response.
|
|
893
|
-
*
|
|
894
|
-
* `ContentType` selects which media type's schema to infer; defaults
|
|
895
|
-
* to {@link DEFAULT_OPENAPI_CONTENT_TYPE} and falls back to the first
|
|
896
|
-
* declared content type when the default is absent.
|
|
897
|
-
*
|
|
898
|
-
* Status-code resolution follows the OpenAPI priority order: concrete
|
|
899
|
-
* code \> class wildcard (e.g. `"2XX"`) \> `"default"`. See
|
|
900
|
-
* {@link ResponseSchemaOf}.
|
|
901
|
-
*
|
|
902
|
-
* Swagger 2.0 documents are not normalised at the type level. When the
|
|
903
|
-
* input is Swagger 2.0, this returns `__SchemaInferenceFellBack` so
|
|
904
|
-
* callers can detect the fallback explicitly via a conditional type.
|
|
905
|
-
*/
|
|
906
|
-
type OpenAPIResponseType<Doc, Path extends string, Method extends string, Status extends string, ContentType extends string = DEFAULT_OPENAPI_CONTENT_TYPE> = IsSwagger2Doc<Doc> extends true ? __SchemaInferenceFellBack : ResolveMaybeRef<Doc, ResponseSchemaOf<OperationOf<PathItemOf<Doc, Path>, Method>, Status, ContentType>>;
|
|
907
|
-
/**
|
|
908
|
-
* Convert a resolved request/response type into the corresponding
|
|
909
|
-
* `fields` prop type used by ApiRequestBody / ApiResponse:
|
|
910
|
-
*
|
|
911
|
-
* - `__SchemaInferenceFellBack` (Swagger 2.0, depth-exceeded refs) is
|
|
912
|
-
* preserved verbatim so callers can detect the brand.
|
|
913
|
-
* - `unknown` (no schema found at the supplied path/status, or the
|
|
914
|
-
* resolved operation itself widened to `unknown`) falls back to the
|
|
915
|
-
* loose `Record<string, FieldOverride>` shape so runtime documents
|
|
916
|
-
* still typecheck.
|
|
917
|
-
* - Any other concrete type is mapped through `FieldOverrides`.
|
|
918
|
-
*
|
|
919
|
-
* The brand check intentionally precedes the `unknown` check. The brand
|
|
920
|
-
* is a structural object type and is therefore NOT assignable to
|
|
921
|
-
* `unknown extends T` — checking that first would always short-circuit
|
|
922
|
-
* to the loose `Record` fallback and the brand would never surface.
|
|
923
|
-
*
|
|
924
|
-
* TRADE-OFF: when the operation resolves to `unknown` (e.g. the path or
|
|
925
|
-
* method does not exist on a typed `Doc`), `FieldsFromInferred` widens
|
|
926
|
-
* silently to `Record<string, FieldOverride>` so any key is accepted.
|
|
927
|
-
* The alternative — surfacing a distinct compile-time error — would
|
|
928
|
-
* trade autocomplete on typed paths for noisy diagnostics on runtime
|
|
929
|
-
* documents, and the existing `@ts-expect-error` regressions in
|
|
930
|
-
* `type-inference.test.ts` rely on the current widening behaviour.
|
|
931
|
-
* The trade-off is pinned by the
|
|
932
|
-
* "FieldsFromInferred widens to `Record<string, FieldOverride>` when the
|
|
933
|
-
* operation is unknown" regression test.
|
|
934
|
-
*/
|
|
935
|
-
type FieldsFromInferred<T> = [T] extends [__SchemaInferenceFellBack] ? __SchemaInferenceFellBack : unknown extends T ? Record<string, FieldOverride> : FieldOverrides<T>;
|
|
936
|
-
/**
|
|
937
|
-
* Infer the fields prop type for ApiRequestBody.
|
|
938
|
-
*
|
|
939
|
-
* Surfaces `__SchemaInferenceFellBack` for Swagger 2.0 documents and
|
|
940
|
-
* for schemas whose $ref chains exceed type-level depth limits. Falls
|
|
941
|
-
* back to `Record<string, FieldOverride>` for runtime documents whose
|
|
942
|
-
* shape cannot be inferred at compile time.
|
|
943
|
-
*
|
|
944
|
-
* `ContentType` mirrors the parameter on
|
|
945
|
-
* {@link OpenAPIRequestBodyType}.
|
|
946
|
-
*/
|
|
947
|
-
type InferRequestBodyFields<Doc, Path extends string, Method extends string, ContentType extends string = DEFAULT_OPENAPI_CONTENT_TYPE> = FieldsFromInferred<OpenAPIRequestBodyType<Doc, Path, Method, ContentType>>;
|
|
948
|
-
/**
|
|
949
|
-
* Infer the fields prop type for ApiResponse.
|
|
950
|
-
*
|
|
951
|
-
* Surfaces `__SchemaInferenceFellBack` for Swagger 2.0 documents and
|
|
952
|
-
* for schemas whose $ref chains exceed type-level depth limits. Falls
|
|
953
|
-
* back to `Record<string, FieldOverride>` for runtime documents whose
|
|
954
|
-
* shape cannot be inferred at compile time.
|
|
955
|
-
*
|
|
956
|
-
* `ContentType` mirrors the parameter on {@link OpenAPIResponseType}.
|
|
957
|
-
*/
|
|
958
|
-
type InferResponseFields<Doc, Path extends string, Method extends string, Status extends string, ContentType extends string = DEFAULT_OPENAPI_CONTENT_TYPE> = FieldsFromInferred<OpenAPIResponseType<Doc, Path, Method, Status, ContentType>>;
|
|
959
|
-
/**
|
|
960
|
-
* Infer the overrides prop type for ApiParameters.
|
|
961
|
-
* Falls back to `Record<string, FieldOverride>` for runtime documents.
|
|
962
|
-
*/
|
|
963
|
-
type InferParameterOverrides<Doc, Path extends string, Method extends string> = string extends ParameterNamesOf<Doc, Path, Method> ? Record<string, FieldOverride> : Partial<Record<ParameterNamesOf<Doc, Path, Method>, FieldOverride>>;
|
|
964
|
-
/**
|
|
965
|
-
* Check if T is a "narrow" type (not wide like object, Record, or unknown).
|
|
966
|
-
* Used to determine if we can enumerate keys for path inference.
|
|
967
|
-
*/
|
|
968
|
-
type IsNarrowObject<T> = T extends string | number | boolean | null | undefined | unknown[] ? false : T extends object ? Record<string, never> extends T ? false : true : false;
|
|
969
|
-
/**
|
|
970
|
-
* Extract all valid dot-separated paths from an object type.
|
|
971
|
-
* Produces paths like "name" | "address.city" | "address.postcode".
|
|
972
|
-
* Stops at leaf types (string, number, boolean, null) and arrays.
|
|
973
|
-
* Returns `string` for wide types (object, Record, unknown).
|
|
974
|
-
* Handles optional/nullable fields by unwrapping T | undefined.
|
|
975
|
-
*/
|
|
976
|
-
type PathOfType<T, Prefix extends string = ""> = IsNarrowObject<T> extends true ? { [K in keyof T & string]: T[K] extends string | number | boolean | null | undefined ? `${Prefix}${K}` : T[K] extends unknown[] ? `${Prefix}${K}` : T[K] extends object | undefined ? PathOfType<Exclude<T[K], undefined>, `${Prefix}${K}.`> | `${Prefix}${K}` : `${Prefix}${K}` }[keyof T & string] : string;
|
|
977
|
-
/**
|
|
978
|
-
* Extract the type at a given dot-separated path.
|
|
979
|
-
* PathOfType<T> produces valid paths; TypeAtPath resolves the leaf type.
|
|
980
|
-
*/
|
|
981
|
-
type TypeAtPath<T, P extends string> = P extends `${infer Key}.${infer Rest}` ? Key extends keyof T ? TypeAtPath<T[Key], Rest> : unknown : P extends keyof T ? T[P] : unknown;
|
|
982
|
-
//#endregion
|
|
1
|
+
import { _ as UnsafeFields, a as InferParameterOverrides, c as IsSwagger2Doc, d as PathOfType, f as RejectUnrepresentableZod, g as UnrepresentableZodType, h as UnrepresentableZodSchemaError, i as FromJSONSchemaMode, l as OpenAPIRequestBodyType, m as TypeAtPath, n as DEFAULT_OPENAPI_CONTENT_TYPE, o as InferRequestBodyFields, p as ResolveOpenAPIRef, r as FromJSONSchema, s as InferResponseFields, t as DEFAULT_MAX_DEPTH, u as OpenAPIResponseType, v as __SchemaInferenceFellBack } from "../typeInference-Y8tNEQJk.mjs";
|
|
983
2
|
export { DEFAULT_MAX_DEPTH, DEFAULT_OPENAPI_CONTENT_TYPE, FromJSONSchema, FromJSONSchemaMode, InferParameterOverrides, InferRequestBodyFields, InferResponseFields, IsSwagger2Doc, OpenAPIRequestBodyType, OpenAPIResponseType, PathOfType, RejectUnrepresentableZod, ResolveOpenAPIRef, TypeAtPath, UnrepresentableZodSchemaError, UnrepresentableZodType, UnsafeFields, __SchemaInferenceFellBack };
|