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/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
- export declare function withVisibility<T>(program: Program, next: Visibility, run: () => T): T;
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
- /** Whether this model is shaped differently at `visibility` than at the canonical `Read`. */
21
- export declare function isTransformedBy(program: Program, model: Model, at: Visibility): boolean;
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 scalars an `@encode` chain passes through, when it returns to one it already passed - and
115
- * `undefined` when it ends at a type that crosses the wire.
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
- * **The one guard for every walk that follows `@encode`.** The schema, the wire kind and the contract
118
- * type each follow the chain by calling themselves on the encoded type, and each one overflowed the
119
- * stack on `Ping -> Pong -> Ping`. A scalar encoded as itself is no cycle: the chain ends there.
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 encodeCycleOf(program: Program, scalar: Scalar): readonly Scalar[] | undefined;
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 stands for, in declaration order.
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
- * **How a `@discriminator` base is validated: a value whose discriminator names a subtype as that
157
- * subtype, and any other value as the base model itself.**
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
- * The document publishes the base as an object of its own properties carrying `discriminator:
160
- * {propertyName, mapping}`, and every subtype as `allOf` that base. The mapping says which subtype a
161
- * value naming it is; nothing in the document refuses a value naming none. This emitter dispatched
162
- * on the mapping and stopped there, so `{ "kind": "wrongKind", "weight": 8 }` was refused for a
163
- * `Dog` whose `kind` is `string | "golden"`, though `Dog` requires only `kind` and `weight`. That is
164
- * the response `@typespec/http-specs` declares for `type/model/inheritance/enum-discriminator`,
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
- * **A closed discriminator every value of which a subtype claims has no base arm**: a value outside
169
- * it is refused by the base's own discriminator, and a value inside it always names a subtype. So a
170
- * fixed enum fully covered by its subtypes is validated exactly as before.
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 validated with a base arm. A `z.union` has no literal values
179
- * to dispatch on, so no `z.discriminatedUnion` can hold one; it sits beside the dispatch, where its
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
- export declare function wireKindOf(program: Program, type: Type): "number" | "boolean" | undefined;
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
- export declare function propertyToZod(program: Program, property: ModelProperty): string;
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 {};