Skip to content

Commit

Permalink
Merge edc6221 into 39e75bd
Browse files Browse the repository at this point in the history
  • Loading branch information
kwhitley committed Mar 28, 2024
2 parents 39e75bd + edc6221 commit 08e326e
Show file tree
Hide file tree
Showing 37 changed files with 256 additions and 135 deletions.
22 changes: 22 additions & 0 deletions example/types/content.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { IRequestStrict, IRequest, RequestHandler } from 'types'
import { IttyRouter } from 'IttyRouter'
import { withContent } from 'withContent'
import { HasContent } from 'types'

type User = {
user: string
}

const router = IttyRouter()

router
// upstream request sees the request as IRequest (default), so anything goes
.post<HasContent<User>>('/', withContent, ({ content }) => {
content.user
})

.get('/pollution', (request) => {
request.content.user
})

export default router
13 changes: 13 additions & 0 deletions example/types/extra-props.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { IRequestStrict, IRequest, RequestHandler } from 'types'
import { IttyRouter } from 'IttyRouter'
import { withContent } from 'withContent'
import { HasContent } from 'types'

const router = IttyRouter({
port: 3000,
})

router.port = 4000
router.port.toLowerCase()
router.puppy() // should not be typed... RIP

3 changes: 2 additions & 1 deletion example/types/middleware.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { IRequestStrict, IRequest, IttyRouter, RequestHandler } from 'IttyRouter'
import { IRequestStrict, IRequest, RequestHandler } from '../../src/types'
import { IttyRouter } from 'IttyRouter'

