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/lib.js CHANGED
@@ -67,7 +67,21 @@ const diagnostics = {
67
67
  "unsupported-type": {
68
68
  severity: "error",
69
69
  messages: {
70
- default: paramMessage `Cannot emit ${"artefact"} for ${"kind"}: ${"why"}. Replace it with a type a value can inhabit. ${"remedy"}`,
70
+ default: paramMessage `Cannot emit ${"artefact"} for ${"kind"}: ${"why"}. Replace or remove the declaration: ${"remedy"}`,
71
+ },
72
+ },
73
+ /**
74
+ * **A default that is no value the position accepts.** openapi3 publishes a default as the JSON it
75
+ * serializes, beside a schema the default need not satisfy - an encoding can change the type
76
+ * under it, a constraint can rule out the literal it names, a key it sets can be one the position
77
+ * does not declare. Filled in, it handed the handler a value its own type refuses, and the module
78
+ * did not compile. A warning, because the property itself is still validated as the document
79
+ * says; only the fallback is dropped.
80
+ */
81
+ "invalid-default": {
82
+ severity: "warning",
83
+ messages: {
84
+ default: paramMessage `The default of '${"name"}' is not emitted: ${"why"}, so filling it in would hand the handler a value the property refuses. Replace it with a value the property accepts, or remove it.`,
71
85
  },
72
86
  },
73
87
  "unsupported-default": {
@@ -110,6 +124,8 @@ const diagnostics = {
110
124
  severity: "warning",
111
125
  messages: {
112
126
  default: paramMessage `'${"name"}' is required, so its default can never apply: a required property is never absent. The OpenAPI document publishes it in 'required' and carries the default only as an annotation, and this validator does the same - a request omitting '${"name"}' is refused. Declare it optional, '${"name"}?: ... = ...', if callers may omit it.`,
127
+ discriminator: paramMessage `'${"name"}' is a discriminator, which '@typespec/openapi3' always publishes in its base's 'required', so it is required here however it is declared and its default can never apply. The validator refuses a value omitting it, as the document does. Remove the '?' and the default.`,
128
+ ancestor: paramMessage `'${"name"}' is required by a declaration of its key above it, which the document holds the value to through 'allOf', so it is required here however it is declared and its default can never apply. The validator refuses a value omitting it, as the document does. Remove the '?' and the default, or make the declaration above optional.`,
113
129
  },
114
130
  },
115
131
  /**
@@ -184,6 +200,7 @@ const diagnostics = {
184
200
  severity: "error",
185
201
  messages: {
186
202
  default: paramMessage `'${"name"}' serialises with '${"property"}' inside each variant, but '${"variant"}' does not declare it - so the OpenAPI document omits it, which OpenAPI 3.1 forbids for a discriminator ("the discriminator field MUST be a required field"). Emitting a validator that required it anyway would enforce a rule the published contract does not state. Declare '${"property"}' on '${"variant"}'.`,
203
+ open: paramMessage `'${"name"}' serialises with '${"property"}' inside each variant, but '${"variant"}' declares it as no one fixed value, so nothing can tell its values from another variant's and no dispatch can hold it. Declare '${"property"}' on '${"variant"}' as the literal its variant name says.`,
187
204
  },
188
205
  },
189
206
  "empty-union": {
@@ -220,6 +237,60 @@ const diagnostics = {
220
237
  default: paramMessage `'seal-object-schemas' resolves to true for ${"sealed"} and false for ${"open"}, and '@typespec/openapi3' has no per-service options - it applies one value to the whole program. So one of these services would publish a document that disagrees with the validator emitted beside it. Give every service the same value, or split the surfaces into separate compiles.`,
221
238
  },
222
239
  },
240
+ /**
241
+ * **A model a discriminated base's `mapping` names, which no dispatch can hold.**
242
+ *
243
+ * The mapping names a subtype for the values its discriminator fixes, and a dispatch reads one
244
+ * fixed value per subtype. A subtype whose discriminator is not fixed - `"lab" | string`, or `kind`
245
+ * renamed with `@encodedName` so its base's open `string` is what it publishes - made Zod throw
246
+ * "Invalid discriminated union option" on import or on the first parse. A subtype whose value a
247
+ * declaration above it refuses - `kind: "cobra"` under `@maxLength(3)` - is published as `allOf`
248
+ * that declaration, so no value is one, and was accepted.
249
+ *
250
+ * **An error, as `undeclared-discriminator` is**: the spec says something the document cannot
251
+ * hold as a dispatch, so it is refused rather than read some other way, and the base dispatches to
252
+ * its other subtypes until it is fixed.
253
+ */
254
+ "invalid-subtype-discriminator": {
255
+ severity: "error",
256
+ messages: {
257
+ default: paramMessage `'${"name"}' is named by the mapping of '${"base"}''s discriminator '${"property"}', and ${"why"}. Declare '${"property"}' on '${"name"}' as one fixed value that every declaration of '${"property"}' above it admits.`,
258
+ invisible: paramMessage `'${"name"}' is named by the mapping of '${"base"}''s discriminator '${"property"}', and ${"why"}. Make '${"property"}' visible at '${"at"}' on '${"name"}' and every model above it that declares it: a value is told as a '${"name"}' by its '${"property"}'.`,
259
+ },
260
+ },
261
+ /**
262
+ * **A template instance that adds properties to a base openapi3 seals.**
263
+ *
264
+ * openapi3 seals a model with no derived models it publishes, and does not count a template
265
+ * instance as one, so under `seal-object-schemas` `Pet<string> extends Animal` leaves `Animal`
266
+ * sealed. `Pet<string>` is published as `allOf` that sealed `Animal`, whose own
267
+ * `unevaluatedProperties` cannot see `Pet<string>`'s properties - so the document refuses every
268
+ * value carrying one. An error, because the published schema refuses what the spec declares, and
269
+ * no validator can agree with both.
270
+ */
271
+ "sealed-base-extended": {
272
+ severity: "error",
273
+ messages: {
274
+ default: paramMessage `'${"name"}' extends '${"base"}', which '@typespec/openapi3' seals under 'seal-object-schemas' because it does not count a template instance as a derived model. The document publishes '${"name"}' as 'allOf' the sealed '${"base"}', so it refuses every value carrying ${"keys"}, which '${"name"}' adds. Declare it as a model of its own with 'model ... is ${"name"}', which openapi3 counts as derived and so leaves '${"base"}' open.`,
275
+ },
276
+ },
277
+ /**
278
+ * **A redeclared property no one validator can hold to both of its declarations.**
279
+ *
280
+ * openapi3 publishes a subtype as `allOf` its base, so a property the subtype redeclares must meet
281
+ * every declaration of it. Where the subtype's narrows the base's - a derived scalar, a model that
282
+ * extends it, a literal the base admits - the base's constraints are applied to the subtype's
283
+ * value. Where it does not - a model that only looks like the base's, a scalar that is not one of
284
+ * the base's - the value is held to two schemas, and intersecting them in Zod merged sealed
285
+ * objects' keys and threw on two defaults. Refused rather than read some other way.
286
+ */
287
+ "unrepresentable-override": {
288
+ severity: "error",
289
+ messages: {
290
+ default: paramMessage `'${"model"}' redeclares '${"name"}', which '${"base"}' declares as '${"declared"}', and ${"why"}: the document holds the value to both, and no one validator here can. Declare '${"name"}' on '${"model"}' as '${"declared"}', or as a type derived from it.`,
291
+ recursive: paramMessage `'${"model"}.${"name"}' is held to '${"base"}.${"name"}' as well, through 'allOf', and merging the two declarations refers back to itself. This emitter declares no recursive merge, so the value is refused rather than validated against one of the two. Declare '${"name"}' on '${"model"}' as '${"declared"}', or as a type derived from it.`,
292
+ },
293
+ },
223
294
  "unknown-key-vocabulary": {
224
295
  severity: "error",
225
296
  messages: {
@@ -287,14 +358,25 @@ const diagnostics = {
287
358
  },
288
359
  },
289
360
  /**
361
+ * **A constraint `@encode` has moved onto a JSON type it does not apply to.**
362
+ *
363
+ * openapi3 applies a property's or a scalar's constraints and then lets the encoding replace
364
+ * `type`, so `@encode(string)` on a numeric property publishes `minimum` beside `type: string`,
365
+ * and `@encode("x", int32)` on a string publishes `pattern` beside `type: integer`. JSON Schema
366
+ * ignores each there, so the document asserts nothing, and the validator drops it too: applied,
367
+ * a numeric bound on text became a LENGTH check, and `.regex()` on an integer or `.min()` on a
368
+ * boolean threw when the module loaded. A default of the declared type under an encoding that
369
+ * changes it is the same case: it is no value of the encoded type.
370
+ *
290
371
  * **A diagnostic, not a linter rule**, because here the emitter cannot do what the spec asks: it
291
- * drops a bound the author wrote. That is the case TypeSpec reserves for `$onValidate`-style
292
- * diagnostics, where a linter is for a program that is correct but could be better.
372
+ * drops a rule the author wrote. That is the case TypeSpec reserves for `$onValidate`-style
373
+ * diagnostics, where a linter is for a program that is correct but could be better. It replaces
374
+ * `unenforceable-encoded-bound`, which reported numeric bounds on text and nothing else.
293
375
  */
294
- "unenforceable-encoded-bound": {
376
+ "unenforceable-encoded-constraint": {
295
377
  severity: "warning",
296
378
  messages: {
297
- default: paramMessage `The bound on '${"name"}' cannot be enforced, and is not emitted. '@encode(string)' makes the wire value text, so '@minValue'/'@maxValue' would become a check on how many CHARACTERS it has - '@minValue(10)' would refuse '"42"' - and the exclusive form has no string equivalent at all. The document is no help either: it publishes 'minimum' on a 'type: string' schema, which JSON Schema ignores, so a validator enforcing the bound would refuse what the published contract accepts. Declare the bound as a '@pattern', which the document publishes and every caller can read, or remove '@encode(string)' and let the value be a number.`,
379
+ default: paramMessage `${"keywords"} on '${"name"}' is not emitted: '@encode' makes the wire value a JSON ${"type"}, and ${"keywords"} is written for the declared type. '@typespec/openapi3' publishes it beside 'type: ${"type"}', where JSON Schema ignores a keyword meant for another type and a default is no value of it, so a validator applying it would disagree with the published contract. Declare the rule in a form the encoded value carries, such as a '@pattern' on a string, or remove '@encode'.`,
298
380
  },
299
381
  },
300
382
  /**
@@ -303,7 +385,7 @@ const diagnostics = {
303
385
  * Zod skips a declared `__proto__` key on purpose, so prototype pollution cannot reach through a
304
386
  * parse (colinhacks/zod#6386): a value of any type, or none, is accepted for it and dropped from
305
387
  * the result. The document publishes it like any other property, and nothing emitted can enforce
306
- * it - which is the case `unenforceable-encoded-bound` reports for a bound, for the same reason:
388
+ * it - which is the case `unenforceable-encoded-constraint` reports for a constraint, for the same reason:
307
389
  * the spec is representable, so refusing it would make it emit under one emitter and not the
308
390
  * other, and serving it silently would leave a validator looser than its contract unsaid.
309
391
  *
@@ -381,21 +463,77 @@ const diagnostics = {
381
463
  },
382
464
  },
383
465
  /**
384
- * **An `@encode` chain that returns to where it started never reaches a wire type.**
385
- *
386
- * `@encode("x", Pong) scalar Ping` and `@encode("x", Ping) scalar Pong` compile cleanly - the
387
- * compiler checks neither the encoding's name nor where its type leads - and the walks here
388
- * followed `@encode` by calling themselves on the encoded type. Measured before this existed:
389
- * `RangeError: Maximum call stack size exceeded`, "Emitter crashed! This is a bug."
390
- *
391
- * **An error, as `inline-cycle` is.** A value of such a scalar has no form on the wire to validate,
392
- * so there is no validator to emit. openapi3 reads one link of the chain and publishes a schema
393
- * with no `type` at all - `{ format: "x" }` - which states nothing about the value either.
466
+ * **A body declared `never`.** The type has no values, so the body cannot exist, and
467
+ * `@typespec/openapi3` - whose body paths test only for `void` - publishes it as
468
+ * `content: { "application/json": { schema: {} } }`, any value at all. A validator could agree
469
+ * with the spec or with the document, not both, so the declaration is refused. `void` declares no
470
+ * body, and the document publishes it as none.
471
+ */
472
+ "never-body": {
473
+ severity: "error",
474
+ messages: {
475
+ default: paramMessage `'${"operationId"}' declares a ${"position"} body of type 'never', which has no values, and '@typespec/openapi3' publishes it as any JSON value. Replace it with 'void' to declare no body.`,
476
+ },
477
+ },
478
+ /**
479
+ * **A `null` the OpenAPI 3.0 document cannot publish.** OAS 3.0.4: `nullable` "only takes effect if
480
+ * `type` is explicitly defined within the same Schema Object", and "Other Schema Object constraints
481
+ * retain their defined behavior, and therefore may disallow the use of `null`". `@typespec/openapi3`
482
+ * publishes `Inner | null` as `{ type: object, allOf: [Inner], nullable: true }`, `Color | null` as
483
+ * `{ allOf: [Color], nullable: true }` and `"a" | "b" | null` as `{ type: string, enum: [a, b],
484
+ * nullable: true }`, and each refuses `null` by that text; a bare `null` is `{ nullable: true }`,
485
+ * any value. Refused where openapi3 publishes 3.0, since a validator agreeing with the spec's
486
+ * `| null` disagrees with the document; at 3.1 each is `anyOf` with `{ type: "null" }`.
487
+ */
488
+ "unpublishable-null": {
489
+ severity: "error",
490
+ messages: {
491
+ default: paramMessage `'${"type"}' admits null, and the OpenAPI 3.0 document '@typespec/openapi3' publishes does not: ${"why"}. Set 'openapi-versions: ["3.1.0"]' under '@typespec/openapi3' in tspconfig.yaml's options, whether or not it is emitted, to publish OpenAPI 3.1, which says null.`,
492
+ },
493
+ },
494
+ /**
495
+ * **A sealed model whose base is a record, where openapi3 publishes 3.0.** openapi3's 3.0 sealing
496
+ * is `additionalProperties: { not: {} }` beside the model's own `properties`, which cannot see the
497
+ * record its base declares in `allOf`, so the document refuses every key the record admits; 3.1
498
+ * seals with `unevaluatedProperties`, which can.
499
+ */
500
+ "unpublishable-record": {
501
+ severity: "error",
502
+ messages: {
503
+ default: paramMessage `'${"type"}' extends the record '${"base"}' and is sealed, and the OpenAPI 3.0 document '@typespec/openapi3' publishes seals it with 'additionalProperties: { not: {} }', which does not see the record in its 'allOf': every key the record admits is refused. Set 'openapi-versions: ["3.1.0"]' under '@typespec/openapi3', which seals with 'unevaluatedProperties', or spread the record onto the model, '...${"base"}', which is published open.`,
504
+ },
505
+ },
506
+ /**
507
+ * **A required read-only property in a request, where openapi3 publishes 3.0 and a later version.**
508
+ * OAS 3.0.4 says such a `required` "will take effect on the response only"; 3.1 made `readOnly`
509
+ * an annotation, so `required` holds in a request too. One request validator cannot agree with both
510
+ * documents.
394
511
  */
395
- "encode-cycle": {
512
+ "ambiguous-read-only": {
396
513
  severity: "error",
397
514
  messages: {
398
- default: paramMessage `'${"type"}' is encoded as ${"chain"}: the '@encode' chain returns to a scalar it already passed, so it never reaches a type that crosses the wire. Encode one of them as a standard scalar such as 'string', or break the cycle.`,
515
+ default: paramMessage `'${"type"}' is sent in a request and holds the required read-only property '${"property"}', and '@typespec/openapi3' publishes OpenAPI 3.0, where a request may leave it out, and ${"later"}, where it must send it: a request validator agrees with only one. Set one version in 'openapi-versions', or declare the request's own view of it, '${"view"}', which openapi3 publishes without it.`,
516
+ },
517
+ },
518
+ /**
519
+ * **An `@encode` whose published schema no value satisfies, or which states no type.**
520
+ *
521
+ * `@typespec/openapi3` keeps the declared type beside the encoded one, so `@encode("x", string)`
522
+ * on a model publishes `{ $ref: Filter, type: "string" }`: an object and a string at once, which
523
+ * nothing is. The same holds for a list model, an integer enum, or a string scalar encoded as
524
+ * `int32`. An encoding to a scalar outside the standard library publishes no `type` at all, which
525
+ * is also how two scalars whose `@encode` name each other once overflowed the stack. This emitter
526
+ * validated the encoded scalar beside each, so a value the document refuses was accepted, or one
527
+ * it accepts refused.
528
+ *
529
+ * **An error, as `undeclared-discriminator` is**: the published contract is not one a validator
530
+ * can agree with, so the declaration is refused rather than read some other way, and the property
531
+ * validates nothing until the spec says what it means.
532
+ */
533
+ "unsatisfiable-encoding": {
534
+ severity: "error",
535
+ messages: {
536
+ default: paramMessage `'${"name"}' is declared '${"declared"}' and encoded as '${"encoded"}': ${"why"}. Encode it as a standard scalar its declared type admits, or remove '@encode'.`,
399
537
  },
400
538
  },
401
539
  /**
@@ -412,16 +550,67 @@ const diagnostics = {
412
550
  * validated as both declarations at once, and the contract types still compile. A server emitter
413
551
  * reading `EmittedRoute.sharedInputKeys` declines to mount the operation.
414
552
  */
553
+ /**
554
+ * **Two header parameters whose names differ only in case**, which HTTP reads as one header and
555
+ * openapi3 publishes as two parameters of that one value. Refused; see `headerCaseCollisionOf`.
556
+ */
557
+ "header-case-collision": {
558
+ severity: "error",
559
+ messages: {
560
+ default: paramMessage `'${"operation"}' declares the header '${"first"}' and the header '${"second"}', which HTTP reads as one header - a header's name is case-insensitive - and openapi3 publishes as two parameters of the one value it carries. Neither is validated. Give one of them a different name.`,
561
+ },
562
+ },
415
563
  "shared-input-key": {
416
564
  severity: "warning",
417
565
  messages: {
418
566
  default: paramMessage `'${"operation"}' takes the ${"first"} '${"key"}' and the ${"second"} '${"key"}', and a handler's input holds one value under each key, so '${"key"}' is stated as both declarations at once and accepts only a value satisfying both. Give one of them a different name.`,
567
+ repeated: paramMessage `'${"operation"}' takes the ${"first"} '${"key"}' twice, and the document publishes only the first: the wire carries one value, which is validated and typed as the first declaration, and the second states nothing. Give one of them a different name.`,
568
+ },
569
+ },
570
+ /**
571
+ * **A schema `@typespec/openapi3` publishes with its properties and its `required` read at two
572
+ * visibilities.** openapi3 writes a component from the first reference that reaches it; where that
573
+ * reference is a request's, and the model has nothing to reshape there, its `required` is read at
574
+ * `Read` and its properties at the request's visibility - a read-only property is then required and
575
+ * not declared, or, optional, left out - and every use of the component shares it. Refused, by the
576
+ * maintainer's decision, rather than validated against a schema the document does not hold. See
577
+ * `publicationOf` in `published.ts` for the measured mechanism.
578
+ */
579
+ "split-visibility": {
580
+ severity: "error",
581
+ messages: {
582
+ default: paramMessage `'@typespec/openapi3' publishes '${"name"}' as ${"via"} first reaches it: there it reads the properties of ${"model"} at '${"at"}' and its 'required' at 'Read', so ${"property"} is ${"effect"}, in every use of '${"name"}'. Refused rather than validated against a schema the document does not hold. Use the request's own view, which openapi3 publishes apart: ${"remedy"}.`,
583
+ inherited: paramMessage `'${"name"}' holds ${"model"} through 'allOf', and '@typespec/openapi3' publishes ${"model"} as ${"via"} first reaches it: there it reads the properties of ${"model"} at '${"at"}' and its 'required' at 'Read', so ${"property"} is ${"effect"}, in every model holding it. Refused rather than validated against a schema the document does not hold. Use the request's own view, which openapi3 publishes apart: ${"remedy"}.`,
584
+ inline: paramMessage `'@typespec/openapi3' publishes ${"model"} with its properties read at '${"at"}' and its 'required' at 'Read', so ${"property"} is ${"effect"}. Refused rather than validated against a schema the document does not hold. Use the request's own view, which openapi3 publishes apart: ${"remedy"}.`,
585
+ },
586
+ },
587
+ /**
588
+ * **A `@oneOf` body sent as a URL-encoded form, where a member may read one text two ways and
589
+ * another member accepts one and not the other.** The document accepts the form where some
590
+ * reading of its text matches exactly one member, and which reading that is depends on every key
591
+ * and member - finding it means trying every combination, exponential in the keys, and a record's
592
+ * keys are the sender's to choose. Refused rather than validated by a reading the document need
593
+ * not take. See `ambiguousFormKeyOf`.
594
+ */
595
+ "ambiguous-form-reading": {
596
+ severity: "error",
597
+ messages: {
598
+ default: paramMessage `'${"operation"}' accepts the '@oneOf' '${"union"}' as a URL-encoded form, where '${"member"}' may read the text of ${"key"} as ${"first"} and as ${"second"}, and '${"other"}' accepts only the first. The document takes the form where some reading of its text matches exactly one member, and which reading that is depends on every key and member, so the body is not validated. Give ${"key"} one reading in '${"member"}', declare it alike in '${"other"}', or publish the union as 'anyOf' by removing '@oneOf'.`,
419
599
  },
420
600
  },
421
601
  "duplicate-declaration": {
422
602
  severity: "error",
423
603
  messages: {
424
604
  default: paramMessage `Two different types are both named '${"name"}', and a TypeScript module cannot declare one name twice. The document tells them apart by namespace; this file cannot. Give one of them a different name, or set an explicit one with '@friendlyName'.`,
605
+ renamed: paramMessage `Two different types, '${"first"}' and '${"second"}', are both declared as '${"name"}': a character an identifier cannot hold is written '_', and a TypeScript module cannot declare one name twice, so neither is validated. Give one of them a different name, or set an explicit one with '@friendlyName'.`,
606
+ multipart: paramMessage `'${"name"}' is a multipart body and the name of a schema that differs from it - the same model sent as JSON is one: openapi3 publishes one component under the name, and a TypeScript module cannot declare one name twice, so neither is validated. Send the multipart body as a model of its own.`,
607
+ envelope: paramMessage `'${"name"}' is the name openapi3 gives an envelope of a '@discriminated' union, after the union and the variant, and the name of another type: a TypeScript module cannot declare one name twice, so neither is validated. Rename the type, the union or the variant.`,
608
+ instances: paramMessage `Two instances of the template '${"name"}' are both named '${"name"}', as openapi3 names a union instance after its template alone: it refuses its own document for them as 'duplicate-type-name', and a TypeScript module cannot declare one name twice, so neither is validated. Declare a named union for each, or set an explicit name with '@friendlyName'.`,
609
+ binary: paramMessage `'${"name"}' holds 'bytes' and is sent as a multipart part, where openapi3 publishes 'bytes' as any value, and used elsewhere, where it is a base64 string: openapi3 publishes both under the name and refuses its own document as 'duplicate-type-name', and a TypeScript module cannot declare one name twice, so neither is validated. Give the part a union of its own.`,
610
+ cased: paramMessage `Two different types, '${"first"}' and '${"second"}', are both validated by '${"name"}': a validator is named for its type with the first letter lowered, and a TypeScript module cannot declare one name twice, so neither is validated. Give one of them a different name, or set an explicit one with '@friendlyName'.`,
611
+ suffixed: paramMessage `'${"name"}' is the name openapi3 gives '${"first"}' at '${"at"}', a type it suffixes for being used at more than one visibility, and the name of '${"second"}': openapi3 refuses its own document for it as 'duplicate-type-name', and a TypeScript module cannot declare one name twice, so neither is validated. Rename '${"second"}', or set an explicit name with '@friendlyName'.`,
612
+ visibility: paramMessage `'${"name"}' is the document's one name for two different schemas of one model, at '${"first"}' and at '${"second"}': openapi3 publishes both under it and refuses its own document as 'duplicate-type-name', and this module cannot declare one name twice, so neither is validated. ${"remedy"}`,
613
+ context: paramMessage `'${"name"}' is published inside an explicit '@body', where '@typespec/http' ignores its metadata annotations and the document holds them as data, and outside one, where they are metadata: two shapes under one name, which '@typespec/openapi3' refuses too, as 'duplicate-type-name'. Mark the body '@bodyRoot', or give the body a model of its own.`,
425
614
  },
426
615
  },
427
616
  };
@@ -0,0 +1,20 @@
1
+ import type { Program } from "@typespec/compiler";
2
+ /**
3
+ * **The OpenAPI versions `@typespec/openapi3` publishes from this compile**: its `openapi-versions`
4
+ * option, read from the compile's options as `typespec-openapi-document` reads it, and openapi3's own
5
+ * default, `3.0.0`, where nothing sets it - whether or not openapi3 is emitted, since a project
6
+ * serving no document is still described by the one openapi3 would write.
7
+ *
8
+ * **The versions are two dialects, and a validator agrees with the one published.** 0.31.0 read every
9
+ * schema as OpenAPI 3.1 reads it while openapi3 published 3.0 by default, and the two answer one
10
+ * schema differently in two places: a required read-only property is required "on the response only"
11
+ * at 3.0 and in both directions at 3.1, and 3.0's `nullable` takes effect only beside `type` - see
12
+ * {@link publishesOpenApi30Only} and `unpublishable-null`.
13
+ */
14
+ export declare function openApiVersionsOf(program: Program): readonly string[];
15
+ /** Whether every document openapi3 publishes here is OpenAPI 3.0, which reads a request as 3.0 does. */
16
+ export declare function publishesOpenApi30Only(program: Program): boolean;
17
+ /** Whether openapi3 publishes an OpenAPI 3.0 document here, beside any other. */
18
+ export declare function publishesOpenApi30(program: Program): boolean;
19
+ /** Whether openapi3 publishes OpenAPI 3.0 and a later version here, two documents a request reads apart. */
20
+ export declare function publishesOpenApi30AndLater(program: Program): boolean;
@@ -0,0 +1,31 @@
1
+ /**
2
+ * **The OpenAPI versions `@typespec/openapi3` publishes from this compile**: its `openapi-versions`
3
+ * option, read from the compile's options as `typespec-openapi-document` reads it, and openapi3's own
4
+ * default, `3.0.0`, where nothing sets it - whether or not openapi3 is emitted, since a project
5
+ * serving no document is still described by the one openapi3 would write.
6
+ *
7
+ * **The versions are two dialects, and a validator agrees with the one published.** 0.31.0 read every
8
+ * schema as OpenAPI 3.1 reads it while openapi3 published 3.0 by default, and the two answer one
9
+ * schema differently in two places: a required read-only property is required "on the response only"
10
+ * at 3.0 and in both directions at 3.1, and 3.0's `nullable` takes effect only beside `type` - see
11
+ * {@link publishesOpenApi30Only} and `unpublishable-null`.
12
+ */
13
+ export function openApiVersionsOf(program) {
14
+ const options = program.compilerOptions.options?.["@typespec/openapi3"];
15
+ const versions = options?.["openapi-versions"];
16
+ return versions === undefined || versions.length === 0 ? ["3.0.0"] : versions;
17
+ }
18
+ const is30 = (version) => version.startsWith("3.0.");
19
+ /** Whether every document openapi3 publishes here is OpenAPI 3.0, which reads a request as 3.0 does. */
20
+ export function publishesOpenApi30Only(program) {
21
+ return openApiVersionsOf(program).every(is30);
22
+ }
23
+ /** Whether openapi3 publishes an OpenAPI 3.0 document here, beside any other. */
24
+ export function publishesOpenApi30(program) {
25
+ return openApiVersionsOf(program).some(is30);
26
+ }
27
+ /** Whether openapi3 publishes OpenAPI 3.0 and a later version here, two documents a request reads apart. */
28
+ export function publishesOpenApi30AndLater(program) {
29
+ const versions = openApiVersionsOf(program);
30
+ return versions.some(is30) && versions.some((version) => !is30(version));
31
+ }
@@ -0,0 +1,139 @@
1
+ import { type Model, type ModelProperty, type Namespace, type Program, type Type } from "@typespec/compiler";
2
+ import { Visibility, type HttpOperation } from "@typespec/http";
3
+ export declare function withServiceView<T>(program: Program, service: Namespace, run: () => T): T;
4
+ /** The same scope, held across an `await` - returns what puts the previous one back. */
5
+ export declare function beginServiceView(program: Program, service: Namespace): () => void;
6
+ /**
7
+ * **The types of the service no operation reaches, which openapi3 publishes as components all the
8
+ * same** - its `processUnreferencedSchemas`, with `omitUnreachableTypes` unset: every model, enum and
9
+ * union declared in the service namespace and below it (only the service's own, where the service is
10
+ * the global namespace), but not one an operation reaches, a model whose properties were spread as
11
+ * parameters (published as parameters), a type openapi3 inlines, an HTTP file, or an authentication
12
+ * scheme's model. Rendered at `All` by the caller, as openapi3 renders them. A scalar is validated
13
+ * inline wherever it is used, reached or not, so an unreached one has nothing to declare.
14
+ */
15
+ export declare function unreachedTypes(): readonly Type[];
16
+ /** Every type the service publishes, reached by an operation or not - read as {@link usageOf} reads it. */
17
+ export declare function publishedTypes(): Iterable<Type>;
18
+ /** Whether openapi3 suffixes this type's component with the visibility it is rendered at. */
19
+ export declare function isSuffixedByUsage(type: Type): boolean;
20
+ /**
21
+ * An operation's body, part or parameter a walk starts from: `where`, as the diagnostic says it, and
22
+ * `root`, the name of its type where it has one - wrapped in a visibility template, as
23
+ * `Create<Outer>`, the request is published from a model of its own and leaves the shared ones alone.
24
+ */
25
+ interface Via {
26
+ readonly where: string;
27
+ readonly root: string | undefined;
28
+ /** Whether the root is a model, which a visibility template such as `Create<...>` takes. */
29
+ readonly model: boolean;
30
+ /** The root itself, where it is a named model or union - see {@link viewRemedyOf}. */
31
+ readonly type?: Type;
32
+ }
33
+ /**
34
+ * **The visibility openapi3 reads a model's own properties at, where it publishes the model at
35
+ * `at`: the visibility the reference it writes the schema from arrived at** - see
36
+ * {@link publicationOf}. Where nothing reshapes the model there, the two are one schema - anything
37
+ * else is `split-visibility` - but a type a property refers to is reached at the first, and a list
38
+ * or a union instance is not reduced: `Mid { tags: Tags }` first sent in a create body and also
39
+ * returned is published as `Mid`, its `tags` referring to `TagsCreate`. A component is read as
40
+ * `components.schemas` keeps it, the later of two metadata contexts; a model written out where it
41
+ * is used, as the context `ignore` writes it. Where no reference wrote it, at `at`.
42
+ */
43
+ export declare function readVisibilityOf(program: Program, model: Model, at: Visibility, ignore: boolean): Visibility;
44
+ /**
45
+ * **The visibility of the component the document keeps for a type rendered at `at`.** Where the type
46
+ * is published at several visibilities under one name - unsuffixed, as openapi3 names a type it
47
+ * counts at one - `components.schemas` keeps the one written last, and every `$ref` to the name is
48
+ * to it. `K extends P`, written at `All` by the pass over types no operation reaches and at `Read` by
49
+ * `P`'s discriminator mapping, is `K` at `Read` in the document; `G extends K`, written out from
50
+ * `K` at `All`, required a create-only `s` the `K` it refers to leaves out. Elsewhere, `at`.
51
+ */
52
+ export declare function keptVisibilityOf(type: Type, at: Visibility): Visibility;
53
+ /**
54
+ * **Every component openapi3 writes, at the visibility it publishes it at, in the metadata context
55
+ * `components.schemas` keeps** - declared after the routes, so a component no route renders by name
56
+ * is declared all the same. A model a merge holds is written into the merge here, and openapi3
57
+ * publishes it as a component of its own: `L1 extends L0 { m: B }` and `L2 extends L1 { m: Sub }`
58
+ * publish `B` and `Sub`, which had no validator.
59
+ */
60
+ export declare function publishedComponents(program: Program): readonly {
61
+ readonly type: Type;
62
+ readonly at: Visibility;
63
+ readonly ignore: boolean;
64
+ readonly binary: boolean;
65
+ }[];
66
+ /**
67
+ * **The visibility openapi3 publishes a parameter at, where it is not the operation's.** A parameter
68
+ * spread from a named model is published once, as a shared parameter component, from the first
69
+ * operation that sends it, and every later one refers to it: `...CommonA` sent by a GET and then by a
70
+ * POST is read at `Query` for both. Read at each operation's own, the POST was held to a create view
71
+ * the document does not publish for it.
72
+ */
73
+ /**
74
+ * Whether openapi3 publishes a shared route's parameter optional though it is declared required -
75
+ * see `resolveSharedRouteParameters` - asked of the declaration, before or after openapi3 sets its
76
+ * `optional`, with one answer.
77
+ */
78
+ export declare function isSharedRouteOptional(property: ModelProperty): boolean;
79
+ export declare function parameterVisibilityOf(property: ModelProperty): Visibility | undefined;
80
+ /**
81
+ * **The visibility openapi3 publishes an operation's request at.** Its own, unless it shares its
82
+ * verb and path with another: openapi3 publishes one operation for the route, every body and
83
+ * parameter at the first one's visibility, and reports `inconsistent-shared-route-request-visibility`
84
+ * where they differ - graded against what it publishes, as the maintainer decided.
85
+ */
86
+ export declare function requestVisibilityOf(program: Program, operation: HttpOperation): Visibility;
87
+ /**
88
+ * **How openapi3's schema for a model differs from the schema of that model at the visibility it is
89
+ * rendered at.** openapi3 reads a model's properties at the visibility its first reference arrived
90
+ * at - `document` here - and its `required` at the one it reduces that to; the schema it means, and
91
+ * the one rendered here, reads both at the reduced one - `rendered`. Where no reduction intervenes
92
+ * they are one schema. Only the model's own properties are compared: a type a property refers to has
93
+ * a first reference of its own, and is judged by it where it is rendered - a component wherever it
94
+ * is declared, and a model expression where it is written out, as openapi3 writes it out from its
95
+ * cache. A base is part of the model's schema, through `allOf`.
96
+ */
97
+ export interface Misreading {
98
+ /** The model whose properties openapi3 reads at {@link at}: the one judged, or a base of it. */
99
+ readonly model: Model;
100
+ /** The property its schema gets wrong. */
101
+ readonly property: ModelProperty;
102
+ /** The visibility openapi3 reads the properties at. */
103
+ readonly at: Visibility;
104
+ /** What openapi3 publishes for the property. */
105
+ readonly effect: "undeclared" | "dropped" | "added" | "reshaped";
106
+ /** Where the schema it is found in was first reached from. */
107
+ readonly via: Via;
108
+ }
109
+ /**
110
+ * **Whether the schema rendered for `type` here is one openapi3 publishes differently - and if so,
111
+ * `split-visibility`, an error, reported once per declaration.** Refused, as the maintainer decided,
112
+ * rather than validated against a schema the document does not hold: the document's `required` names
113
+ * a property its properties leave out, which no value satisfies under `seal-object-schemas`, or it
114
+ * leaves out an optional one, which a sealed schema then refuses.
115
+ *
116
+ * `declared` is the component's name, for a type published as a component; `type` is then its
117
+ * canonical type and `at` the visibility it is declared at. Otherwise `type` is written out where it
118
+ * is used - a model expression, an inlined template instance - and judged by the emission openapi3
119
+ * writes out in the metadata context `ignore`.
120
+ */
121
+ export declare function isSplitByVisibility(program: Program, type: Type, at: Visibility, ignore: boolean, declared: string | undefined): boolean;
122
+ /**
123
+ * Whether openapi3 publishes the component of `type` at `at` split - the refusal
124
+ * {@link isSplitByVisibility} reports, asked without reporting it. A dispatch leaves such a subtype
125
+ * out, as it leaves out one no value is: its validator is `z.never()`, which no
126
+ * `z.discriminatedUnion` can hold.
127
+ */
128
+ export declare function isPublishedSplit(program: Program, type: Type, at: Visibility): boolean;
129
+ /** The standard template that is a model's view at a request visibility, where there is one. */
130
+ /**
131
+ * **The view a remedy names, declared so that it works where it is pasted**: a model of its own,
132
+ * `model OuterCreate is Create<Ns.Outer>;`, with the type spelled from the global namespace. Named
133
+ * `Create<Outer>`, the view inherited `Outer`'s `@friendlyName` and collided with it; spelled by the
134
+ * friendly name, or without its namespace from a sub-namespace, it named nothing; and sent through
135
+ * a spread, `...Create<O1>`, it left the body the document publishes as it was.
136
+ */
137
+ export declare function viewRemedyOf(template: string, type: Type): string;
138
+ export declare function viewTemplateOf(at: Visibility): string | undefined;
139
+ export {};