rajt
Version:
A serverless bundler layer, fully typed for AWS Lambda (Node.js and LLRT) and Cloudflare Workers.
121 lines (104 loc) • 3.8 kB
text/typescript
/* eslint-disable @typescript-eslint/no-explicit-any */
import { Hono } from 'hono'
import type { Env, Context, ErrorHandler, NotFoundHandler, Next } from 'hono'
// import type { MiddlewareHandler } from 'hono'
// import { createMiddleware } from 'hono/factory'
// import type { H, Handler, HandlerResponse } from 'hono/types'
import type { HTTPResponseError } from 'hono/types'
import type { Routes } from './types'
import { BadRequest, Unauthorized } from './exceptions'
import { resolve, resolveMiddleware } from './utils/resolve'
import { getMiddlewares, getHandler } from './register'
import { isDev } from './utils/environment'
import { Auth } from './auth'
import response from './response'
import cx from './context'
type InitFunction<E extends Env = Env> = (app: Hono<E>) => void
export type ServerOptions<E extends Env = Env> = Partial<{
routes: Routes,
notFound: NotFoundHandler<E>,
onError: ErrorHandler<E>,
root: string,
app?: Hono<E>,
init?: InitFunction<E>,
}>
const NFHandler = () => response.notFound()
const EHandler = async (e: Error | HTTPResponseError) => {
console.error(e)
switch (true) {
case e instanceof Unauthorized:
case 'status' in e && e.status === 401:
return response.unauthorized()
case e instanceof BadRequest:
case 'status' in e && e.status === 400:
// @ts-ignore
return response.badRequest(undefined, e?.message)
default:
return response.internalError(
// @ts-ignore
isDev()
? e.stack?.split('\n').map(line =>
line.replace(
/at (.+ )?\(?([^)]+)\)?/g,
(match, method, path) => {
if (!path) return match
const nodeModulesIndex = path.indexOf('node_modules')
if (nodeModulesIndex > -1)
return `${method || ''}(node_modules${path.slice(nodeModulesIndex + 'node_modules'.length)})`
const projectRoot = process.cwd()
const relativePath = path.startsWith(projectRoot) ? path.slice(projectRoot.length + 1) : path
return `${method || ''}(${relativePath})`
}
).trim()
)
: undefined,
e.message || 'Internal Error'
)
}
// return json.internalError(
// // @ts-ignore
// isDev() ? e.stack?.split('\n at ').map() : undefined,
// e.message || 'Internal Error'
// )
// error: e.message,
// cause: e.cause || '???',
// stack: isDev (? e.stack : undefined
}
export const createApp = <E extends Env>(options?: ServerOptions<E>) => {
// const root = options?.root ?? '/'
const app = options?.app ?? new Hono<E>()
app.use(async (c: Context, next: Next) => {
cx.setContext(c)
Auth.resolve()
await next()
})
getMiddlewares().forEach(mw => {
const h = async (c: Context, next: Next) => await resolveMiddleware(mw)(cx.cx, next)
// @ts-ignore
mw?.path ? app.use(String(mw.path), h) : app.use(h)
})
// @ts-ignore
app.onError(options?.onError || EHandler)
// @ts-ignore
app.notFound(options?.notFound || NFHandler)
if (options?.init) options.init(app)
const routes = options?.routes || []
for (const route of routes) {
if (Array.isArray(route)) {
// @ts-ignore
app[route[0]](route[1], ...mw(route[2], route[3]), ...resolve(getHandler(route[3]), route[3]))
} else {
// @ts-ignore
app[route.method](route.path, ...mw(route.middlewares, route.name), ...resolve(route.handle, route.name))
}
}
return app
}
function mw(...objs: string[]): Function[] {
return objs.flatMap(obj => {
if (typeof obj !== 'string') return null
// @ts-ignore
return getHandler(obj)?.mw || null
}).flat().filter(Boolean)
}
export default createApp