type UserRequest = {
user: string
Expand Down
11 changes: 11 additions & 0 deletions example/types/requestlike.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { IttyRouter } from 'IttyRouter'

const router = IttyRouter()

const request = new Request('https://foo.bar')

router.fetch(request) // should be OK
router.fetch({ method: 'GET', url: 'foo' }) // should be OK
router.fetch({ method: 'GET' }) // should NOT be OK


8 changes: 8 additions & 0 deletions example/types/router.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { Router } from 'Router'

const router = Router({
port: 4000,
before: [],
})

router.finally = () => {} // should NOT work
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "itty-router",
"version": "4.3.0-next.4",
"version": "5.0.0-next.2",
"description": "A tiny, zero-dependency router, designed to make beautiful APIs in any environment.",
"main": "./index.js",
"module": "./index.mjs",
Expand Down
2 changes: 1 addition & 1 deletion rollup.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import fs from 'fs-extra'

// scan files to build
const files = (await globby('./src/*.ts', {
ignore: ['**/*.spec.ts', 'example'],
ignore: ['**/*.spec.ts', 'example', '**/types.ts'],
})).map(path => ({
path,
shortPath: path.replace(/(\/src)|(\.ts)/g, '').replace('./index', '.'),
Expand Down
21 changes: 2 additions & 19 deletions src/AutoRouter.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,9 @@
import { RequestHandler } from 'IttyRouter'
import { ResponseHandler, Router, RouterOptions } from './Router'
import { Router } from './Router'
import { error } from './error'
import { json } from './json'
import { AutoRouterOptions } from './types'
import { withParams } from './withParams'

type AutoRouterOptions = {
missing?: RequestHandler
format?: ResponseHandler
} & RouterOptions

// MORE FINE-GRAINED/SIMPLIFIED CONTROL, BUT CANNOT FULLY REPLACE BEFORE/FINALLY STAGES
export const AutoRouter = ({
format = json,
missing = () => error(404),
Expand All @@ -29,14 +23,3 @@ export const AutoRouter = ({
],
...options,
})

// LESS FINE-GRAINED CONTROL, BUT CAN COMPLETELY REPLACE BEFORE/FINALLY STAGES
// export const AutoRouter2 = ({ ...options }: RouterOptions = {}) => Router({
// before: [withParams],
// onError: [error],
// finally: [
// (r: any) => r ?? error(404),
// json,
// ],
// ...options,
// })
69 changes: 8 additions & 61 deletions src/IttyRouter.ts
Original file line number Diff line number Diff line change
@@ -1,63 +1,10 @@
export type GenericTraps = Record<string, any>

export type RequestLike = {
method: string
url: string
} & GenericTraps

export type IRequestStrict = {
method: string
url: string
route: string
params: {
[key: string]: string
}
query: {
[key: string]: string | string[] | undefined
}
proxy?: any
} & Request

export type IRequest = IRequestStrict & GenericTraps

export type IttyRouterOptions = {
base?: string
routes?: RouteEntry[]
} & Record<string, any>

export type RequestHandler<R = IRequest, Args extends Array<any> = any[]> =
(request: R, ...args: Args) => any

export type RouteEntry = [
httpMethod: string,
match: RegExp,
handlers: RequestHandler[],
path?: string,
]

// this is the generic "Route", which allows per-route overrides
export type Route<R = IRequest, A extends Array<any> = any[]> = <RequestType = R, Args extends Array<any> = A>(
path: string,
...handlers: RequestHandler<RequestType, Args>[]
) => IttyRouterType<RequestType, Args>

export type CustomRoutes<R = Route> = {
[key: string]: R
}

export type IttyRouterType<R = IRequest, A extends any[] = any[], Output = any> = {
__proto__: IttyRouterType<R>
routes: RouteEntry[]
fetch: <Args extends any[] = A>(request: RequestLike, ...extra: Args) => Promise<Output>
all: Route<R, A>
delete: Route<R, A>
get: Route<R, A>
head: Route<R, A>
options: Route<R, A>
patch: Route<R, A>
post: Route<R, A>
put: Route<R, A>
} & CustomRoutes<Route<R, A>>
import {
IRequest,
IttyRouterOptions,
IttyRouterType,
RequestHandler,
RequestLike,
} from './types'

export const IttyRouter = <
RequestType extends IRequest = IRequest,
Expand All @@ -78,7 +25,7 @@ export const IttyRouter = <
.replace(/\./g, '\\.') // dot in path
.replace(/(\/?)\*/g, '($1.*)?') // wildcard
}/*$`),
// @ts-expect-error - fiddly
// @ts-ignore
handlers, // embed handlers
path, // embed clean route path
]
Expand Down
28 changes: 5 additions & 23 deletions src/Router.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,10 @@
import {
IRequest,
IttyRouterOptions,
IttyRouterType,
RequestHandler,
RequestLike
} from './IttyRouter'

export type ResponseHandler<ResponseType = Response, RequestType = IRequest, Args extends any[] = any[]> =
(response: ResponseType & any, request: RequestType & any, ...args: Args) => any

export type ErrorHandler<ErrorType = Error, RequestType = IRequest, Args extends any[] = any[]> =
(response: ErrorType, request: RequestType, ...args: Args) => any

export type RouterType<R = IRequest, Args extends any[] = any[]> = {
before?: RequestHandler<any>[]
catch?: ErrorHandler
finally?: ResponseHandler[]
} & IttyRouterType<R, Args>

export type RouterOptions = {
before?: RequestHandler<any>[]
catch?: ErrorHandler
finally?: ResponseHandler[]
} & IttyRouterOptions
RequestLike,
RouterOptions,
RouterType
} from './types'

export const Router = <
RequestType = IRequest,
Expand All @@ -43,7 +25,7 @@ export const Router = <
.replace(/\./g, '\\.') // dot in path
.replace(/(\/?)\*/g, '($1.*)?') // wildcard
}/*$`),
// @ts-expect-error - fiddly
// @ts-ignore
handlers, // embed handlers
path, // embed clean route path
]
Expand Down
2 changes: 1 addition & 1 deletion src/cors.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { IRequest } from 'IttyRouter'
import { IRequest } from './types'

export type CorsOptions = {
credentials?: true
Expand Down
10 changes: 2 additions & 8 deletions src/createResponse.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,9 @@
export interface ResponseFormatter {
(body?: any, options?: ResponseInit): Response
}

export interface BodyTransformer {
(body: any): string
}
import { ResponseFormatter } from './types'

export const createResponse =
(
format = 'text/plain; charset=utf-8',
transform?: BodyTransformer
transform?: (body: any) => any,
): ResponseFormatter =>
(body, { ...options } = {}) => {
if (body === undefined || body instanceof Response) return body
Expand Down
15 changes: 2 additions & 13 deletions src/error.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,5 @@
import { json } from './json'

interface ErrorLike extends Error {
status?: number
[any: string]: any
}

export type ErrorBody = string | object

export interface ErrorFormatter {
(statusCode?: number, body?: ErrorBody): Response
(error: ErrorLike): Response
}
import { ErrorFormatter } from './types'

const getMessage = (code: number): string => ({
400: 'Bad Request',
Expand All @@ -20,7 +9,7 @@ const getMessage = (code: number): string => ({
500: 'Internal Server Error',
})[code] || 'Unknown Error'

export const error: ErrorFormatter = (a = 500, b?: ErrorBody) => {
export const error: ErrorFormatter = (a = 500, b?) => {
// handle passing an Error | StatusError directly in
if (a instanceof Error) {
const { message, ...err } = a
Expand Down
3 changes: 3 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,6 @@ export * from './withParams'

// CORS
export * from './cors'

// TYPES
export * from './types'
26 changes: 26 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// routers
export * from './types/AutoRouterOptions'
export * from './types/CustomRoutes'
export * from './types/ErrorHandler'
export * from './types/IRequest'
export * from './types/IRequestStrict'
export * from './types/IttyRouterOptions'
export * from './types/IttyRouterType'
export * from './types/RequestHandler'
export * from './types/ResponseHandler'
export * from './types/RequestLike'
export * from './types/ResponseHandler'
export * from './types/Route'
export * from './types/RouteEntry'
export * from './types/RouterOptions'
export * from './types/RouterType'

// withContent
export * from './types/HasContent'

// createResponse
export * from './types/ResponseFormatter'

// errors
export * from './types/ErrorFormatter'

8 changes: 8 additions & 0 deletions src/types/AutoRouterOptions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { RequestHandler } from './RequestHandler'
import { ResponseHandler } from './ResponseHandler'
import { RouterOptions } from './RouterOptions'

export type AutoRouterOptions = {
missing?: RequestHandler
format?: ResponseHandler
} & RouterOptions
5 changes: 5 additions & 0 deletions src/types/CustomRoutes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { Route } from './Route'

export type CustomRoutes<R = Route> = {
[key: string]: R
}
11 changes: 11 additions & 0 deletions src/types/ErrorFormatter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
interface ErrorLike extends Error {
status?: number
[any: string]: any
}

type ErrorBody = string | object

export interface ErrorFormatter {
(statusCode?: number, body?: ErrorBody): Response
(error: ErrorLike): Response
}
7 changes: 7 additions & 0 deletions src/types/ErrorHandler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { StatusError } from '../StatusError'
import { IRequest } from './IRequest'

export type ErrorHandler<
ErrorType extends Error = StatusError,
RequestType = IRequest, Args extends any[] = any[]
> = (error: ErrorType, request: RequestType, ...args: Args) => any
1 change: 1 addition & 0 deletions src/types/GenericTraps.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export type GenericTraps = Record<string, any>
5 changes: 5 additions & 0 deletions src/types/HasContent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { IRequestStrict } from './IRequestStrict'

export type HasContent<ContentType> = {
content: ContentType
} & IRequestStrict
4 changes: 4 additions & 0 deletions src/types/IRequest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { GenericTraps } from './GenericTraps'
import { IRequestStrict } from './IRequestStrict'

export type IRequest = IRequestStrict & GenericTraps
10 changes: 10 additions & 0 deletions src/types/IRequestStrict.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export type IRequestStrict = {
route: string
params: {
[key: string]: string
}
query: {
[key: string]: string | string[] | undefined
}
proxy?: any
} & Request
7 changes: 7 additions & 0 deletions src/types/IttyRouterOptions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { GenericTraps } from './GenericTraps'
import { RouteEntry } from './RouteEntry'

export type IttyRouterOptions = {
base?: string
routes?: RouteEntry[]
} & GenericTraps

0 comments on commit 08e326e

Please sign in to comment.