typespec-http-zod 0.30.0 → 0.32.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 +22 -0
- package/dist/src/api.d.ts +31 -0
- package/dist/src/api.js +1475 -362
- package/dist/src/constraints.d.ts +31 -2
- package/dist/src/constraints.js +242 -46
- package/dist/src/lib.d.ts +163 -13
- package/dist/src/lib.js +207 -18
- package/dist/src/openapi-versions.d.ts +20 -0
- package/dist/src/openapi-versions.js +31 -0
- package/dist/src/published.d.ts +139 -0
- package/dist/src/published.js +798 -0
- package/dist/src/registry.d.ts +79 -45
- package/dist/src/registry.js +788 -200
- package/dist/src/types.d.ts +12 -2
- package/dist/src/types.js +267 -92
- package/dist/src/uri-template.d.ts +110 -8
- package/dist/src/uri-template.js +251 -46
- package/dist/src/zod.d.ts +764 -39
- package/dist/src/zod.js +4313 -541
- package/package.json +1 -1
package/dist/src/zod.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { type Enum, type Model, type ModelProperty, type Program, type Scalar, type Type } from "@typespec/compiler";
|
|
2
|
-
import { Visibility } from "@typespec/http";
|
|
1
|
+
import { type Enum, type Model, type ModelProperty, type Namespace, type Program, type Scalar, type Type, type Union } from "@typespec/compiler";
|
|
2
|
+
import { type MetadataInfo, Visibility } from "@typespec/http";
|
|
3
3
|
/**
|
|
4
4
|
* What a walk returns after reporting a refusal.
|
|
5
5
|
*
|
|
@@ -10,17 +10,116 @@ import { Visibility } from "@typespec/http";
|
|
|
10
10
|
* failed the compile that would have written it.
|
|
11
11
|
*/
|
|
12
12
|
export declare const UNREPRESENTABLE = "z.never()";
|
|
13
|
-
export declare function withDeferredAnnotator<T>(annotator: (property: ModelProperty) => string | undefined, run: () => T): T;
|
|
13
|
+
export declare function withDeferredAnnotator<T>(annotator: (property: ModelProperty, position: PropertyPosition) => string | undefined, typeAnnotator: (type: Type) => string | undefined, run: () => T): T;
|
|
14
14
|
export declare function withSealedObjects<T>(sealed: boolean, run: () => T): T;
|
|
15
|
-
|
|
15
|
+
/**
|
|
16
|
+
* **openapi3's own metadata view: `Read` is canonical, and a read-only property is shareable.**
|
|
17
|
+
*
|
|
18
|
+
* Built with `@typespec/http`'s defaults, the canonical visibility was `All`, so a model whose
|
|
19
|
+
* create-only property is visible at `All` was not transformed at `Create`: its create body
|
|
20
|
+
* collapsed onto the bare declaration, and every response was then validated as the CREATE view -
|
|
21
|
+
* `{name}` refused for want of the `secret` no response carries. And a read-only `id` made a model
|
|
22
|
+
* transformed at `Create`, where openapi3 shares the canonical schema, so the create body was
|
|
23
|
+
* validated without the `id` the document requires. Both are openapi3's arguments in
|
|
24
|
+
* `openapi.js`, passed as it passes them.
|
|
25
|
+
*/
|
|
26
|
+
export declare function metadataInfoOf(program: Program): MetadataInfo;
|
|
27
|
+
export declare function withVisibility<T>(next: Visibility, run: () => T): T;
|
|
16
28
|
/** Whether a property is optional AT THE CURRENT POSITION - PATCH makes update fields optional. */
|
|
17
29
|
export declare function isOptionalAt(program: Program, property: ModelProperty): boolean;
|
|
30
|
+
export declare function withRequestReading<T>(program: Program, requested: Visibility, run: () => T): T;
|
|
31
|
+
/** A declaration's recorded reading, restored to render it again - see `Declaration.reading`. */
|
|
32
|
+
export declare function withRequestReadingAs<T>(reading: Visibility | undefined, run: () => T): T;
|
|
33
|
+
/** The request visibility a 3.0 request reading is under, or `undefined` outside one. */
|
|
34
|
+
export declare function requestReadingAt(): Visibility | undefined;
|
|
35
|
+
/**
|
|
36
|
+
* **Whether a type, rendered at `at`, is read apart in a 3.0 request**: it reaches a required
|
|
37
|
+
* read-only property openapi3 publishes in it - through its own and inherited properties, a list's
|
|
38
|
+
* elements, a record's values, a union's members and a discriminated base's subtypes, each at the
|
|
39
|
+
* visibility openapi3 renders it at. Such a type's request reading lets that property be left out
|
|
40
|
+
* and its response reading does not, so the two are two declarations; a type reaching none is one.
|
|
41
|
+
*/
|
|
42
|
+
export declare function requestReadsApart(program: Program, type: Type, at: Visibility): boolean;
|
|
43
|
+
/** The first required read-only property `type` reaches at `at` - see {@link requestReadsApart}. */
|
|
44
|
+
export declare function requiredReadOnlyIn(program: Program, type: Type, at: Visibility, seen: Set<Type>): ModelProperty | undefined;
|
|
18
45
|
/** Whether a property belongs in the BODY at the current position, rather than being metadata. */
|
|
19
46
|
export declare function isPayloadProperty(program: Program, property: ModelProperty): boolean;
|
|
20
|
-
|
|
21
|
-
export declare function
|
|
47
|
+
export declare function withMetadataAnnotationsIgnored<T>(ignored: boolean, run: () => T): T;
|
|
48
|
+
export declare function withMultipartBinary<T>(binary: boolean, run: () => T): T;
|
|
49
|
+
export declare function multipartBinaryContext(): boolean;
|
|
50
|
+
/** `bytes` kept raw, as openapi3's `isBytesKeptRaw` reads it: the standard scalar, not encoded. */
|
|
51
|
+
export declare function isRawBytes(program: Program, type: Type): boolean;
|
|
52
|
+
/** A union one of whose own members is raw `bytes` - see `withMultipartBinary`. */
|
|
53
|
+
export declare function holdsRawBytes(program: Program, type: Type): boolean;
|
|
54
|
+
/** Whether metadata annotations are ignored where the walk is - the registry keys declarations on it. */
|
|
55
|
+
export declare function metadataAnnotationsIgnored(): boolean;
|
|
56
|
+
/** Whether this model or union is shaped differently at `visibility` than at the canonical `Read`. */
|
|
57
|
+
export declare function isTransformedBy(program: Program, type: Model | Union, at: Visibility): boolean;
|
|
58
|
+
/**
|
|
59
|
+
* **The visibility a type is rendered at where another type refers to it: openapi3's
|
|
60
|
+
* `reduceContext`.** A type that is not transformed at the requested visibility renders at `Read`, so
|
|
61
|
+
* a model with nothing create-specific inside a create body IS the canonical schema - a read-only
|
|
62
|
+
* property shared into it, as `canShareProperty` says. Rendered at the requested visibility under the
|
|
63
|
+
* canonical name, the body dropped that property while the document requires it.
|
|
64
|
+
*
|
|
65
|
+
* **Only where openapi3 reduces.** Its asset emitter asks for a reference context by what it emits:
|
|
66
|
+
* a named model, a model expression, a template instance, a named union, a scalar or an enum. A list -
|
|
67
|
+
* `T[]` or `model Tags is Tag[]` - and a union expression or instance have none, so they keep the
|
|
68
|
+
* visibility they were reached at, and so does everything inside them. Reduced here as well, a
|
|
69
|
+
* `model Tags is Tag[]` sent in a create body and returned was one declaration where the document
|
|
70
|
+
* publishes `TagsCreate` and `Tags`, and a model expression inside a union expression in a create
|
|
71
|
+
* body was judged at `Read`, where openapi3 reads its properties at `Create`.
|
|
72
|
+
*/
|
|
73
|
+
export declare function renderedVisibilityOf(program: Program, type: Type, requested: Visibility): Visibility;
|
|
74
|
+
/**
|
|
75
|
+
* **The visibility an operation's body, parameter or response is rendered at: openapi3's
|
|
76
|
+
* `emitTypeWithSchemaEmitter`**, which renders a type at `Read` unless it is transformed at the
|
|
77
|
+
* requested visibility - whatever kind of type it is, a list included.
|
|
78
|
+
*/
|
|
79
|
+
export declare function rootVisibilityOf(program: Program, type: Type, requested: Visibility): Visibility;
|
|
80
|
+
/**
|
|
81
|
+
* **Whether openapi3 can name a template instance**: its asset emitter's `declarationName`, which
|
|
82
|
+
* spells each argument's own name after the template's and gives up where one has none - a model
|
|
83
|
+
* expression, a literal, an instance of no nameable arguments. Such an instance is written out where
|
|
84
|
+
* it is used: `Maybe<{ x: string }>` is no component, and declared as `Maybe` it was one name for two
|
|
85
|
+
* schemas wherever a second argument appeared.
|
|
86
|
+
*/
|
|
87
|
+
export declare function isSpeakableInstance(type: Type): boolean;
|
|
88
|
+
/** A named list model, `model Tags is Tag[]`, which openapi3 emits as an `arrayDeclaration`. */
|
|
89
|
+
export declare function isNamedList(type: Model): boolean;
|
|
90
|
+
/**
|
|
91
|
+
* **An array literal's elements are rendered at `Item` visibility**, as openapi3's
|
|
92
|
+
* `arrayLiteralReferenceContext` renders them: PATCH leaves a list's items required, and metadata on
|
|
93
|
+
* an element is data, since no element carries a header. Read at the list's own visibility, a PATCH
|
|
94
|
+
* made every item's properties optional, a `@header` on an element dropped out of the list the
|
|
95
|
+
* document requires it in, and one such model in a request list and a response list was two shapes
|
|
96
|
+
* under one name - `TaggedCreateItem` and `TaggedItem` in the document. A named list model is a
|
|
97
|
+
* declaration, rendered at its own visibility.
|
|
98
|
+
*/
|
|
99
|
+
export declare function atItem<T>(program: Program, list: Model, run: () => T): T;
|
|
100
|
+
/**
|
|
101
|
+
* **An array literal, `T[]`, rather than a named list model** - as openapi3's asset emitter tells
|
|
102
|
+
* them apart, by name: a list called `Array` with no properties of its own. A visibility template's
|
|
103
|
+
* copy of `Pet[]` is no standard type, and read as a named list it was declared as `Array`, one
|
|
104
|
+
* name for every such copy.
|
|
105
|
+
*/
|
|
106
|
+
export declare function isArrayLiteral(program: Program, list: Model): boolean;
|
|
107
|
+
export declare function withParameterPosition<T>(inside: boolean, run: () => T): T;
|
|
108
|
+
export declare function inParameterPosition(): boolean;
|
|
109
|
+
/** Why a tuple parameter is refused, and what to write instead - both walks say the same. */
|
|
110
|
+
export declare const TUPLE_PARAMETER: {
|
|
111
|
+
readonly why: "a parameter typed as a tuple, which openapi3 publishes as `prefixItems` over the values it carries and this emitter does not read from the wire";
|
|
112
|
+
readonly remedy: "Declare the parameter as a list of one element type, or carry the value in the body.";
|
|
113
|
+
};
|
|
22
114
|
/** The visibility the current walk is emitting for - the registry keys declarations on it. */
|
|
23
115
|
export declare function currentVisibility(): Visibility;
|
|
116
|
+
/**
|
|
117
|
+
* openapi3's own rule for which derived models count - `@typespec/openapi3`'s `util.ts`.
|
|
118
|
+
*
|
|
119
|
+
* A template declaration is not a real subtype, and neither is an uninstantiated template; both
|
|
120
|
+
* would otherwise stop a base from being sealed for no reason a reader could see.
|
|
121
|
+
*/
|
|
122
|
+
export declare function includeDerivedModel(model: Model): boolean;
|
|
24
123
|
/**
|
|
25
124
|
* Record every enum a service DECLARES, not merely every one its success bodies happen to reach.
|
|
26
125
|
*
|
|
@@ -75,6 +174,21 @@ export declare function withDeclarationSite<T>(site: Type | undefined, run: () =
|
|
|
75
174
|
* walked, which does.
|
|
76
175
|
*/
|
|
77
176
|
export declare function refusalTarget(type: Type): Type;
|
|
177
|
+
/**
|
|
178
|
+
* **The external document a type is published as a reference to**, by `@useRef`: openapi3 writes
|
|
179
|
+
* `$ref: <url>` wherever it is used and no component, so the document states no schema this emitter
|
|
180
|
+
* can read. Read off the decorator applied, as `@typespec/openapi3`, which declares it, is no
|
|
181
|
+
* dependency of this package.
|
|
182
|
+
*/
|
|
183
|
+
export declare function externalReferenceOf(type: Type): string | undefined;
|
|
184
|
+
/** Refuse a type the document publishes only as a reference to another document. */
|
|
185
|
+
export declare function refuseExternalReference(program: Program, type: Type, url: string): string;
|
|
186
|
+
/**
|
|
187
|
+
* **The Zod a standard scalar becomes, or `undefined` for any other scalar** - looked up only for
|
|
188
|
+
* the standard library's own: a spec's `scalar uint8 extends string` is a string, and found in this
|
|
189
|
+
* table by its name it was validated as a byte, refusing every string the document accepts.
|
|
190
|
+
*/
|
|
191
|
+
export declare function standardScalarSchemaOf(program: Program, scalar: Scalar): string | undefined;
|
|
78
192
|
/**
|
|
79
193
|
* **A doc becomes `.describe()`, which is the one place Zod keeps a description.**
|
|
80
194
|
*
|
|
@@ -109,17 +223,108 @@ export declare function described(expression: string, doc: string | undefined):
|
|
|
109
223
|
* Without that, `string`'s own "A sequence of textual characters." would sit on every `z.string()`.
|
|
110
224
|
*/
|
|
111
225
|
export declare function scalarDocOf(program: Program, scalar: Scalar): string | undefined;
|
|
112
|
-
/** A scalar's schema without its description, for a position that publishes its own. */
|
|
113
226
|
/**
|
|
114
|
-
* The
|
|
115
|
-
*
|
|
227
|
+
* **The `@encode` a scalar is published under: its own, or the nearest one up its `extends` chain.**
|
|
228
|
+
*
|
|
229
|
+
* openapi3 builds a scalar that is not standard from its base's schema - encoding included - and
|
|
230
|
+
* then applies its own decorators over it, so `scalar TsChild extends Ts` with
|
|
231
|
+
* `@encode(unixTimestamp, int32)` on `Ts` is published as an integer, exactly as `Ts` is. This read
|
|
232
|
+
* the scalar's own `@encode` alone, so `TsChild` was validated as an ISO date-time string, refusing
|
|
233
|
+
* every value the document accepts and accepting every value it refuses. `link` is the scalar that
|
|
234
|
+
* declares the encoding.
|
|
235
|
+
*/
|
|
236
|
+
export declare function scalarEncodingOf(program: Program, scalar: Scalar): {
|
|
237
|
+
readonly link: Scalar;
|
|
238
|
+
readonly encoded: Scalar;
|
|
239
|
+
} | undefined;
|
|
240
|
+
export declare function withSyntheticDeclarations<T>(hook: (name: string, build: () => string, site: Type) => string, run: () => T): T;
|
|
241
|
+
/**
|
|
242
|
+
* **Why a variant of an envelope-less `@discriminated` union cannot be dispatched to by its wire
|
|
243
|
+
* discriminator, or `undefined` where it can.** The variant must carry the discriminator under its
|
|
244
|
+
* WIRE name, as one fixed value. Matched on the TypeSpec name as well, a variant whose `kind` was
|
|
245
|
+
* renamed `t` passed, and the module threw "Invalid discriminated union option" on import; one
|
|
246
|
+
* inheriting an open `type: string` threw on every parse.
|
|
247
|
+
*/
|
|
248
|
+
export declare function undeclaredVariantOf(program: Program, variant: Type, property: string): "missing" | "open" | "refused" | undefined;
|
|
249
|
+
/**
|
|
250
|
+
* **The variants a `@discriminated` union dispatches to: each but one no value can be.**
|
|
251
|
+
*
|
|
252
|
+
* A variant without an envelope that accepts nothing - its fixed discriminator refused by a
|
|
253
|
+
* declaration above it, `kind: "cobra"` under `@maxLength(3) kind: string`, or a key it requires
|
|
254
|
+
* refused by a base openapi3 seals - is published by the document as a member no value satisfies, and
|
|
255
|
+
* a `oneOf` with such a member accepts what the others accept. Refusing the union for it refused every
|
|
256
|
+
* `asp` the document accepts, and dispatching to it put a `z.never()` where Zod reads a literal: the
|
|
257
|
+
* first parse threw "Invalid discriminated union option".
|
|
258
|
+
*/
|
|
259
|
+
export declare function dispatchedVariantsOf(program: Program, union: Union): readonly [string, Type][];
|
|
260
|
+
/**
|
|
261
|
+
* One variant of a `@discriminated` union, as it appears ON THE WIRE.
|
|
262
|
+
*
|
|
263
|
+
* **The union is not a choice between its variant types - it is a choice between wrappings of
|
|
264
|
+
* them**, and this emitter used to ignore the wrapping entirely. `z.discriminatedUnion("kind",
|
|
265
|
+
* [catSchema, dogSchema])` over models that carry no `kind` at all **matches nothing**: every
|
|
266
|
+
* request to a polymorphic endpoint failed, and the differential could not see it because it read a
|
|
267
|
+
* discriminated component as an object.
|
|
268
|
+
*
|
|
269
|
+
* `envelope: "object"` (the default) wraps the variant - `{kind: "cat", value: {...Cat}}` - and
|
|
270
|
+
* openapi3 publishes each wrapper as a component named `union.type.name + capitalize(variantName)`.
|
|
271
|
+
* Mirrored exactly, so the identifiers line up with the component names.
|
|
272
|
+
*
|
|
273
|
+
* `envelope: "none"` injects the discriminator into the variant - `{kind: "cat", ...Cat}` - and is
|
|
274
|
+
* emitted **only when the variant declares that property itself**. Otherwise the document does not
|
|
275
|
+
* publish it and the union is refused; see `undeclared-discriminator`.
|
|
276
|
+
*/
|
|
277
|
+
/**
|
|
278
|
+
* **Whether a required property is optional at `at`**: a PATCH with implicit optionality, outside a
|
|
279
|
+
* list's elements - `@typespec/http`'s `isOptional`, for a property with no declaration of its own
|
|
280
|
+
* to ask, as an envelope's are.
|
|
281
|
+
*/
|
|
282
|
+
export declare function implicitlyOptional(at: Visibility): boolean;
|
|
283
|
+
/**
|
|
284
|
+
* **The visibility an envelope is published at, within a union rendered at the current one.**
|
|
285
|
+
* openapi3 synthesises the envelope as a model of two properties - the discriminator and the variant
|
|
286
|
+
* - and emits it from the union's context, which reduces it to `Read` unless it is transformed
|
|
287
|
+
* there: where the variant is, or where a PATCH makes both its properties optional. Read as one
|
|
288
|
+
* envelope for every visibility, a PATCH body was held to the read wrapper's required `kind` and
|
|
289
|
+
* `value`, and a response to the create wrapper's variant.
|
|
290
|
+
*/
|
|
291
|
+
export declare function envelopeVisibilityOf(program: Program, union: Union, variant: Type): Visibility;
|
|
292
|
+
/** Whether a variant's discriminator may be absent where the union is rendered - see `unionToZod`. */
|
|
293
|
+
export declare function optionalDiscriminator(program: Program, union: Union, variant: Type, options: {
|
|
294
|
+
envelope: string;
|
|
295
|
+
discriminatorPropertyName: string;
|
|
296
|
+
}): boolean;
|
|
297
|
+
/**
|
|
298
|
+
* **The component openapi3 publishes an envelope as**: the union's name and the variant's, suffixed
|
|
299
|
+
* with the envelope's visibility where the union - whose usage openapi3 gives the envelope - is used
|
|
300
|
+
* at more than one. None for a union with no name, whose envelopes are written out where it is.
|
|
301
|
+
*/
|
|
302
|
+
export declare function envelopeNameOf(program: Program, union: Union, variantName: string, variant: Type): string | undefined;
|
|
303
|
+
/**
|
|
304
|
+
* **Why the OpenAPI 3.0 document refuses a `null` the spec admits**, or `undefined` where it does not -
|
|
305
|
+
* asked only where openapi3 publishes 3.0. Read off openapi3's 3.0 `unionSchema` and OAS 3.0.4's
|
|
306
|
+
* `nullable`, which "only takes effect if `type` is explicitly defined within the same Schema Object",
|
|
307
|
+
* while "Other Schema Object constraints retain their defined behavior":
|
|
116
308
|
*
|
|
117
|
-
*
|
|
118
|
-
*
|
|
119
|
-
*
|
|
309
|
+
* - Each JSON kind of literal is one `{ type, enum }` member, and the `enum` refuses `null`.
|
|
310
|
+
* - **One member**, or any member a `$ref`, is written with `nullable` beside it: a `$ref` is wrapped
|
|
311
|
+
* as `{ type?, allOf: [$ref], nullable }` and the component refuses `null` unless it has no type; an
|
|
312
|
+
* inline schema admits it where it carries a `type` - a standard scalar, a list, a record, a model
|
|
313
|
+
* expression - or none at all.
|
|
314
|
+
* - **Several inline members** are `{ anyOf: [...], nullable: true }`, with no `type`, so `nullable`
|
|
315
|
+
* does nothing and only a member admitting `null` on its own does.
|
|
316
|
+
* - A bare `null` is `{ nullable: true }` - any value - and a union of nothing but `null` is refused by
|
|
317
|
+
* openapi3 itself, as `union-null`.
|
|
318
|
+
*/
|
|
319
|
+
export declare function unpublishableNullWhy(program: Program, type: Type): string | undefined;
|
|
320
|
+
/**
|
|
321
|
+
* **Everything a service declares that its 3.0 document cannot publish, reported once each**,
|
|
322
|
+
* whichever walk renders it or none: a `null` in a union, named or written where it is used, a bare
|
|
323
|
+
* `null`, and a sealed model's record - see `unpublishable-null` and `unpublishable-record`.
|
|
324
|
+
* The walks refuse each as they reach it; this is what makes sure every one is named, the way a
|
|
325
|
+
* property's `T | null` is peeled off before any union is rendered.
|
|
120
326
|
*/
|
|
121
|
-
export declare function
|
|
122
|
-
export declare function withSyntheticDeclarations<T>(hook: (name: string, build: () => string) => string, run: () => T): T;
|
|
327
|
+
export declare function reportUnpublishable(program: Program, service: Namespace): void;
|
|
123
328
|
/**
|
|
124
329
|
* Every property the model declares, **including the ones it inherits**.
|
|
125
330
|
*
|
|
@@ -142,32 +347,355 @@ export declare function withSyntheticDeclarations<T>(hook: (name: string, build:
|
|
|
142
347
|
* refers back to the derived model - `type/model/inheritance/recursive`, the exact case - would throw
|
|
143
348
|
* during module initialisation. Flattening asks nothing of the base but its properties.
|
|
144
349
|
*/
|
|
145
|
-
export declare function inheritedAndOwnProperties(model: Model): ModelProperty[];
|
|
350
|
+
export declare function inheritedAndOwnProperties(program: Program, model: Model): ModelProperty[];
|
|
351
|
+
/**
|
|
352
|
+
* A property of a model as the document publishes the model: the most derived declaration of a wire
|
|
353
|
+
* key, and the declarations of the same key further up the chain, nearest first.
|
|
354
|
+
*
|
|
355
|
+
* **Keyed on the WIRE name, because that is what the document keys a schema's properties on.** A
|
|
356
|
+
* subtype is published as `allOf` its base, so every declaration of a key along the chain still
|
|
357
|
+
* applies to the value. Keyed on the TypeSpec name, `@encodedName("application/json", "k") kind` on a
|
|
358
|
+
* base was REPLACED by a subtype's `kind`, so the subtype lost the `k` its base requires; and a base's
|
|
359
|
+
* `kind` renamed to `type` beside a subtype's own `type` was written as two `type` keys, which does not
|
|
360
|
+
* compile.
|
|
361
|
+
*/
|
|
362
|
+
export interface FlattenedProperty {
|
|
363
|
+
readonly property: ModelProperty;
|
|
364
|
+
readonly overridden: readonly ModelProperty[];
|
|
365
|
+
}
|
|
366
|
+
export declare function flattenedPropertiesOf(program: Program, model: Model): readonly FlattenedProperty[];
|
|
367
|
+
/**
|
|
368
|
+
* The keys a model publishes at the current position, each with every declaration of it the
|
|
369
|
+
* document holds the value to - its most derived published declaration first.
|
|
370
|
+
*
|
|
371
|
+
* **A declaration counts where openapi3 publishes it, and only there.** Each model in the chain is
|
|
372
|
+
* published as `allOf` the one above, and each judges its own properties at the position: a
|
|
373
|
+
* `@header` or a create-only property above a response is in no schema the response is held to, so
|
|
374
|
+
* its `@maxLength` bounds nothing there; a `never` or an invisible redeclaration drops out of its own
|
|
375
|
+
* model's schema and leaves the key to the ones above it. Judged on the most derived declaration
|
|
376
|
+
* alone, the first was enforced where the document does not, and the second dropped a key the base
|
|
377
|
+
* still requires.
|
|
378
|
+
*
|
|
379
|
+
* **HTTP metadata is not payload.** A `@header` or `@statusCode` property describes the envelope, so
|
|
380
|
+
* it never appears in the body: RFC 9457 puts `@header("content-type") contentType:
|
|
381
|
+
* "application/problem+json"` on the problem model, the document described a body without it, and
|
|
382
|
+
* this emitter required `contentType` as a member. **A `never` property is dropped, as the document
|
|
383
|
+
* drops it**: `model N { value: never; other: string }` is published as `{other}`, and a validator
|
|
384
|
+
* keeping `value: z.never()` refused `{"other": "x"}`, the exact body the document describes.
|
|
385
|
+
*/
|
|
386
|
+
export declare function publishedPropertiesOf(program: Program, model: Model): readonly FlattenedProperty[];
|
|
387
|
+
/**
|
|
388
|
+
* The discriminators a model is published under: its own and every one up its chain, by wire name.
|
|
389
|
+
* openapi3 lists each in its base's `required`, and the base applies through `allOf`.
|
|
390
|
+
*/
|
|
391
|
+
export declare function discriminatorsOf(program: Program, model: Model): readonly string[];
|
|
392
|
+
/**
|
|
393
|
+
* **What the document asserts of a property at its position in a model, beyond its own declaration.**
|
|
394
|
+
*
|
|
395
|
+
* openapi3 publishes a subtype as `allOf` its base, so every declaration of a key along the chain
|
|
396
|
+
* still applies. Flattened to the subtype's declaration alone, `Pet { @maxLength(3) name: string }`
|
|
397
|
+
* let `Dog`'s own `name: string` be `"rexie"`, which the document refuses - and, under a discriminated
|
|
398
|
+
* base, so did the base, whose dispatch accepted it.
|
|
399
|
+
*
|
|
400
|
+
* **Where the overriding value narrows each declaration above it, the rest is their keywords**:
|
|
401
|
+
*
|
|
402
|
+
* - `constrainedBy`: the ancestors' own constraints, and their declared scalars', applied to the
|
|
403
|
+
* value for its own JSON type; a literal is checked against them statically.
|
|
404
|
+
* - `never`: no value is both - another JSON type, or a literal the ancestor refuses.
|
|
405
|
+
* - `nullOnly`: the override's literals are all refused, and both sides admit `null`.
|
|
406
|
+
* - `nonNull`: the override admits `null` and a declaration above it does not.
|
|
407
|
+
* - `stringOnly`: openapi3 adds the discriminator as a `string` to the model declaring
|
|
408
|
+
* `@discriminator` where that model's OWN properties lack it, whatever a model above it declares.
|
|
409
|
+
*
|
|
410
|
+
* **Where it does not, the declarations are merged into one schema** - `value`, see
|
|
411
|
+
* {@link mergeParts}: two unrelated models key by key, two lists by element, a union member by
|
|
412
|
+
* member. Zod's `.and()` is never put between two objects: it pools the keys one sealed side refuses,
|
|
413
|
+
* throws on two defaults, and is no option a dispatch can read.
|
|
414
|
+
*
|
|
415
|
+
* `required` holds where the property is a discriminator, which openapi3 adds to the base's
|
|
416
|
+
* `required` however it is declared, or where any declaration above requires it.
|
|
417
|
+
*/
|
|
418
|
+
export interface PropertyPosition {
|
|
419
|
+
readonly constrainedBy: readonly Keyworded[];
|
|
420
|
+
readonly required: boolean;
|
|
421
|
+
readonly stringOnly: boolean;
|
|
422
|
+
readonly never: boolean;
|
|
423
|
+
readonly nullOnly: boolean;
|
|
424
|
+
readonly nonNull: boolean;
|
|
425
|
+
/** The value as one schema, where the declarations above are merged with it rather than implied. */
|
|
426
|
+
readonly value: Merged | undefined;
|
|
427
|
+
/** The declarations above, each published at this position. */
|
|
428
|
+
readonly ancestors: readonly ModelProperty[];
|
|
429
|
+
/**
|
|
430
|
+
* **The visibility the most derived declaration's model is published at**, where a merge holds
|
|
431
|
+
* declarations published apart - see `mergeShapes`. Its optionality is read there: `M6 { b }`
|
|
432
|
+
* merged at a PATCH beside an `S8` published at `Read` was `b` required, where the document's
|
|
433
|
+
* `M6` leaves it optional.
|
|
434
|
+
*/
|
|
435
|
+
readonly at?: Visibility;
|
|
436
|
+
/** A merge no one schema states - see `unrepresentable-override`. */
|
|
437
|
+
readonly refused: {
|
|
438
|
+
readonly ancestor: ModelProperty;
|
|
439
|
+
readonly why: string;
|
|
440
|
+
readonly recursive: boolean;
|
|
441
|
+
} | undefined;
|
|
442
|
+
}
|
|
443
|
+
export declare const OWN_POSITION: PropertyPosition;
|
|
444
|
+
export declare function positionOf(program: Program, model: Model, entry: FlattenedProperty): PropertyPosition;
|
|
445
|
+
/**
|
|
446
|
+
* **A declaration is judged at the visibility its own model is rendered at**, as openapi3 renders
|
|
447
|
+
* each model of a chain: a subtype's create body is `allOf` its base, and a base with nothing
|
|
448
|
+
* create-specific is the canonical schema - its read-only `id` shared in, `readOnly` and required.
|
|
449
|
+
* Judged at the subtype's visibility, the base's `id` dropped out of the create body the document
|
|
450
|
+
* requires it in. See `renderedVisibilityOf`.
|
|
451
|
+
*/
|
|
452
|
+
/** At the visibility a merged member's declaration is published at, where it is not the merge's. */
|
|
453
|
+
export declare function atPosition<T>(position: PropertyPosition, run: () => T): T;
|
|
454
|
+
export declare function atDeclaration<T>(program: Program, property: ModelProperty, run: () => T): T;
|
|
455
|
+
/**
|
|
456
|
+
* **A property's type is referred to at the visibility openapi3 reads its declaration's properties
|
|
457
|
+
* at** - see `readVisibilityOf` - where the declaration is rendered at the current one. Its
|
|
458
|
+
* optionality and its position stay where the model is published; only what the type is named, a
|
|
459
|
+
* list or a union instance openapi3 does not reduce, can differ.
|
|
460
|
+
*/
|
|
461
|
+
export declare function atReading<T>(program: Program, property: ModelProperty, run: () => T): T;
|
|
462
|
+
/**
|
|
463
|
+
* **Whether a union reaches itself through union members alone**: `union string { int32, string }`
|
|
464
|
+
* in a namespace, where the member `string` names the union and not the scalar, or `A { int32, B }`
|
|
465
|
+
* beside `B { string, A }`. No property or list element stands between the union and its own
|
|
466
|
+
* reference, so the schema openapi3 publishes, `anyOf: [int32, { $ref: string }]`, refers to itself
|
|
467
|
+
* with no value passed through on the way, and JSON Schema leaves that undefined - measured, Ajv
|
|
468
|
+
* overflows its stack on `5`, `"abc"` and `null` alike. TypeScript refuses the alias it would be,
|
|
469
|
+
* and the walks here that flatten a union recursed until the compiler reported "Emitter crashed!
|
|
470
|
+
* This is a bug." A union with an envelope wraps each member in an object, which a value does pass
|
|
471
|
+
* through, so the walk stops there.
|
|
472
|
+
*/
|
|
473
|
+
export declare function reachesItselfThroughUnions(program: Program, union: Union): boolean;
|
|
474
|
+
/** Refuse a union that reaches itself through union members alone - see `reachesItselfThroughUnions`. */
|
|
475
|
+
export declare function refuseSelfReachingUnion(program: Program, union: Union): void;
|
|
476
|
+
/** TypeSpec's own assignability, which an override is checked against and a rename is not. */
|
|
477
|
+
export declare function isAssignable(program: Program, source: Type, target: Type): boolean;
|
|
478
|
+
/**
|
|
479
|
+
* A declaration whose keywords - `@minLength`, `@minItems` and the like - apply to a value beside its
|
|
480
|
+
* type: a property, a declared scalar above it, a model.
|
|
481
|
+
*/
|
|
482
|
+
export type Keyworded = ModelProperty | Scalar | Model;
|
|
483
|
+
/**
|
|
484
|
+
* The wrapper openapi3 synthesises for one variant of a `@discriminated` union with an envelope -
|
|
485
|
+
* `{kind: "cat", value: Cat}` - which no TypeSpec type stands for. A member of a merge like any other.
|
|
486
|
+
*/
|
|
487
|
+
interface Envelope {
|
|
488
|
+
readonly kind: "Envelope";
|
|
489
|
+
readonly union: Union;
|
|
490
|
+
readonly name: string;
|
|
491
|
+
readonly variant: Type;
|
|
492
|
+
readonly discriminator: string;
|
|
493
|
+
readonly property: string;
|
|
494
|
+
}
|
|
495
|
+
type Member = Type | Envelope;
|
|
496
|
+
/** One schema of the `allOf` a value is held to - see {@link mergeParts}. */
|
|
497
|
+
interface Part {
|
|
498
|
+
/** What the value may be, `null` aside: one member, or each member of a union. */
|
|
499
|
+
readonly members: readonly Member[];
|
|
500
|
+
readonly nullable: boolean;
|
|
501
|
+
readonly keywords: readonly Keyworded[];
|
|
502
|
+
/** The declared type, where the part is one - rendered by reference where it narrows the rest. */
|
|
503
|
+
readonly type?: Type;
|
|
504
|
+
/** A declaration only valid whole: an encoding that keeps its declared type beside another. */
|
|
505
|
+
readonly whole?: ModelProperty;
|
|
506
|
+
/** Why no one schema states the part - a `@discriminated` union with a default variant. */
|
|
507
|
+
readonly refused?: string;
|
|
508
|
+
/** Its members are a `@oneOf` union's: exactly one may accept a value. */
|
|
509
|
+
readonly exclusive?: boolean;
|
|
510
|
+
/**
|
|
511
|
+
* **The visibility its value is published at, where that is not the merge's own** - each
|
|
512
|
+
* declaration's, as openapi3 publishes each: a list literal's element at `Item`, a named list's
|
|
513
|
+
* at the list's own visibility, a record's value at the record's. Merged at one visibility, a
|
|
514
|
+
* PATCH made every element of a list literal optional beside a named list, and a record's values
|
|
515
|
+
* optional where the document keeps them as declared.
|
|
516
|
+
*/
|
|
517
|
+
readonly at?: Visibility;
|
|
518
|
+
}
|
|
519
|
+
/**
|
|
520
|
+
* **The declarations of one key, merged into the one schema the document holds its value to.**
|
|
521
|
+
*
|
|
522
|
+
* The document publishes each declaration in its own model and joins the models with `allOf`, so a
|
|
523
|
+
* value at the key is valid where every declaration accepts it. This is that conjunction, worked out
|
|
524
|
+
* as the document's own validator works it out, keyword family by keyword family:
|
|
525
|
+
*
|
|
526
|
+
* - a declaration whose type every other declaration's type admits is the value, with every
|
|
527
|
+
* declaration's keywords on it - a subtype, a derived scalar, a literal the others allow;
|
|
528
|
+
* - a union is merged member by member, and a member no other declaration admits is dropped;
|
|
529
|
+
* - two objects are merged key by key: a key one sealed side does not declare is refused, a key
|
|
530
|
+
* either requires is required, and a typed indexer holds every key it does not name;
|
|
531
|
+
* - two lists are merged element by element, with every declaration's `@minItems` and `@maxItems`;
|
|
532
|
+
* - two scalars of one JSON type that neither narrows are both checked, `.and()` between leaves.
|
|
533
|
+
*
|
|
534
|
+
* `null` is valid only where every declaration admits it, and a JSON type no declaration shares is no
|
|
535
|
+
* value at all. Refused only where no one schema can say it: a merge that refers back to itself, and a
|
|
536
|
+
* `@discriminated` union with a default variant, which this emitter refuses on its own.
|
|
537
|
+
*/
|
|
538
|
+
export type Merged =
|
|
539
|
+
/** No value - `withheld` where a refused declaration leaves it so, not the document. */
|
|
540
|
+
{
|
|
541
|
+
readonly kind: "never";
|
|
542
|
+
readonly withheld?: true;
|
|
543
|
+
} | {
|
|
544
|
+
readonly kind: "refused";
|
|
545
|
+
readonly why: string;
|
|
546
|
+
readonly recursive?: true;
|
|
547
|
+
} | {
|
|
548
|
+
readonly kind: "type";
|
|
549
|
+
readonly type: Type;
|
|
550
|
+
readonly keywords: readonly Keyworded[];
|
|
551
|
+
/** The visibility the type is published at, where it is not the merge's own - see `Part`. */
|
|
552
|
+
readonly at?: Visibility;
|
|
553
|
+
} | {
|
|
554
|
+
readonly kind: "whole";
|
|
555
|
+
readonly property: ModelProperty;
|
|
556
|
+
} | {
|
|
557
|
+
readonly kind: "nullable";
|
|
558
|
+
readonly inner: Merged;
|
|
559
|
+
} | {
|
|
560
|
+
readonly kind: "union";
|
|
561
|
+
readonly members: readonly Merged[];
|
|
562
|
+
readonly exclusive?: boolean;
|
|
563
|
+
} | {
|
|
564
|
+
readonly kind: "all";
|
|
565
|
+
readonly parts: readonly Merged[];
|
|
566
|
+
} | {
|
|
567
|
+
readonly kind: "array";
|
|
568
|
+
readonly element: Merged;
|
|
569
|
+
readonly keywords: readonly Keyworded[];
|
|
570
|
+
/** An array literal, whose element is computed and rendered at `Item` visibility. */
|
|
571
|
+
readonly list?: Model;
|
|
572
|
+
} | MergedObject;
|
|
573
|
+
export interface MergedObject {
|
|
574
|
+
readonly kind: "object";
|
|
575
|
+
readonly members: readonly MergedMember[];
|
|
576
|
+
/**
|
|
577
|
+
* What a key no member names may hold: nothing (`sealed`), anything, kept (`loose`) or stripped
|
|
578
|
+
* (`open`), or a value of a schema - a typed indexer.
|
|
579
|
+
*/
|
|
580
|
+
readonly rest: "sealed" | "open" | "loose" | Merged;
|
|
581
|
+
/** Every key held to `rest`, and none declared: a dictionary. */
|
|
582
|
+
readonly dictionary: boolean;
|
|
583
|
+
readonly keywords: readonly Keyworded[];
|
|
584
|
+
/** Where a refusal inside the indexer is reported. */
|
|
585
|
+
readonly site: Type | undefined;
|
|
586
|
+
}
|
|
587
|
+
export interface MergedMember {
|
|
588
|
+
readonly key: string;
|
|
589
|
+
/** The declaration whose optionality, default and doc the member takes, or none where none states it. */
|
|
590
|
+
readonly property: ModelProperty | undefined;
|
|
591
|
+
readonly position: PropertyPosition;
|
|
592
|
+
}
|
|
593
|
+
export declare function mergeParts(program: Program, given: readonly (Part | undefined)[]): Merged;
|
|
594
|
+
/**
|
|
595
|
+
* **The indexer that holds a model's keys: the TOPMOST string indexer in its chain.**
|
|
596
|
+
*
|
|
597
|
+
* openapi3 publishes a root model's indexer as `additionalProperties`, which holds every key the
|
|
598
|
+
* model does not declare itself - a subtype's own keys included, since the subtype is `allOf` the
|
|
599
|
+
* root - and a lower model's as `unevaluatedProperties`, which the root's has already evaluated. So
|
|
600
|
+
* the topmost indexer holds every key its own model and those above it do not declare, and any below
|
|
601
|
+
* it never applies. Read as the nearest, a subtype's `m: Inner2` escaped the base's
|
|
602
|
+
* `...Record<Inner>` and a subtype's own indexer refused keys the document accepts.
|
|
603
|
+
*/
|
|
604
|
+
export declare function bindingIndexerOf(model: Model): {
|
|
605
|
+
readonly link: Model;
|
|
606
|
+
readonly value: Type;
|
|
607
|
+
} | undefined;
|
|
146
608
|
/** The indexer in force, which a model can inherit - `model X extends Record<unknown>`. */
|
|
147
609
|
export declare function effectiveIndexer(model: Model): Model["indexer"];
|
|
148
610
|
/**
|
|
149
|
-
* The subtypes a `@discriminator` base
|
|
611
|
+
* The subtypes a `@discriminator` base dispatches to: the models its document's `mapping` names.
|
|
612
|
+
*
|
|
613
|
+
* **The compiler's own reading, which is what openapi3 writes the mapping from.** A derived model
|
|
614
|
+
* that declares no value for the discriminator is looked through to the models derived from it
|
|
615
|
+
* that do. This used to be the base's DIRECT subtypes, so `Animal` -> `Mammal` (no `kind`) -> `Cat`
|
|
616
|
+
* dispatched to `Mammal`, which carries no literal `kind`, and Zod threw "Invalid discriminated union
|
|
617
|
+
* option" on every parse - a known kind, an unknown one, anything - while `Cat`, the model the
|
|
618
|
+
* mapping names for `"cat"`, was not in the dispatch at all.
|
|
150
619
|
*
|
|
151
620
|
* Filtered by openapi3's own {@link includeDerivedModel} so a template declaration is not mistaken
|
|
152
621
|
* for a real subtype - the same rule that decides whether a base may be sealed.
|
|
153
622
|
*/
|
|
154
623
|
export declare function discriminatedSubtypes(program: Program, model: Model): readonly Model[];
|
|
155
624
|
/**
|
|
156
|
-
*
|
|
157
|
-
*
|
|
625
|
+
* The subtypes a discriminated base dispatches to, each with the discriminator value its mapping
|
|
626
|
+
* names it for - see {@link discriminatedSubtypes}.
|
|
627
|
+
*/
|
|
628
|
+
export declare function dispatchedSubtypesOf(program: Program, model: Model): readonly (readonly [string, Model])[];
|
|
629
|
+
/**
|
|
630
|
+
* **Why a model the mapping names cannot be dispatched to, or `undefined` where it can.**
|
|
631
|
+
*
|
|
632
|
+
* A dispatch reads one fixed value per subtype - `z.discriminatedUnion` has no other way to choose -
|
|
633
|
+
* and the document's `mapping` names a subtype for exactly the values its discriminator fixes. Two
|
|
634
|
+
* declarations break that, and each used to break the emitted module:
|
|
635
|
+
*
|
|
636
|
+
* - **a discriminator that is not one fixed value** - `kind: "lab" | string`, or a subtype that
|
|
637
|
+
* renames `kind` with `@encodedName` so the `kind` it publishes is its base's open `string`. Zod
|
|
638
|
+
* threw "Invalid discriminated union option" on the first parse, or on import.
|
|
639
|
+
* - **a value a declaration above it refuses** - `kind: "cobra"` under `@maxLength(3) kind: string`.
|
|
640
|
+
* The document publishes the subtype as `allOf` that base, so no value is one; validated as its own
|
|
641
|
+
* declaration alone, every `"cobra"` was accepted.
|
|
642
|
+
*
|
|
643
|
+
* Each is refused as `invalid-subtype-discriminator`, as `undeclared-discriminator` refuses a
|
|
644
|
+
* discriminator the document cannot state, and the base dispatches to its other subtypes.
|
|
645
|
+
*/
|
|
646
|
+
export declare function invalidSubtypeOf(program: Program, subtype: Model, propertyName: string): {
|
|
647
|
+
readonly why: string;
|
|
648
|
+
readonly satisfiable: boolean;
|
|
649
|
+
readonly invisibleAt?: Visibility;
|
|
650
|
+
} | undefined;
|
|
651
|
+
/** A visibility as its flags read: `Update | Patch`, `Create | Item`. */
|
|
652
|
+
export declare function visibilityName(at: Visibility): string;
|
|
653
|
+
/**
|
|
654
|
+
* **Whether a model is a subtype its base's mapping names that no value can be** - its fixed
|
|
655
|
+
* discriminator refused by a declaration above it. Refused as `invalid-subtype-discriminator` and
|
|
656
|
+
* emitted as nothing, so used directly as a body it accepts nothing, as the document does, where it
|
|
657
|
+
* used to accept its own declaration.
|
|
658
|
+
*/
|
|
659
|
+
export declare function unsatisfiableSubtype(program: Program, model: Model): boolean;
|
|
660
|
+
/**
|
|
661
|
+
* **A model extending a base that openapi3 seals because it does not count the model as derived.**
|
|
662
|
+
*
|
|
663
|
+
* openapi3 seals a model with no derived models it publishes, and a template instance is not one
|
|
664
|
+
* (`includeDerivedModel`). So with `seal-object-schemas` on, `Pet<string> extends Animal` leaves
|
|
665
|
+
* `Animal` sealed, and `Pet<string>` - published as `allOf` that sealed `Animal` - refuses every
|
|
666
|
+
* value carrying a property `Pet<string>` adds, since `Animal`'s own `unevaluatedProperties` cannot
|
|
667
|
+
* see it. Validated as its own flattened properties, such a value was accepted. Returns the sealed
|
|
668
|
+
* base and the keys it refuses, or `undefined` where nothing is refused.
|
|
669
|
+
*/
|
|
670
|
+
export declare function sealedBaseExtensionOf(program: Program, model: Model): {
|
|
671
|
+
readonly base: Model;
|
|
672
|
+
readonly keys: readonly string[];
|
|
673
|
+
readonly required: boolean;
|
|
674
|
+
} | undefined;
|
|
675
|
+
/**
|
|
676
|
+
* **How a `@discriminator` base is validated: as the document's base, deserialized as the subtype
|
|
677
|
+
* its discriminator names where the value is one.**
|
|
678
|
+
*
|
|
679
|
+
* The document publishes the base as an object of its own properties beside `discriminator:
|
|
680
|
+
* {propertyName, mapping}`, and every subtype as `allOf` that base. OAS 3.1.1 section 4.8.25 is
|
|
681
|
+
* normative that the discriminator "MUST NOT change the validation outcome of the schema", and
|
|
682
|
+
* section 4.8.25.2 that in this `allOf` form validating the parent "does not perform a search for
|
|
683
|
+
* child schemas or use them in validation in any way" - restated in 3.2.0 and 3.2.1, and by the
|
|
684
|
+
* spec's editors (OAI/OpenAPI-Specification#3901: "`discriminator` only really helps with things
|
|
685
|
+
* like code generation and data de-serialization. It doesn't help with validation"). Ajv defines it
|
|
686
|
+
* as a no-op for validation.
|
|
158
687
|
*
|
|
159
|
-
*
|
|
160
|
-
*
|
|
161
|
-
*
|
|
162
|
-
*
|
|
163
|
-
*
|
|
164
|
-
* the
|
|
165
|
-
* `single-discriminator` and `nested-discriminator`, and a handler returning it failed its own
|
|
166
|
-
* response contract (typespec-http-graphql, 2026-10-16).
|
|
688
|
+
* **So the verdict is the base's, and the subtype is a deserialization.** A union of the dispatch
|
|
689
|
+
* and the base accepts exactly what the base accepts - every subtype is `allOf` the base, so a valid
|
|
690
|
+
* subtype is a valid base - and hands back, for a value that is a valid instance of the subtype its
|
|
691
|
+
* discriminator names, that subtype with its own properties, as an SDK deserializing the same body
|
|
692
|
+
* would. A value naming no subtype, or one that is not a valid instance of the subtype it names, is
|
|
693
|
+
* the base.
|
|
167
694
|
*
|
|
168
|
-
*
|
|
169
|
-
*
|
|
170
|
-
*
|
|
695
|
+
* This emitter refused a value naming no subtype until typespec-http-graphql's report of
|
|
696
|
+
* 2026-10-16, and 0.30.0 then guarded the base against a value naming a subtype, so a `"golden"`
|
|
697
|
+
* value missing `Golden`'s own fields was refused though the document accepts it as a `Dog`. Both
|
|
698
|
+
* made the validator stricter than the document it exists to agree with.
|
|
171
699
|
*/
|
|
172
700
|
export interface DiscriminatedBase {
|
|
173
701
|
/** The discriminator's wire name. */
|
|
@@ -175,18 +703,57 @@ export interface DiscriminatedBase {
|
|
|
175
703
|
/** The subtypes a `z.discriminatedUnion` dispatches to, each carrying literal discriminator values. */
|
|
176
704
|
readonly dispatched: readonly Model[];
|
|
177
705
|
/**
|
|
178
|
-
* Subtypes that are themselves bases
|
|
179
|
-
*
|
|
180
|
-
* own literal discriminator keeps it exclusive.
|
|
706
|
+
* Subtypes that are themselves discriminated bases, and so `z.union`s with no literal values to
|
|
707
|
+
* dispatch on; no `z.discriminatedUnion` can hold one, so each sits beside the dispatch.
|
|
181
708
|
*/
|
|
182
709
|
readonly beside: readonly Model[];
|
|
183
|
-
/** Every value a subtype claims, which are the keys of the document's `mapping`. */
|
|
184
|
-
readonly claimed: readonly string[];
|
|
185
|
-
/** Whether a value no subtype claims is validated as the base model. */
|
|
186
|
-
readonly open: boolean;
|
|
187
710
|
}
|
|
188
711
|
export declare function discriminatedBaseOf(program: Program, model: Model): DiscriminatedBase | undefined;
|
|
189
|
-
|
|
712
|
+
/** What the registry knows of the declarations a value refers to, which the value is built around. */
|
|
713
|
+
export interface HeldDeclarations {
|
|
714
|
+
/**
|
|
715
|
+
* **A declaration refused as validating nothing - a collision - which no dispatch can hold**: its
|
|
716
|
+
* validator is `z.never()`, and `z.discriminatedUnion` throws on every parse given one. The
|
|
717
|
+
* registry re-renders a dispatch that held it before the refusal.
|
|
718
|
+
*/
|
|
719
|
+
readonly refused: (expression: string) => boolean;
|
|
720
|
+
/**
|
|
721
|
+
* **A declaration annotated `z.ZodType<T>`, which no dispatch can hold**: the annotation
|
|
722
|
+
* `z.lazy()` requires for a recursive declaration erases the literal values
|
|
723
|
+
* `z.discriminatedUnion` reads, and the module failed to typecheck with `TS2322` - "not
|
|
724
|
+
* assignable to `$ZodTypeDiscriminable`".
|
|
725
|
+
*/
|
|
726
|
+
readonly annotated: (expression: string) => boolean;
|
|
727
|
+
/**
|
|
728
|
+
* **A type whose declaration at a visibility is refused** - as a collision, or as
|
|
729
|
+
* `split-visibility` - which a merge or a `@oneOf` holding it holds to nothing. The registry
|
|
730
|
+
* re-renders a value that asked before the refusal.
|
|
731
|
+
*/
|
|
732
|
+
readonly refusedAt: (type: Type, at: Visibility) => boolean;
|
|
733
|
+
/**
|
|
734
|
+
* **A type whose declaration at a visibility holds a refused one anywhere it refers to** - see
|
|
735
|
+
* `holdsRefusedMember`. The registry re-renders a value that asked before the refusal.
|
|
736
|
+
*/
|
|
737
|
+
readonly holdsRefusalAt: (type: Type, at: Visibility) => boolean;
|
|
738
|
+
}
|
|
739
|
+
export declare function withHeldDeclarations<T>(given: HeldDeclarations, run: () => T): T;
|
|
740
|
+
/**
|
|
741
|
+
* **The record a sealed model's 3.0 document cannot see**, or `undefined`. openapi3's 3.0 sealing is
|
|
742
|
+
* `additionalProperties: { not: {} }` beside the model's own `properties` - it re-declares a base's
|
|
743
|
+
* properties there, so they survive - and a base that is a record declares none: `model M extends
|
|
744
|
+
* Record<string> { name: string }` sealed refuses every key the record admits, where 3.1's
|
|
745
|
+
* `unevaluatedProperties` sees the record through `allOf`. Mirrors {@link isSealed}: sealed here,
|
|
746
|
+
* with an ancestor whose indexer admits a value.
|
|
747
|
+
*/
|
|
748
|
+
export declare function unpublishableRecordOf(program: Program, model: Model): Model | undefined;
|
|
749
|
+
/**
|
|
750
|
+
* A model as the object it publishes at the current position: the merge of one - see
|
|
751
|
+
* {@link mergeShapes}. **HTTP metadata is not payload**, and neither is a property invisible here or
|
|
752
|
+
* a `never` one, which the document drops; each is judged declaration by declaration - see
|
|
753
|
+
* {@link publishedPropertiesOf}.
|
|
754
|
+
*/
|
|
755
|
+
export declare function objectOfModel(program: Program, model: Model): Merged;
|
|
756
|
+
export declare function readsFromText(program: Program, type: Type): boolean;
|
|
190
757
|
/**
|
|
191
758
|
* A name as an object key - quoted when it is not a legal JavaScript identifier.
|
|
192
759
|
*
|
|
@@ -221,8 +788,154 @@ export declare function objectKey(name: string): string;
|
|
|
221
788
|
* comment indented differently from the member it documents is the tell that a generator wrote it.
|
|
222
789
|
*/
|
|
223
790
|
export declare function jsDocComment(text: string | undefined, indent: string): string;
|
|
791
|
+
/**
|
|
792
|
+
* The name a property carries **on the wire**, which is not always the name the spec gave it.
|
|
793
|
+
*
|
|
794
|
+
* **`@encodedName` is a first-party decorator, and a decorator is a declaration rather than a
|
|
795
|
+
* behaviour.** It attaches metadata to the type graph and does nothing on its own; every emitter has
|
|
796
|
+
* to go and ask. `@typespec/openapi3` asks - `resolveEncodedName` at `schema-emitter.js:214` - so the
|
|
797
|
+
* document publishes the wire name. This emitter did not, so the validator was keyed on the TypeSpec
|
|
798
|
+
* name and a request carrying exactly what the contract required failed twice over: the declared
|
|
799
|
+
* property was missing, and the one actually sent was an unrecognised key a sealed model rejects.
|
|
800
|
+
*
|
|
801
|
+
* The corpus scenario for this is called "Testing that you send the right JSON name on the wire",
|
|
802
|
+
* and it sat in the green column throughout - compiling cleanly while emitting the wrong key.
|
|
803
|
+
*
|
|
804
|
+
* **The domain therefore sees the wire name too**, because `types.ts` resolves it identically. A
|
|
805
|
+
* validator that renamed on the way through would be a transform, which JSON Schema cannot express
|
|
806
|
+
* and no document could state - the same reason `@trimmed` was removed.
|
|
807
|
+
*/
|
|
808
|
+
export declare function rawPropertyKey(program: Program, property: ModelProperty): string;
|
|
224
809
|
export declare function encodedTypeOf(program: Program, target: ModelProperty | Scalar): Scalar | undefined;
|
|
225
|
-
|
|
810
|
+
/**
|
|
811
|
+
* **What a property under `@encode` must be: what `@typespec/openapi3` publishes for it.**
|
|
812
|
+
*
|
|
813
|
+
* Its `applyEncoding` takes the schema the property would publish - a `$ref` to a declared type, or
|
|
814
|
+
* the type written out inline - and replaces only its `type` with the encoded scalar's, dropping
|
|
815
|
+
* `items` unless that is an array. Everything else stays. So `@encode("x", string) color: Color` is
|
|
816
|
+
* `{ $ref: Color, type: "string" }`: a string, and one of `Color`'s values. This emitter replaced the
|
|
817
|
+
* type outright, so it validated `z.string()` beside that and accepted `"green"`; it dropped a named
|
|
818
|
+
* scalar's `@pattern`, a literal union's values, and the `null` of a nullable property, the last
|
|
819
|
+
* contradicting the comment that said it was kept.
|
|
820
|
+
*
|
|
821
|
+
* - `declared`: no type change - no `@encode`, a query parameter's array style, `rfc3339` on a
|
|
822
|
+
* date-time.
|
|
823
|
+
* - `replaced`: the declared type is written out inline, and nothing it kept constrains a value of
|
|
824
|
+
* the new type - a standard scalar, an inline list, record or object.
|
|
825
|
+
* - `both`: the declared type is kept beside the new one and still constrains it. `kept` is what is
|
|
826
|
+
* left of it once the new `type` has ruled out every member of another JSON type - the whole
|
|
827
|
+
* declared type where nothing was ruled out - and `within` where that alone says both.
|
|
828
|
+
* - `unsatisfiable`: no value is both - `{ $ref: Filter, type: "string" }` - or only `null` is, or
|
|
829
|
+
* the encoded scalar is not standard, so the document states no type at all. Refused, so no
|
|
830
|
+
* validator contradicts it.
|
|
831
|
+
*
|
|
832
|
+
* `nullable` is the document's: openapi3 3.1 applies the encoding to the one member beside `null`
|
|
833
|
+
* when the property publishes as `anyOf: [<member>, { type: "null" }]`, and to the whole `anyOf`
|
|
834
|
+
* otherwise, where the new `type` rules `null` out with everything else of another type.
|
|
835
|
+
*/
|
|
836
|
+
export type EncodedValue = {
|
|
837
|
+
readonly kind: "declared";
|
|
838
|
+
} | {
|
|
839
|
+
readonly kind: "replaced";
|
|
840
|
+
readonly scalar: Scalar;
|
|
841
|
+
readonly nullable: boolean;
|
|
842
|
+
} | {
|
|
843
|
+
readonly kind: "both";
|
|
844
|
+
readonly scalar: Scalar;
|
|
845
|
+
/** The encoding's name - `rfc7231`, `ISO8601` - which decides what a member's check is. */
|
|
846
|
+
readonly form: string | undefined;
|
|
847
|
+
readonly nullable: boolean;
|
|
848
|
+
readonly kept: Type | readonly Type[];
|
|
849
|
+
readonly within: boolean;
|
|
850
|
+
} | {
|
|
851
|
+
readonly kind: "unsatisfiable";
|
|
852
|
+
readonly scalar: Scalar;
|
|
853
|
+
readonly declared: Type;
|
|
854
|
+
readonly why: "typeless" | "nothing" | "null";
|
|
855
|
+
};
|
|
856
|
+
export declare function encodedValueOf(program: Program, property: ModelProperty): EncodedValue;
|
|
857
|
+
/**
|
|
858
|
+
* The JSON type a value is published as. `fraction` is a number literal that is not an integer: a
|
|
859
|
+
* `number` scalar may hold an integer, so an `integer` encoding admits it, but `1.5` never can.
|
|
860
|
+
*/
|
|
861
|
+
type JsonType = "string" | "integer" | "number" | "fraction" | "boolean" | "object" | "array" | "null";
|
|
862
|
+
/**
|
|
863
|
+
* The JSON type a scalar is published as, its own scalar-level `@encode` included - or `undefined`
|
|
864
|
+
* for a scalar with no standard base, which openapi3 publishes as `{}`, any value. Read as a string,
|
|
865
|
+
* `@encode("x", int32)` on one was refused as unsatisfiable while the document accepts `5`.
|
|
866
|
+
*/
|
|
867
|
+
export declare function jsonTypeOfScalar(program: Program, scalar: Scalar): JsonType | undefined;
|
|
868
|
+
/**
|
|
869
|
+
* **Whether an encoded value is written as the encoded scalar intersected with what it keeps.**
|
|
870
|
+
*
|
|
871
|
+
* Where the kept type alone says both it is written alone - but a constraint on the property cannot
|
|
872
|
+
* go on a kept union or literal, which carries no `.min` or `.regex`, so then the constraints go on
|
|
873
|
+
* the encoded scalar and the kept type is intersected beside it. Applied to the union,
|
|
874
|
+
* `@encode("x", string) @minLength(2) v: string | "a"` emitted `z.union(...).min(2)`, and the module
|
|
875
|
+
* threw on import.
|
|
876
|
+
*/
|
|
877
|
+
export declare function encodedIntersects(program: Program, property: ModelProperty, encoding: Extract<EncodedValue, {
|
|
878
|
+
kind: "both";
|
|
879
|
+
}>): boolean;
|
|
880
|
+
/**
|
|
881
|
+
* **A union property's constraints, applied to each member as JSON Schema applies them.**
|
|
882
|
+
*
|
|
883
|
+
* `@minLength(2) v: string | "a" | int32` is published `{ anyOf: [...], minLength: 2 }`, and
|
|
884
|
+
* `minLength` constrains a value only where it is a string: `"ab"` and `5` are valid, `"a"` is not.
|
|
885
|
+
* Each member takes the keywords of its own JSON type, and a literal member breaking one is no value
|
|
886
|
+
* at all. Applied to the union, this emitted `z.union(...).min(2)`, and the module threw on import.
|
|
887
|
+
* The constraints are the property's own and those of every declaration above it the position
|
|
888
|
+
* leaves - see `PropertyPosition` - and, where openapi3 adds the discriminator as a `string`, only
|
|
889
|
+
* the members that can be a string are left. Returns the members left, or `undefined` where the
|
|
890
|
+
* property is not a union read member by member.
|
|
891
|
+
*/
|
|
892
|
+
export declare function constrainedMembersOf(program: Program, property: ModelProperty, position?: PropertyPosition): readonly Type[] | undefined;
|
|
893
|
+
/** A member with no JSON type of its own: `unknown`, or a scalar with no standard base. */
|
|
894
|
+
export declare function isTypelessMember(program: Program, member: Type): boolean;
|
|
895
|
+
export declare const ANY_JSON_TS: string;
|
|
896
|
+
/** Whether a kept member is a scalar whose own check the encoding's wire form replaces. */
|
|
897
|
+
export declare function replacesFormOf(program: Program, encoding: Extract<EncodedValue, {
|
|
898
|
+
kind: "both";
|
|
899
|
+
}>, member: Type): member is Scalar;
|
|
900
|
+
export declare function isExclusive(program: Program, target: Type): boolean;
|
|
901
|
+
/** Whether a union is exclusive where it is rendered: its own `@oneOf`, or its property's. */
|
|
902
|
+
export declare function isExclusiveHere(program: Program, union: Union): boolean;
|
|
903
|
+
/** The inline union a property's `@oneOf` makes exclusive; a named one is a `$ref` it leaves alone. */
|
|
904
|
+
export declare function exclusiveSiteOf(program: Program, property: ModelProperty, declared: Type): Type | undefined;
|
|
905
|
+
/**
|
|
906
|
+
* **Whether a `@oneOf` holds a member whose declaration is refused, or refers to one that is**, so
|
|
907
|
+
* that it validates nothing. openapi3 still publishes the member, and counts it: `@oneOf union O {
|
|
908
|
+
* a: S, b: N }` with `S` refused as `split-visibility` accepted `{"id": "i"}`, which matches both
|
|
909
|
+
* members in the document and so is refused there - `z.xor` counted the refused `S` as matching
|
|
910
|
+
* nothing. **A member holding a refused declaration further down is held stricter in the same way**:
|
|
911
|
+
* `@oneOf { v0: M16, v1: M13 }`, with `M13`'s `c?: M14` refused, accepted `{ c: {...}, e: "red" }`,
|
|
912
|
+
* which matches both in the document, as `M16` alone. Both walks ask it, so the validator and the
|
|
913
|
+
* contract type are `never` together.
|
|
914
|
+
*/
|
|
915
|
+
export declare function holdsRefusedMember(program: Program, union: Union, exclusive: boolean): boolean;
|
|
916
|
+
export declare function withExclusiveSite<T>(site: Type | undefined, run: () => T): T;
|
|
917
|
+
/**
|
|
918
|
+
* The members of a union a value's keywords leave, or `undefined` where the type is no union: a
|
|
919
|
+
* literal member breaking one is no value at all. Shared by both walks, so they list the same members.
|
|
920
|
+
*/
|
|
921
|
+
export declare function keptMembersOf(program: Program, type: Type, keywords: readonly Keyworded[]): readonly Type[] | undefined;
|
|
922
|
+
/** The keywords that constrain anything, of the declarations a merged value carries. */
|
|
923
|
+
export declare function constrainingKeywords(program: Program, keywords: readonly Keyworded[]): readonly Keyworded[];
|
|
924
|
+
/**
|
|
925
|
+
* **The members a merged union is written with, by the validator and the contract type alike: one
|
|
926
|
+
* of each text the validator writes, in an `anyOf`.** `M19 | M19` merged with `Record<M19>` was two
|
|
927
|
+
* identical objects, whose two `z.input` types the wire assertion told apart from the one `Record` the
|
|
928
|
+
* contract type is (`TS2344`). Each walk deduplicating by its own text disagreed the other way: `S3`
|
|
929
|
+
* merged under `M1 | M2` is two objects the validator writes apart - `b` held to one `@minLength` and
|
|
930
|
+
* to two - and the contract type wrote as one, and TypeScript counts a union of two such objects as
|
|
931
|
+
* no object. So the validator decides, as it writes them, and the contract type writes the members
|
|
932
|
+
* it kept - see {@link noteWrittenMembers}. Repeated in a `@oneOf`, a value matching both is refused,
|
|
933
|
+
* so there each stays.
|
|
934
|
+
*/
|
|
935
|
+
export declare function membersToWrite(program: Program, value: Merged & {
|
|
936
|
+
readonly kind: "union";
|
|
937
|
+
}): readonly Merged[];
|
|
938
|
+
export declare function propertyToZod(program: Program, property: ModelProperty, position?: PropertyPosition): string;
|
|
226
939
|
/**
|
|
227
940
|
* **Whether `propertyToZod` emits `.default(...)` for this property, which is exactly whether its
|
|
228
941
|
* INPUT admits an explicit `undefined`.**
|
|
@@ -235,7 +948,9 @@ export declare function propertyToZod(program: Program, property: ModelProperty)
|
|
|
235
948
|
*
|
|
236
949
|
* Pure: it reports nothing, so the type walk can ask it without duplicating a diagnostic.
|
|
237
950
|
*/
|
|
238
|
-
export declare function emitsDefault(program: Program, property: ModelProperty): boolean;
|
|
951
|
+
export declare function emitsDefault(program: Program, property: ModelProperty, position?: PropertyPosition): boolean;
|
|
952
|
+
/** The non-null half of a two-variant `T | null` union, or `undefined` if it is not one. */
|
|
953
|
+
export declare function nullableInnerOf(type: Type): Type | undefined;
|
|
239
954
|
export declare function withRefResolver<T>(resolver: (type: Type) => string | undefined, run: () => T): T;
|
|
240
955
|
/** Called by the registry when it hands out the identifier of a declaration that is still open. */
|
|
241
956
|
export declare function noteBackEdge(): void;
|
|
@@ -260,4 +975,14 @@ export declare function typeToZod(program: Program, type: Type): string;
|
|
|
260
975
|
* occurrences still go through {@link typeToZod} and do resolve - which is what makes a recursive
|
|
261
976
|
* model reference itself instead of inlining itself forever.
|
|
262
977
|
*/
|
|
978
|
+
/**
|
|
979
|
+
* **An instance of a list template is refused**: `model Page<T> is T[]` made `Page<Item>`, which
|
|
980
|
+
* openapi3 emits as a template instance - an object - and publishes as `type: object` with the
|
|
981
|
+
* element as `additionalProperties`, a dictionary where the spec declares a list. No validator agrees
|
|
982
|
+
* with both. A named list per element type, `model Items is Item[]`, or `Item[]` itself, is a list in
|
|
983
|
+
* the document too.
|
|
984
|
+
*/
|
|
985
|
+
export declare function listTemplateInstanceWhy(program: Program, type: Model): string | undefined;
|
|
986
|
+
export declare const LIST_TEMPLATE_REMEDY = "Declare a named list model per element type, `model Items is Item[]`, or use `Item[]` itself.";
|
|
263
987
|
export declare function typeToZodBody(program: Program, type: Type): string;
|
|
988
|
+
export {};
|