better-call 0.0.5-beta.2 → 0.0.5-beta.3
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 +57 -31
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +146 -92
- package/dist/index.d.ts +146 -92
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -4,7 +4,6 @@ Better call is a tiny web framework for creating endpoints that can be invoked a
|
|
|
4
4
|
|
|
5
5
|
Built for typescript and it comes with a very high performance router based on [rou3](https://github.com/unjs/rou3).
|
|
6
6
|
|
|
7
|
-
|
|
8
7
|
> ⚠️ This project early in development and not ready for production use. But feel free to try it out and give feedback.
|
|
9
8
|
|
|
10
9
|
## Install
|
|
@@ -15,7 +14,7 @@ pnpm i better-call
|
|
|
15
14
|
|
|
16
15
|
## Usage
|
|
17
16
|
|
|
18
|
-
The building blocks for better-call are endpoints. You can create an endpoint by calling `createEndpoint` and passing it a path,
|
|
17
|
+
The building blocks for better-call are endpoints. You can create an endpoint by calling `createEndpoint` and passing it a path, [options](#endpointoptions) and a function that will be invoked when the endpoint is called.
|
|
19
18
|
|
|
20
19
|
```ts
|
|
21
20
|
import { createEndpoint, createRouter } from "better-call"
|
|
@@ -58,36 +57,45 @@ Bun.serve({
|
|
|
58
57
|
|
|
59
58
|
### Middleware
|
|
60
59
|
|
|
61
|
-
|
|
60
|
+
Endpoints can use middleware by passing the `use` option to the endpoint. To create a middleware, you can call `createMiddleware` and pass it a function or an options object and a handler function.
|
|
62
61
|
|
|
63
|
-
If you return a context object from the middleware, it will be
|
|
62
|
+
If you return a context object from the middleware, it will be available in the endpoint context.
|
|
64
63
|
|
|
65
64
|
```ts
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
const createProtectedEndpoint = createMiddleware(async (ctx) => {
|
|
69
|
-
if(ctx.headers.get("Authorization") !== "Bearer 123") {
|
|
70
|
-
throw new Error("Unauthorized")
|
|
71
|
-
}
|
|
65
|
+
const middleware = createMiddleware(async (ctx) => {
|
|
72
66
|
return {
|
|
73
|
-
|
|
74
|
-
session: {
|
|
75
|
-
id: "123",
|
|
76
|
-
}
|
|
77
|
-
}
|
|
67
|
+
name: "hello"
|
|
78
68
|
}
|
|
79
69
|
})
|
|
70
|
+
const endpoint = createEndpoint("/", {
|
|
71
|
+
method: "GET",
|
|
72
|
+
use: [middleware],
|
|
73
|
+
}, async (ctx) => {
|
|
74
|
+
//this will be the context object returned by the middleware with the name property
|
|
75
|
+
ctx.context
|
|
76
|
+
})
|
|
77
|
+
```
|
|
80
78
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
79
|
+
You can also pass an options object to the middleware and a handler function.
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
const middleware = createMiddleware({
|
|
83
|
+
body: z.object({
|
|
84
|
+
name: z.string()
|
|
85
|
+
})
|
|
86
|
+
}, async (ctx) => {
|
|
87
87
|
return {
|
|
88
|
-
|
|
88
|
+
name: "hello"
|
|
89
89
|
}
|
|
90
90
|
})
|
|
91
|
+
|
|
92
|
+
const endpoint = createEndpoint("/", {
|
|
93
|
+
method: "GET",
|
|
94
|
+
use: [middleware],
|
|
95
|
+
}, async (ctx) => {
|
|
96
|
+
//the body will also contain the middleware body
|
|
97
|
+
ctx.body
|
|
98
|
+
})
|
|
91
99
|
```
|
|
92
100
|
|
|
93
101
|
### Router
|
|
@@ -102,8 +110,32 @@ const router = createRouter([
|
|
|
102
110
|
createItem
|
|
103
111
|
])
|
|
104
112
|
```
|
|
113
|
+
|
|
105
114
|
Behind the scenes, the router uses [rou3](https://github.com/unjs/rou3) to match the endpoints and invoke the correct endpoint. You can look at the [rou3 documentation](https://github.com/unjs/rou3) for more information.
|
|
106
115
|
|
|
116
|
+
#### Router Options
|
|
117
|
+
|
|
118
|
+
**routerMiddleware**: A router middleware is similar to an endpoint middleware but it's applied to any path that matches the route. It's like a traditional middleware you specify a path matcher and a handler function and it will be called before the endpoint.
|
|
119
|
+
|
|
120
|
+
```ts
|
|
121
|
+
const router = createRouter([
|
|
122
|
+
createItem
|
|
123
|
+
], {
|
|
124
|
+
routerMiddleware: [
|
|
125
|
+
{
|
|
126
|
+
path: "/api/**",
|
|
127
|
+
handler: middleware
|
|
128
|
+
}
|
|
129
|
+
]
|
|
130
|
+
})
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
**basePath**: The base path for the router. All paths will be relative to this path.
|
|
134
|
+
|
|
135
|
+
**onError**: The router will call this function if an error occurs in the middleware or the endpoint.
|
|
136
|
+
|
|
137
|
+
**throwError**: If true, the router will throw an error if an error occurs in the middleware or the endpoint.
|
|
138
|
+
|
|
107
139
|
|
|
108
140
|
### Returning non 200 responses
|
|
109
141
|
|
|
@@ -168,15 +200,7 @@ const createItem = createEndpoint("/item", {
|
|
|
168
200
|
})
|
|
169
201
|
```
|
|
170
202
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
### `createEndpoint`
|
|
174
|
-
|
|
175
|
-
```ts
|
|
176
|
-
createEndpoint(path: string, options: EndpointOptions, fn: EndpointFunction): Endpoint
|
|
177
|
-
```
|
|
178
|
-
|
|
179
|
-
Creates an endpoint. The `path` is the path that will be used to match the endpoint. The `options` are the options that will be used to validate the request. The `fn` is the function that will be invoked when the endpoint is called.
|
|
203
|
+
### Endpoint Parameters
|
|
180
204
|
|
|
181
205
|
### `path`
|
|
182
206
|
|
|
@@ -205,6 +229,7 @@ type EndpointOptions = {
|
|
|
205
229
|
params?: ZodSchema
|
|
206
230
|
requireHeaders?: boolean
|
|
207
231
|
requireRequest?: boolean
|
|
232
|
+
use?: Endpoint[]
|
|
208
233
|
}
|
|
209
234
|
```
|
|
210
235
|
|
|
@@ -220,6 +245,7 @@ type EndpointOptions = {
|
|
|
220
245
|
|
|
221
246
|
- **requireRequest**: if true, the endpoint will throw an error if the request doesn't have a request object. And even when you call the endpoint as a function, it will require a request to be passed in the context.
|
|
222
247
|
|
|
248
|
+
- **use**: the use option accepts an array of endpoints and will be called before the endpoint is called.
|
|
223
249
|
|
|
224
250
|
### `EndpointFunction`
|
|
225
251
|
|
package/dist/index.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var m=Object.defineProperty;var g=Object.getOwnPropertyDescriptor;var S=Object.getOwnPropertyNames;var b=Object.prototype.hasOwnProperty;var U=(t,e,n)=>e in t?m(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n;var L=(t,e)=>{for(var n in e)m(t,n,{get:e[n],enumerable:!0})},M=(t,e,n,a)=>{if(e&&typeof e=="object"||typeof e=="function")for(let d of S(e))!b.call(t,d)&&d!==n&&m(t,d,{get:()=>e[d],enumerable:!(a=g(e,d))||a.enumerable});return t};var H=t=>M(m({},"__esModule",{value:!0}),t);var O=(t,e,n)=>U(t,typeof e!="symbol"?e+"":e,n);var F={};L(F,{APIError:()=>c,createEndpoint:()=>l,createMiddleware:()=>C,createRouter:()=>B,getBody:()=>y,shouldSerialize:()=>h,statusCode:()=>T});module.exports=H(F);var D=require("zod");var c=class extends Error{constructor(n,a){super(`API Error: ${n} ${a?.message??""}`,{cause:a});O(this,"status");O(this,"body");this.status=n,this.body=a??{},this.stack="",this.name="BetterCallAPIError"}};function l(t,e,n){let a=new Headers,d=async(...o)=>{let r={setHeader(s,i){a.set(s,i)},setCookie(s,i){a.append("Set-Cookie",`${s}=${i}`)},getCookie(s){return o[0]?.headers?.get("cookie")?.split(";").find(u=>u.startsWith(`${s}=`))?.split("=")[1]},...o[0]||{},context:{}};if(e.use?.length)for(let s of e.use){let i=await s(r),u=i.options?.body?i.options.body.parse(r.body):void 0;i&&(r={...r,body:u?{...u,...r.body}:r.body,context:{...r.context||{},...i}})}try{let s=e.body?e.body.parse(r.body):r.body;r={...r,body:s?{...s,...r.body}:r.body},r.query=e.query?e.query.parse(r.query):r.query,r.params=e.params?e.params.parse(r.params):r.params}catch(s){throw s instanceof D.ZodError?new c("BAD_REQUEST",{message:s.message,details:s.errors}):s}if(e.requireHeaders&&!r.headers)throw new c("BAD_REQUEST",{message:"Headers are required"});if(e.requireRequest&&!r.request)throw new c("BAD_REQUEST",{message:"Request is required"});return await n(r)};return d.path=t,d.options=e,d.method=e.method,d.headers=a,d}var E=require("rou3");async function y(t){let e=t.headers.get("content-type")||"";if(t.body){if(e.includes("application/json"))return await t.json();if(e.includes("application/x-www-form-urlencoded")){let n=await t.formData(),a={};return n.forEach((d,o)=>{a[o]=d.toString()}),a}if(e.includes("multipart/form-data")){let n=await t.formData(),a={};return n.forEach((d,o)=>{a[o]=d}),a}return e.includes("text/plain")?await t.text():e.includes("application/octet-stream")?await t.arrayBuffer():e.includes("application/pdf")||e.includes("image/")||e.includes("video/")?await t.blob():e.includes("application/stream")||t.body instanceof ReadableStream?t.body:await t.text()}}function h(t){return typeof t=="object"&&t!==null&&!(t instanceof Blob)&&!(t instanceof FormData)}var T={OK:200,CREATED:201,ACCEPTED:202,NO_CONTENT:204,MULTIPLE_CHOICES:300,MOVED_PERMANENTLY:301,FOUND:302,SEE_OTHER:303,NOT_MODIFIED:304,TEMPORARY_REDIRECT:307,BAD_REQUEST:400,UNAUTHORIZED:401,PAYMENT_REQUIRED:402,FORBIDDEN:403,NOT_FOUND:404,METHOD_NOT_ALLOWED:405,NOT_ACCEPTABLE:406,PROXY_AUTHENTICATION_REQUIRED:407,REQUEST_TIMEOUT:408,CONFLICT:409,GONE:410,LENGTH_REQUIRED:411,PRECONDITION_FAILED:412,PAYLOAD_TOO_LARGE:413,URI_TOO_LONG:414,UNSUPPORTED_MEDIA_TYPE:415,RANGE_NOT_SATISFIABLE:416,EXPECTATION_FAILED:417,"I'M_A_TEAPOT":418,MISDIRECTED_REQUEST:421,UNPROCESSABLE_ENTITY:422,LOCKED:423,FAILED_DEPENDENCY:424,TOO_EARLY:425,UPGRADE_REQUIRED:426,PRECONDITION_REQUIRED:428,TOO_MANY_REQUESTS:429,REQUEST_HEADER_FIELDS_TOO_LARGE:431,UNAVAILABLE_FOR_LEGAL_REASONS:451,INTERNAL_SERVER_ERROR:500,NOT_IMPLEMENTED:501,BAD_GATEWAY:502,SERVICE_UNAVAILABLE:503,GATEWAY_TIMEOUT:504,HTTP_VERSION_NOT_SUPPORTED:505,VARIANT_ALSO_NEGOTIATES:506,INSUFFICIENT_STORAGE:507,LOOP_DETECTED:508,NOT_EXTENDED:510,NETWORK_AUTHENTICATION_REQUIRED:511};var B=(t,e)=>{let n=(0,E.createRouter)();for(let o of t)if(Array.isArray(o.options?.method))for(let r of o.options.method)(0,E.addRoute)(n,r,o.path,o);else(0,E.addRoute)(n,o.options.method,o.path,o);let a=(0,E.createRouter)();for(let o of e?.routerMiddleware||[]){let r=Array.isArray(o.method)?o.method:[o.method];for(let R of r)(0,E.addRoute)(a,R,o.path,o.handler)}return{handler:async o=>{let r=new URL(o.url),R=r.pathname;e?.basePath&&(R=R.split(e.basePath)[1]);let s=o.method,i=(0,E.findRoute)(n,s,R),u=i?.data,_=await y(o),I=o.headers,w=Object.fromEntries(r.searchParams),N=(0,E.findRoute)(a,s,R)?.data;if(!u)return new Response(null,{status:404,statusText:"Not Found"});try{let p={};if(N){let x=await N({path:R,method:s,headers:I,params:i?.params,request:o,body:_,query:w});x&&(p={...x,...p})}let f=await u({path:R,method:s,headers:I,params:i?.params,request:o,body:_,query:w,...p});if(f instanceof Response)return f;let P=h(f)?JSON.stringify(f):f;return new Response(P,{headers:u.headers})}catch(p){if(e?.onError){let f=await e.onError(p);if(f instanceof Response)return f}if(p instanceof c)return new Response(p.body?JSON.stringify(p.body):null,{status:T[p.status],statusText:p.status,headers:{"Content-Type":"application/json"}});if(e?.throwError)throw p;return new Response(null,{status:500,statusText:"Internal Server Error"})}}}};var A=require("zod");function C(t,e){if(typeof t=="function")return l("*",{method:"*"},t);if(!e)throw new Error("Middleware handler is required");return l("*",{...t,method:"*"},e)}var z=C({body:A.z.object({name:A.z.string()})},async t=>{});0&&(module.exports={APIError,createEndpoint,createMiddleware,createRouter,getBody,shouldSerialize,statusCode});
|
|
2
2
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/endpoint.ts","../src/better-call-error.ts","../src/router.ts","../src/utils.ts","../src/middleware.ts"],"sourcesContent":["export * from \"./endpoint\"\nexport * from \"./router\"\nexport * from \"./middleware\"\nexport * from \"./better-call-error\"\nexport * from \"./utils\"","import { z, ZodError, type ZodOptional, type ZodSchema } from \"zod\"\nimport type { Middleware } from \"./middleware\"\nimport { APIError } from \"./better-call-error\";\n\n\nexport type RequiredKeysOf<BaseType extends object> = Exclude<{\n [Key in keyof BaseType]: BaseType extends Record<Key, BaseType[Key]>\n ? Key\n : never\n}[keyof BaseType], undefined>;\n\nexport type HasRequiredKeys<BaseType extends object> = RequiredKeysOf<BaseType> extends never ? false : true;\n\ntype Method = \"GET\" | \"POST\" | \"PUT\" | \"DELETE\" | \"PATCH\" | \"*\"\n\nexport interface EndpointOptions {\n method: Method | Method[]\n body?: ZodSchema\n query?: ZodSchema\n params?: ZodSchema<any>\n /**\n * If true headers will be required to be passed in the context\n */\n requireHeaders?: boolean\n /**\n * If true request object will be required\n */\n requireRequest?: boolean\n}\n\ntype InferParamPath<Path> =\n Path extends `${infer _Start}:${infer Param}/${infer Rest}`\n ? { [K in Param | keyof InferParamPath<Rest>]: string }\n : Path extends `${infer _Start}:${infer Param}`\n ? { [K in Param]: string }\n : Path extends `${infer _Start}/${infer Rest}`\n ? InferParamPath<Rest>\n : undefined;\n\ntype InferParamWildCard<Path> = Path extends `${infer _Start}/*:${infer Param}/${infer Rest}` | `${infer _Start}/**:${infer Param}/${infer Rest}`\n ? { [K in Param | keyof InferParamPath<Rest>]: string }\n : Path extends `${infer _Start}/*`\n ? { [K in \"_\"]: string }\n : Path extends `${infer _Start}/${infer Rest}`\n ? InferParamPath<Rest>\n : undefined;\n\n\nexport type Prettify<T> = {\n [key in keyof T]: T[key];\n} & {};\n\ntype ContextTools = {\n setHeader: (key: string, value: string) => void\n setCookie: (key: string, value: string) => void\n getCookie: (key: string) => string | undefined\n}\n\nexport type Context<Path extends string, Opts extends EndpointOptions, Extra extends Record<string, any> = {}> = InferBody<Opts[\"body\"]> & InferParam<Path> & InferMethod<Opts['method']> & InferHeaders<Opts[\"requireHeaders\"]> & InferRequest<Opts[\"requireRequest\"]> & InferQuery<Opts[\"query\"]> & Extra\n\ntype InferMethod<M extends Method | Method[]> = M extends Array<Method> ? {\n method: M[number]\n} : {\n method?: M\n}\n\ntype InferHeaders<HeaderReq> = HeaderReq extends true ? {\n headers: Headers\n} : {\n headers?: Headers\n}\n\ntype InferRequest<RequestReq> = RequestReq extends true ? {\n request: Request\n} : {\n request?: Request\n}\n\n\ntype InferBody<Body> = Body extends ZodSchema ? Body extends ZodOptional<any> ? {\n body?: z.infer<Body>\n} : {\n body: z.infer<Body>\n} : {\n body?: undefined\n}\n\ntype InferQuery<Query> = Query extends ZodSchema ? Query extends ZodOptional<any> ? {\n query?: z.infer<Query>\n} : {\n query: z.infer<Query>\n} : {\n query?: undefined\n}\n\ntype InferParam<Path extends string, ParamPath extends InferParamPath<Path> = InferParamPath<Path>, WildCard extends InferParamWildCard<Path> = InferParamWildCard<Path>> = ParamPath extends undefined ? WildCard extends undefined ? {\n params?: undefined\n} : {\n params: WildCard\n} : {\n params: ParamPath & (WildCard extends undefined ? {} : WildCard)\n}\n\n\nexport type EndpointResponse = Record<string, any> | string | boolean | number | void | undefined\n\nexport type Handler<Path extends string, Opts extends EndpointOptions, R extends EndpointResponse, Extra extends Record<string, any> = Record<string, any>> = (ctx: Context<Path, Opts, Extra>) => Promise<R>\n\nexport interface EndpointConfig {\n /**\n * Throw when the response isn't in 200 range\n */\n throwOnError?: boolean\n}\n\nexport function createEndpoint<Path extends string, Opts extends EndpointOptions, R extends EndpointResponse>(path: Path, options: Opts, handler: Handler<Path, Opts, R, ContextTools>) {\n const responseHeader = new Headers()\n type Ctx = Context<Path, Opts>\n const handle = async (...ctx: HasRequiredKeys<Ctx> extends true ? [Ctx] : [Ctx?]) => {\n const internalCtx = ({\n setHeader(key, value) {\n responseHeader.set(key, value)\n },\n setCookie(key, value) {\n responseHeader.append(\"Set-Cookie\", `${key}=${value}`)\n },\n getCookie(key) {\n const header = ctx[0]?.headers\n return header?.get(\"cookie\")?.split(\";\").find(cookie => cookie.startsWith(`${key}=`))?.split(\"=\")[1]\n },\n ...(ctx[0] || {})\n }) as (Ctx & ContextTools)\n try {\n internalCtx.body = options.body ? options.body.parse(internalCtx.body) : undefined\n internalCtx.query = options.query ? options.query.parse(internalCtx.query) : undefined\n internalCtx.params = options.params ? options.params.parse(internalCtx.params) : undefined\n } catch (e) {\n if (e instanceof ZodError) {\n throw new APIError(\"Bad Request\", {\n message: e.message,\n details: e.errors\n })\n }\n throw e\n }\n if (options.requireHeaders && !internalCtx.headers) {\n throw new APIError(\"Bad Request\", {\n message: \"Headers are required\"\n })\n }\n if (options.requireRequest && !internalCtx.request) {\n throw new APIError(\"Bad Request\", {\n message: \"Request is required\"\n })\n }\n const res = await handler(internalCtx)\n return res as ReturnType<Handler<Path, Opts, R>>\n }\n handle.path = path\n handle.options = options\n handle.method = options.method\n handle.headers = responseHeader\n //for type inference\n handle.middleware = undefined as (Middleware<any> | undefined)\n\n return handle\n}\n\nexport type Endpoint = {\n path: string\n options: EndpointOptions\n middleware?: Middleware<any>\n headers?: Headers\n} & ((ctx: any) => Promise<any>)","import type { statusCode } from \"./utils\"\n\ntype Status = keyof typeof statusCode\n\nexport class APIError extends Error {\n status: Status\n body: Record<string, any>\n constructor(\n status: Status,\n body?: Record<string, any>,\n ) {\n super(\n `API Error: ${status} ${body?.message ?? \"\"}`,\n {\n cause: body,\n }\n )\n this.status = status\n this.body = body ?? {}\n this.stack = \"\";\n this.name = \"BetterCallAPIError\"\n }\n}","import type { Endpoint } from \"./endpoint\";\nimport {\n createRouter as createRou3Router,\n addRoute,\n findRoute,\n} from \"rou3\";\nimport { getBody, shouldSerialize, statusCode } from \"./utils\";\nimport { APIError } from \"./better-call-error\";\n\ninterface RouterConfig {\n /**\n * Throw error if error occurred other than APIError\n */\n throwError?: boolean\n /**\n * Handle error\n */\n onError?: (e: unknown) => void | Promise<void> | Response | Promise<Response>\n /**\n * Base path for the router\n */\n basePath?: string\n}\n\nexport const createRouter = <E extends Endpoint, Config extends RouterConfig>(endpoints: E[], config?: Config) => {\n const router = createRou3Router()\n for (const endpoint of endpoints) {\n if (Array.isArray(endpoint.options?.method)) {\n for (const method of endpoint.options.method) {\n addRoute(router, method, endpoint.path, endpoint)\n }\n } else {\n addRoute(router, endpoint.options.method, endpoint.path, endpoint)\n }\n }\n\n const handler = async (request: Request) => {\n const url = new URL(request.url);\n let path = url.pathname\n if (config?.basePath) {\n path = path.split(config.basePath)[1]\n }\n const method = request.method;\n const route = findRoute(router, method, path)\n const handler = route?.data as Endpoint\n const body = await getBody(request)\n const headers = request.headers\n\n //handler 404\n if (!handler) {\n return new Response(null, {\n status: 404,\n statusText: \"Not Found\"\n })\n }\n try {\n let middleware: Record<string, any> = {}\n if (handler.middleware) {\n middleware = await handler.middleware({\n path: path,\n method: method,\n headers,\n params: url.searchParams,\n request: request.clone(),\n body: body,\n }) || {}\n }\n const handlerRes = await handler({\n path: path,\n method: method as \"GET\",\n headers,\n params: route?.params as any,\n request: request,\n body: body,\n ...middleware?.context,\n })\n if (handlerRes instanceof Response) {\n return handlerRes\n }\n const resBody = shouldSerialize(handlerRes) ? JSON.stringify(handlerRes) : handlerRes\n return new Response(resBody as any, {\n headers: handler.headers\n })\n } catch (e) {\n if (config?.onError) {\n const onErrorRes = await config.onError(e)\n if (onErrorRes instanceof Response) {\n return onErrorRes\n }\n }\n if (e instanceof APIError) {\n return new Response(e.body ? JSON.stringify(e.body) : null, {\n status: statusCode[e.status],\n statusText: e.status,\n headers: {\n \"Content-Type\": \"application/json\",\n }\n })\n }\n if (config?.throwError) {\n throw e\n }\n return new Response(null, {\n status: 500,\n statusText: \"Internal Server Error\"\n })\n }\n }\n return { handler }\n}","export async function getBody(request: Request) {\n const contentType = request.headers.get('content-type') || '';\n\n if (!request.body) {\n return undefined\n }\n\n if (contentType.includes('application/json')) {\n return await request.json();\n }\n\n if (contentType.includes('application/x-www-form-urlencoded')) {\n const formData = await request.formData();\n const result: Record<string, string> = {};\n formData.forEach((value, key) => {\n result[key] = value.toString();\n });\n return result;\n }\n\n if (contentType.includes('multipart/form-data')) {\n const formData = await request.formData();\n const result: Record<string, any> = {};\n formData.forEach((value, key) => {\n result[key] = value;\n });\n return result;\n }\n\n if (contentType.includes('text/plain')) {\n return await request.text();\n }\n\n if (contentType.includes('application/octet-stream')) {\n return await request.arrayBuffer();\n }\n\n if (contentType.includes('application/pdf') || contentType.includes('image/') || contentType.includes('video/')) {\n const blob = await request.blob();\n return blob;\n }\n\n if (contentType.includes('application/stream') || request.body instanceof ReadableStream) {\n return request.body;\n }\n\n return await request.text();\n}\n\n\nexport function shouldSerialize(body: any) {\n return typeof body === \"object\" && body !== null && !(body instanceof Blob) && !(body instanceof FormData)\n}\n\nexport const statusCode = {\n \"OK\": 200,\n \"Created\": 201,\n \"Accepted\": 202,\n \"No Content\": 204,\n \"Multiple Choices\": 300,\n \"Moved Permanently\": 301,\n \"Found\": 302,\n \"See Other\": 303,\n \"Not Modified\": 304,\n \"Temporary Redirect\": 307,\n \"Bad Request\": 400,\n \"Unauthorized\": 401,\n \"Payment Required\": 402,\n \"Forbidden\": 403,\n \"Not Found\": 404,\n \"Method Not Allowed\": 405,\n \"Not Acceptable\": 406,\n \"Proxy Authentication Required\": 407,\n \"Request Timeout\": 408,\n \"Conflict\": 409,\n \"Gone\": 410,\n \"Length Required\": 411,\n \"Precondition Failed\": 412,\n \"Payload Too Large\": 413,\n \"URI Too Long\": 414,\n \"Unsupported Media Type\": 415,\n \"Range Not Satisfiable\": 416,\n \"Expectation Failed\": 417,\n \"I'm a teapot\": 418,\n \"Misdirected Request\": 421,\n \"Unprocessable Entity\": 422,\n \"Locked\": 423,\n \"Failed Dependency\": 424,\n \"Too Early\": 425,\n \"Upgrade Required\": 426,\n \"Precondition Required\": 428,\n \"Too Many Requests\": 429,\n \"Request Header Fields Too Large\": 431,\n \"Unavailable For Legal Reasons\": 451,\n \"Internal Server Error\": 500,\n \"Not Implemented\": 501,\n \"Bad Gateway\": 502,\n \"Service Unavailable\": 503,\n \"Gateway Timeout\": 504,\n \"HTTP Version Not Supported\": 505,\n \"Variant Also Negotiates\": 506,\n \"Insufficient Storage\": 507,\n \"Loop Detected\": 508,\n \"Not Extended\": 510,\n \"Network Authentication Required\": 511,\n}","import type { HasRequiredKeys } from \"type-fest\"\nimport { type Context, type EndpointOptions, type EndpointResponse, type Handler } from \"./endpoint\"\n\n\nexport type Middleware<E extends Record<string, string>> = (ctx: Context<string, EndpointOptions, E>) => Promise<{\n context: E\n} | void>\n\nexport const createMiddleware = <E extends Record<string, any>, M extends Middleware<E>>(middleware: M) => {\n type MiddlewareContext = Awaited<ReturnType<M>> extends {\n context: infer C\n } ? C extends Record<string, any> ? C : {} : {}\n return <Path extends string, Opts extends EndpointOptions, R extends EndpointResponse>(path: Path, options: Opts, handler: Handler<Path, Opts, R, MiddlewareContext>) => {\n type Ctx = Context<Path, Opts, MiddlewareContext>\n const handle = async (...ctx: HasRequiredKeys<Ctx> extends true ? [Ctx] : [Ctx?]) => {\n const res = await handler((ctx[0] || {}) as Ctx)\n return res as ReturnType<Handler<Path, Opts, R>>\n }\n handle.path = path\n handle.options = options\n handle.middleware = middleware\n return handle\n }\n}"],"mappings":"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","names":["src_exports","__export","APIError","createEndpoint","createMiddleware","createRouter","getBody","shouldSerialize","statusCode","__toCommonJS","import_zod","APIError","status","body","__publicField","createEndpoint","path","options","handler","responseHeader","handle","ctx","internalCtx","key","value","cookie","e","APIError","import_rou3","getBody","request","contentType","formData","result","value","key","shouldSerialize","body","statusCode","createRouter","endpoints","config","router","createRou3Router","endpoint","method","request","url","path","route","handler","body","getBody","headers","middleware","handlerRes","resBody","shouldSerialize","e","onErrorRes","APIError","statusCode","createMiddleware","middleware","path","options","handler","handle","ctx"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/endpoint.ts","../src/better-call-error.ts","../src/router.ts","../src/utils.ts","../src/middleware.ts"],"sourcesContent":["export * from \"./endpoint\"\nexport * from \"./router\"\nexport * from \"./middleware\"\nexport * from \"./better-call-error\"\nexport * from \"./utils\"","import { z, ZodError, type ZodOptional, type ZodSchema } from \"zod\"\nimport type { Middleware } from \"./middleware\"\nimport { APIError } from \"./better-call-error\";\nimport type { HasRequiredKeys, UnionToIntersection } from \"./helper\";\nimport type { Context, ContextTools, Endpoint, EndpointOptions, EndpointResponse, Handler } from \"./types\";\n\n\nexport interface EndpointConfig {\n /**\n * Throw when the response isn't in 200 range\n */\n throwOnError?: boolean\n}\n\nexport function createEndpoint<Path extends string, Opts extends EndpointOptions, R extends EndpointResponse>(path: Path, options: Opts, handler: Handler<Path, Opts, R>) {\n const responseHeader = new Headers()\n type Ctx = Context<Path, Opts>\n const handle = async (...ctx: HasRequiredKeys<Ctx> extends true ? [Ctx] : [Ctx?]) => {\n let internalCtx = ({\n setHeader(key: string, value: string) {\n responseHeader.set(key, value)\n },\n setCookie(key: string, value: string) {\n responseHeader.append(\"Set-Cookie\", `${key}=${value}`)\n },\n getCookie(key: string) {\n const header = ctx[0]?.headers\n return header?.get(\"cookie\")?.split(\";\").find(cookie => cookie.startsWith(`${key}=`))?.split(\"=\")[1]\n },\n ...(ctx[0] || {}),\n context: {}\n })\n if (options.use?.length) {\n for (const middleware of options.use) {\n const res = await middleware(internalCtx) as Endpoint\n const body = res.options?.body ? res.options.body.parse(internalCtx.body) : undefined\n if (res) {\n internalCtx = {\n ...internalCtx,\n body: body ? {\n ...body,\n ...internalCtx.body\n } : internalCtx.body,\n context: {\n ...internalCtx.context || {},\n ...res\n }\n }\n }\n }\n }\n try {\n const body = options.body ? options.body.parse(internalCtx.body) : internalCtx.body\n internalCtx = {\n ...internalCtx,\n body: body ? {\n ...body,\n ...internalCtx.body\n } : internalCtx.body,\n }\n internalCtx.query = options.query ? options.query.parse(internalCtx.query) : internalCtx.query\n internalCtx.params = options.params ? options.params.parse(internalCtx.params) : internalCtx.params\n } catch (e) {\n if (e instanceof ZodError) {\n throw new APIError(\"BAD_REQUEST\", {\n message: e.message,\n details: e.errors\n })\n }\n throw e\n }\n if (options.requireHeaders && !internalCtx.headers) {\n throw new APIError(\"BAD_REQUEST\", {\n message: \"Headers are required\"\n })\n }\n if (options.requireRequest && !internalCtx.request) {\n throw new APIError(\"BAD_REQUEST\", {\n message: \"Request is required\"\n })\n }\n //@ts-expect-error\n const res = await handler(internalCtx)\n return res as ReturnType<Handler<Path, Opts, R>>\n }\n handle.path = path\n handle.options = options\n handle.method = options.method\n handle.headers = responseHeader\n return handle\n}","import type { statusCode } from \"./utils\"\n\ntype Status = keyof typeof statusCode\n\nexport class APIError extends Error {\n status: Status\n body: Record<string, any>\n constructor(\n status: Status,\n body?: Record<string, any>,\n ) {\n super(\n `API Error: ${status} ${body?.message ?? \"\"}`,\n {\n cause: body,\n }\n )\n this.status = status\n this.body = body ?? {}\n this.stack = \"\";\n this.name = \"BetterCallAPIError\"\n }\n}","import {\n createRouter as createRou3Router,\n addRoute,\n findRoute,\n} from \"rou3\";\nimport { getBody, shouldSerialize, statusCode } from \"./utils\";\nimport { APIError } from \"./better-call-error\";\nimport type { Middleware, MiddlewareHandler } from \"./middleware\";\nimport type { Endpoint, Method } from \"./types\";\n\ninterface RouterConfig {\n /**\n * Throw error if error occurred other than APIError\n */\n throwError?: boolean\n /**\n * Handle error\n */\n onError?: (e: unknown) => void | Promise<void> | Response | Promise<Response>\n /**\n * Base path for the router\n */\n basePath?: string\n /**\n * Middlewares for the router\n */\n routerMiddleware?: ({\n /**\n * The path to match for the middleware to be called\n */\n path: string,\n /**\n * The method to match for the middleware to be called\n * \n * @default \"*\"\n */\n method?: Method | Method[],\n /**\n * The middleware handler\n */\n handler: Endpoint\n })[]\n}\n\nexport const createRouter = <E extends Endpoint, Config extends RouterConfig>(endpoints: E[], config?: Config) => {\n const router = createRou3Router()\n for (const endpoint of endpoints) {\n if (Array.isArray(endpoint.options?.method)) {\n for (const method of endpoint.options.method) {\n addRoute(router, method, endpoint.path, endpoint)\n }\n } else {\n addRoute(router, endpoint.options.method, endpoint.path, endpoint)\n }\n }\n\n const middlewareRouter = createRou3Router()\n for (const route of (config?.routerMiddleware || [])) {\n const methods = Array.isArray(route.method) ? route.method : [route.method]\n for (const method of methods) {\n addRoute(middlewareRouter, method, route.path, route.handler)\n }\n }\n\n const handler = async (request: Request) => {\n const url = new URL(request.url);\n let path = url.pathname\n if (config?.basePath) {\n path = path.split(config.basePath)[1]\n }\n const method = request.method;\n const route = findRoute(router, method, path)\n const handler = route?.data as Endpoint\n const body = await getBody(request)\n const headers = request.headers\n const query = Object.fromEntries(url.searchParams)\n const middleware = findRoute(middlewareRouter, method, path)?.data as Endpoint | undefined\n\n //handler 404\n if (!handler) {\n return new Response(null, {\n status: 404,\n statusText: \"Not Found\"\n })\n }\n try {\n let middlewareContext: Record<string, any> = {}\n if (middleware) {\n const res = await middleware({\n path: path,\n method: method as \"GET\",\n headers,\n params: route?.params as any,\n request: request,\n body: body,\n query\n })\n if (res) {\n middlewareContext = {\n ...res,\n ...middlewareContext\n }\n }\n }\n const handlerRes = await handler({\n path: path,\n method: method as \"GET\",\n headers,\n params: route?.params as any,\n request: request,\n body: body,\n query,\n ...middlewareContext,\n })\n if (handlerRes instanceof Response) {\n return handlerRes\n }\n const resBody = shouldSerialize(handlerRes) ? JSON.stringify(handlerRes) : handlerRes\n return new Response(resBody as any, {\n headers: handler.headers\n })\n } catch (e) {\n if (config?.onError) {\n const onErrorRes = await config.onError(e)\n if (onErrorRes instanceof Response) {\n return onErrorRes\n }\n }\n if (e instanceof APIError) {\n return new Response(e.body ? JSON.stringify(e.body) : null, {\n status: statusCode[e.status],\n statusText: e.status,\n headers: {\n \"Content-Type\": \"application/json\",\n }\n })\n }\n if (config?.throwError) {\n throw e\n }\n return new Response(null, {\n status: 500,\n statusText: \"Internal Server Error\"\n })\n }\n }\n return {\n handler\n }\n}","export async function getBody(request: Request) {\n const contentType = request.headers.get('content-type') || '';\n\n if (!request.body) {\n return undefined\n }\n\n if (contentType.includes('application/json')) {\n return await request.json();\n }\n\n if (contentType.includes('application/x-www-form-urlencoded')) {\n const formData = await request.formData();\n const result: Record<string, string> = {};\n formData.forEach((value, key) => {\n result[key] = value.toString();\n });\n return result;\n }\n\n if (contentType.includes('multipart/form-data')) {\n const formData = await request.formData();\n const result: Record<string, any> = {};\n formData.forEach((value, key) => {\n result[key] = value;\n });\n return result;\n }\n\n if (contentType.includes('text/plain')) {\n return await request.text();\n }\n\n if (contentType.includes('application/octet-stream')) {\n return await request.arrayBuffer();\n }\n\n if (contentType.includes('application/pdf') || contentType.includes('image/') || contentType.includes('video/')) {\n const blob = await request.blob();\n return blob;\n }\n\n if (contentType.includes('application/stream') || request.body instanceof ReadableStream) {\n return request.body;\n }\n\n return await request.text();\n}\n\n\nexport function shouldSerialize(body: any) {\n return typeof body === \"object\" && body !== null && !(body instanceof Blob) && !(body instanceof FormData)\n}\n\nexport const statusCode = {\n \"OK\": 200,\n \"CREATED\": 201,\n \"ACCEPTED\": 202,\n \"NO_CONTENT\": 204,\n \"MULTIPLE_CHOICES\": 300,\n \"MOVED_PERMANENTLY\": 301,\n \"FOUND\": 302,\n \"SEE_OTHER\": 303,\n \"NOT_MODIFIED\": 304,\n \"TEMPORARY_REDIRECT\": 307,\n \"BAD_REQUEST\": 400,\n \"UNAUTHORIZED\": 401,\n \"PAYMENT_REQUIRED\": 402,\n \"FORBIDDEN\": 403,\n \"NOT_FOUND\": 404,\n \"METHOD_NOT_ALLOWED\": 405,\n \"NOT_ACCEPTABLE\": 406,\n \"PROXY_AUTHENTICATION_REQUIRED\": 407,\n \"REQUEST_TIMEOUT\": 408,\n \"CONFLICT\": 409,\n \"GONE\": 410,\n \"LENGTH_REQUIRED\": 411,\n \"PRECONDITION_FAILED\": 412,\n \"PAYLOAD_TOO_LARGE\": 413,\n \"URI_TOO_LONG\": 414,\n \"UNSUPPORTED_MEDIA_TYPE\": 415,\n \"RANGE_NOT_SATISFIABLE\": 416,\n \"EXPECTATION_FAILED\": 417,\n \"I'M_A_TEAPOT\": 418,\n \"MISDIRECTED_REQUEST\": 421,\n \"UNPROCESSABLE_ENTITY\": 422,\n \"LOCKED\": 423,\n \"FAILED_DEPENDENCY\": 424,\n \"TOO_EARLY\": 425,\n \"UPGRADE_REQUIRED\": 426,\n \"PRECONDITION_REQUIRED\": 428,\n \"TOO_MANY_REQUESTS\": 429,\n \"REQUEST_HEADER_FIELDS_TOO_LARGE\": 431,\n \"UNAVAILABLE_FOR_LEGAL_REASONS\": 451,\n \"INTERNAL_SERVER_ERROR\": 500,\n \"NOT_IMPLEMENTED\": 501,\n \"BAD_GATEWAY\": 502,\n \"SERVICE_UNAVAILABLE\": 503,\n \"GATEWAY_TIMEOUT\": 504,\n \"HTTP_VERSION_NOT_SUPPORTED\": 505,\n \"VARIANT_ALSO_NEGOTIATES\": 506,\n \"INSUFFICIENT_STORAGE\": 507,\n \"LOOP_DETECTED\": 508,\n \"NOT_EXTENDED\": 510,\n \"NETWORK_AUTHENTICATION_REQUIRED\": 511,\n}\n","import { z } from \"zod\"\nimport type { ContextTools, Endpoint, EndpointOptions, EndpointResponse, Handler, InferBody, InferHeaders, InferRequest, Prettify } from \"./types\"\nimport { createEndpoint } from \"./endpoint\"\n\nexport type MiddlewareHandler<Opts extends EndpointOptions, R extends EndpointResponse> = (ctx: Prettify<InferBody<Opts> & InferRequest<Opts> & InferHeaders<Opts> & {\n params?: Record<string, string>,\n query?: Record<string, string>,\n} & ContextTools>) => Promise<R>\n\nexport function createMiddleware<Opts extends EndpointOptions, R extends EndpointResponse>(optionsOrHandler: MiddlewareHandler<Opts, R>): Endpoint<Handler<string, Opts, R>, Opts>\nexport function createMiddleware<Opts extends Omit<EndpointOptions, \"method\">, R extends EndpointResponse>(optionsOrHandler: Opts, handler: MiddlewareHandler<Opts & {\n method: \"*\"\n}, R>): Endpoint<Handler<string, Opts & {\n method: \"*\"\n}, R>, Opts & {\n method: \"*\"\n}>\nexport function createMiddleware(optionsOrHandler: any, handler?: any) {\n if (typeof optionsOrHandler === \"function\") {\n return createEndpoint(\"*\", {\n method: \"*\"\n }, optionsOrHandler)\n }\n if (!handler) {\n throw new Error(\"Middleware handler is required\")\n }\n const endpoint = createEndpoint(\"*\", {\n ...optionsOrHandler,\n method: \"*\"\n }, handler)\n return endpoint as any\n}\n\nexport type Middleware<Opts extends EndpointOptions, R> = (opts: Opts, handler: (ctx: {\n body?: InferBody<Opts>,\n params?: Record<string, string>,\n query?: Record<string, string>\n}) => Promise<R>) => Endpoint\n\nconst m1 = createMiddleware({\n body: z.object({\n name: z.string()\n }),\n}, async (ctx) => {\n ctx\n})"],"mappings":"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","names":["src_exports","__export","APIError","createEndpoint","createMiddleware","createRouter","getBody","shouldSerialize","statusCode","__toCommonJS","import_zod","APIError","status","body","__publicField","createEndpoint","path","options","handler","responseHeader","handle","ctx","internalCtx","key","value","cookie","middleware","res","body","e","APIError","import_rou3","getBody","request","contentType","formData","result","value","key","shouldSerialize","body","statusCode","createRouter","endpoints","config","router","createRou3Router","endpoint","method","middlewareRouter","route","methods","request","url","path","handler","body","getBody","headers","query","middleware","middlewareContext","res","handlerRes","resBody","shouldSerialize","e","onErrorRes","APIError","statusCode","import_zod","createMiddleware","optionsOrHandler","handler","createEndpoint","m1","ctx"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,29 +1,11 @@
|
|
|
1
1
|
import { ZodSchema, ZodOptional, z } from 'zod';
|
|
2
|
-
import { HasRequiredKeys as HasRequiredKeys$1 } from 'type-fest';
|
|
3
|
-
|
|
4
|
-
type Middleware<E extends Record<string, string>> = (ctx: Context<string, EndpointOptions, E>) => Promise<{
|
|
5
|
-
context: E;
|
|
6
|
-
} | void>;
|
|
7
|
-
declare const createMiddleware: <E extends Record<string, any>, M extends Middleware<E>>(middleware: M) => <Path extends string, Opts extends EndpointOptions, R extends EndpointResponse>(path: Path, options: Opts, handler: Handler<Path, Opts, R, Awaited<ReturnType<M>> extends {
|
|
8
|
-
context: infer C;
|
|
9
|
-
} ? C extends Record<string, any> ? C : {} : {}>) => {
|
|
10
|
-
(...ctx: HasRequiredKeys$1<Context<Path, Opts, Awaited<ReturnType<M>> extends {
|
|
11
|
-
context: infer C;
|
|
12
|
-
} ? C extends Record<string, any> ? C : {} : {}>> extends true ? [Context<Path, Opts, Awaited<ReturnType<M>> extends {
|
|
13
|
-
context: infer C;
|
|
14
|
-
} ? C extends Record<string, any> ? C : {} : {}>] : [Context<Path, Opts, Awaited<ReturnType<M>> extends {
|
|
15
|
-
context: infer C;
|
|
16
|
-
} ? C extends Record<string, any> ? C : {} : {}>?]): Promise<R>;
|
|
17
|
-
path: Path;
|
|
18
|
-
options: Opts;
|
|
19
|
-
middleware: M;
|
|
20
|
-
};
|
|
21
2
|
|
|
3
|
+
type UnionToIntersection<Union> = (Union extends unknown ? (distributedUnion: Union) => void : never) extends ((mergedIntersection: infer Intersection) => void) ? Intersection & Union : never;
|
|
22
4
|
type RequiredKeysOf<BaseType extends object> = Exclude<{
|
|
23
5
|
[Key in keyof BaseType]: BaseType extends Record<Key, BaseType[Key]> ? Key : never;
|
|
24
6
|
}[keyof BaseType], undefined>;
|
|
25
7
|
type HasRequiredKeys<BaseType extends object> = RequiredKeysOf<BaseType> extends never ? false : true;
|
|
26
|
-
|
|
8
|
+
|
|
27
9
|
interface EndpointOptions {
|
|
28
10
|
method: Method | Method[];
|
|
29
11
|
body?: ZodSchema;
|
|
@@ -37,7 +19,16 @@ interface EndpointOptions {
|
|
|
37
19
|
* If true request object will be required
|
|
38
20
|
*/
|
|
39
21
|
requireRequest?: boolean;
|
|
22
|
+
/**
|
|
23
|
+
* List of endpoints that will be called before this endpoint
|
|
24
|
+
*/
|
|
25
|
+
use?: Endpoint[];
|
|
40
26
|
}
|
|
27
|
+
type Endpoint<Handler extends (ctx: any) => Promise<any> = (ctx: any) => Promise<any>, Option extends EndpointOptions = EndpointOptions> = {
|
|
28
|
+
path: string;
|
|
29
|
+
options: Option;
|
|
30
|
+
headers?: Headers;
|
|
31
|
+
} & (Handler);
|
|
41
32
|
type InferParamPath<Path> = Path extends `${infer _Start}:${infer Param}/${infer Rest}` ? {
|
|
42
33
|
[K in Param | keyof InferParamPath<Rest>]: string;
|
|
43
34
|
} : Path extends `${infer _Start}:${infer Param}` ? {
|
|
@@ -52,33 +43,57 @@ type Prettify<T> = {
|
|
|
52
43
|
[key in keyof T]: T[key];
|
|
53
44
|
} & {};
|
|
54
45
|
type ContextTools = {
|
|
46
|
+
/**
|
|
47
|
+
* Set header
|
|
48
|
+
*
|
|
49
|
+
* If it's called outside of a request it will just be ignored.
|
|
50
|
+
*/
|
|
55
51
|
setHeader: (key: string, value: string) => void;
|
|
52
|
+
/**
|
|
53
|
+
* cookie setter.
|
|
54
|
+
*
|
|
55
|
+
* If it's called outside of a request it will just be ignored.
|
|
56
|
+
*/
|
|
56
57
|
setCookie: (key: string, value: string) => void;
|
|
58
|
+
/**
|
|
59
|
+
* Get cookie value
|
|
60
|
+
*
|
|
61
|
+
* If it's called outside of a request it will just be ignored.
|
|
62
|
+
*/
|
|
57
63
|
getCookie: (key: string) => string | undefined;
|
|
58
64
|
};
|
|
59
|
-
type Context<Path extends string, Opts extends EndpointOptions
|
|
65
|
+
type Context<Path extends string, Opts extends EndpointOptions> = InferBody<Opts> & InferParam<Path> & InferMethod<Opts['method']> & InferHeaders<Opts> & InferRequest<Opts> & InferQuery<Opts["query"]>;
|
|
66
|
+
type InferUse<Opts extends EndpointOptions> = Opts["use"] extends Endpoint[] ? {
|
|
67
|
+
context: UnionToIntersection<Awaited<ReturnType<Opts["use"][number]>>>;
|
|
68
|
+
} : {};
|
|
69
|
+
type InferUseOptions<Opts extends EndpointOptions> = Opts["use"] extends Array<infer U> ? UnionToIntersection<U extends Endpoint ? U['options'] : {
|
|
70
|
+
body?: {};
|
|
71
|
+
requireRequest?: boolean;
|
|
72
|
+
requireHeaders?: boolean;
|
|
73
|
+
}> : {
|
|
74
|
+
body?: {};
|
|
75
|
+
requireRequest?: boolean;
|
|
76
|
+
requireHeaders?: boolean;
|
|
77
|
+
};
|
|
60
78
|
type InferMethod<M extends Method | Method[]> = M extends Array<Method> ? {
|
|
61
79
|
method: M[number];
|
|
62
80
|
} : {
|
|
63
81
|
method?: M;
|
|
64
82
|
};
|
|
65
|
-
type InferHeaders<HeaderReq> = HeaderReq extends true ? {
|
|
83
|
+
type InferHeaders<Opt extends EndpointOptions, HeaderReq = Opt["requireHeaders"]> = HeaderReq extends true ? {
|
|
84
|
+
headers: Headers;
|
|
85
|
+
} : InferUseOptions<Opt>['requireHeaders'] extends true ? {
|
|
66
86
|
headers: Headers;
|
|
67
87
|
} : {
|
|
68
88
|
headers?: Headers;
|
|
69
89
|
};
|
|
70
|
-
type InferRequest<RequestReq> = RequestReq extends true ? {
|
|
90
|
+
type InferRequest<Opt extends EndpointOptions, RequestReq = Opt["requireRequest"]> = RequestReq extends true ? {
|
|
91
|
+
request: Request;
|
|
92
|
+
} : InferUseOptions<Opt>['requireRequest'] extends true ? {
|
|
71
93
|
request: Request;
|
|
72
94
|
} : {
|
|
73
95
|
request?: Request;
|
|
74
96
|
};
|
|
75
|
-
type InferBody<Body> = Body extends ZodSchema ? Body extends ZodOptional<any> ? {
|
|
76
|
-
body?: z.infer<Body>;
|
|
77
|
-
} : {
|
|
78
|
-
body: z.infer<Body>;
|
|
79
|
-
} : {
|
|
80
|
-
body?: undefined;
|
|
81
|
-
};
|
|
82
97
|
type InferQuery<Query> = Query extends ZodSchema ? Query extends ZodOptional<any> ? {
|
|
83
98
|
query?: z.infer<Query>;
|
|
84
99
|
} : {
|
|
@@ -87,34 +102,36 @@ type InferQuery<Query> = Query extends ZodSchema ? Query extends ZodOptional<any
|
|
|
87
102
|
query?: undefined;
|
|
88
103
|
};
|
|
89
104
|
type InferParam<Path extends string, ParamPath extends InferParamPath<Path> = InferParamPath<Path>, WildCard extends InferParamWildCard<Path> = InferParamWildCard<Path>> = ParamPath extends undefined ? WildCard extends undefined ? {
|
|
90
|
-
params?:
|
|
105
|
+
params?: Record<string, string>;
|
|
91
106
|
} : {
|
|
92
107
|
params: WildCard;
|
|
93
108
|
} : {
|
|
94
|
-
params: ParamPath & (WildCard extends undefined ? {} : WildCard)
|
|
109
|
+
params: Prettify<ParamPath & (WildCard extends undefined ? {} : WildCard)>;
|
|
95
110
|
};
|
|
96
111
|
type EndpointResponse = Record<string, any> | string | boolean | number | void | undefined;
|
|
97
|
-
type Handler<Path extends string, Opts extends EndpointOptions, R extends EndpointResponse
|
|
112
|
+
type Handler<Path extends string, Opts extends EndpointOptions, R extends EndpointResponse> = (ctx: Prettify<Context<Path, Opts> & InferUse<Opts> & ContextTools>) => Promise<R>;
|
|
113
|
+
type Method = "GET" | "POST" | "PUT" | "DELETE" | "PATCH" | "*";
|
|
114
|
+
type InferBody<Opts extends EndpointOptions, Body extends ZodSchema | undefined = (Opts["body"] & (undefined extends InferUseOptions<Opts>['body'] ? {} : InferUseOptions<Opts>['body']))> = Body extends ZodSchema ? Body extends ZodOptional<any> ? {
|
|
115
|
+
body?: Prettify<z.infer<Body>>;
|
|
116
|
+
} : {
|
|
117
|
+
body: Prettify<z.infer<Body>>;
|
|
118
|
+
} : {
|
|
119
|
+
body?: undefined;
|
|
120
|
+
};
|
|
121
|
+
|
|
98
122
|
interface EndpointConfig {
|
|
99
123
|
/**
|
|
100
124
|
* Throw when the response isn't in 200 range
|
|
101
125
|
*/
|
|
102
126
|
throwOnError?: boolean;
|
|
103
127
|
}
|
|
104
|
-
declare function createEndpoint<Path extends string, Opts extends EndpointOptions, R extends EndpointResponse>(path: Path, options: Opts, handler: Handler<Path, Opts, R
|
|
105
|
-
(...ctx: HasRequiredKeys<Context<Path, Opts
|
|
128
|
+
declare function createEndpoint<Path extends string, Opts extends EndpointOptions, R extends EndpointResponse>(path: Path, options: Opts, handler: Handler<Path, Opts, R>): {
|
|
129
|
+
(...ctx: HasRequiredKeys<Context<Path, Opts>> extends true ? [Context<Path, Opts>] : [Context<Path, Opts>?]): Promise<R>;
|
|
106
130
|
path: Path;
|
|
107
131
|
options: Opts;
|
|
108
132
|
method: Method | Method[];
|
|
109
133
|
headers: Headers;
|
|
110
|
-
middleware: Middleware<any> | undefined;
|
|
111
134
|
};
|
|
112
|
-
type Endpoint = {
|
|
113
|
-
path: string;
|
|
114
|
-
options: EndpointOptions;
|
|
115
|
-
middleware?: Middleware<any>;
|
|
116
|
-
headers?: Headers;
|
|
117
|
-
} & ((ctx: any) => Promise<any>);
|
|
118
135
|
|
|
119
136
|
interface RouterConfig {
|
|
120
137
|
/**
|
|
@@ -129,64 +146,101 @@ interface RouterConfig {
|
|
|
129
146
|
* Base path for the router
|
|
130
147
|
*/
|
|
131
148
|
basePath?: string;
|
|
149
|
+
/**
|
|
150
|
+
* Middlewares for the router
|
|
151
|
+
*/
|
|
152
|
+
routerMiddleware?: ({
|
|
153
|
+
/**
|
|
154
|
+
* The path to match for the middleware to be called
|
|
155
|
+
*/
|
|
156
|
+
path: string;
|
|
157
|
+
/**
|
|
158
|
+
* The method to match for the middleware to be called
|
|
159
|
+
*
|
|
160
|
+
* @default "*"
|
|
161
|
+
*/
|
|
162
|
+
method?: Method | Method[];
|
|
163
|
+
/**
|
|
164
|
+
* The middleware handler
|
|
165
|
+
*/
|
|
166
|
+
handler: Endpoint;
|
|
167
|
+
})[];
|
|
132
168
|
}
|
|
133
169
|
declare const createRouter: <E extends Endpoint, Config extends RouterConfig>(endpoints: E[], config?: Config) => {
|
|
134
170
|
handler: (request: Request) => Promise<Response>;
|
|
135
171
|
};
|
|
136
172
|
|
|
173
|
+
type MiddlewareHandler<Opts extends EndpointOptions, R extends EndpointResponse> = (ctx: Prettify<InferBody<Opts> & InferRequest<Opts> & InferHeaders<Opts> & {
|
|
174
|
+
params?: Record<string, string>;
|
|
175
|
+
query?: Record<string, string>;
|
|
176
|
+
} & ContextTools>) => Promise<R>;
|
|
177
|
+
declare function createMiddleware<Opts extends EndpointOptions, R extends EndpointResponse>(optionsOrHandler: MiddlewareHandler<Opts, R>): Endpoint<Handler<string, Opts, R>, Opts>;
|
|
178
|
+
declare function createMiddleware<Opts extends Omit<EndpointOptions, "method">, R extends EndpointResponse>(optionsOrHandler: Opts, handler: MiddlewareHandler<Opts & {
|
|
179
|
+
method: "*";
|
|
180
|
+
}, R>): Endpoint<Handler<string, Opts & {
|
|
181
|
+
method: "*";
|
|
182
|
+
}, R>, Opts & {
|
|
183
|
+
method: "*";
|
|
184
|
+
}>;
|
|
185
|
+
type Middleware<Opts extends EndpointOptions, R> = (opts: Opts, handler: (ctx: {
|
|
186
|
+
body?: InferBody<Opts>;
|
|
187
|
+
params?: Record<string, string>;
|
|
188
|
+
query?: Record<string, string>;
|
|
189
|
+
}) => Promise<R>) => Endpoint;
|
|
190
|
+
|
|
137
191
|
declare function getBody(request: Request): Promise<any>;
|
|
138
192
|
declare function shouldSerialize(body: any): boolean;
|
|
139
193
|
declare const statusCode: {
|
|
140
194
|
OK: number;
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
"I'
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
195
|
+
CREATED: number;
|
|
196
|
+
ACCEPTED: number;
|
|
197
|
+
NO_CONTENT: number;
|
|
198
|
+
MULTIPLE_CHOICES: number;
|
|
199
|
+
MOVED_PERMANENTLY: number;
|
|
200
|
+
FOUND: number;
|
|
201
|
+
SEE_OTHER: number;
|
|
202
|
+
NOT_MODIFIED: number;
|
|
203
|
+
TEMPORARY_REDIRECT: number;
|
|
204
|
+
BAD_REQUEST: number;
|
|
205
|
+
UNAUTHORIZED: number;
|
|
206
|
+
PAYMENT_REQUIRED: number;
|
|
207
|
+
FORBIDDEN: number;
|
|
208
|
+
NOT_FOUND: number;
|
|
209
|
+
METHOD_NOT_ALLOWED: number;
|
|
210
|
+
NOT_ACCEPTABLE: number;
|
|
211
|
+
PROXY_AUTHENTICATION_REQUIRED: number;
|
|
212
|
+
REQUEST_TIMEOUT: number;
|
|
213
|
+
CONFLICT: number;
|
|
214
|
+
GONE: number;
|
|
215
|
+
LENGTH_REQUIRED: number;
|
|
216
|
+
PRECONDITION_FAILED: number;
|
|
217
|
+
PAYLOAD_TOO_LARGE: number;
|
|
218
|
+
URI_TOO_LONG: number;
|
|
219
|
+
UNSUPPORTED_MEDIA_TYPE: number;
|
|
220
|
+
RANGE_NOT_SATISFIABLE: number;
|
|
221
|
+
EXPECTATION_FAILED: number;
|
|
222
|
+
"I'M_A_TEAPOT": number;
|
|
223
|
+
MISDIRECTED_REQUEST: number;
|
|
224
|
+
UNPROCESSABLE_ENTITY: number;
|
|
225
|
+
LOCKED: number;
|
|
226
|
+
FAILED_DEPENDENCY: number;
|
|
227
|
+
TOO_EARLY: number;
|
|
228
|
+
UPGRADE_REQUIRED: number;
|
|
229
|
+
PRECONDITION_REQUIRED: number;
|
|
230
|
+
TOO_MANY_REQUESTS: number;
|
|
231
|
+
REQUEST_HEADER_FIELDS_TOO_LARGE: number;
|
|
232
|
+
UNAVAILABLE_FOR_LEGAL_REASONS: number;
|
|
233
|
+
INTERNAL_SERVER_ERROR: number;
|
|
234
|
+
NOT_IMPLEMENTED: number;
|
|
235
|
+
BAD_GATEWAY: number;
|
|
236
|
+
SERVICE_UNAVAILABLE: number;
|
|
237
|
+
GATEWAY_TIMEOUT: number;
|
|
238
|
+
HTTP_VERSION_NOT_SUPPORTED: number;
|
|
239
|
+
VARIANT_ALSO_NEGOTIATES: number;
|
|
240
|
+
INSUFFICIENT_STORAGE: number;
|
|
241
|
+
LOOP_DETECTED: number;
|
|
242
|
+
NOT_EXTENDED: number;
|
|
243
|
+
NETWORK_AUTHENTICATION_REQUIRED: number;
|
|
190
244
|
};
|
|
191
245
|
|
|
192
246
|
type Status = keyof typeof statusCode;
|
|
@@ -196,4 +250,4 @@ declare class APIError extends Error {
|
|
|
196
250
|
constructor(status: Status, body?: Record<string, any>);
|
|
197
251
|
}
|
|
198
252
|
|
|
199
|
-
export { APIError, type
|
|
253
|
+
export { APIError, type EndpointConfig, type Middleware, type MiddlewareHandler, createEndpoint, createMiddleware, createRouter, getBody, shouldSerialize, statusCode };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,29 +1,11 @@
|
|
|
1
1
|
import { ZodSchema, ZodOptional, z } from 'zod';
|
|
2
|
-
import { HasRequiredKeys as HasRequiredKeys$1 } from 'type-fest';
|
|
3
|
-
|
|
4
|
-
type Middleware<E extends Record<string, string>> = (ctx: Context<string, EndpointOptions, E>) => Promise<{
|
|
5
|
-
context: E;
|
|
6
|
-
} | void>;
|
|
7
|
-
declare const createMiddleware: <E extends Record<string, any>, M extends Middleware<E>>(middleware: M) => <Path extends string, Opts extends EndpointOptions, R extends EndpointResponse>(path: Path, options: Opts, handler: Handler<Path, Opts, R, Awaited<ReturnType<M>> extends {
|
|
8
|
-
context: infer C;
|
|
9
|
-
} ? C extends Record<string, any> ? C : {} : {}>) => {
|
|
10
|
-
(...ctx: HasRequiredKeys$1<Context<Path, Opts, Awaited<ReturnType<M>> extends {
|
|
11
|
-
context: infer C;
|
|
12
|
-
} ? C extends Record<string, any> ? C : {} : {}>> extends true ? [Context<Path, Opts, Awaited<ReturnType<M>> extends {
|
|
13
|
-
context: infer C;
|
|
14
|
-
} ? C extends Record<string, any> ? C : {} : {}>] : [Context<Path, Opts, Awaited<ReturnType<M>> extends {
|
|
15
|
-
context: infer C;
|
|
16
|
-
} ? C extends Record<string, any> ? C : {} : {}>?]): Promise<R>;
|
|
17
|
-
path: Path;
|
|
18
|
-
options: Opts;
|
|
19
|
-
middleware: M;
|
|
20
|
-
};
|
|
21
2
|
|
|
3
|
+
type UnionToIntersection<Union> = (Union extends unknown ? (distributedUnion: Union) => void : never) extends ((mergedIntersection: infer Intersection) => void) ? Intersection & Union : never;
|
|
22
4
|
type RequiredKeysOf<BaseType extends object> = Exclude<{
|
|
23
5
|
[Key in keyof BaseType]: BaseType extends Record<Key, BaseType[Key]> ? Key : never;
|
|
24
6
|
}[keyof BaseType], undefined>;
|
|
25
7
|
type HasRequiredKeys<BaseType extends object> = RequiredKeysOf<BaseType> extends never ? false : true;
|
|
26
|
-
|
|
8
|
+
|
|
27
9
|
interface EndpointOptions {
|
|
28
10
|
method: Method | Method[];
|
|
29
11
|
body?: ZodSchema;
|
|
@@ -37,7 +19,16 @@ interface EndpointOptions {
|
|
|
37
19
|
* If true request object will be required
|
|
38
20
|
*/
|
|
39
21
|
requireRequest?: boolean;
|
|
22
|
+
/**
|
|
23
|
+
* List of endpoints that will be called before this endpoint
|
|
24
|
+
*/
|
|
25
|
+
use?: Endpoint[];
|
|
40
26
|
}
|
|
27
|
+
type Endpoint<Handler extends (ctx: any) => Promise<any> = (ctx: any) => Promise<any>, Option extends EndpointOptions = EndpointOptions> = {
|
|
28
|
+
path: string;
|
|
29
|
+
options: Option;
|
|
30
|
+
headers?: Headers;
|
|
31
|
+
} & (Handler);
|
|
41
32
|
type InferParamPath<Path> = Path extends `${infer _Start}:${infer Param}/${infer Rest}` ? {
|
|
42
33
|
[K in Param | keyof InferParamPath<Rest>]: string;
|
|
43
34
|
} : Path extends `${infer _Start}:${infer Param}` ? {
|
|
@@ -52,33 +43,57 @@ type Prettify<T> = {
|
|
|
52
43
|
[key in keyof T]: T[key];
|
|
53
44
|
} & {};
|
|
54
45
|
type ContextTools = {
|
|
46
|
+
/**
|
|
47
|
+
* Set header
|
|
48
|
+
*
|
|
49
|
+
* If it's called outside of a request it will just be ignored.
|
|
50
|
+
*/
|
|
55
51
|
setHeader: (key: string, value: string) => void;
|
|
52
|
+
/**
|
|
53
|
+
* cookie setter.
|
|
54
|
+
*
|
|
55
|
+
* If it's called outside of a request it will just be ignored.
|
|
56
|
+
*/
|
|
56
57
|
setCookie: (key: string, value: string) => void;
|
|
58
|
+
/**
|
|
59
|
+
* Get cookie value
|
|
60
|
+
*
|
|
61
|
+
* If it's called outside of a request it will just be ignored.
|
|
62
|
+
*/
|
|
57
63
|
getCookie: (key: string) => string | undefined;
|
|
58
64
|
};
|
|
59
|
-
type Context<Path extends string, Opts extends EndpointOptions
|
|
65
|
+
type Context<Path extends string, Opts extends EndpointOptions> = InferBody<Opts> & InferParam<Path> & InferMethod<Opts['method']> & InferHeaders<Opts> & InferRequest<Opts> & InferQuery<Opts["query"]>;
|
|
66
|
+
type InferUse<Opts extends EndpointOptions> = Opts["use"] extends Endpoint[] ? {
|
|
67
|
+
context: UnionToIntersection<Awaited<ReturnType<Opts["use"][number]>>>;
|
|
68
|
+
} : {};
|
|
69
|
+
type InferUseOptions<Opts extends EndpointOptions> = Opts["use"] extends Array<infer U> ? UnionToIntersection<U extends Endpoint ? U['options'] : {
|
|
70
|
+
body?: {};
|
|
71
|
+
requireRequest?: boolean;
|
|
72
|
+
requireHeaders?: boolean;
|
|
73
|
+
}> : {
|
|
74
|
+
body?: {};
|
|
75
|
+
requireRequest?: boolean;
|
|
76
|
+
requireHeaders?: boolean;
|
|
77
|
+
};
|
|
60
78
|
type InferMethod<M extends Method | Method[]> = M extends Array<Method> ? {
|
|
61
79
|
method: M[number];
|
|
62
80
|
} : {
|
|
63
81
|
method?: M;
|
|
64
82
|
};
|
|
65
|
-
type InferHeaders<HeaderReq> = HeaderReq extends true ? {
|
|
83
|
+
type InferHeaders<Opt extends EndpointOptions, HeaderReq = Opt["requireHeaders"]> = HeaderReq extends true ? {
|
|
84
|
+
headers: Headers;
|
|
85
|
+
} : InferUseOptions<Opt>['requireHeaders'] extends true ? {
|
|
66
86
|
headers: Headers;
|
|
67
87
|
} : {
|
|
68
88
|
headers?: Headers;
|
|
69
89
|
};
|
|
70
|
-
type InferRequest<RequestReq> = RequestReq extends true ? {
|
|
90
|
+
type InferRequest<Opt extends EndpointOptions, RequestReq = Opt["requireRequest"]> = RequestReq extends true ? {
|
|
91
|
+
request: Request;
|
|
92
|
+
} : InferUseOptions<Opt>['requireRequest'] extends true ? {
|
|
71
93
|
request: Request;
|
|
72
94
|
} : {
|
|
73
95
|
request?: Request;
|
|
74
96
|
};
|
|
75
|
-
type InferBody<Body> = Body extends ZodSchema ? Body extends ZodOptional<any> ? {
|
|
76
|
-
body?: z.infer<Body>;
|
|
77
|
-
} : {
|
|
78
|
-
body: z.infer<Body>;
|
|
79
|
-
} : {
|
|
80
|
-
body?: undefined;
|
|
81
|
-
};
|
|
82
97
|
type InferQuery<Query> = Query extends ZodSchema ? Query extends ZodOptional<any> ? {
|
|
83
98
|
query?: z.infer<Query>;
|
|
84
99
|
} : {
|
|
@@ -87,34 +102,36 @@ type InferQuery<Query> = Query extends ZodSchema ? Query extends ZodOptional<any
|
|
|
87
102
|
query?: undefined;
|
|
88
103
|
};
|
|
89
104
|
type InferParam<Path extends string, ParamPath extends InferParamPath<Path> = InferParamPath<Path>, WildCard extends InferParamWildCard<Path> = InferParamWildCard<Path>> = ParamPath extends undefined ? WildCard extends undefined ? {
|
|
90
|
-
params?:
|
|
105
|
+
params?: Record<string, string>;
|
|
91
106
|
} : {
|
|
92
107
|
params: WildCard;
|
|
93
108
|
} : {
|
|
94
|
-
params: ParamPath & (WildCard extends undefined ? {} : WildCard)
|
|
109
|
+
params: Prettify<ParamPath & (WildCard extends undefined ? {} : WildCard)>;
|
|
95
110
|
};
|
|
96
111
|
type EndpointResponse = Record<string, any> | string | boolean | number | void | undefined;
|
|
97
|
-
type Handler<Path extends string, Opts extends EndpointOptions, R extends EndpointResponse
|
|
112
|
+
type Handler<Path extends string, Opts extends EndpointOptions, R extends EndpointResponse> = (ctx: Prettify<Context<Path, Opts> & InferUse<Opts> & ContextTools>) => Promise<R>;
|
|
113
|
+
type Method = "GET" | "POST" | "PUT" | "DELETE" | "PATCH" | "*";
|
|
114
|
+
type InferBody<Opts extends EndpointOptions, Body extends ZodSchema | undefined = (Opts["body"] & (undefined extends InferUseOptions<Opts>['body'] ? {} : InferUseOptions<Opts>['body']))> = Body extends ZodSchema ? Body extends ZodOptional<any> ? {
|
|
115
|
+
body?: Prettify<z.infer<Body>>;
|
|
116
|
+
} : {
|
|
117
|
+
body: Prettify<z.infer<Body>>;
|
|
118
|
+
} : {
|
|
119
|
+
body?: undefined;
|
|
120
|
+
};
|
|
121
|
+
|
|
98
122
|
interface EndpointConfig {
|
|
99
123
|
/**
|
|
100
124
|
* Throw when the response isn't in 200 range
|
|
101
125
|
*/
|
|
102
126
|
throwOnError?: boolean;
|
|
103
127
|
}
|
|
104
|
-
declare function createEndpoint<Path extends string, Opts extends EndpointOptions, R extends EndpointResponse>(path: Path, options: Opts, handler: Handler<Path, Opts, R
|
|
105
|
-
(...ctx: HasRequiredKeys<Context<Path, Opts
|
|
128
|
+
declare function createEndpoint<Path extends string, Opts extends EndpointOptions, R extends EndpointResponse>(path: Path, options: Opts, handler: Handler<Path, Opts, R>): {
|
|
129
|
+
(...ctx: HasRequiredKeys<Context<Path, Opts>> extends true ? [Context<Path, Opts>] : [Context<Path, Opts>?]): Promise<R>;
|
|
106
130
|
path: Path;
|
|
107
131
|
options: Opts;
|
|
108
132
|
method: Method | Method[];
|
|
109
133
|
headers: Headers;
|
|
110
|
-
middleware: Middleware<any> | undefined;
|
|
111
134
|
};
|
|
112
|
-
type Endpoint = {
|
|
113
|
-
path: string;
|
|
114
|
-
options: EndpointOptions;
|
|
115
|
-
middleware?: Middleware<any>;
|
|
116
|
-
headers?: Headers;
|
|
117
|
-
} & ((ctx: any) => Promise<any>);
|
|
118
135
|
|
|
119
136
|
interface RouterConfig {
|
|
120
137
|
/**
|
|
@@ -129,64 +146,101 @@ interface RouterConfig {
|
|
|
129
146
|
* Base path for the router
|
|
130
147
|
*/
|
|
131
148
|
basePath?: string;
|
|
149
|
+
/**
|
|
150
|
+
* Middlewares for the router
|
|
151
|
+
*/
|
|
152
|
+
routerMiddleware?: ({
|
|
153
|
+
/**
|
|
154
|
+
* The path to match for the middleware to be called
|
|
155
|
+
*/
|
|
156
|
+
path: string;
|
|
157
|
+
/**
|
|
158
|
+
* The method to match for the middleware to be called
|
|
159
|
+
*
|
|
160
|
+
* @default "*"
|
|
161
|
+
*/
|
|
162
|
+
method?: Method | Method[];
|
|
163
|
+
/**
|
|
164
|
+
* The middleware handler
|
|
165
|
+
*/
|
|
166
|
+
handler: Endpoint;
|
|
167
|
+
})[];
|
|
132
168
|
}
|
|
133
169
|
declare const createRouter: <E extends Endpoint, Config extends RouterConfig>(endpoints: E[], config?: Config) => {
|
|
134
170
|
handler: (request: Request) => Promise<Response>;
|
|
135
171
|
};
|
|
136
172
|
|
|
173
|
+
type MiddlewareHandler<Opts extends EndpointOptions, R extends EndpointResponse> = (ctx: Prettify<InferBody<Opts> & InferRequest<Opts> & InferHeaders<Opts> & {
|
|
174
|
+
params?: Record<string, string>;
|
|
175
|
+
query?: Record<string, string>;
|
|
176
|
+
} & ContextTools>) => Promise<R>;
|
|
177
|
+
declare function createMiddleware<Opts extends EndpointOptions, R extends EndpointResponse>(optionsOrHandler: MiddlewareHandler<Opts, R>): Endpoint<Handler<string, Opts, R>, Opts>;
|
|
178
|
+
declare function createMiddleware<Opts extends Omit<EndpointOptions, "method">, R extends EndpointResponse>(optionsOrHandler: Opts, handler: MiddlewareHandler<Opts & {
|
|
179
|
+
method: "*";
|
|
180
|
+
}, R>): Endpoint<Handler<string, Opts & {
|
|
181
|
+
method: "*";
|
|
182
|
+
}, R>, Opts & {
|
|
183
|
+
method: "*";
|
|
184
|
+
}>;
|
|
185
|
+
type Middleware<Opts extends EndpointOptions, R> = (opts: Opts, handler: (ctx: {
|
|
186
|
+
body?: InferBody<Opts>;
|
|
187
|
+
params?: Record<string, string>;
|
|
188
|
+
query?: Record<string, string>;
|
|
189
|
+
}) => Promise<R>) => Endpoint;
|
|
190
|
+
|
|
137
191
|
declare function getBody(request: Request): Promise<any>;
|
|
138
192
|
declare function shouldSerialize(body: any): boolean;
|
|
139
193
|
declare const statusCode: {
|
|
140
194
|
OK: number;
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
"I'
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
195
|
+
CREATED: number;
|
|
196
|
+
ACCEPTED: number;
|
|
197
|
+
NO_CONTENT: number;
|
|
198
|
+
MULTIPLE_CHOICES: number;
|
|
199
|
+
MOVED_PERMANENTLY: number;
|
|
200
|
+
FOUND: number;
|
|
201
|
+
SEE_OTHER: number;
|
|
202
|
+
NOT_MODIFIED: number;
|
|
203
|
+
TEMPORARY_REDIRECT: number;
|
|
204
|
+
BAD_REQUEST: number;
|
|
205
|
+
UNAUTHORIZED: number;
|
|
206
|
+
PAYMENT_REQUIRED: number;
|
|
207
|
+
FORBIDDEN: number;
|
|
208
|
+
NOT_FOUND: number;
|
|
209
|
+
METHOD_NOT_ALLOWED: number;
|
|
210
|
+
NOT_ACCEPTABLE: number;
|
|
211
|
+
PROXY_AUTHENTICATION_REQUIRED: number;
|
|
212
|
+
REQUEST_TIMEOUT: number;
|
|
213
|
+
CONFLICT: number;
|
|
214
|
+
GONE: number;
|
|
215
|
+
LENGTH_REQUIRED: number;
|
|
216
|
+
PRECONDITION_FAILED: number;
|
|
217
|
+
PAYLOAD_TOO_LARGE: number;
|
|
218
|
+
URI_TOO_LONG: number;
|
|
219
|
+
UNSUPPORTED_MEDIA_TYPE: number;
|
|
220
|
+
RANGE_NOT_SATISFIABLE: number;
|
|
221
|
+
EXPECTATION_FAILED: number;
|
|
222
|
+
"I'M_A_TEAPOT": number;
|
|
223
|
+
MISDIRECTED_REQUEST: number;
|
|
224
|
+
UNPROCESSABLE_ENTITY: number;
|
|
225
|
+
LOCKED: number;
|
|
226
|
+
FAILED_DEPENDENCY: number;
|
|
227
|
+
TOO_EARLY: number;
|
|
228
|
+
UPGRADE_REQUIRED: number;
|
|
229
|
+
PRECONDITION_REQUIRED: number;
|
|
230
|
+
TOO_MANY_REQUESTS: number;
|
|
231
|
+
REQUEST_HEADER_FIELDS_TOO_LARGE: number;
|
|
232
|
+
UNAVAILABLE_FOR_LEGAL_REASONS: number;
|
|
233
|
+
INTERNAL_SERVER_ERROR: number;
|
|
234
|
+
NOT_IMPLEMENTED: number;
|
|
235
|
+
BAD_GATEWAY: number;
|
|
236
|
+
SERVICE_UNAVAILABLE: number;
|
|
237
|
+
GATEWAY_TIMEOUT: number;
|
|
238
|
+
HTTP_VERSION_NOT_SUPPORTED: number;
|
|
239
|
+
VARIANT_ALSO_NEGOTIATES: number;
|
|
240
|
+
INSUFFICIENT_STORAGE: number;
|
|
241
|
+
LOOP_DETECTED: number;
|
|
242
|
+
NOT_EXTENDED: number;
|
|
243
|
+
NETWORK_AUTHENTICATION_REQUIRED: number;
|
|
190
244
|
};
|
|
191
245
|
|
|
192
246
|
type Status = keyof typeof statusCode;
|
|
@@ -196,4 +250,4 @@ declare class APIError extends Error {
|
|
|
196
250
|
constructor(status: Status, body?: Record<string, any>);
|
|
197
251
|
}
|
|
198
252
|
|
|
199
|
-
export { APIError, type
|
|
253
|
+
export { APIError, type EndpointConfig, type Middleware, type MiddlewareHandler, createEndpoint, createMiddleware, createRouter, getBody, shouldSerialize, statusCode };
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var
|
|
1
|
+
var P=Object.defineProperty;var g=(t,e,s)=>e in t?P(t,e,{enumerable:!0,configurable:!0,writable:!0,value:s}):t[e]=s;var f=(t,e,s)=>g(t,typeof e!="symbol"?e+"":e,s);import{ZodError as S}from"zod";var u=class extends Error{constructor(s,a){super(`API Error: ${s} ${a?.message??""}`,{cause:a});f(this,"status");f(this,"body");this.status=s,this.body=a??{},this.stack="",this.name="BetterCallAPIError"}};function m(t,e,s){let a=new Headers,p=async(...o)=>{let r={setHeader(n,d){a.set(n,d)},setCookie(n,d){a.append("Set-Cookie",`${n}=${d}`)},getCookie(n){return o[0]?.headers?.get("cookie")?.split(";").find(R=>R.startsWith(`${n}=`))?.split("=")[1]},...o[0]||{},context:{}};if(e.use?.length)for(let n of e.use){let d=await n(r),R=d.options?.body?d.options.body.parse(r.body):void 0;d&&(r={...r,body:R?{...R,...r.body}:r.body,context:{...r.context||{},...d}})}try{let n=e.body?e.body.parse(r.body):r.body;r={...r,body:n?{...n,...r.body}:r.body},r.query=e.query?e.query.parse(r.query):r.query,r.params=e.params?e.params.parse(r.params):r.params}catch(n){throw n instanceof S?new u("BAD_REQUEST",{message:n.message,details:n.errors}):n}if(e.requireHeaders&&!r.headers)throw new u("BAD_REQUEST",{message:"Headers are required"});if(e.requireRequest&&!r.request)throw new u("BAD_REQUEST",{message:"Request is required"});return await s(r)};return p.path=t,p.options=e,p.method=e.method,p.headers=a,p}import{createRouter as N,addRoute as l,findRoute as x}from"rou3";async function _(t){let e=t.headers.get("content-type")||"";if(t.body){if(e.includes("application/json"))return await t.json();if(e.includes("application/x-www-form-urlencoded")){let s=await t.formData(),a={};return s.forEach((p,o)=>{a[o]=p.toString()}),a}if(e.includes("multipart/form-data")){let s=await t.formData(),a={};return s.forEach((p,o)=>{a[o]=p}),a}return e.includes("text/plain")?await t.text():e.includes("application/octet-stream")?await t.arrayBuffer():e.includes("application/pdf")||e.includes("image/")||e.includes("video/")?await t.blob():e.includes("application/stream")||t.body instanceof ReadableStream?t.body:await t.text()}}function I(t){return typeof t=="object"&&t!==null&&!(t instanceof Blob)&&!(t instanceof FormData)}var w={OK:200,CREATED:201,ACCEPTED:202,NO_CONTENT:204,MULTIPLE_CHOICES:300,MOVED_PERMANENTLY:301,FOUND:302,SEE_OTHER:303,NOT_MODIFIED:304,TEMPORARY_REDIRECT:307,BAD_REQUEST:400,UNAUTHORIZED:401,PAYMENT_REQUIRED:402,FORBIDDEN:403,NOT_FOUND:404,METHOD_NOT_ALLOWED:405,NOT_ACCEPTABLE:406,PROXY_AUTHENTICATION_REQUIRED:407,REQUEST_TIMEOUT:408,CONFLICT:409,GONE:410,LENGTH_REQUIRED:411,PRECONDITION_FAILED:412,PAYLOAD_TOO_LARGE:413,URI_TOO_LONG:414,UNSUPPORTED_MEDIA_TYPE:415,RANGE_NOT_SATISFIABLE:416,EXPECTATION_FAILED:417,"I'M_A_TEAPOT":418,MISDIRECTED_REQUEST:421,UNPROCESSABLE_ENTITY:422,LOCKED:423,FAILED_DEPENDENCY:424,TOO_EARLY:425,UPGRADE_REQUIRED:426,PRECONDITION_REQUIRED:428,TOO_MANY_REQUESTS:429,REQUEST_HEADER_FIELDS_TOO_LARGE:431,UNAVAILABLE_FOR_LEGAL_REASONS:451,INTERNAL_SERVER_ERROR:500,NOT_IMPLEMENTED:501,BAD_GATEWAY:502,SERVICE_UNAVAILABLE:503,GATEWAY_TIMEOUT:504,HTTP_VERSION_NOT_SUPPORTED:505,VARIANT_ALSO_NEGOTIATES:506,INSUFFICIENT_STORAGE:507,LOOP_DETECTED:508,NOT_EXTENDED:510,NETWORK_AUTHENTICATION_REQUIRED:511};var V=(t,e)=>{let s=N();for(let o of t)if(Array.isArray(o.options?.method))for(let r of o.options.method)l(s,r,o.path,o);else l(s,o.options.method,o.path,o);let a=N();for(let o of e?.routerMiddleware||[]){let r=Array.isArray(o.method)?o.method:[o.method];for(let E of r)l(a,E,o.path,o.handler)}return{handler:async o=>{let r=new URL(o.url),E=r.pathname;e?.basePath&&(E=E.split(e.basePath)[1]);let n=o.method,d=x(s,n,E),R=d?.data,O=await _(o),y=o.headers,h=Object.fromEntries(r.searchParams),T=x(a,n,E)?.data;if(!R)return new Response(null,{status:404,statusText:"Not Found"});try{let i={};if(T){let A=await T({path:E,method:n,headers:y,params:d?.params,request:o,body:O,query:h});A&&(i={...A,...i})}let c=await R({path:E,method:n,headers:y,params:d?.params,request:o,body:O,query:h,...i});if(c instanceof Response)return c;let C=I(c)?JSON.stringify(c):c;return new Response(C,{headers:R.headers})}catch(i){if(e?.onError){let c=await e.onError(i);if(c instanceof Response)return c}if(i instanceof u)return new Response(i.body?JSON.stringify(i.body):null,{status:w[i.status],statusText:i.status,headers:{"Content-Type":"application/json"}});if(e?.throwError)throw i;return new Response(null,{status:500,statusText:"Internal Server Error"})}}}};import{z as D}from"zod";function b(t,e){if(typeof t=="function")return m("*",{method:"*"},t);if(!e)throw new Error("Middleware handler is required");return m("*",{...t,method:"*"},e)}var $=b({body:D.object({name:D.string()})},async t=>{});export{u as APIError,m as createEndpoint,b as createMiddleware,V as createRouter,_ as getBody,I as shouldSerialize,w as statusCode};
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/endpoint.ts","../src/better-call-error.ts","../src/router.ts","../src/utils.ts","../src/middleware.ts"],"sourcesContent":["import { z, ZodError, type ZodOptional, type ZodSchema } from \"zod\"\nimport type { Middleware } from \"./middleware\"\nimport { APIError } from \"./better-call-error\";\n\n\nexport type RequiredKeysOf<BaseType extends object> = Exclude<{\n [Key in keyof BaseType]: BaseType extends Record<Key, BaseType[Key]>\n ? Key\n : never\n}[keyof BaseType], undefined>;\n\nexport type HasRequiredKeys<BaseType extends object> = RequiredKeysOf<BaseType> extends never ? false : true;\n\ntype Method = \"GET\" | \"POST\" | \"PUT\" | \"DELETE\" | \"PATCH\" | \"*\"\n\nexport interface EndpointOptions {\n method: Method | Method[]\n body?: ZodSchema\n query?: ZodSchema\n params?: ZodSchema<any>\n /**\n * If true headers will be required to be passed in the context\n */\n requireHeaders?: boolean\n /**\n * If true request object will be required\n */\n requireRequest?: boolean\n}\n\ntype InferParamPath<Path> =\n Path extends `${infer _Start}:${infer Param}/${infer Rest}`\n ? { [K in Param | keyof InferParamPath<Rest>]: string }\n : Path extends `${infer _Start}:${infer Param}`\n ? { [K in Param]: string }\n : Path extends `${infer _Start}/${infer Rest}`\n ? InferParamPath<Rest>\n : undefined;\n\ntype InferParamWildCard<Path> = Path extends `${infer _Start}/*:${infer Param}/${infer Rest}` | `${infer _Start}/**:${infer Param}/${infer Rest}`\n ? { [K in Param | keyof InferParamPath<Rest>]: string }\n : Path extends `${infer _Start}/*`\n ? { [K in \"_\"]: string }\n : Path extends `${infer _Start}/${infer Rest}`\n ? InferParamPath<Rest>\n : undefined;\n\n\nexport type Prettify<T> = {\n [key in keyof T]: T[key];\n} & {};\n\ntype ContextTools = {\n setHeader: (key: string, value: string) => void\n setCookie: (key: string, value: string) => void\n getCookie: (key: string) => string | undefined\n}\n\nexport type Context<Path extends string, Opts extends EndpointOptions, Extra extends Record<string, any> = {}> = InferBody<Opts[\"body\"]> & InferParam<Path> & InferMethod<Opts['method']> & InferHeaders<Opts[\"requireHeaders\"]> & InferRequest<Opts[\"requireRequest\"]> & InferQuery<Opts[\"query\"]> & Extra\n\ntype InferMethod<M extends Method | Method[]> = M extends Array<Method> ? {\n method: M[number]\n} : {\n method?: M\n}\n\ntype InferHeaders<HeaderReq> = HeaderReq extends true ? {\n headers: Headers\n} : {\n headers?: Headers\n}\n\ntype InferRequest<RequestReq> = RequestReq extends true ? {\n request: Request\n} : {\n request?: Request\n}\n\n\ntype InferBody<Body> = Body extends ZodSchema ? Body extends ZodOptional<any> ? {\n body?: z.infer<Body>\n} : {\n body: z.infer<Body>\n} : {\n body?: undefined\n}\n\ntype InferQuery<Query> = Query extends ZodSchema ? Query extends ZodOptional<any> ? {\n query?: z.infer<Query>\n} : {\n query: z.infer<Query>\n} : {\n query?: undefined\n}\n\ntype InferParam<Path extends string, ParamPath extends InferParamPath<Path> = InferParamPath<Path>, WildCard extends InferParamWildCard<Path> = InferParamWildCard<Path>> = ParamPath extends undefined ? WildCard extends undefined ? {\n params?: undefined\n} : {\n params: WildCard\n} : {\n params: ParamPath & (WildCard extends undefined ? {} : WildCard)\n}\n\n\nexport type EndpointResponse = Record<string, any> | string | boolean | number | void | undefined\n\nexport type Handler<Path extends string, Opts extends EndpointOptions, R extends EndpointResponse, Extra extends Record<string, any> = Record<string, any>> = (ctx: Context<Path, Opts, Extra>) => Promise<R>\n\nexport interface EndpointConfig {\n /**\n * Throw when the response isn't in 200 range\n */\n throwOnError?: boolean\n}\n\nexport function createEndpoint<Path extends string, Opts extends EndpointOptions, R extends EndpointResponse>(path: Path, options: Opts, handler: Handler<Path, Opts, R, ContextTools>) {\n const responseHeader = new Headers()\n type Ctx = Context<Path, Opts>\n const handle = async (...ctx: HasRequiredKeys<Ctx> extends true ? [Ctx] : [Ctx?]) => {\n const internalCtx = ({\n setHeader(key, value) {\n responseHeader.set(key, value)\n },\n setCookie(key, value) {\n responseHeader.append(\"Set-Cookie\", `${key}=${value}`)\n },\n getCookie(key) {\n const header = ctx[0]?.headers\n return header?.get(\"cookie\")?.split(\";\").find(cookie => cookie.startsWith(`${key}=`))?.split(\"=\")[1]\n },\n ...(ctx[0] || {})\n }) as (Ctx & ContextTools)\n try {\n internalCtx.body = options.body ? options.body.parse(internalCtx.body) : undefined\n internalCtx.query = options.query ? options.query.parse(internalCtx.query) : undefined\n internalCtx.params = options.params ? options.params.parse(internalCtx.params) : undefined\n } catch (e) {\n if (e instanceof ZodError) {\n throw new APIError(\"Bad Request\", {\n message: e.message,\n details: e.errors\n })\n }\n throw e\n }\n if (options.requireHeaders && !internalCtx.headers) {\n throw new APIError(\"Bad Request\", {\n message: \"Headers are required\"\n })\n }\n if (options.requireRequest && !internalCtx.request) {\n throw new APIError(\"Bad Request\", {\n message: \"Request is required\"\n })\n }\n const res = await handler(internalCtx)\n return res as ReturnType<Handler<Path, Opts, R>>\n }\n handle.path = path\n handle.options = options\n handle.method = options.method\n handle.headers = responseHeader\n //for type inference\n handle.middleware = undefined as (Middleware<any> | undefined)\n\n return handle\n}\n\nexport type Endpoint = {\n path: string\n options: EndpointOptions\n middleware?: Middleware<any>\n headers?: Headers\n} & ((ctx: any) => Promise<any>)","import type { statusCode } from \"./utils\"\n\ntype Status = keyof typeof statusCode\n\nexport class APIError extends Error {\n status: Status\n body: Record<string, any>\n constructor(\n status: Status,\n body?: Record<string, any>,\n ) {\n super(\n `API Error: ${status} ${body?.message ?? \"\"}`,\n {\n cause: body,\n }\n )\n this.status = status\n this.body = body ?? {}\n this.stack = \"\";\n this.name = \"BetterCallAPIError\"\n }\n}","import type { Endpoint } from \"./endpoint\";\nimport {\n createRouter as createRou3Router,\n addRoute,\n findRoute,\n} from \"rou3\";\nimport { getBody, shouldSerialize, statusCode } from \"./utils\";\nimport { APIError } from \"./better-call-error\";\n\ninterface RouterConfig {\n /**\n * Throw error if error occurred other than APIError\n */\n throwError?: boolean\n /**\n * Handle error\n */\n onError?: (e: unknown) => void | Promise<void> | Response | Promise<Response>\n /**\n * Base path for the router\n */\n basePath?: string\n}\n\nexport const createRouter = <E extends Endpoint, Config extends RouterConfig>(endpoints: E[], config?: Config) => {\n const router = createRou3Router()\n for (const endpoint of endpoints) {\n if (Array.isArray(endpoint.options?.method)) {\n for (const method of endpoint.options.method) {\n addRoute(router, method, endpoint.path, endpoint)\n }\n } else {\n addRoute(router, endpoint.options.method, endpoint.path, endpoint)\n }\n }\n\n const handler = async (request: Request) => {\n const url = new URL(request.url);\n let path = url.pathname\n if (config?.basePath) {\n path = path.split(config.basePath)[1]\n }\n const method = request.method;\n const route = findRoute(router, method, path)\n const handler = route?.data as Endpoint\n const body = await getBody(request)\n const headers = request.headers\n\n //handler 404\n if (!handler) {\n return new Response(null, {\n status: 404,\n statusText: \"Not Found\"\n })\n }\n try {\n let middleware: Record<string, any> = {}\n if (handler.middleware) {\n middleware = await handler.middleware({\n path: path,\n method: method,\n headers,\n params: url.searchParams,\n request: request.clone(),\n body: body,\n }) || {}\n }\n const handlerRes = await handler({\n path: path,\n method: method as \"GET\",\n headers,\n params: route?.params as any,\n request: request,\n body: body,\n ...middleware?.context,\n })\n if (handlerRes instanceof Response) {\n return handlerRes\n }\n const resBody = shouldSerialize(handlerRes) ? JSON.stringify(handlerRes) : handlerRes\n return new Response(resBody as any, {\n headers: handler.headers\n })\n } catch (e) {\n if (config?.onError) {\n const onErrorRes = await config.onError(e)\n if (onErrorRes instanceof Response) {\n return onErrorRes\n }\n }\n if (e instanceof APIError) {\n return new Response(e.body ? JSON.stringify(e.body) : null, {\n status: statusCode[e.status],\n statusText: e.status,\n headers: {\n \"Content-Type\": \"application/json\",\n }\n })\n }\n if (config?.throwError) {\n throw e\n }\n return new Response(null, {\n status: 500,\n statusText: \"Internal Server Error\"\n })\n }\n }\n return { handler }\n}","export async function getBody(request: Request) {\n const contentType = request.headers.get('content-type') || '';\n\n if (!request.body) {\n return undefined\n }\n\n if (contentType.includes('application/json')) {\n return await request.json();\n }\n\n if (contentType.includes('application/x-www-form-urlencoded')) {\n const formData = await request.formData();\n const result: Record<string, string> = {};\n formData.forEach((value, key) => {\n result[key] = value.toString();\n });\n return result;\n }\n\n if (contentType.includes('multipart/form-data')) {\n const formData = await request.formData();\n const result: Record<string, any> = {};\n formData.forEach((value, key) => {\n result[key] = value;\n });\n return result;\n }\n\n if (contentType.includes('text/plain')) {\n return await request.text();\n }\n\n if (contentType.includes('application/octet-stream')) {\n return await request.arrayBuffer();\n }\n\n if (contentType.includes('application/pdf') || contentType.includes('image/') || contentType.includes('video/')) {\n const blob = await request.blob();\n return blob;\n }\n\n if (contentType.includes('application/stream') || request.body instanceof ReadableStream) {\n return request.body;\n }\n\n return await request.text();\n}\n\n\nexport function shouldSerialize(body: any) {\n return typeof body === \"object\" && body !== null && !(body instanceof Blob) && !(body instanceof FormData)\n}\n\nexport const statusCode = {\n \"OK\": 200,\n \"Created\": 201,\n \"Accepted\": 202,\n \"No Content\": 204,\n \"Multiple Choices\": 300,\n \"Moved Permanently\": 301,\n \"Found\": 302,\n \"See Other\": 303,\n \"Not Modified\": 304,\n \"Temporary Redirect\": 307,\n \"Bad Request\": 400,\n \"Unauthorized\": 401,\n \"Payment Required\": 402,\n \"Forbidden\": 403,\n \"Not Found\": 404,\n \"Method Not Allowed\": 405,\n \"Not Acceptable\": 406,\n \"Proxy Authentication Required\": 407,\n \"Request Timeout\": 408,\n \"Conflict\": 409,\n \"Gone\": 410,\n \"Length Required\": 411,\n \"Precondition Failed\": 412,\n \"Payload Too Large\": 413,\n \"URI Too Long\": 414,\n \"Unsupported Media Type\": 415,\n \"Range Not Satisfiable\": 416,\n \"Expectation Failed\": 417,\n \"I'm a teapot\": 418,\n \"Misdirected Request\": 421,\n \"Unprocessable Entity\": 422,\n \"Locked\": 423,\n \"Failed Dependency\": 424,\n \"Too Early\": 425,\n \"Upgrade Required\": 426,\n \"Precondition Required\": 428,\n \"Too Many Requests\": 429,\n \"Request Header Fields Too Large\": 431,\n \"Unavailable For Legal Reasons\": 451,\n \"Internal Server Error\": 500,\n \"Not Implemented\": 501,\n \"Bad Gateway\": 502,\n \"Service Unavailable\": 503,\n \"Gateway Timeout\": 504,\n \"HTTP Version Not Supported\": 505,\n \"Variant Also Negotiates\": 506,\n \"Insufficient Storage\": 507,\n \"Loop Detected\": 508,\n \"Not Extended\": 510,\n \"Network Authentication Required\": 511,\n}","import type { HasRequiredKeys } from \"type-fest\"\nimport { type Context, type EndpointOptions, type EndpointResponse, type Handler } from \"./endpoint\"\n\n\nexport type Middleware<E extends Record<string, string>> = (ctx: Context<string, EndpointOptions, E>) => Promise<{\n context: E\n} | void>\n\nexport const createMiddleware = <E extends Record<string, any>, M extends Middleware<E>>(middleware: M) => {\n type MiddlewareContext = Awaited<ReturnType<M>> extends {\n context: infer C\n } ? C extends Record<string, any> ? C : {} : {}\n return <Path extends string, Opts extends EndpointOptions, R extends EndpointResponse>(path: Path, options: Opts, handler: Handler<Path, Opts, R, MiddlewareContext>) => {\n type Ctx = Context<Path, Opts, MiddlewareContext>\n const handle = async (...ctx: HasRequiredKeys<Ctx> extends true ? [Ctx] : [Ctx?]) => {\n const res = await handler((ctx[0] || {}) as Ctx)\n return res as ReturnType<Handler<Path, Opts, R>>\n }\n handle.path = path\n handle.options = options\n handle.middleware = middleware\n return handle\n }\n}"],"mappings":"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","names":["ZodError","APIError","status","body","__publicField","createEndpoint","path","options","handler","responseHeader","handle","ctx","internalCtx","key","value","cookie","e","ZodError","APIError","createRou3Router","addRoute","findRoute","getBody","request","contentType","formData","result","value","key","shouldSerialize","body","statusCode","createRouter","endpoints","config","router","createRou3Router","endpoint","method","addRoute","request","url","path","route","findRoute","handler","body","getBody","headers","middleware","handlerRes","resBody","shouldSerialize","e","onErrorRes","APIError","statusCode","createMiddleware","middleware","path","options","handler","handle","ctx"]}
|
|
1
|
+
{"version":3,"sources":["../src/endpoint.ts","../src/better-call-error.ts","../src/router.ts","../src/utils.ts","../src/middleware.ts"],"sourcesContent":["import { z, ZodError, type ZodOptional, type ZodSchema } from \"zod\"\nimport type { Middleware } from \"./middleware\"\nimport { APIError } from \"./better-call-error\";\nimport type { HasRequiredKeys, UnionToIntersection } from \"./helper\";\nimport type { Context, ContextTools, Endpoint, EndpointOptions, EndpointResponse, Handler } from \"./types\";\n\n\nexport interface EndpointConfig {\n /**\n * Throw when the response isn't in 200 range\n */\n throwOnError?: boolean\n}\n\nexport function createEndpoint<Path extends string, Opts extends EndpointOptions, R extends EndpointResponse>(path: Path, options: Opts, handler: Handler<Path, Opts, R>) {\n const responseHeader = new Headers()\n type Ctx = Context<Path, Opts>\n const handle = async (...ctx: HasRequiredKeys<Ctx> extends true ? [Ctx] : [Ctx?]) => {\n let internalCtx = ({\n setHeader(key: string, value: string) {\n responseHeader.set(key, value)\n },\n setCookie(key: string, value: string) {\n responseHeader.append(\"Set-Cookie\", `${key}=${value}`)\n },\n getCookie(key: string) {\n const header = ctx[0]?.headers\n return header?.get(\"cookie\")?.split(\";\").find(cookie => cookie.startsWith(`${key}=`))?.split(\"=\")[1]\n },\n ...(ctx[0] || {}),\n context: {}\n })\n if (options.use?.length) {\n for (const middleware of options.use) {\n const res = await middleware(internalCtx) as Endpoint\n const body = res.options?.body ? res.options.body.parse(internalCtx.body) : undefined\n if (res) {\n internalCtx = {\n ...internalCtx,\n body: body ? {\n ...body,\n ...internalCtx.body\n } : internalCtx.body,\n context: {\n ...internalCtx.context || {},\n ...res\n }\n }\n }\n }\n }\n try {\n const body = options.body ? options.body.parse(internalCtx.body) : internalCtx.body\n internalCtx = {\n ...internalCtx,\n body: body ? {\n ...body,\n ...internalCtx.body\n } : internalCtx.body,\n }\n internalCtx.query = options.query ? options.query.parse(internalCtx.query) : internalCtx.query\n internalCtx.params = options.params ? options.params.parse(internalCtx.params) : internalCtx.params\n } catch (e) {\n if (e instanceof ZodError) {\n throw new APIError(\"BAD_REQUEST\", {\n message: e.message,\n details: e.errors\n })\n }\n throw e\n }\n if (options.requireHeaders && !internalCtx.headers) {\n throw new APIError(\"BAD_REQUEST\", {\n message: \"Headers are required\"\n })\n }\n if (options.requireRequest && !internalCtx.request) {\n throw new APIError(\"BAD_REQUEST\", {\n message: \"Request is required\"\n })\n }\n //@ts-expect-error\n const res = await handler(internalCtx)\n return res as ReturnType<Handler<Path, Opts, R>>\n }\n handle.path = path\n handle.options = options\n handle.method = options.method\n handle.headers = responseHeader\n return handle\n}","import type { statusCode } from \"./utils\"\n\ntype Status = keyof typeof statusCode\n\nexport class APIError extends Error {\n status: Status\n body: Record<string, any>\n constructor(\n status: Status,\n body?: Record<string, any>,\n ) {\n super(\n `API Error: ${status} ${body?.message ?? \"\"}`,\n {\n cause: body,\n }\n )\n this.status = status\n this.body = body ?? {}\n this.stack = \"\";\n this.name = \"BetterCallAPIError\"\n }\n}","import {\n createRouter as createRou3Router,\n addRoute,\n findRoute,\n} from \"rou3\";\nimport { getBody, shouldSerialize, statusCode } from \"./utils\";\nimport { APIError } from \"./better-call-error\";\nimport type { Middleware, MiddlewareHandler } from \"./middleware\";\nimport type { Endpoint, Method } from \"./types\";\n\ninterface RouterConfig {\n /**\n * Throw error if error occurred other than APIError\n */\n throwError?: boolean\n /**\n * Handle error\n */\n onError?: (e: unknown) => void | Promise<void> | Response | Promise<Response>\n /**\n * Base path for the router\n */\n basePath?: string\n /**\n * Middlewares for the router\n */\n routerMiddleware?: ({\n /**\n * The path to match for the middleware to be called\n */\n path: string,\n /**\n * The method to match for the middleware to be called\n * \n * @default \"*\"\n */\n method?: Method | Method[],\n /**\n * The middleware handler\n */\n handler: Endpoint\n })[]\n}\n\nexport const createRouter = <E extends Endpoint, Config extends RouterConfig>(endpoints: E[], config?: Config) => {\n const router = createRou3Router()\n for (const endpoint of endpoints) {\n if (Array.isArray(endpoint.options?.method)) {\n for (const method of endpoint.options.method) {\n addRoute(router, method, endpoint.path, endpoint)\n }\n } else {\n addRoute(router, endpoint.options.method, endpoint.path, endpoint)\n }\n }\n\n const middlewareRouter = createRou3Router()\n for (const route of (config?.routerMiddleware || [])) {\n const methods = Array.isArray(route.method) ? route.method : [route.method]\n for (const method of methods) {\n addRoute(middlewareRouter, method, route.path, route.handler)\n }\n }\n\n const handler = async (request: Request) => {\n const url = new URL(request.url);\n let path = url.pathname\n if (config?.basePath) {\n path = path.split(config.basePath)[1]\n }\n const method = request.method;\n const route = findRoute(router, method, path)\n const handler = route?.data as Endpoint\n const body = await getBody(request)\n const headers = request.headers\n const query = Object.fromEntries(url.searchParams)\n const middleware = findRoute(middlewareRouter, method, path)?.data as Endpoint | undefined\n\n //handler 404\n if (!handler) {\n return new Response(null, {\n status: 404,\n statusText: \"Not Found\"\n })\n }\n try {\n let middlewareContext: Record<string, any> = {}\n if (middleware) {\n const res = await middleware({\n path: path,\n method: method as \"GET\",\n headers,\n params: route?.params as any,\n request: request,\n body: body,\n query\n })\n if (res) {\n middlewareContext = {\n ...res,\n ...middlewareContext\n }\n }\n }\n const handlerRes = await handler({\n path: path,\n method: method as \"GET\",\n headers,\n params: route?.params as any,\n request: request,\n body: body,\n query,\n ...middlewareContext,\n })\n if (handlerRes instanceof Response) {\n return handlerRes\n }\n const resBody = shouldSerialize(handlerRes) ? JSON.stringify(handlerRes) : handlerRes\n return new Response(resBody as any, {\n headers: handler.headers\n })\n } catch (e) {\n if (config?.onError) {\n const onErrorRes = await config.onError(e)\n if (onErrorRes instanceof Response) {\n return onErrorRes\n }\n }\n if (e instanceof APIError) {\n return new Response(e.body ? JSON.stringify(e.body) : null, {\n status: statusCode[e.status],\n statusText: e.status,\n headers: {\n \"Content-Type\": \"application/json\",\n }\n })\n }\n if (config?.throwError) {\n throw e\n }\n return new Response(null, {\n status: 500,\n statusText: \"Internal Server Error\"\n })\n }\n }\n return {\n handler\n }\n}","export async function getBody(request: Request) {\n const contentType = request.headers.get('content-type') || '';\n\n if (!request.body) {\n return undefined\n }\n\n if (contentType.includes('application/json')) {\n return await request.json();\n }\n\n if (contentType.includes('application/x-www-form-urlencoded')) {\n const formData = await request.formData();\n const result: Record<string, string> = {};\n formData.forEach((value, key) => {\n result[key] = value.toString();\n });\n return result;\n }\n\n if (contentType.includes('multipart/form-data')) {\n const formData = await request.formData();\n const result: Record<string, any> = {};\n formData.forEach((value, key) => {\n result[key] = value;\n });\n return result;\n }\n\n if (contentType.includes('text/plain')) {\n return await request.text();\n }\n\n if (contentType.includes('application/octet-stream')) {\n return await request.arrayBuffer();\n }\n\n if (contentType.includes('application/pdf') || contentType.includes('image/') || contentType.includes('video/')) {\n const blob = await request.blob();\n return blob;\n }\n\n if (contentType.includes('application/stream') || request.body instanceof ReadableStream) {\n return request.body;\n }\n\n return await request.text();\n}\n\n\nexport function shouldSerialize(body: any) {\n return typeof body === \"object\" && body !== null && !(body instanceof Blob) && !(body instanceof FormData)\n}\n\nexport const statusCode = {\n \"OK\": 200,\n \"CREATED\": 201,\n \"ACCEPTED\": 202,\n \"NO_CONTENT\": 204,\n \"MULTIPLE_CHOICES\": 300,\n \"MOVED_PERMANENTLY\": 301,\n \"FOUND\": 302,\n \"SEE_OTHER\": 303,\n \"NOT_MODIFIED\": 304,\n \"TEMPORARY_REDIRECT\": 307,\n \"BAD_REQUEST\": 400,\n \"UNAUTHORIZED\": 401,\n \"PAYMENT_REQUIRED\": 402,\n \"FORBIDDEN\": 403,\n \"NOT_FOUND\": 404,\n \"METHOD_NOT_ALLOWED\": 405,\n \"NOT_ACCEPTABLE\": 406,\n \"PROXY_AUTHENTICATION_REQUIRED\": 407,\n \"REQUEST_TIMEOUT\": 408,\n \"CONFLICT\": 409,\n \"GONE\": 410,\n \"LENGTH_REQUIRED\": 411,\n \"PRECONDITION_FAILED\": 412,\n \"PAYLOAD_TOO_LARGE\": 413,\n \"URI_TOO_LONG\": 414,\n \"UNSUPPORTED_MEDIA_TYPE\": 415,\n \"RANGE_NOT_SATISFIABLE\": 416,\n \"EXPECTATION_FAILED\": 417,\n \"I'M_A_TEAPOT\": 418,\n \"MISDIRECTED_REQUEST\": 421,\n \"UNPROCESSABLE_ENTITY\": 422,\n \"LOCKED\": 423,\n \"FAILED_DEPENDENCY\": 424,\n \"TOO_EARLY\": 425,\n \"UPGRADE_REQUIRED\": 426,\n \"PRECONDITION_REQUIRED\": 428,\n \"TOO_MANY_REQUESTS\": 429,\n \"REQUEST_HEADER_FIELDS_TOO_LARGE\": 431,\n \"UNAVAILABLE_FOR_LEGAL_REASONS\": 451,\n \"INTERNAL_SERVER_ERROR\": 500,\n \"NOT_IMPLEMENTED\": 501,\n \"BAD_GATEWAY\": 502,\n \"SERVICE_UNAVAILABLE\": 503,\n \"GATEWAY_TIMEOUT\": 504,\n \"HTTP_VERSION_NOT_SUPPORTED\": 505,\n \"VARIANT_ALSO_NEGOTIATES\": 506,\n \"INSUFFICIENT_STORAGE\": 507,\n \"LOOP_DETECTED\": 508,\n \"NOT_EXTENDED\": 510,\n \"NETWORK_AUTHENTICATION_REQUIRED\": 511,\n}\n","import { z } from \"zod\"\nimport type { ContextTools, Endpoint, EndpointOptions, EndpointResponse, Handler, InferBody, InferHeaders, InferRequest, Prettify } from \"./types\"\nimport { createEndpoint } from \"./endpoint\"\n\nexport type MiddlewareHandler<Opts extends EndpointOptions, R extends EndpointResponse> = (ctx: Prettify<InferBody<Opts> & InferRequest<Opts> & InferHeaders<Opts> & {\n params?: Record<string, string>,\n query?: Record<string, string>,\n} & ContextTools>) => Promise<R>\n\nexport function createMiddleware<Opts extends EndpointOptions, R extends EndpointResponse>(optionsOrHandler: MiddlewareHandler<Opts, R>): Endpoint<Handler<string, Opts, R>, Opts>\nexport function createMiddleware<Opts extends Omit<EndpointOptions, \"method\">, R extends EndpointResponse>(optionsOrHandler: Opts, handler: MiddlewareHandler<Opts & {\n method: \"*\"\n}, R>): Endpoint<Handler<string, Opts & {\n method: \"*\"\n}, R>, Opts & {\n method: \"*\"\n}>\nexport function createMiddleware(optionsOrHandler: any, handler?: any) {\n if (typeof optionsOrHandler === \"function\") {\n return createEndpoint(\"*\", {\n method: \"*\"\n }, optionsOrHandler)\n }\n if (!handler) {\n throw new Error(\"Middleware handler is required\")\n }\n const endpoint = createEndpoint(\"*\", {\n ...optionsOrHandler,\n method: \"*\"\n }, handler)\n return endpoint as any\n}\n\nexport type Middleware<Opts extends EndpointOptions, R> = (opts: Opts, handler: (ctx: {\n body?: InferBody<Opts>,\n params?: Record<string, string>,\n query?: Record<string, string>\n}) => Promise<R>) => Endpoint\n\nconst m1 = createMiddleware({\n body: z.object({\n name: z.string()\n }),\n}, async (ctx) => {\n ctx\n})"],"mappings":"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","names":["ZodError","APIError","status","body","__publicField","createEndpoint","path","options","handler","responseHeader","handle","ctx","internalCtx","key","value","cookie","middleware","res","body","e","ZodError","APIError","createRou3Router","addRoute","findRoute","getBody","request","contentType","formData","result","value","key","shouldSerialize","body","statusCode","createRouter","endpoints","config","router","createRou3Router","endpoint","method","addRoute","middlewareRouter","route","methods","request","url","path","findRoute","handler","body","getBody","headers","query","middleware","middlewareContext","res","handlerRes","resBody","shouldSerialize","e","onErrorRes","APIError","statusCode","z","createMiddleware","optionsOrHandler","handler","createEndpoint","m1","z","ctx"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "better-call",
|
|
3
|
-
"version": "0.0.5-beta.
|
|
3
|
+
"version": "0.0.5-beta.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.cjs",
|
|
6
6
|
"module": "./dist/index.js",
|
|
@@ -12,10 +12,11 @@
|
|
|
12
12
|
"build": "tsup"
|
|
13
13
|
},
|
|
14
14
|
"devDependencies": {
|
|
15
|
+
"@better-fetch/fetch": "^1.1.4",
|
|
15
16
|
"@types/bun": "latest",
|
|
16
|
-
"type-fest": "^4.23.0",
|
|
17
17
|
"bumpp": "^9.4.1",
|
|
18
18
|
"tsup": "^8.2.3",
|
|
19
|
+
"type-fest": "^4.23.0",
|
|
19
20
|
"vitest": "^2.0.4",
|
|
20
21
|
"zod": "^3.23.8"
|
|
21
22
|
},
|