Skip to content

Commit c6a7e88

Browse files
ianhcoG4brym
andauthored
feat: type inference support for data argument (#130)
Co-authored-by: Gabriel Massadas <[email protected]>
1 parent 1e801cb commit c6a7e88

File tree

7 files changed

+287
-75
lines changed

7 files changed

+287
-75
lines changed

package-lock.json

Lines changed: 12 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
"package": "npm run build && npm pack",
2525
"test:cov": "jest --coverage --no-cache --runInBand",
2626
"addscope": "node config/packagejson name @cloudflare/itty-router-openapi",
27-
"prettify": "prettier --check src tests README.md || (prettier -w src tests README.md; exit 1)",
27+
"prettify": "prettier --check src tests README.md || (prettier -w src tests README.md)",
2828
"lint": "npm run prettify",
2929
"prepare": "husky install",
3030
"test": "jest --no-cache --runInBand --config jestconfig.json --verbose",
@@ -84,7 +84,7 @@
8484
"jest": "^29.0.0",
8585
"jest-openapi": "^0.14.2",
8686
"pinst": "^2.1.6",
87-
"prettier": "^2.4.0",
87+
"prettier": "^3.1.0",
8888
"rollup": "^3.25.1",
8989
"rollup-plugin-bundle-size": "^1.0.3",
9090
"rollup-plugin-copy": "^3.4.0",

src/openapi.ts

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import yaml from 'js-yaml'
2121
export type Route = <
2222
RequestType = IRequest,
2323
Args extends any[] = any[],
24-
RT = OpenAPIRouterType
24+
RT = OpenAPIRouterType,
2525
>(
2626
path: string,
2727
...handlers: (RouteHandler<RequestType, Args> | OpenAPIRouterType | any)[] // TODO: fix this any to be instance of OpenAPIRoute
@@ -34,18 +34,17 @@ export type OpenAPIRouterType<R = Route, Args extends any[] = any[]> = {
3434
} & RouterType<R>
3535

3636
// helper function to detect equality in types (used to detect custom Request on router)
37-
type Equal<X, Y> = (<T>() => T extends X ? 1 : 2) extends <T>() => T extends Y
38-
? 1
39-
: 2
40-
? true
41-
: false
37+
type Equal<X, Y> =
38+
(<T>() => T extends X ? 1 : 2) extends <T>() => T extends Y ? 1 : 2
39+
? true
40+
: false
4241

4342
export function OpenAPIRouter<
4443
RequestType = IRequest,
4544
Args extends any[] = any[],
4645
RouteType = Equal<RequestType, IRequest> extends true
4746
? Route
48-
: UniversalRoute<RequestType, Args>
47+
: UniversalRoute<RequestType, Args>,
4948
>(options?: RouterOptions): OpenAPIRouterType<RouteType, Args> {
5049
const registry: OpenAPIRegistryMerger = new OpenAPIRegistryMerger()
5150

@@ -86,7 +85,7 @@ export function OpenAPIRouter<
8685
return (
8786
route: string,
8887
...handlers: RouteHandler<RequestType, Args>[] &
89-
typeof OpenAPIRoute[] &
88+
(typeof OpenAPIRoute)[] &
9089
OpenAPIRouterType<RouteType, Args>[]
9190
) => {
9291
if (prop !== 'handle') {

src/parameters.ts

Lines changed: 102 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,22 @@ import {
33
ParameterLocation,
44
ParameterType,
55
RegexParameterType,
6-
RouteParameter,
6+
HeaderParameter,
7+
QueryParameter,
8+
PathParameter,
79
StringParameterType,
10+
LegacyParameter,
811
} from './types'
9-
import { z, ZodObject } from 'zod'
12+
import { z } from 'zod'
1013
import { isSpecificZodType, legacyTypeIntoZod } from './zod/utils'
1114
import { extendZodWithOpenApi } from '@asteasolutions/zod-to-openapi'
12-
import { ZodType } from 'zod'
1315

1416
if (z.string().openapi === undefined) {
1517
// console.log('zod extension applied')
1618
extendZodWithOpenApi(z)
1719
}
1820

19-
export function convertParams(field: any, params: any): ZodType {
21+
export function convertParams(field: any, params: any): z.ZodType {
2022
params = params || {}
2123
if (params.required === false)
2224
// @ts-ignore
@@ -60,7 +62,7 @@ export class Obj {
6062
}
6163
}
6264

63-
export class Num {
65+
export const Num: LegacyParameter<z.ZodNumber> = class Num {
6466
static generator = true
6567

6668
constructor(params?: ParameterType) {
@@ -71,9 +73,9 @@ export class Num {
7173
type: 'number',
7274
})
7375
}
74-
}
76+
} as unknown as LegacyParameter<z.ZodNumber>
7577

76-
export class Int {
78+
export const Int: LegacyParameter<z.ZodNumber> = class Int {
7779
static generator = true
7880

7981
constructor(params?: ParameterType) {
@@ -84,17 +86,17 @@ export class Int {
8486
type: 'integer',
8587
})
8688
}
87-
}
89+
} as unknown as LegacyParameter<z.ZodNumber>
8890

89-
export class Str {
91+
export const Str: LegacyParameter<z.ZodString> = class Str {
9092
static generator = true
9193

9294
constructor(params?: StringParameterType) {
9395
return convertParams(z.string(), params)
9496
}
95-
}
97+
} as unknown as LegacyParameter<z.ZodString>
9698

97-
export class DateTime {
99+
export const DateTime: LegacyParameter<z.ZodString> = class DateTime {
98100
static generator = true
99101

100102
constructor(params?: ParameterType) {
@@ -105,9 +107,9 @@ export class DateTime {
105107
params
106108
)
107109
}
108-
}
110+
} as unknown as LegacyParameter<z.ZodString>
109111

110-
export class Regex {
112+
export const Regex: LegacyParameter<z.ZodString> = class Regex {
111113
static generator = true
112114

113115
constructor(params: RegexParameterType) {
@@ -117,25 +119,25 @@ export class Regex {
117119
params
118120
)
119121
}
120-
}
122+
} as unknown as LegacyParameter<z.ZodString>
121123

122-
export class Email {
124+
export const Email: LegacyParameter<z.ZodString> = class Email {
123125
static generator = true
124126

125127
constructor(params?: ParameterType) {
126128
return convertParams(z.string().email(), params)
127129
}
128-
}
130+
} as unknown as LegacyParameter<z.ZodString>
129131

130-
export class Uuid {
132+
export const Uuid: LegacyParameter<z.ZodString> = class Uuid {
131133
static generator = true
132134

133135
constructor(params?: ParameterType) {
134136
return convertParams(z.string().uuid(), params)
135137
}
136-
}
138+
} as unknown as LegacyParameter<z.ZodString>
137139

138-
export class Hostname {
140+
export const Hostname: LegacyParameter<z.ZodString> = class Hostname {
139141
static generator = true
140142

141143
constructor(params?: ParameterType) {
@@ -148,33 +150,33 @@ export class Hostname {
148150
params
149151
)
150152
}
151-
}
153+
} as unknown as LegacyParameter<z.ZodString>
152154

153-
export class Ipv4 {
155+
export const Ipv4: LegacyParameter<z.ZodString> = class Ipv4 {
154156
static generator = true
155157

156158
constructor(params?: ParameterType) {
157159
return convertParams(z.coerce.string().ip({ version: 'v4' }), params)
158160
}
159-
}
161+
} as unknown as LegacyParameter<z.ZodString>
160162

161-
export class Ipv6 {
163+
export const Ipv6: LegacyParameter<z.ZodString> = class Ipv6 {
162164
static generator = true
163165

164166
constructor(params?: ParameterType) {
165167
return convertParams(z.string().ip({ version: 'v6' }), params)
166168
}
167-
}
169+
} as unknown as LegacyParameter<z.ZodString>
168170

169-
export class DateOnly {
171+
export const DateOnly: LegacyParameter<z.ZodString> = class DateOnly {
170172
static generator = true
171173

172174
constructor(params?: ParameterType) {
173175
return convertParams(z.coerce.date(), params)
174176
}
175-
}
177+
} as unknown as LegacyParameter<z.ZodString>
176178

177-
export class Bool {
179+
export const Bool: LegacyParameter<z.ZodBoolean> = class Bool {
178180
static generator = true
179181

180182
constructor(params?: ParameterType) {
@@ -188,7 +190,7 @@ export class Bool {
188190
type: 'boolean',
189191
})
190192
}
191-
}
193+
} as unknown as LegacyParameter<z.ZodBoolean>
192194

193195
export class Enumeration {
194196
static generator = true
@@ -202,7 +204,7 @@ export class Enumeration {
202204

203205
const originalKeys: [string, ...string[]] = Object.keys(values) as [
204206
string,
205-
...string[]
207+
...string[],
206208
]
207209

208210
if (params.enumCaseSensitive === false) {
@@ -215,7 +217,7 @@ export class Enumeration {
215217

216218
const keys: [string, ...string[]] = Object.keys(values) as [
217219
string,
218-
...string[]
220+
...string[],
219221
]
220222

221223
let field
@@ -239,32 +241,95 @@ export class Enumeration {
239241
}
240242
}
241243

244+
export function Query<Z extends z.ZodType>(type: Z): QueryParameter<Z>
245+
export function Query<Z extends z.ZodType>(
246+
type: Z,
247+
params: ParameterLocation & { required: false }
248+
): QueryParameter<z.ZodOptional<Z>>
249+
export function Query<Z extends z.ZodType>(
250+
type: Z,
251+
params: ParameterLocation
252+
): QueryParameter<Z>
253+
export function Query<Z extends z.ZodType>(
254+
type: [Z]
255+
): QueryParameter<z.ZodArray<Z>>
256+
export function Query<Z extends z.ZodType>(
257+
type: [Z],
258+
params: ParameterLocation & { required: false }
259+
): QueryParameter<z.ZodOptional<z.ZodArray<Z>>>
260+
export function Query<Z extends z.ZodType>(
261+
type: [Z],
262+
params: ParameterLocation
263+
): QueryParameter<z.ZodArray<Z>>
264+
export function Query(type: any): QueryParameter
265+
export function Query(type: any, params: ParameterLocation): QueryParameter
242266
export function Query(
243267
type: any,
244268
params: ParameterLocation = {}
245-
): RouteParameter {
269+
): QueryParameter {
246270
return {
247271
name: params.name,
248272
location: 'query',
249273
type: legacyTypeIntoZod(type, params),
250274
}
251275
}
252276

253-
export function Path(
254-
type: any,
255-
params: ParameterLocation = {}
256-
): RouteParameter {
277+
export function Path<Z extends z.ZodType>(type: Z): PathParameter<Z>
278+
export function Path<Z extends z.ZodType>(
279+
type: Z,
280+
params: ParameterLocation & { required: false }
281+
): PathParameter<z.ZodOptional<Z>>
282+
export function Path<Z extends z.ZodType>(
283+
type: Z,
284+
params: ParameterLocation
285+
): PathParameter<Z>
286+
export function Path<Z extends z.ZodType>(
287+
type: [Z]
288+
): PathParameter<z.ZodArray<Z>>
289+
export function Path<Z extends z.ZodType>(
290+
type: [Z],
291+
params: ParameterLocation & { required: false }
292+
): PathParameter<z.ZodOptional<z.ZodArray<Z>>>
293+
export function Path<Z extends z.ZodType>(
294+
type: [Z],
295+
params: ParameterLocation
296+
): PathParameter<z.ZodArray<Z>>
297+
export function Path(type: any): PathParameter
298+
export function Path(type: any, params: ParameterLocation): PathParameter
299+
export function Path(type: any, params: ParameterLocation = {}): PathParameter {
257300
return {
258301
name: params.name,
259302
location: 'params',
260303
type: legacyTypeIntoZod(type, params),
261304
}
262305
}
263306

307+
export function Header<Z extends z.ZodType>(type: Z): HeaderParameter<Z>
308+
export function Header<Z extends z.ZodType>(
309+
type: Z,
310+
params: ParameterLocation & { required: false }
311+
): HeaderParameter<z.ZodOptional<Z>>
312+
export function Header<Z extends z.ZodType>(
313+
type: Z,
314+
params: ParameterLocation
315+
): HeaderParameter<Z>
316+
export function Header<Z extends z.ZodType>(
317+
type: [Z]
318+
): HeaderParameter<z.ZodArray<Z>>
319+
export function Header<Z extends z.ZodType>(
320+
type: [Z],
321+
params: ParameterLocation & { required: false }
322+
): HeaderParameter<z.ZodOptional<z.ZodArray<Z>>>
323+
export function Header<Z extends z.ZodType>(
324+
type: [Z],
325+
params: ParameterLocation
326+
): HeaderParameter<z.ZodArray<Z>>
327+
export function Header(type: any): HeaderParameter
328+
export function Header(type: any, params: ParameterLocation): HeaderParameter
264329
export function Header(
265330
type: any,
266331
params: ParameterLocation = {}
267-
): RouteParameter {
332+
): HeaderParameter {
268333
return {
269334
name: params.name,
270335
location: 'headers',
@@ -296,7 +361,7 @@ export function extractParameter(
296361

297362
export function extractQueryParameters(
298363
request: Request,
299-
schema?: ZodObject<any>
364+
schema?: z.ZodObject<any>
300365
): Record<string, any> | null {
301366
const { searchParams } = new URL(request.url)
302367

0 commit comments

Comments
 (0)