better-call 0.0.5-beta.2 → 0.0.5-beta.4

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 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, inputs that can be validated with zod (body, query) and a function that will be invoked when the endpoint is called.
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
- You can create middleware by calling `createMiddleware` and passing it a function that will be invoked before the endpoint is called.
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 merged with the context object on the endpoint.
62
+ If you return a context object from the middleware, it will be available in the endpoint context.
64
63
 
65
64
  ```ts
66
- import { createMiddleware, createEndpoint } from "better-call"
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
- context: {
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
+ ```
78
+
79
+ You can also pass an options object to the middleware and a handler function.
80
80
 
81
- const getUser = createProtectedEndpoint("/:id", {
82
- method: "GET"
83
- },
84
- async (ctx) => {
85
- const user = await getUserFromDatabase(ctx.params.id)
86
- // ctx.session is the session object
81
+ ```ts
82
+ const middleware = createMiddleware({
83
+ body: z.object({
84
+ name: z.string()
85
+ })
86
+ }, async (ctx) => {
87
87
  return {
88
- user: ctx.session.user
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,34 @@ 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. You have to pass endpoints to the router middleware as an array.
119
+
120
+ ```ts
121
+ const routeMiddleware = createEndpoint("/api/**", {
122
+ method: "GET",
123
+ }, async (ctx) => {
124
+ return {
125
+ name: "hello"
126
+ }
127
+ })
128
+ const router = createRouter([
129
+ createItem
130
+ ], {
131
+ routerMiddleware: [routeMiddleware]
132
+ })
133
+ ```
134
+
135
+ **basePath**: The base path for the router. All paths will be relative to this path.
136
+
137
+ **onError**: The router will call this function if an error occurs in the middleware or the endpoint.
138
+
139
+ **throwError**: If true, the router will throw an error if an error occurs in the middleware or the endpoint.
140
+
107
141
 
108
142
  ### Returning non 200 responses
109
143
 
@@ -168,15 +202,7 @@ const createItem = createEndpoint("/item", {
168
202
  })
169
203
  ```
170
204
 
171
- ## API
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.
205
+ ### Endpoint Parameters
180
206
 
181
207
  ### `path`
182
208
 
@@ -205,6 +231,7 @@ type EndpointOptions = {
205
231
  params?: ZodSchema
206
232
  requireHeaders?: boolean
207
233
  requireRequest?: boolean
234
+ use?: Endpoint[]
208
235
  }
209
236
  ```
210
237
 
@@ -220,6 +247,7 @@ type EndpointOptions = {
220
247
 
221
248
  - **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
249
 
250
+ - **use**: the use option accepts an array of endpoints and will be called before the endpoint is called.
223
251
 
224
252
  ### `EndpointFunction`
225
253
 
package/dist/index.cjs CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";var h=Object.defineProperty;var C=Object.getOwnPropertyDescriptor;var q=Object.getOwnPropertyNames;var b=Object.prototype.hasOwnProperty;var T=(t,e,n)=>e in t?h(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n;var O=(t,e)=>{for(var n in e)h(t,n,{get:e[n],enumerable:!0})},I=(t,e,n,a)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of q(e))!b.call(t,r)&&r!==n&&h(t,r,{get:()=>e[r],enumerable:!(a=C(e,r))||a.enumerable});return t};var M=t=>I(h({},"__esModule",{value:!0}),t);var m=(t,e,n)=>T(t,typeof e!="symbol"?e+"":e,n);var k={};O(k,{APIError:()=>u,createEndpoint:()=>S,createMiddleware:()=>B,createRouter:()=>H,getBody:()=>x,shouldSerialize:()=>R,statusCode:()=>P});module.exports=M(k);var E=require("zod");var u=class extends Error{constructor(n,a){super(`API Error: ${n} ${a?.message??""}`,{cause:a});m(this,"status");m(this,"body");this.status=n,this.body=a??{},this.stack="",this.name="BetterCallAPIError"}};function S(t,e,n){let a=new Headers,r=async(...s)=>{let o={setHeader(d,i){a.set(d,i)},setCookie(d,i){a.append("Set-Cookie",`${d}=${i}`)},getCookie(d){return s[0]?.headers?.get("cookie")?.split(";").find(l=>l.startsWith(`${d}=`))?.split("=")[1]},...s[0]||{}};try{o.body=e.body?e.body.parse(o.body):void 0,o.query=e.query?e.query.parse(o.query):void 0,o.params=e.params?e.params.parse(o.params):void 0}catch(d){throw d instanceof E.ZodError?new u("Bad Request",{message:d.message,details:d.errors}):d}if(e.requireHeaders&&!o.headers)throw new u("Bad Request",{message:"Headers are required"});if(e.requireRequest&&!o.request)throw new u("Bad Request",{message:"Request is required"});return await n(o)};return r.path=t,r.options=e,r.method=e.method,r.headers=a,r.middleware=void 0,r}var f=require("rou3");async function x(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((r,s)=>{a[s]=r.toString()}),a}if(e.includes("multipart/form-data")){let n=await t.formData(),a={};return n.forEach((r,s)=>{a[s]=r}),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 R(t){return typeof t=="object"&&t!==null&&!(t instanceof Blob)&&!(t instanceof FormData)}var P={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 H=(t,e)=>{let n=(0,f.createRouter)();for(let r of t)if(Array.isArray(r.options?.method))for(let s of r.options.method)(0,f.addRoute)(n,s,r.path,r);else(0,f.addRoute)(n,r.options.method,r.path,r);return{handler:async r=>{let s=new URL(r.url),o=s.pathname;e?.basePath&&(o=o.split(e.basePath)[1]);let c=r.method,d=(0,f.findRoute)(n,c,o),i=d?.data,l=await x(r),g=r.headers;if(!i)return new Response(null,{status:404,statusText:"Not Found"});try{let p={};i.middleware&&(p=await i.middleware({path:o,method:c,headers:g,params:s.searchParams,request:r.clone(),body:l})||{});let y=await i({path:o,method:c,headers:g,params:d?.params,request:r,body:l,...p?.context});if(y instanceof Response)return y;let w=R(y)?JSON.stringify(y):y;return new Response(w,{headers:i.headers})}catch(p){if(e?.onError){let y=await e.onError(p);if(y instanceof Response)return y}if(p instanceof u)return new Response(p.body?JSON.stringify(p.body):null,{status:P[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 B=t=>(e,n,a)=>{let r=async(...s)=>await a(s[0]||{});return r.path=e,r.options=n,r.middleware=t,r};0&&(module.exports={APIError,createEndpoint,createMiddleware,createRouter,getBody,shouldSerialize,statusCode});
1
+ "use strict";var m=Object.defineProperty;var S=Object.getOwnPropertyDescriptor;var g=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 i of g(e))!b.call(t,i)&&i!==n&&m(t,i,{get:()=>e[i],enumerable:!(a=S(e,i))||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:()=>T,statusCode:()=>h});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,i=async(...o)=>{let r={setHeader(s,d){a.set(s,d)},setCookie(s,d){a.append("Set-Cookie",`${s}=${d}`)},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 d=await s(r),u=d.options?.body?d.options.body.parse(r.body):void 0;d&&(r={...r,body:u?{...u,...r.body}:r.body,context:{...r.context||{},...d}})}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 i.path=t,i.options=e,i.method=e.method,i.headers=a,i}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((i,o)=>{a[o]=i.toString()}),a}if(e.includes("multipart/form-data")){let n=await t.formData(),a={};return n.forEach((i,o)=>{a[o]=i}),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 T(t){return typeof t=="object"&&t!==null&&!(t instanceof Blob)&&!(t instanceof FormData)}var h={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.options.method)?o.options.method:[o.options.method];for(let R of r)(0,E.addRoute)(a,R,o.path,o)}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,d=(0,E.findRoute)(n,s,R),u=d?.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:d?.params,request:o,body:_,query:w});x&&(p={...x,...p})}let f=await u({path:R,method:s,headers:I,params:d?.params,request:o,body:_,query:w,...p});if(f instanceof Response)return f;let P=T(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:h[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
@@ -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?: Endpoint[]\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.options.method) ? route.options.method : [route.options.method]\n for (const method of methods) {\n addRoute(middlewareRouter, method, route.path, route)\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
- type Method = "GET" | "POST" | "PUT" | "DELETE" | "PATCH" | "*";
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, Extra extends Record<string, any> = {}> = InferBody<Opts["body"]> & InferParam<Path> & InferMethod<Opts['method']> & InferHeaders<Opts["requireHeaders"]> & InferRequest<Opts["requireRequest"]> & InferQuery<Opts["query"]> & Extra;
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?: undefined;
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, Extra extends Record<string, any> = Record<string, any>> = (ctx: Context<Path, Opts, Extra>) => Promise<R>;
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, ContextTools>): {
105
- (...ctx: HasRequiredKeys<Context<Path, Opts, {}>> extends true ? [Context<Path, Opts, {}>] : [Context<Path, Opts, {}>?]): Promise<R>;
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,86 @@ interface RouterConfig {
129
146
  * Base path for the router
130
147
  */
131
148
  basePath?: string;
149
+ /**
150
+ * Middlewares for the router
151
+ */
152
+ routerMiddleware?: Endpoint[];
132
153
  }
133
154
  declare const createRouter: <E extends Endpoint, Config extends RouterConfig>(endpoints: E[], config?: Config) => {
134
155
  handler: (request: Request) => Promise<Response>;
135
156
  };
136
157
 
158
+ type MiddlewareHandler<Opts extends EndpointOptions, R extends EndpointResponse> = (ctx: Prettify<InferBody<Opts> & InferRequest<Opts> & InferHeaders<Opts> & {
159
+ params?: Record<string, string>;
160
+ query?: Record<string, string>;
161
+ } & ContextTools>) => Promise<R>;
162
+ declare function createMiddleware<Opts extends EndpointOptions, R extends EndpointResponse>(optionsOrHandler: MiddlewareHandler<Opts, R>): Endpoint<Handler<string, Opts, R>, Opts>;
163
+ declare function createMiddleware<Opts extends Omit<EndpointOptions, "method">, R extends EndpointResponse>(optionsOrHandler: Opts, handler: MiddlewareHandler<Opts & {
164
+ method: "*";
165
+ }, R>): Endpoint<Handler<string, Opts & {
166
+ method: "*";
167
+ }, R>, Opts & {
168
+ method: "*";
169
+ }>;
170
+ type Middleware<Opts extends EndpointOptions, R> = (opts: Opts, handler: (ctx: {
171
+ body?: InferBody<Opts>;
172
+ params?: Record<string, string>;
173
+ query?: Record<string, string>;
174
+ }) => Promise<R>) => Endpoint;
175
+
137
176
  declare function getBody(request: Request): Promise<any>;
138
177
  declare function shouldSerialize(body: any): boolean;
139
178
  declare const statusCode: {
140
179
  OK: number;
141
- Created: number;
142
- Accepted: number;
143
- "No Content": number;
144
- "Multiple Choices": number;
145
- "Moved Permanently": number;
146
- Found: number;
147
- "See Other": number;
148
- "Not Modified": number;
149
- "Temporary Redirect": number;
150
- "Bad Request": number;
151
- Unauthorized: number;
152
- "Payment Required": number;
153
- Forbidden: number;
154
- "Not Found": number;
155
- "Method Not Allowed": number;
156
- "Not Acceptable": number;
157
- "Proxy Authentication Required": number;
158
- "Request Timeout": number;
159
- Conflict: number;
160
- Gone: number;
161
- "Length Required": number;
162
- "Precondition Failed": number;
163
- "Payload Too Large": number;
164
- "URI Too Long": number;
165
- "Unsupported Media Type": number;
166
- "Range Not Satisfiable": number;
167
- "Expectation Failed": number;
168
- "I'm a teapot": number;
169
- "Misdirected Request": number;
170
- "Unprocessable Entity": number;
171
- Locked: number;
172
- "Failed Dependency": number;
173
- "Too Early": number;
174
- "Upgrade Required": number;
175
- "Precondition Required": number;
176
- "Too Many Requests": number;
177
- "Request Header Fields Too Large": number;
178
- "Unavailable For Legal Reasons": number;
179
- "Internal Server Error": number;
180
- "Not Implemented": number;
181
- "Bad Gateway": number;
182
- "Service Unavailable": number;
183
- "Gateway Timeout": number;
184
- "HTTP Version Not Supported": number;
185
- "Variant Also Negotiates": number;
186
- "Insufficient Storage": number;
187
- "Loop Detected": number;
188
- "Not Extended": number;
189
- "Network Authentication Required": number;
180
+ CREATED: number;
181
+ ACCEPTED: number;
182
+ NO_CONTENT: number;
183
+ MULTIPLE_CHOICES: number;
184
+ MOVED_PERMANENTLY: number;
185
+ FOUND: number;
186
+ SEE_OTHER: number;
187
+ NOT_MODIFIED: number;
188
+ TEMPORARY_REDIRECT: number;
189
+ BAD_REQUEST: number;
190
+ UNAUTHORIZED: number;
191
+ PAYMENT_REQUIRED: number;
192
+ FORBIDDEN: number;
193
+ NOT_FOUND: number;
194
+ METHOD_NOT_ALLOWED: number;
195
+ NOT_ACCEPTABLE: number;
196
+ PROXY_AUTHENTICATION_REQUIRED: number;
197
+ REQUEST_TIMEOUT: number;
198
+ CONFLICT: number;
199
+ GONE: number;
200
+ LENGTH_REQUIRED: number;
201
+ PRECONDITION_FAILED: number;
202
+ PAYLOAD_TOO_LARGE: number;
203
+ URI_TOO_LONG: number;
204
+ UNSUPPORTED_MEDIA_TYPE: number;
205
+ RANGE_NOT_SATISFIABLE: number;
206
+ EXPECTATION_FAILED: number;
207
+ "I'M_A_TEAPOT": number;
208
+ MISDIRECTED_REQUEST: number;
209
+ UNPROCESSABLE_ENTITY: number;
210
+ LOCKED: number;
211
+ FAILED_DEPENDENCY: number;
212
+ TOO_EARLY: number;
213
+ UPGRADE_REQUIRED: number;
214
+ PRECONDITION_REQUIRED: number;
215
+ TOO_MANY_REQUESTS: number;
216
+ REQUEST_HEADER_FIELDS_TOO_LARGE: number;
217
+ UNAVAILABLE_FOR_LEGAL_REASONS: number;
218
+ INTERNAL_SERVER_ERROR: number;
219
+ NOT_IMPLEMENTED: number;
220
+ BAD_GATEWAY: number;
221
+ SERVICE_UNAVAILABLE: number;
222
+ GATEWAY_TIMEOUT: number;
223
+ HTTP_VERSION_NOT_SUPPORTED: number;
224
+ VARIANT_ALSO_NEGOTIATES: number;
225
+ INSUFFICIENT_STORAGE: number;
226
+ LOOP_DETECTED: number;
227
+ NOT_EXTENDED: number;
228
+ NETWORK_AUTHENTICATION_REQUIRED: number;
190
229
  };
191
230
 
192
231
  type Status = keyof typeof statusCode;
@@ -196,4 +235,4 @@ declare class APIError extends Error {
196
235
  constructor(status: Status, body?: Record<string, any>);
197
236
  }
198
237
 
199
- export { APIError, type Context, type Endpoint, type EndpointConfig, type EndpointOptions, type EndpointResponse, type Handler, type HasRequiredKeys, type Middleware, type Prettify, type RequiredKeysOf, createEndpoint, createMiddleware, createRouter, getBody, shouldSerialize, statusCode };
238
+ 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
- type Method = "GET" | "POST" | "PUT" | "DELETE" | "PATCH" | "*";
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, Extra extends Record<string, any> = {}> = InferBody<Opts["body"]> & InferParam<Path> & InferMethod<Opts['method']> & InferHeaders<Opts["requireHeaders"]> & InferRequest<Opts["requireRequest"]> & InferQuery<Opts["query"]> & Extra;
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?: undefined;
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, Extra extends Record<string, any> = Record<string, any>> = (ctx: Context<Path, Opts, Extra>) => Promise<R>;
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, ContextTools>): {
105
- (...ctx: HasRequiredKeys<Context<Path, Opts, {}>> extends true ? [Context<Path, Opts, {}>] : [Context<Path, Opts, {}>?]): Promise<R>;
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,86 @@ interface RouterConfig {
129
146
  * Base path for the router
130
147
  */
131
148
  basePath?: string;
149
+ /**
150
+ * Middlewares for the router
151
+ */
152
+ routerMiddleware?: Endpoint[];
132
153
  }
133
154
  declare const createRouter: <E extends Endpoint, Config extends RouterConfig>(endpoints: E[], config?: Config) => {
134
155
  handler: (request: Request) => Promise<Response>;
135
156
  };
136
157
 
158
+ type MiddlewareHandler<Opts extends EndpointOptions, R extends EndpointResponse> = (ctx: Prettify<InferBody<Opts> & InferRequest<Opts> & InferHeaders<Opts> & {
159
+ params?: Record<string, string>;
160
+ query?: Record<string, string>;
161
+ } & ContextTools>) => Promise<R>;
162
+ declare function createMiddleware<Opts extends EndpointOptions, R extends EndpointResponse>(optionsOrHandler: MiddlewareHandler<Opts, R>): Endpoint<Handler<string, Opts, R>, Opts>;
163
+ declare function createMiddleware<Opts extends Omit<EndpointOptions, "method">, R extends EndpointResponse>(optionsOrHandler: Opts, handler: MiddlewareHandler<Opts & {
164
+ method: "*";
165
+ }, R>): Endpoint<Handler<string, Opts & {
166
+ method: "*";
167
+ }, R>, Opts & {
168
+ method: "*";
169
+ }>;
170
+ type Middleware<Opts extends EndpointOptions, R> = (opts: Opts, handler: (ctx: {
171
+ body?: InferBody<Opts>;
172
+ params?: Record<string, string>;
173
+ query?: Record<string, string>;
174
+ }) => Promise<R>) => Endpoint;
175
+
137
176
  declare function getBody(request: Request): Promise<any>;
138
177
  declare function shouldSerialize(body: any): boolean;
139
178
  declare const statusCode: {
140
179
  OK: number;
141
- Created: number;
142
- Accepted: number;
143
- "No Content": number;
144
- "Multiple Choices": number;
145
- "Moved Permanently": number;
146
- Found: number;
147
- "See Other": number;
148
- "Not Modified": number;
149
- "Temporary Redirect": number;
150
- "Bad Request": number;
151
- Unauthorized: number;
152
- "Payment Required": number;
153
- Forbidden: number;
154
- "Not Found": number;
155
- "Method Not Allowed": number;
156
- "Not Acceptable": number;
157
- "Proxy Authentication Required": number;
158
- "Request Timeout": number;
159
- Conflict: number;
160
- Gone: number;
161
- "Length Required": number;
162
- "Precondition Failed": number;
163
- "Payload Too Large": number;
164
- "URI Too Long": number;
165
- "Unsupported Media Type": number;
166
- "Range Not Satisfiable": number;
167
- "Expectation Failed": number;
168
- "I'm a teapot": number;
169
- "Misdirected Request": number;
170
- "Unprocessable Entity": number;
171
- Locked: number;
172
- "Failed Dependency": number;
173
- "Too Early": number;
174
- "Upgrade Required": number;
175
- "Precondition Required": number;
176
- "Too Many Requests": number;
177
- "Request Header Fields Too Large": number;
178
- "Unavailable For Legal Reasons": number;
179
- "Internal Server Error": number;
180
- "Not Implemented": number;
181
- "Bad Gateway": number;
182
- "Service Unavailable": number;
183
- "Gateway Timeout": number;
184
- "HTTP Version Not Supported": number;
185
- "Variant Also Negotiates": number;
186
- "Insufficient Storage": number;
187
- "Loop Detected": number;
188
- "Not Extended": number;
189
- "Network Authentication Required": number;
180
+ CREATED: number;
181
+ ACCEPTED: number;
182
+ NO_CONTENT: number;
183
+ MULTIPLE_CHOICES: number;
184
+ MOVED_PERMANENTLY: number;
185
+ FOUND: number;
186
+ SEE_OTHER: number;
187
+ NOT_MODIFIED: number;
188
+ TEMPORARY_REDIRECT: number;
189
+ BAD_REQUEST: number;
190
+ UNAUTHORIZED: number;
191
+ PAYMENT_REQUIRED: number;
192
+ FORBIDDEN: number;
193
+ NOT_FOUND: number;
194
+ METHOD_NOT_ALLOWED: number;
195
+ NOT_ACCEPTABLE: number;
196
+ PROXY_AUTHENTICATION_REQUIRED: number;
197
+ REQUEST_TIMEOUT: number;
198
+ CONFLICT: number;
199
+ GONE: number;
200
+ LENGTH_REQUIRED: number;
201
+ PRECONDITION_FAILED: number;
202
+ PAYLOAD_TOO_LARGE: number;
203
+ URI_TOO_LONG: number;
204
+ UNSUPPORTED_MEDIA_TYPE: number;
205
+ RANGE_NOT_SATISFIABLE: number;
206
+ EXPECTATION_FAILED: number;
207
+ "I'M_A_TEAPOT": number;
208
+ MISDIRECTED_REQUEST: number;
209
+ UNPROCESSABLE_ENTITY: number;
210
+ LOCKED: number;
211
+ FAILED_DEPENDENCY: number;
212
+ TOO_EARLY: number;
213
+ UPGRADE_REQUIRED: number;
214
+ PRECONDITION_REQUIRED: number;
215
+ TOO_MANY_REQUESTS: number;
216
+ REQUEST_HEADER_FIELDS_TOO_LARGE: number;
217
+ UNAVAILABLE_FOR_LEGAL_REASONS: number;
218
+ INTERNAL_SERVER_ERROR: number;
219
+ NOT_IMPLEMENTED: number;
220
+ BAD_GATEWAY: number;
221
+ SERVICE_UNAVAILABLE: number;
222
+ GATEWAY_TIMEOUT: number;
223
+ HTTP_VERSION_NOT_SUPPORTED: number;
224
+ VARIANT_ALSO_NEGOTIATES: number;
225
+ INSUFFICIENT_STORAGE: number;
226
+ LOOP_DETECTED: number;
227
+ NOT_EXTENDED: number;
228
+ NETWORK_AUTHENTICATION_REQUIRED: number;
190
229
  };
191
230
 
192
231
  type Status = keyof typeof statusCode;
@@ -196,4 +235,4 @@ declare class APIError extends Error {
196
235
  constructor(status: Status, body?: Record<string, any>);
197
236
  }
198
237
 
199
- export { APIError, type Context, type Endpoint, type EndpointConfig, type EndpointOptions, type EndpointResponse, type Handler, type HasRequiredKeys, type Middleware, type Prettify, type RequiredKeysOf, createEndpoint, createMiddleware, createRouter, getBody, shouldSerialize, statusCode };
238
+ 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 E=Object.defineProperty;var w=(r,e,n)=>e in r?E(r,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):r[e]=n;var l=(r,e,n)=>w(r,typeof e!="symbol"?e+"":e,n);import{ZodError as C}from"zod";var y=class extends Error{constructor(n,a){super(`API Error: ${n} ${a?.message??""}`,{cause:a});l(this,"status");l(this,"body");this.status=n,this.body=a??{},this.stack="",this.name="BetterCallAPIError"}};function B(r,e,n){let a=new Headers,t=async(...s)=>{let o={setHeader(d,i){a.set(d,i)},setCookie(d,i){a.append("Set-Cookie",`${d}=${i}`)},getCookie(d){return s[0]?.headers?.get("cookie")?.split(";").find(c=>c.startsWith(`${d}=`))?.split("=")[1]},...s[0]||{}};try{o.body=e.body?e.body.parse(o.body):void 0,o.query=e.query?e.query.parse(o.query):void 0,o.params=e.params?e.params.parse(o.params):void 0}catch(d){throw d instanceof C?new y("Bad Request",{message:d.message,details:d.errors}):d}if(e.requireHeaders&&!o.headers)throw new y("Bad Request",{message:"Headers are required"});if(e.requireRequest&&!o.request)throw new y("Bad Request",{message:"Request is required"});return await n(o)};return t.path=r,t.options=e,t.method=e.method,t.headers=a,t.middleware=void 0,t}import{createRouter as q,addRoute as P,findRoute as b}from"rou3";async function m(r){let e=r.headers.get("content-type")||"";if(r.body){if(e.includes("application/json"))return await r.json();if(e.includes("application/x-www-form-urlencoded")){let n=await r.formData(),a={};return n.forEach((t,s)=>{a[s]=t.toString()}),a}if(e.includes("multipart/form-data")){let n=await r.formData(),a={};return n.forEach((t,s)=>{a[s]=t}),a}return e.includes("text/plain")?await r.text():e.includes("application/octet-stream")?await r.arrayBuffer():e.includes("application/pdf")||e.includes("image/")||e.includes("video/")?await r.blob():e.includes("application/stream")||r.body instanceof ReadableStream?r.body:await r.text()}}function x(r){return typeof r=="object"&&r!==null&&!(r instanceof Blob)&&!(r instanceof FormData)}var R={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 N=(r,e)=>{let n=q();for(let t of r)if(Array.isArray(t.options?.method))for(let s of t.options.method)P(n,s,t.path,t);else P(n,t.options.method,t.path,t);return{handler:async t=>{let s=new URL(t.url),o=s.pathname;e?.basePath&&(o=o.split(e.basePath)[1]);let f=t.method,d=b(n,f,o),i=d?.data,c=await m(t),h=t.headers;if(!i)return new Response(null,{status:404,statusText:"Not Found"});try{let p={};i.middleware&&(p=await i.middleware({path:o,method:f,headers:h,params:s.searchParams,request:t.clone(),body:c})||{});let u=await i({path:o,method:f,headers:h,params:d?.params,request:t,body:c,...p?.context});if(u instanceof Response)return u;let g=x(u)?JSON.stringify(u):u;return new Response(g,{headers:i.headers})}catch(p){if(e?.onError){let u=await e.onError(p);if(u instanceof Response)return u}if(p instanceof y)return new Response(p.body?JSON.stringify(p.body):null,{status:R[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 L=r=>(e,n,a)=>{let t=async(...s)=>await a(s[0]||{});return t.path=e,t.options=n,t.middleware=r,t};export{y as APIError,B as createEndpoint,L as createMiddleware,N as createRouter,m as getBody,x as shouldSerialize,R as statusCode};
1
+ var P=Object.defineProperty;var S=(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)=>S(t,typeof e!="symbol"?e+"":e,s);import{ZodError as g}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,i){a.set(n,i)},setCookie(n,i){a.append("Set-Cookie",`${n}=${i}`)},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 i=await n(r),R=i.options?.body?i.options.body.parse(r.body):void 0;i&&(r={...r,body:R?{...R,...r.body}:r.body,context:{...r.context||{},...i}})}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 g?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.options.method)?o.options.method:[o.options.method];for(let E of r)l(a,E,o.path,o)}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,i=x(s,n,E),R=i?.data,O=await _(o),y=o.headers,T=Object.fromEntries(r.searchParams),h=x(a,n,E)?.data;if(!R)return new Response(null,{status:404,statusText:"Not Found"});try{let d={};if(h){let A=await h({path:E,method:n,headers:y,params:i?.params,request:o,body:O,query:T});A&&(d={...A,...d})}let c=await R({path:E,method:n,headers:y,params:i?.params,request:o,body:O,query:T,...d});if(c instanceof Response)return c;let C=I(c)?JSON.stringify(c):c;return new Response(C,{headers:R.headers})}catch(d){if(e?.onError){let c=await e.onError(d);if(c instanceof Response)return c}if(d instanceof u)return new Response(d.body?JSON.stringify(d.body):null,{status:w[d.status],statusText:d.status,headers:{"Content-Type":"application/json"}});if(e?.throwError)throw d;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?: Endpoint[]\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.options.method) ? route.options.method : [route.options.method]\n for (const method of methods) {\n addRoute(middlewareRouter, method, route.path, route)\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.2",
3
+ "version": "0.0.5-beta.4",
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
  },
@@ -35,4 +36,4 @@
35
36
  "files": [
36
37
  "dist"
37
38
  ]
38
- }
39
+ }