typespec-http-zod 0.30.0 → 0.31.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -0
- package/dist/src/api.d.ts +31 -0
- package/dist/src/api.js +1443 -360
- package/dist/src/constraints.d.ts +31 -2
- package/dist/src/constraints.js +242 -46
- package/dist/src/lib.d.ts +136 -13
- package/dist/src/lib.js +167 -18
- package/dist/src/published.d.ts +139 -0
- package/dist/src/published.js +798 -0
- package/dist/src/registry.d.ts +77 -45
- package/dist/src/registry.js +759 -198
- package/dist/src/types.d.ts +12 -2
- package/dist/src/types.js +267 -92
- package/dist/src/uri-template.d.ts +110 -8
- package/dist/src/uri-template.js +251 -46
- package/dist/src/zod.d.ts +715 -39
- package/dist/src/zod.js +4029 -547
- package/package.json +1 -1
package/dist/src/registry.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type ModelProperty, type Program, type Type } from "@typespec/compiler";
|
|
2
2
|
import { Visibility } from "@typespec/http";
|
|
3
|
+
import { type HeldDeclarations, type PropertyPosition } from "./zod.js";
|
|
3
4
|
/**
|
|
4
5
|
* Assigns each named TypeSpec model/enum a single Zod declaration, emitted in dependency order.
|
|
5
6
|
*
|
|
@@ -47,6 +48,10 @@ interface Declaration {
|
|
|
47
48
|
readonly identifier: string;
|
|
48
49
|
readonly typeName: string;
|
|
49
50
|
readonly source: string;
|
|
51
|
+
/** The type declared, to tell one type in two contexts from two types of one name. */
|
|
52
|
+
readonly type?: Type;
|
|
53
|
+
/** The visibility it is declared at, to tell one type at two visibilities from two contexts. */
|
|
54
|
+
readonly at?: Visibility;
|
|
50
55
|
/**
|
|
51
56
|
* The structural TypeScript body for a declaration on a cycle, emitted INSTEAD of the
|
|
52
57
|
* `z.infer<typeof ...>` alias - which cannot be used here without closing the type-level loop.
|
|
@@ -54,47 +59,20 @@ interface Declaration {
|
|
|
54
59
|
readonly structural?: string;
|
|
55
60
|
/** Whether the declaration is annotated `: z.ZodType<typeName>`, which `z.lazy()` requires. */
|
|
56
61
|
readonly annotated?: boolean;
|
|
62
|
+
/** The registry key it was declared under, and its metadata context, to render it again. */
|
|
63
|
+
readonly key?: string;
|
|
64
|
+
readonly ignore?: boolean;
|
|
65
|
+
/** A shape no type stands for, and the type it is published for - see `declareNamed`. */
|
|
66
|
+
readonly synthetic?: Synthetic;
|
|
67
|
+
/** The keys a merge in it asked to be refused, to render it again when one is - see `refusedAt`. */
|
|
68
|
+
readonly consulted?: ReadonlySet<string>;
|
|
57
69
|
}
|
|
58
|
-
/**
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
* reason. Keyed on the type alone, whichever position was walked first won and every other position
|
|
65
|
-
* silently got its shape - measured on the corpus, one strict model carrying all six lifecycle
|
|
66
|
-
* properties, so a create request was required to send read-only fields.
|
|
67
|
-
*
|
|
68
|
-
* The suffix rule is openapi3's: canonical (`Read`) keeps the bare name, and another visibility only
|
|
69
|
-
* earns a suffix when the model is actually TRANSFORMED at it. Same rule, so the identifiers line up
|
|
70
|
-
* with the component names the document publishes.
|
|
71
|
-
*/
|
|
72
|
-
/**
|
|
73
|
-
* The visibility a type is actually DECLARED at, which is not always the one requested.
|
|
74
|
-
*
|
|
75
|
-
* **A type not reshaped at this visibility IS the canonical declaration, not a copy of it.** Keying
|
|
76
|
-
* on the requested visibility while only suffixing transformed types produced two cache entries
|
|
77
|
-
* sharing one identifier and an emitted file that stopped parsing:
|
|
78
|
-
* `Identifier 'approvalTierSchema' has already been declared`. An enum is the same enum in a request
|
|
79
|
-
* and a response, which is what the `Model` guard is for.
|
|
80
|
-
*
|
|
81
|
-
* It is the same collapse `@typespec/openapi3` performs before naming a component, and
|
|
82
|
-
* `isTransformedBy` is its own predicate, `metadataInfo.isTransformed`.
|
|
83
|
-
*
|
|
84
|
-
* **Extracted so both registries answer it identically.** They must: the emitted assertion pairs a
|
|
85
|
-
* validator with a contract type BY NAME, so a rule applied in one walk and paraphrased in the other
|
|
86
|
-
* is a pairing that silently compares the wrong two things.
|
|
87
|
-
*/
|
|
88
|
-
export declare function visibilityFor(program: Program, type: Type, requested: Visibility): Visibility;
|
|
89
|
-
/**
|
|
90
|
-
* A declaration's name at a visibility: the bare name at canonical, suffixed anywhere else.
|
|
91
|
-
*
|
|
92
|
-
* The suffix is `@typespec/http`'s `getVisibilitySuffix`, called with `Visibility.Read` as the
|
|
93
|
-
* canonical exactly as `@typespec/openapi3` calls it in `schema-emitter.ts`. Same function, same
|
|
94
|
-
* argument, so these identifiers line up with the component names the document publishes rather than
|
|
95
|
-
* merely resembling them.
|
|
96
|
-
*/
|
|
97
|
-
export declare function nameAt(bare: string, at: Visibility): string;
|
|
70
|
+
/** What a declaration no type stands for is: a multipart body, or a `@discriminated` envelope. */
|
|
71
|
+
interface Synthetic {
|
|
72
|
+
readonly kind: "multipart" | "envelope";
|
|
73
|
+
readonly site: Type;
|
|
74
|
+
}
|
|
75
|
+
export declare function nameAt(bare: string, at: Visibility, type: Type): string;
|
|
98
76
|
export declare class SchemaRegistry {
|
|
99
77
|
#private;
|
|
100
78
|
constructor(program: Program);
|
|
@@ -103,15 +81,27 @@ export declare class SchemaRegistry {
|
|
|
103
81
|
* inline source. Declaring is depth-first so a model's dependencies are emitted above it, which is
|
|
104
82
|
* what lets the generated file be plain `const` declarations with no forward references.
|
|
105
83
|
*/
|
|
106
|
-
expressionFor(
|
|
84
|
+
expressionFor(given: Type, payload?: boolean): string;
|
|
85
|
+
/** What the contract types' walk asks of these declarations, so a merge is `never` on both sides. */
|
|
86
|
+
get held(): HeldDeclarations;
|
|
87
|
+
/**
|
|
88
|
+
* Declare a component the document publishes, at the visibility the walk is at, as a type
|
|
89
|
+
* another refers to - see `publishedComponents`. Nothing refers to it here, so a declaration
|
|
90
|
+
* still in flight is no back edge.
|
|
91
|
+
*/
|
|
92
|
+
declare(type: Type): void;
|
|
107
93
|
/**
|
|
108
94
|
* The Zod for a single property, constraints and optionality included.
|
|
109
95
|
*
|
|
110
96
|
* Used for path/query parameters, which are `ModelProperty`s that belong to no model - so they
|
|
111
97
|
* need the property-level walk rather than the type-level one, or `@minLength` on a path param
|
|
112
98
|
* would be documented and never enforced.
|
|
99
|
+
*
|
|
100
|
+
* **The property is where a refusal inside it points**, as a model's member is - see
|
|
101
|
+
* `withDeclarationSite`. A parameter is walked outside any model, so `@query a?: never[]` was
|
|
102
|
+
* reported at `<unknown location>:1:1`, the intrinsic's own place, which names nothing to edit.
|
|
113
103
|
*/
|
|
114
|
-
expressionForProperty(property: ModelProperty): string;
|
|
104
|
+
expressionForProperty(property: ModelProperty, position?: PropertyPosition): string;
|
|
115
105
|
/**
|
|
116
106
|
* A declaration for a shape the spec never wrote, under a name the document publishes.
|
|
117
107
|
*
|
|
@@ -124,9 +114,11 @@ export declare class SchemaRegistry {
|
|
|
124
114
|
* body: the document publishes `MultiPartRequest` as a component, and there is no walkable type
|
|
125
115
|
* for it - the parts have to be assembled from `@typespec/http`'s own view of the payload.
|
|
126
116
|
*/
|
|
127
|
-
declareNamed(name: string, build: () => string): string;
|
|
117
|
+
declareNamed(name: string, build: () => string, site: Type): string;
|
|
128
118
|
/** Every declaration, dependencies first. */
|
|
129
119
|
declarations(): readonly Declaration[];
|
|
120
|
+
/** The names of the declarations that validate nothing, refused here or in the walk beneath. */
|
|
121
|
+
refusedNames(): ReadonlySet<string>;
|
|
130
122
|
}
|
|
131
123
|
export interface TsDeclaration {
|
|
132
124
|
readonly name: string;
|
|
@@ -135,11 +127,42 @@ export interface TsDeclaration {
|
|
|
135
127
|
/** `true` for an enum-derived alias - declared but not exported, see below. */
|
|
136
128
|
readonly isVocabulary: boolean;
|
|
137
129
|
readonly source: string;
|
|
130
|
+
/** The type declared, to tell one type in two contexts from two types of one name. */
|
|
131
|
+
readonly type?: Type;
|
|
132
|
+
/** The visibility it is declared at - see {@link Declaration.at}. */
|
|
133
|
+
readonly at?: Visibility;
|
|
134
|
+
/** A shape no type stands for - see {@link Declaration.synthetic}. */
|
|
135
|
+
readonly synthetic?: Synthetic;
|
|
138
136
|
}
|
|
137
|
+
/**
|
|
138
|
+
* The same walk as {@link SchemaRegistry}, emitting plain TypeScript instead of Zod.
|
|
139
|
+
*
|
|
140
|
+
* Kept as a separate class rather than a mode on the schema registry because the two produce
|
|
141
|
+
* different *kinds* of declaration - an `interface` versus a `const` - and their reference resolvers
|
|
142
|
+
* hand back different text. Sharing {@link declaredNameOf} is the part that matters: a model named in
|
|
143
|
+
* one artefact is named the same way in the other, so the emitted assertion can pair them by name.
|
|
144
|
+
*/
|
|
145
|
+
/**
|
|
146
|
+
* Whether a rendered body is ONE object literal, and so may be emitted as an `interface`.
|
|
147
|
+
*
|
|
148
|
+
* **This was `source.startsWith("{") && !source.includes("} & ")`, which named the two shapes it had
|
|
149
|
+
* met rather than the property it needed.** A discriminated union with an envelope renders as
|
|
150
|
+
* `{ kind: "cat"; value: Cat; } | { kind: "dog"; value: Dog; }`: it starts with a brace and holds no
|
|
151
|
+
* intersection, so it was emitted as `export interface PetWithEnvelope { ... } | { ... }`. An
|
|
152
|
+
* interface cannot be a union, so the file did not parse at all - and that is the DEFAULT envelope,
|
|
153
|
+
* the shape a spec gets from writing `@discriminated` and nothing else.
|
|
154
|
+
*
|
|
155
|
+
* Balanced braces answer the question actually being asked: an interface is valid exactly when the
|
|
156
|
+
* whole body is a single `{...}`, whatever it contains. A composition operator cannot be missed,
|
|
157
|
+
* because anything following the matching close brace fails the test whatever it happens to be.
|
|
158
|
+
*/
|
|
159
|
+
export declare function isSingleObjectLiteral(source: string): boolean;
|
|
139
160
|
export declare class TypeRegistry {
|
|
140
161
|
#private;
|
|
141
|
-
constructor(program: Program);
|
|
142
|
-
expressionFor(
|
|
162
|
+
constructor(program: Program, held?: HeldDeclarations);
|
|
163
|
+
expressionFor(given: Type, payload?: boolean): string;
|
|
164
|
+
/** Declare a component the document publishes, as {@link SchemaRegistry.declare} does. */
|
|
165
|
+
declare(type: Type): void;
|
|
143
166
|
/**
|
|
144
167
|
* The Zod-free type for a single property - used for path and query parameters, which are
|
|
145
168
|
* `ModelProperty`s belonging to no model. Without routing them through the registry a named model
|
|
@@ -147,7 +170,16 @@ export declare class TypeRegistry {
|
|
|
147
170
|
*/
|
|
148
171
|
/** `wireName` names an HTTP parameter as the wire does - see {@link propertyToTs}. */
|
|
149
172
|
expressionForProperty(property: ModelProperty, wireName?: string): string;
|
|
173
|
+
/** A multipart body's type, under the component's name - see {@link SchemaRegistry.declareNamed}. */
|
|
174
|
+
declareNamed(name: string, build: () => string, site: Type): string;
|
|
150
175
|
/** Every declaration, dependencies first. */
|
|
151
176
|
declarations(): readonly TsDeclaration[];
|
|
177
|
+
/**
|
|
178
|
+
* **A contract type whose validator validates nothing is `never` too**, whatever refused it: two
|
|
179
|
+
* shapes under one name, a union this emitter refuses, a model openapi3 publishes split. The wire
|
|
180
|
+
* assertion pairs the two by name, and a contract type stating a shape beside `z.never()` failed
|
|
181
|
+
* it - `interface HBase { name: string }` beside a refused `hBaseSchema`.
|
|
182
|
+
*/
|
|
183
|
+
refuse(names: ReadonlySet<string>): void;
|
|
152
184
|
}
|
|
153
185
|
export {};
|