better-call 0.0.2 → 0.0.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
@@ -1,18 +1,11 @@
1
1
  # better-call
2
2
 
3
- Better call is a tiny web framework for creating endpoints that can be invoked as a function or mounted to a router and can be served by any web standard compatible server (like Bun, node, nextjs, sveltekit...). Built for typescript and it comes with a very high performance router based on [rou3](https://github.com/unjs/rou3).
3
+ Better call is a tiny web framework for creating endpoints that can be invoked as a normal function or mounted to a router and can be served by any web standard compatible server (like Bun, node, nextjs, sveltekit...).
4
4
 
5
- ### Use cases:
5
+ Built for typescript and it comes with a very high performance router based on [rou3](https://github.com/unjs/rou3).
6
6
 
7
- ### Use Cases:
8
7
 
9
- - **Single Function Endpoint**: Define an endpoint once and use it in different context. Like in React Server Components (RSC), as a server action, or directly as api route in your Next.js app from a single function.
10
-
11
- - **Typed Endpoints**: Create endpoints with typed parameters and return types that can be invoked from other endpoints or used throughout your code.
12
-
13
- - **Library Integration**: Ideal for libraries that need to expose server endpoints. Export a function that can be mounted to a route and used as a function, for example an auth library with a `getSession` function.
14
-
15
- > ⚠️ This project is still in development and not ready for production use. But feel free to try it out and give feedback.
8
+ > ⚠️ This project early in development and not ready for production use. But feel free to try it out and give feedback.
16
9
 
17
10
  ## Install
18
11
 
@@ -67,7 +60,7 @@ Bun.serve({
67
60
 
68
61
  You can create middleware by calling `createMiddleware` and passing it a function that will be invoked before the endpoint is called.
69
62
 
70
- If you return an object from the middleware, it will be merged with the context object on the endpoint.
63
+ If you return a context object from the middleware, it will be merged with the context object on the endpoint.
71
64
 
72
65
  ```ts
73
66
  import { createMiddleware, createEndpoint } from "better-call"
@@ -77,9 +70,11 @@ const createProtectedEndpoint = createMiddleware(async (ctx) => {
77
70
  throw new Error("Unauthorized")
78
71
  }
79
72
  return {
80
- session: {
81
- id: "123",
82
- }
73
+ context: {
74
+ session: {
75
+ id: "123",
76
+ }
77
+ }
83
78
  }
84
79
  })
85
80
 
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})},M=(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 H=t=>M(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:()=>I,createMiddleware:()=>B,createRouter:()=>S,getBody:()=>x,shouldSerialize:()=>R,statusCode:()=>P});module.exports=H(k);var w=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=void 0,this.name="BetterCallAPIError"}};function I(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 w.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 S=(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,c=r.method,d=(0,f.findRoute)(n,c,o),i=d?.data,l=await x(r),$=new Headers,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});if(y instanceof Response)return y;let E=R(y)?JSON.stringify(y):y;return new Response(E,{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 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})},H=(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 I=t=>H(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:()=>M,createMiddleware:()=>B,createRouter:()=>S,getBody:()=>x,shouldSerialize:()=>R,statusCode:()=>P});module.exports=I(k);var w=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 M(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 w.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 S=(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,c=r.method,d=(0,f.findRoute)(n,c,o),i=d?.data,l=await x(r),$=new Headers,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 E=R(y)?JSON.stringify(y):y;return new Response(E,{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});
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 | {\n status: string,\n body: any,\n headers: Headers | Record<string, any>\n [key: string]: any\n} | 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 = undefined\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\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 const path = url.pathname;\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 responseHeaders = new Headers()\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,\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<E | void>\n\nexport const createMiddleware = <E extends Record<string, any>, M extends Middleware<E>>(middleware: M) => {\n type MiddlewareReturn = Awaited<ReturnType<M>>\n type MiddlewareContext = MiddlewareReturn extends void ? {} : MiddlewareReturn\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","responseHeaders","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\";\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 | {\n status: string,\n body: any,\n headers: Headers | Record<string, any>\n [key: string]: any\n} | 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\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 const path = url.pathname;\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 responseHeaders = new Headers()\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}\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","responseHeaders","headers","middleware","handlerRes","resBody","shouldSerialize","e","onErrorRes","APIError","statusCode","createMiddleware","middleware","path","options","handler","handle","ctx"]}
package/dist/index.d.cts CHANGED
@@ -1,9 +1,19 @@
1
1
  import { ZodSchema, ZodOptional, z } from 'zod';
2
2
  import { HasRequiredKeys as HasRequiredKeys$1 } from 'type-fest';
3
3
 
4
- type Middleware<E extends Record<string, string>> = (ctx: Context<string, EndpointOptions, E>) => Promise<E | void>;
5
- 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 void ? {} : Awaited<ReturnType<M>>>) => {
6
- (...ctx: HasRequiredKeys$1<Context<Path, Opts, Awaited<ReturnType<M>> extends void ? {} : Awaited<ReturnType<M>>>> extends true ? [Context<Path, Opts, Awaited<ReturnType<M>> extends void ? {} : Awaited<ReturnType<M>>>] : [Context<Path, Opts, Awaited<ReturnType<M>> extends void ? {} : Awaited<ReturnType<M>>>?]): Promise<R>;
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>;
7
17
  path: Path;
8
18
  options: Opts;
9
19
  middleware: M;
package/dist/index.d.ts CHANGED
@@ -1,9 +1,19 @@
1
1
  import { ZodSchema, ZodOptional, z } from 'zod';
2
2
  import { HasRequiredKeys as HasRequiredKeys$1 } from 'type-fest';
3
3
 
4
- type Middleware<E extends Record<string, string>> = (ctx: Context<string, EndpointOptions, E>) => Promise<E | void>;
5
- 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 void ? {} : Awaited<ReturnType<M>>>) => {
6
- (...ctx: HasRequiredKeys$1<Context<Path, Opts, Awaited<ReturnType<M>> extends void ? {} : Awaited<ReturnType<M>>>> extends true ? [Context<Path, Opts, Awaited<ReturnType<M>> extends void ? {} : Awaited<ReturnType<M>>>] : [Context<Path, Opts, Awaited<ReturnType<M>> extends void ? {} : Awaited<ReturnType<M>>>?]): Promise<R>;
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>;
7
17
  path: Path;
8
18
  options: Opts;
9
19
  middleware: M;
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- var w=Object.defineProperty;var E=(r,e,n)=>e in r?w(r,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):r[e]=n;var l=(r,e,n)=>E(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=void 0,this.name="BetterCallAPIError"}};function k(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 F=(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,f=t.method,d=b(n,f,o),i=d?.data,c=await m(t),T=new Headers,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});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 U=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,k as createEndpoint,U as createMiddleware,F as createRouter,m as getBody,x as shouldSerialize,R as statusCode};
1
+ var w=Object.defineProperty;var E=(r,e,n)=>e in r?w(r,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):r[e]=n;var l=(r,e,n)=>E(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 k(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 F=(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,f=t.method,d=b(n,f,o),i=d?.data,c=await m(t),T=new Headers,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 U=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,k as createEndpoint,U as createMiddleware,F as createRouter,m as getBody,x as shouldSerialize,R 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 | {\n status: string,\n body: any,\n headers: Headers | Record<string, any>\n [key: string]: any\n} | 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 = undefined\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\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 const path = url.pathname;\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 responseHeaders = new Headers()\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,\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<E | void>\n\nexport const createMiddleware = <E extends Record<string, any>, M extends Middleware<E>>(middleware: M) => {\n type MiddlewareReturn = Awaited<ReturnType<M>>\n type MiddlewareContext = MiddlewareReturn extends void ? {} : MiddlewareReturn\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","responseHeaders","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\";\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 | {\n status: string,\n body: any,\n headers: Headers | Record<string, any>\n [key: string]: any\n} | 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\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 const path = url.pathname;\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 responseHeaders = new Headers()\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}\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","responseHeaders","headers","middleware","handlerRes","resBody","shouldSerialize","e","onErrorRes","APIError","statusCode","createMiddleware","middleware","path","options","handler","handle","ctx"]}
package/package.json CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "better-call",
3
- "version": "0.0.2",
3
+ "version": "0.0.4",
4
4
  "type": "module",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.js",
7
7
  "types": "./dist/index.d.ts",
8
8
  "scripts": {
9
9
  "test": "vitest",
10
+ "typecheck": "tsc --noEmit",
10
11
  "bump": "bumpp",
11
12
  "build": "tsup"
12
13
  },