2022-12-20 23:05:00 +01:00
|
|
|
import { HonoRequest } from './request.ts'
|
2022-12-29 06:59:57 +01:00
|
|
|
import type { TypeResponse, Route } from './types.ts'
|
2022-12-28 03:25:48 +01:00
|
|
|
import type { Environment, NotFoundHandler } from './types.ts'
|
2022-07-10 17:17:29 +02:00
|
|
|
import type { CookieOptions } from './utils/cookie.ts'
|
|
|
|
import { serialize } from './utils/cookie.ts'
|
2022-07-02 08:09:45 +02:00
|
|
|
import type { StatusCode } from './utils/http-status.ts'
|
|
|
|
|
2022-12-28 03:25:48 +01:00
|
|
|
type Runtime = 'node' | 'deno' | 'bun' | 'cloudflare' | 'fastly' | 'vercel' | 'lagon' | 'other'
|
2022-09-20 14:41:45 +02:00
|
|
|
type HeaderField = [string, string]
|
|
|
|
type Headers = Record<string, string | string[]>
|
2022-12-28 03:25:48 +01:00
|
|
|
type Data = string | ArrayBuffer | ReadableStream
|
|
|
|
|
|
|
|
export interface ExecutionContext {
|
|
|
|
waitUntil(promise: Promise<void>): void
|
|
|
|
passThroughOnException(): void
|
|
|
|
}
|
|
|
|
export interface ContextVariableMap {}
|
2022-07-02 08:09:45 +02:00
|
|
|
|
2023-01-03 14:00:29 +01:00
|
|
|
type GetVariable<K, E extends Partial<Environment>> = K extends keyof E['Variables']
|
|
|
|
? E['Variables'][K]
|
|
|
|
: K extends keyof ContextVariableMap
|
|
|
|
? ContextVariableMap[K]
|
|
|
|
: unknown
|
|
|
|
|
2022-12-29 06:59:57 +01:00
|
|
|
type ContextOptions<E extends Partial<Environment>, R extends Route> = {
|
2022-12-20 23:05:00 +01:00
|
|
|
env?: E['Bindings']
|
|
|
|
executionCtx?: FetchEvent | ExecutionContext | undefined
|
2022-12-29 06:59:57 +01:00
|
|
|
notFoundHandler?: NotFoundHandler<E, R>
|
2022-12-20 23:05:00 +01:00
|
|
|
paramData?: Record<string, string>
|
|
|
|
}
|
|
|
|
|
2022-11-03 07:53:41 +01:00
|
|
|
export class Context<
|
2022-10-23 01:10:00 +02:00
|
|
|
E extends Partial<Environment> = Environment,
|
2022-12-29 06:59:57 +01:00
|
|
|
R extends Route = Route,
|
2022-12-10 08:37:14 +01:00
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
2022-12-29 06:59:57 +01:00
|
|
|
I = any
|
2022-08-23 13:53:25 +02:00
|
|
|
> {
|
2022-12-20 23:05:00 +01:00
|
|
|
env: E['Bindings'] = {}
|
|
|
|
finalized: boolean = false
|
2022-10-02 08:44:54 +02:00
|
|
|
error: Error | undefined = undefined
|
2022-07-10 16:44:23 +02:00
|
|
|
|
2022-12-29 06:59:57 +01:00
|
|
|
private _req?: HonoRequest<R, I>
|
2022-11-23 10:42:24 +01:00
|
|
|
private _status: StatusCode = 200
|
2022-07-17 11:11:09 +02:00
|
|
|
private _executionCtx: FetchEvent | ExecutionContext | undefined
|
2022-07-02 08:09:45 +02:00
|
|
|
private _pretty: boolean = false
|
|
|
|
private _prettySpace: number = 2
|
2022-10-23 01:10:00 +02:00
|
|
|
private _map: Record<string, unknown> | undefined
|
2022-09-20 14:41:45 +02:00
|
|
|
private _headers: Record<string, string[]> | undefined
|
2022-07-02 08:09:45 +02:00
|
|
|
private _res: Response | undefined
|
2022-12-20 23:05:00 +01:00
|
|
|
private _paramData: Record<string, string> | undefined
|
|
|
|
private rawRequest: Request
|
2022-12-29 06:59:57 +01:00
|
|
|
private notFoundHandler: NotFoundHandler<E, R> = () => new Response()
|
2022-12-20 23:05:00 +01:00
|
|
|
|
2022-12-29 06:59:57 +01:00
|
|
|
constructor(req: Request, options?: ContextOptions<E, R>) {
|
2022-12-20 23:05:00 +01:00
|
|
|
this.rawRequest = req
|
|
|
|
if (options) {
|
|
|
|
this._executionCtx = options.executionCtx
|
|
|
|
this._paramData = options.paramData
|
|
|
|
this.env = options.env
|
|
|
|
if (options.notFoundHandler) {
|
|
|
|
this.notFoundHandler = options.notFoundHandler
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-29 06:59:57 +01:00
|
|
|
get req(): HonoRequest<R, I> {
|
2022-12-20 23:05:00 +01:00
|
|
|
if (this._req) {
|
|
|
|
return this._req
|
|
|
|
} else {
|
2022-12-29 06:59:57 +01:00
|
|
|
this._req = new HonoRequest<R, I>(this.rawRequest, this._paramData)
|
2022-12-20 23:05:00 +01:00
|
|
|
return this._req
|
|
|
|
}
|
2022-07-17 11:11:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
get event(): FetchEvent {
|
|
|
|
if (this._executionCtx instanceof FetchEvent) {
|
|
|
|
return this._executionCtx
|
2022-07-02 08:09:45 +02:00
|
|
|
} else {
|
2022-07-17 11:11:09 +02:00
|
|
|
throw Error('This context has no FetchEvent')
|
2022-07-02 08:09:45 +02:00
|
|
|
}
|
2022-07-17 11:11:09 +02:00
|
|
|
}
|
2022-07-02 08:09:45 +02:00
|
|
|
|
2022-07-17 11:11:09 +02:00
|
|
|
get executionCtx(): ExecutionContext {
|
|
|
|
if (this._executionCtx) {
|
2022-11-22 23:27:42 +01:00
|
|
|
return this._executionCtx as ExecutionContext
|
2022-07-17 11:11:09 +02:00
|
|
|
} else {
|
|
|
|
throw Error('This context has no ExecutionContext')
|
|
|
|
}
|
2022-07-02 08:09:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
get res(): Response {
|
2022-09-21 16:05:26 +02:00
|
|
|
return (this._res ||= new Response('404 Not Found', { status: 404 }))
|
2022-07-02 08:09:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
set res(_res: Response) {
|
|
|
|
this._res = _res
|
|
|
|
this.finalized = true
|
|
|
|
}
|
|
|
|
|
2023-01-18 00:23:58 +01:00
|
|
|
header = (name: string, value: string, options?: { append?: boolean }): void => {
|
2022-07-02 08:09:45 +02:00
|
|
|
this._headers ||= {}
|
2022-09-20 14:41:45 +02:00
|
|
|
const key = name.toLowerCase()
|
|
|
|
|
|
|
|
let shouldAppend = false
|
|
|
|
if (options && options.append) {
|
|
|
|
const vAlreadySet = this._headers[key]
|
|
|
|
if (vAlreadySet && vAlreadySet.length) {
|
|
|
|
shouldAppend = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (shouldAppend) {
|
|
|
|
this._headers[key].push(value)
|
|
|
|
} else {
|
|
|
|
this._headers[key] = [value]
|
|
|
|
}
|
|
|
|
|
2022-07-02 08:09:45 +02:00
|
|
|
if (this.finalized) {
|
2022-09-20 14:41:45 +02:00
|
|
|
if (shouldAppend) {
|
|
|
|
this.res.headers.append(name, value)
|
|
|
|
} else {
|
|
|
|
this.res.headers.set(name, value)
|
|
|
|
}
|
2022-07-02 08:09:45 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-18 00:23:58 +01:00
|
|
|
status = (status: StatusCode): void => {
|
2022-07-02 08:09:45 +02:00
|
|
|
this._status = status
|
|
|
|
}
|
|
|
|
|
2023-01-03 14:00:29 +01:00
|
|
|
set<Key extends keyof E['Variables'] | keyof ContextVariableMap>(
|
|
|
|
key: Key,
|
|
|
|
value: GetVariable<Key, E>
|
|
|
|
): void {
|
2022-07-02 08:09:45 +02:00
|
|
|
this._map ||= {}
|
2023-01-03 14:00:29 +01:00
|
|
|
this._map[key as string] = value
|
2022-07-02 08:09:45 +02:00
|
|
|
}
|
|
|
|
|
2023-01-03 14:00:29 +01:00
|
|
|
get<Key extends keyof E['Variables'] | keyof ContextVariableMap>(key: Key): GetVariable<Key, E> {
|
|
|
|
return this._map?.[key as string] as GetVariable<Key, E>
|
2022-07-02 08:09:45 +02:00
|
|
|
}
|
|
|
|
|
2023-01-18 00:23:58 +01:00
|
|
|
pretty = (prettyJSON: boolean, space: number = 2): void => {
|
2022-07-02 08:09:45 +02:00
|
|
|
this._pretty = prettyJSON
|
|
|
|
this._prettySpace = space
|
|
|
|
}
|
|
|
|
|
2023-01-18 00:23:58 +01:00
|
|
|
newResponse = (data: Data | null, status: StatusCode, headers: Headers = {}): Response => {
|
2022-09-20 14:41:45 +02:00
|
|
|
return new Response(data, {
|
2022-11-23 10:42:24 +01:00
|
|
|
status,
|
2022-09-20 14:41:45 +02:00
|
|
|
headers: this._finalizeHeaders(headers),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
private _finalizeHeaders(incomingHeaders: Headers): HeaderField[] {
|
|
|
|
const finalizedHeaders: HeaderField[] = []
|
|
|
|
const headersKv = this._headers || {}
|
|
|
|
// If Response is already set
|
2022-07-02 08:09:45 +02:00
|
|
|
if (this._res) {
|
|
|
|
this._res.headers.forEach((v, k) => {
|
2022-09-20 14:41:45 +02:00
|
|
|
headersKv[k] = [v]
|
2022-07-02 08:09:45 +02:00
|
|
|
})
|
|
|
|
}
|
2022-09-20 14:41:45 +02:00
|
|
|
for (const key of Object.keys(incomingHeaders)) {
|
|
|
|
const value = incomingHeaders[key]
|
|
|
|
if (typeof value === 'string') {
|
|
|
|
finalizedHeaders.push([key, value])
|
|
|
|
} else {
|
|
|
|
for (const v of value) {
|
|
|
|
finalizedHeaders.push([key, v])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
delete headersKv[key]
|
|
|
|
}
|
|
|
|
for (const key of Object.keys(headersKv)) {
|
|
|
|
for (const value of headersKv[key]) {
|
|
|
|
const kv: HeaderField = [key, value]
|
|
|
|
finalizedHeaders.push(kv)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return finalizedHeaders
|
2022-07-02 08:09:45 +02:00
|
|
|
}
|
|
|
|
|
2023-01-18 00:23:58 +01:00
|
|
|
body = (
|
|
|
|
data: Data | null,
|
|
|
|
status: StatusCode = this._status,
|
|
|
|
headers: Headers = {}
|
|
|
|
): Response => {
|
2022-07-02 08:09:45 +02:00
|
|
|
return this.newResponse(data, status, headers)
|
|
|
|
}
|
|
|
|
|
2023-01-18 00:23:58 +01:00
|
|
|
text = (text: string, status?: StatusCode, headers?: Headers): Response => {
|
2022-12-14 14:03:02 +01:00
|
|
|
// If the header is empty, return Response immediately.
|
|
|
|
// Content-Type will be added automatically as `text/plain`.
|
|
|
|
if (!headers && !status && !this._res && !this._headers) {
|
|
|
|
return new Response(text)
|
|
|
|
}
|
|
|
|
status ||= this._status
|
|
|
|
headers ||= {}
|
2022-08-10 14:34:33 +02:00
|
|
|
headers['content-type'] = 'text/plain; charset=UTF-8'
|
2022-11-23 10:42:24 +01:00
|
|
|
return this.newResponse(text, status, headers)
|
2022-07-02 08:09:45 +02:00
|
|
|
}
|
|
|
|
|
2023-01-18 00:23:58 +01:00
|
|
|
json = <T>(object: T, status: StatusCode = this._status, headers: Headers = {}): Response => {
|
2022-07-02 08:09:45 +02:00
|
|
|
const body = this._pretty
|
|
|
|
? JSON.stringify(object, null, this._prettySpace)
|
|
|
|
: JSON.stringify(object)
|
2022-08-10 14:34:33 +02:00
|
|
|
headers['content-type'] = 'application/json; charset=UTF-8'
|
2022-11-23 10:42:24 +01:00
|
|
|
return this.newResponse(body, status, headers)
|
2022-07-02 08:09:45 +02:00
|
|
|
}
|
|
|
|
|
2022-12-28 03:25:48 +01:00
|
|
|
<<<<<<< HEAD
|
2023-01-18 00:23:58 +01:00
|
|
|
html = (html: string, status: StatusCode = this._status, headers: Headers = {}): Response => {
|
2022-12-28 03:25:48 +01:00
|
|
|
=======
|
|
|
|
jsonT<T>(object: T, status: StatusCode = this._status, headers: Headers = {}): TypeResponse<T> {
|
|
|
|
return {
|
|
|
|
response: this.json(object, status, headers),
|
|
|
|
data: object,
|
|
|
|
format: 'json',
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
html(html: string, status: StatusCode = this._status, headers: Headers = {}): Response {
|
|
|
|
>>>>>>> fee7292 (feat: new validator middleware using 3rd-party & current middleware obsolete (#745))
|
2022-08-10 14:34:33 +02:00
|
|
|
headers['content-type'] = 'text/html; charset=UTF-8'
|
2022-11-23 10:42:24 +01:00
|
|
|
return this.newResponse(html, status, headers)
|
2022-07-02 08:09:45 +02:00
|
|
|
}
|
|
|
|
|
2023-01-18 00:23:58 +01:00
|
|
|
redirect = (location: string, status: StatusCode = 302): Response => {
|
2022-07-02 08:09:45 +02:00
|
|
|
return this.newResponse(null, status, {
|
|
|
|
Location: location,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2023-01-18 00:23:58 +01:00
|
|
|
cookie = (name: string, value: string, opt?: CookieOptions): void => {
|
2022-07-10 17:17:29 +02:00
|
|
|
const cookie = serialize(name, value, opt)
|
2022-09-20 14:41:45 +02:00
|
|
|
this.header('set-cookie', cookie, { append: true })
|
2022-07-10 17:17:29 +02:00
|
|
|
}
|
|
|
|
|
2022-12-28 03:25:48 +01:00
|
|
|
<<<<<<< HEAD
|
2023-01-18 00:23:58 +01:00
|
|
|
notFound = (): Response | Promise<Response> => {
|
2022-11-03 07:53:41 +01:00
|
|
|
return this.notFoundHandler(this as unknown as Context<string, E>)
|
2022-12-28 03:25:48 +01:00
|
|
|
=======
|
|
|
|
notFound(): Response | Promise<Response> {
|
|
|
|
return this.notFoundHandler(this)
|
|
|
|
>>>>>>> fee7292 (feat: new validator middleware using 3rd-party & current middleware obsolete (#745))
|
2022-07-02 08:09:45 +02:00
|
|
|
}
|
2022-11-03 07:35:23 +01:00
|
|
|
|
|
|
|
get runtime(): Runtime {
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
|
|
const global = globalThis as any
|
|
|
|
|
|
|
|
if (global?.Deno !== undefined) {
|
|
|
|
return 'deno'
|
|
|
|
}
|
|
|
|
|
|
|
|
if (global?.Bun !== undefined) {
|
|
|
|
return 'bun'
|
|
|
|
}
|
|
|
|
|
|
|
|
if (typeof global?.WebSocketPair === 'function') {
|
|
|
|
return 'cloudflare'
|
|
|
|
}
|
|
|
|
|
|
|
|
if (global?.fastly !== undefined) {
|
|
|
|
return 'fastly'
|
|
|
|
}
|
|
|
|
|
2022-12-02 03:28:49 +01:00
|
|
|
if (typeof global?.EdgeRuntime === 'string') {
|
2022-11-03 07:35:23 +01:00
|
|
|
return 'vercel'
|
|
|
|
}
|
|
|
|
|
2022-12-02 03:28:49 +01:00
|
|
|
if (global?.process?.release?.name === 'node') {
|
2022-11-03 23:14:57 +01:00
|
|
|
return 'node'
|
|
|
|
}
|
|
|
|
|
2022-12-24 17:42:59 +01:00
|
|
|
if (global?.__lagon__ !== undefined) {
|
|
|
|
return 'lagon'
|
|
|
|
}
|
|
|
|
|
2022-11-03 07:35:23 +01:00
|
|
|
return 'other'
|
|
|
|
}
|
2022-07-02 08:09:45 +02:00
|
|
|
}
|