@kerebron/extension-server-hono 0.4.28 → 0.4.30
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/esm/HonoYjsMemAdapter.js +1 -0
- package/esm/HonoYjsMemAdapter.js.map +1 -0
- package/esm/deps/jsr.io/@hono/hono/4.11.3/src/compose.js +1 -0
- package/esm/deps/jsr.io/@hono/hono/4.11.3/src/compose.js.map +1 -0
- package/esm/deps/jsr.io/@hono/hono/4.11.3/src/context.js +1 -0
- package/esm/deps/jsr.io/@hono/hono/4.11.3/src/context.js.map +1 -0
- package/esm/deps/jsr.io/@hono/hono/4.11.3/src/helper/websocket/index.js +1 -0
- package/esm/deps/jsr.io/@hono/hono/4.11.3/src/helper/websocket/index.js.map +1 -0
- package/esm/deps/jsr.io/@hono/hono/4.11.3/src/hono-base.js +1 -0
- package/esm/deps/jsr.io/@hono/hono/4.11.3/src/hono-base.js.map +1 -0
- package/esm/deps/jsr.io/@hono/hono/4.11.3/src/http-exception.js +1 -0
- package/esm/deps/jsr.io/@hono/hono/4.11.3/src/http-exception.js.map +1 -0
- package/esm/deps/jsr.io/@hono/hono/4.11.3/src/request/constants.js +1 -0
- package/esm/deps/jsr.io/@hono/hono/4.11.3/src/request/constants.js.map +1 -0
- package/esm/deps/jsr.io/@hono/hono/4.11.3/src/request.js +1 -0
- package/esm/deps/jsr.io/@hono/hono/4.11.3/src/request.js.map +1 -0
- package/esm/deps/jsr.io/@hono/hono/4.11.3/src/router.js +1 -0
- package/esm/deps/jsr.io/@hono/hono/4.11.3/src/router.js.map +1 -0
- package/esm/deps/jsr.io/@hono/hono/4.11.3/src/types.js +1 -0
- package/esm/deps/jsr.io/@hono/hono/4.11.3/src/types.js.map +1 -0
- package/esm/deps/jsr.io/@hono/hono/4.11.3/src/utils/body.js +1 -0
- package/esm/deps/jsr.io/@hono/hono/4.11.3/src/utils/body.js.map +1 -0
- package/esm/deps/jsr.io/@hono/hono/4.11.3/src/utils/constants.js +1 -0
- package/esm/deps/jsr.io/@hono/hono/4.11.3/src/utils/constants.js.map +1 -0
- package/esm/deps/jsr.io/@hono/hono/4.11.3/src/utils/headers.js +1 -0
- package/esm/deps/jsr.io/@hono/hono/4.11.3/src/utils/headers.js.map +1 -0
- package/esm/deps/jsr.io/@hono/hono/4.11.3/src/utils/html.js +1 -0
- package/esm/deps/jsr.io/@hono/hono/4.11.3/src/utils/html.js.map +1 -0
- package/esm/deps/jsr.io/@hono/hono/4.11.3/src/utils/http-status.js +1 -0
- package/esm/deps/jsr.io/@hono/hono/4.11.3/src/utils/http-status.js.map +1 -0
- package/esm/deps/jsr.io/@hono/hono/4.11.3/src/utils/mime.js +1 -0
- package/esm/deps/jsr.io/@hono/hono/4.11.3/src/utils/mime.js.map +1 -0
- package/esm/deps/jsr.io/@hono/hono/4.11.3/src/utils/types.js +1 -0
- package/esm/deps/jsr.io/@hono/hono/4.11.3/src/utils/types.js.map +1 -0
- package/esm/deps/jsr.io/@hono/hono/4.11.3/src/utils/url.js +1 -0
- package/esm/deps/jsr.io/@hono/hono/4.11.3/src/utils/url.js.map +1 -0
- package/esm/mod.js +1 -0
- package/esm/mod.js.map +1 -0
- package/package.json +5 -1
- package/src/HonoYjsMemAdapter.ts +264 -0
- package/src/deps/jsr.io/@hono/hono/4.11.3/src/compose.ts +73 -0
- package/src/deps/jsr.io/@hono/hono/4.11.3/src/context.ts +770 -0
- package/src/deps/jsr.io/@hono/hono/4.11.3/src/helper/websocket/index.ts +140 -0
- package/src/deps/jsr.io/@hono/hono/4.11.3/src/hono-base.ts +539 -0
- package/src/deps/jsr.io/@hono/hono/4.11.3/src/http-exception.ts +78 -0
- package/src/deps/jsr.io/@hono/hono/4.11.3/src/request/constants.ts +1 -0
- package/src/deps/jsr.io/@hono/hono/4.11.3/src/request.ts +487 -0
- package/src/deps/jsr.io/@hono/hono/4.11.3/src/router.ts +103 -0
- package/src/deps/jsr.io/@hono/hono/4.11.3/src/types.ts +2489 -0
- package/src/deps/jsr.io/@hono/hono/4.11.3/src/utils/body.ts +229 -0
- package/src/deps/jsr.io/@hono/hono/4.11.3/src/utils/constants.ts +4 -0
- package/src/deps/jsr.io/@hono/hono/4.11.3/src/utils/headers.ts +333 -0
- package/src/deps/jsr.io/@hono/hono/4.11.3/src/utils/html.ts +182 -0
- package/src/deps/jsr.io/@hono/hono/4.11.3/src/utils/http-status.ts +72 -0
- package/src/deps/jsr.io/@hono/hono/4.11.3/src/utils/mime.ts +96 -0
- package/src/deps/jsr.io/@hono/hono/4.11.3/src/utils/types.ts +116 -0
- package/src/deps/jsr.io/@hono/hono/4.11.3/src/utils/url.ts +310 -0
- package/src/mod.ts +5 -0
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module
|
|
3
|
+
* WebSocket Helper for Hono.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
7
|
+
import type { Context } from '../../context.js'
|
|
8
|
+
import type { MiddlewareHandler, TypedResponse } from '../../types.js'
|
|
9
|
+
import type { StatusCode } from '../../utils/http-status.js'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* WebSocket Event Listeners type
|
|
13
|
+
*/
|
|
14
|
+
export interface WSEvents<T = unknown> {
|
|
15
|
+
onOpen?: (evt: Event, ws: WSContext<T>) => void
|
|
16
|
+
onMessage?: (evt: MessageEvent<WSMessageReceive>, ws: WSContext<T>) => void
|
|
17
|
+
onClose?: (evt: CloseEvent, ws: WSContext<T>) => void
|
|
18
|
+
onError?: (evt: Event, ws: WSContext<T>) => void
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Upgrade WebSocket Type
|
|
23
|
+
*/
|
|
24
|
+
export interface UpgradeWebSocket<T = unknown, U = any, _WSEvents = WSEvents<T>> {
|
|
25
|
+
(
|
|
26
|
+
createEvents: (c: Context) => _WSEvents | Promise<_WSEvents>,
|
|
27
|
+
options?: U
|
|
28
|
+
): MiddlewareHandler<
|
|
29
|
+
any,
|
|
30
|
+
string,
|
|
31
|
+
{
|
|
32
|
+
outputFormat: 'ws'
|
|
33
|
+
}
|
|
34
|
+
>
|
|
35
|
+
(
|
|
36
|
+
c: Context,
|
|
37
|
+
events: _WSEvents,
|
|
38
|
+
options?: U
|
|
39
|
+
): Promise<Response & TypedResponse<{}, StatusCode, 'ws'>>
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* ReadyState for WebSocket
|
|
44
|
+
*/
|
|
45
|
+
export type WSReadyState = 0 | 1 | 2 | 3
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* An argument for WSContext class
|
|
49
|
+
*/
|
|
50
|
+
export interface WSContextInit<T = unknown> {
|
|
51
|
+
send(data: string | ArrayBuffer | Uint8Array, options: SendOptions): void
|
|
52
|
+
close(code?: number, reason?: string): void
|
|
53
|
+
|
|
54
|
+
raw?: T
|
|
55
|
+
readyState: WSReadyState
|
|
56
|
+
url?: string | URL | null
|
|
57
|
+
protocol?: string | null
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Options for sending message
|
|
62
|
+
*/
|
|
63
|
+
export interface SendOptions {
|
|
64
|
+
compress?: boolean
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* A context for controlling WebSockets
|
|
69
|
+
*/
|
|
70
|
+
export class WSContext<T = unknown> {
|
|
71
|
+
#init: WSContextInit<T>
|
|
72
|
+
constructor(init: WSContextInit<T>) {
|
|
73
|
+
this.#init = init
|
|
74
|
+
this.raw = init.raw
|
|
75
|
+
this.url = init.url ? new URL(init.url) : null
|
|
76
|
+
this.protocol = init.protocol ?? null
|
|
77
|
+
}
|
|
78
|
+
send(source: string | ArrayBuffer | Uint8Array<ArrayBuffer>, options?: SendOptions): void {
|
|
79
|
+
this.#init.send(source, options ?? {})
|
|
80
|
+
}
|
|
81
|
+
raw?: T
|
|
82
|
+
binaryType: BinaryType = 'arraybuffer'
|
|
83
|
+
get readyState(): WSReadyState {
|
|
84
|
+
return this.#init.readyState
|
|
85
|
+
}
|
|
86
|
+
url: URL | null
|
|
87
|
+
protocol: string | null
|
|
88
|
+
close(code?: number, reason?: string) {
|
|
89
|
+
this.#init.close(code, reason)
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export type WSMessageReceive = string | Blob | ArrayBufferLike
|
|
94
|
+
|
|
95
|
+
export const createWSMessageEvent = (source: WSMessageReceive): MessageEvent<WSMessageReceive> => {
|
|
96
|
+
return new MessageEvent<WSMessageReceive>('message', {
|
|
97
|
+
data: source,
|
|
98
|
+
})
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export interface WebSocketHelperDefineContext {}
|
|
102
|
+
export type WebSocketHelperDefineHandler<T, U> = (
|
|
103
|
+
c: Context,
|
|
104
|
+
events: WSEvents<T>,
|
|
105
|
+
options?: U
|
|
106
|
+
) => Promise<Response | void> | Response | void
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Create a WebSocket adapter/helper
|
|
110
|
+
*/
|
|
111
|
+
export const defineWebSocketHelper = <T = unknown, U = any>(
|
|
112
|
+
handler: WebSocketHelperDefineHandler<T, U>
|
|
113
|
+
): UpgradeWebSocket<T, U> => {
|
|
114
|
+
return ((
|
|
115
|
+
...args:
|
|
116
|
+
| [createEvents: (c: Context) => WSEvents<T> | Promise<WSEvents<T>>, options?: U]
|
|
117
|
+
| [c: Context, events: WSEvents<T>, options?: U]
|
|
118
|
+
) => {
|
|
119
|
+
if (typeof args[0] === 'function') {
|
|
120
|
+
const [createEvents, options] = args
|
|
121
|
+
return async function upgradeWebSocket(c, next) {
|
|
122
|
+
const events = await createEvents(c)
|
|
123
|
+
const result = await handler(c, events, options as U)
|
|
124
|
+
if (result) {
|
|
125
|
+
return result
|
|
126
|
+
}
|
|
127
|
+
await next()
|
|
128
|
+
}
|
|
129
|
+
} else {
|
|
130
|
+
const [c, events, options] = args as [c: Context, events: WSEvents<T>, options?: U]
|
|
131
|
+
return (async () => {
|
|
132
|
+
const upgraded = await handler(c, events, options as U)
|
|
133
|
+
if (!upgraded) {
|
|
134
|
+
throw new Error('Failed to upgrade WebSocket')
|
|
135
|
+
}
|
|
136
|
+
return upgraded
|
|
137
|
+
})()
|
|
138
|
+
}
|
|
139
|
+
}) as UpgradeWebSocket<T, U>
|
|
140
|
+
}
|
|
@@ -0,0 +1,539 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module
|
|
3
|
+
* This module is the base module for the Hono object.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
7
|
+
import { compose } from './compose.js'
|
|
8
|
+
import { Context } from './context.js'
|
|
9
|
+
import type { ExecutionContext } from './context.js'
|
|
10
|
+
import type { Router } from './router.js'
|
|
11
|
+
import { METHODS, METHOD_NAME_ALL, METHOD_NAME_ALL_LOWERCASE } from './router.js'
|
|
12
|
+
import type {
|
|
13
|
+
Env,
|
|
14
|
+
ErrorHandler,
|
|
15
|
+
FetchEventLike,
|
|
16
|
+
H,
|
|
17
|
+
HandlerInterface,
|
|
18
|
+
MergePath,
|
|
19
|
+
MergeSchemaPath,
|
|
20
|
+
MiddlewareHandler,
|
|
21
|
+
MiddlewareHandlerInterface,
|
|
22
|
+
Next,
|
|
23
|
+
NotFoundHandler,
|
|
24
|
+
OnHandlerInterface,
|
|
25
|
+
RouterRoute,
|
|
26
|
+
Schema,
|
|
27
|
+
} from './types.js'
|
|
28
|
+
import { COMPOSED_HANDLER } from './utils/constants.js'
|
|
29
|
+
import { getPath, getPathNoStrict, mergePath } from './utils/url.js'
|
|
30
|
+
|
|
31
|
+
const notFoundHandler: NotFoundHandler = (c) => {
|
|
32
|
+
return c.text('404 Not Found', 404)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const errorHandler: ErrorHandler = (err, c) => {
|
|
36
|
+
if ('getResponse' in err) {
|
|
37
|
+
const res = err.getResponse()
|
|
38
|
+
return c.newResponse(res.body, res)
|
|
39
|
+
}
|
|
40
|
+
console.error(err)
|
|
41
|
+
return c.text('Internal Server Error', 500)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
type GetPath<E extends Env> = (request: Request, options?: { env?: E['Bindings'] }) => string
|
|
45
|
+
|
|
46
|
+
export type HonoOptions<E extends Env> = {
|
|
47
|
+
/**
|
|
48
|
+
* `strict` option specifies whether to distinguish whether the last path is a directory or not.
|
|
49
|
+
*
|
|
50
|
+
* @see {@link https://hono.dev/docs/api/hono#strict-mode}
|
|
51
|
+
*
|
|
52
|
+
* @default true
|
|
53
|
+
*/
|
|
54
|
+
strict?: boolean
|
|
55
|
+
/**
|
|
56
|
+
* `router` option specifies which router to use.
|
|
57
|
+
*
|
|
58
|
+
* @see {@link https://hono.dev/docs/api/hono#router-option}
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* ```ts
|
|
62
|
+
* const app = new Hono({ router: new RegExpRouter() })
|
|
63
|
+
* ```
|
|
64
|
+
*/
|
|
65
|
+
router?: Router<[H, RouterRoute]>
|
|
66
|
+
/**
|
|
67
|
+
* `getPath` can handle the host header value.
|
|
68
|
+
*
|
|
69
|
+
* @see {@link https://hono.dev/docs/api/routing#routing-with-host-header-value}
|
|
70
|
+
*
|
|
71
|
+
* @example
|
|
72
|
+
* ```ts
|
|
73
|
+
* const app = new Hono({
|
|
74
|
+
* getPath: (req) =>
|
|
75
|
+
* '/' + req.headers.get('host') + req.url.replace(/^https?:\/\/[^/]+(\/[^?]*)/, '$1'),
|
|
76
|
+
* })
|
|
77
|
+
*
|
|
78
|
+
* app.get('/www1.example.com/hello', () => c.text('hello www1'))
|
|
79
|
+
*
|
|
80
|
+
* // A following request will match the route:
|
|
81
|
+
* // new Request('http://www1.example.com/hello', {
|
|
82
|
+
* // headers: { host: 'www1.example.com' },
|
|
83
|
+
* // })
|
|
84
|
+
* ```
|
|
85
|
+
*/
|
|
86
|
+
getPath?: GetPath<E>
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
type MountOptionHandler = (c: Context) => unknown
|
|
90
|
+
type MountReplaceRequest = (originalRequest: Request) => Request
|
|
91
|
+
type MountOptions =
|
|
92
|
+
| MountOptionHandler
|
|
93
|
+
| {
|
|
94
|
+
optionHandler?: MountOptionHandler
|
|
95
|
+
replaceRequest?: MountReplaceRequest | false
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
class Hono<
|
|
99
|
+
E extends Env = Env,
|
|
100
|
+
S extends Schema = {},
|
|
101
|
+
BasePath extends string = '/',
|
|
102
|
+
CurrentPath extends string = BasePath,
|
|
103
|
+
> {
|
|
104
|
+
get!: HandlerInterface<E, 'get', S, BasePath, CurrentPath>
|
|
105
|
+
post!: HandlerInterface<E, 'post', S, BasePath, CurrentPath>
|
|
106
|
+
put!: HandlerInterface<E, 'put', S, BasePath, CurrentPath>
|
|
107
|
+
delete!: HandlerInterface<E, 'delete', S, BasePath, CurrentPath>
|
|
108
|
+
options!: HandlerInterface<E, 'options', S, BasePath, CurrentPath>
|
|
109
|
+
patch!: HandlerInterface<E, 'patch', S, BasePath, CurrentPath>
|
|
110
|
+
all!: HandlerInterface<E, 'all', S, BasePath, CurrentPath>
|
|
111
|
+
on: OnHandlerInterface<E, S, BasePath>
|
|
112
|
+
use: MiddlewareHandlerInterface<E, S, BasePath>
|
|
113
|
+
|
|
114
|
+
/*
|
|
115
|
+
This class is like an abstract class and does not have a router.
|
|
116
|
+
To use it, inherit the class and implement router in the constructor.
|
|
117
|
+
*/
|
|
118
|
+
router!: Router<[H, RouterRoute]>
|
|
119
|
+
readonly getPath: GetPath<E>
|
|
120
|
+
// Cannot use `#` because it requires visibility at JavaScript runtime.
|
|
121
|
+
private _basePath: string = '/'
|
|
122
|
+
#path: string = '/'
|
|
123
|
+
|
|
124
|
+
routes: RouterRoute[] = []
|
|
125
|
+
|
|
126
|
+
constructor(options: HonoOptions<E> = {}) {
|
|
127
|
+
// Implementation of app.get(...handlers[]) or app.get(path, ...handlers[])
|
|
128
|
+
const allMethods = [...METHODS, METHOD_NAME_ALL_LOWERCASE]
|
|
129
|
+
allMethods.forEach((method) => {
|
|
130
|
+
this[method] = (args1: string | H, ...args: H[]) => {
|
|
131
|
+
if (typeof args1 === 'string') {
|
|
132
|
+
this.#path = args1
|
|
133
|
+
} else {
|
|
134
|
+
this.#addRoute(method, this.#path, args1)
|
|
135
|
+
}
|
|
136
|
+
args.forEach((handler) => {
|
|
137
|
+
this.#addRoute(method, this.#path, handler)
|
|
138
|
+
})
|
|
139
|
+
return this as any
|
|
140
|
+
}
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
// Implementation of app.on(method, path, ...handlers[])
|
|
144
|
+
this.on = (method: string | string[], path: string | string[], ...handlers: H[]) => {
|
|
145
|
+
for (const p of [path].flat()) {
|
|
146
|
+
this.#path = p
|
|
147
|
+
for (const m of [method].flat()) {
|
|
148
|
+
handlers.map((handler) => {
|
|
149
|
+
this.#addRoute(m.toUpperCase(), this.#path, handler)
|
|
150
|
+
})
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return this as any
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Implementation of app.use(...handlers[]) or app.use(path, ...handlers[])
|
|
157
|
+
this.use = (arg1: string | MiddlewareHandler<any>, ...handlers: MiddlewareHandler<any>[]) => {
|
|
158
|
+
if (typeof arg1 === 'string') {
|
|
159
|
+
this.#path = arg1
|
|
160
|
+
} else {
|
|
161
|
+
this.#path = '*'
|
|
162
|
+
handlers.unshift(arg1)
|
|
163
|
+
}
|
|
164
|
+
handlers.forEach((handler) => {
|
|
165
|
+
this.#addRoute(METHOD_NAME_ALL, this.#path, handler)
|
|
166
|
+
})
|
|
167
|
+
return this as any
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const { strict, ...optionsWithoutStrict } = options
|
|
171
|
+
Object.assign(this, optionsWithoutStrict)
|
|
172
|
+
this.getPath = (strict ?? true) ? (options.getPath ?? getPath) : getPathNoStrict
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
#clone(): Hono<E, S, BasePath, CurrentPath> {
|
|
176
|
+
const clone = new Hono<E, S, BasePath, CurrentPath>({
|
|
177
|
+
router: this.router,
|
|
178
|
+
getPath: this.getPath,
|
|
179
|
+
})
|
|
180
|
+
clone.errorHandler = this.errorHandler
|
|
181
|
+
clone.#notFoundHandler = this.#notFoundHandler
|
|
182
|
+
clone.routes = this.routes
|
|
183
|
+
return clone
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
#notFoundHandler: NotFoundHandler = notFoundHandler
|
|
187
|
+
// Cannot use `#` because it requires visibility at JavaScript runtime.
|
|
188
|
+
private errorHandler: ErrorHandler = errorHandler
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* `.route()` allows grouping other Hono instance in routes.
|
|
192
|
+
*
|
|
193
|
+
* @see {@link https://hono.dev/docs/api/routing#grouping}
|
|
194
|
+
*
|
|
195
|
+
* @param {string} path - base Path
|
|
196
|
+
* @param {Hono} app - other Hono instance
|
|
197
|
+
* @returns {Hono} routed Hono instance
|
|
198
|
+
*
|
|
199
|
+
* @example
|
|
200
|
+
* ```ts
|
|
201
|
+
* const app = new Hono()
|
|
202
|
+
* const app2 = new Hono()
|
|
203
|
+
*
|
|
204
|
+
* app2.get("/user", (c) => c.text("user"))
|
|
205
|
+
* app.route("/api", app2) // GET /api/user
|
|
206
|
+
* ```
|
|
207
|
+
*/
|
|
208
|
+
route<
|
|
209
|
+
SubPath extends string,
|
|
210
|
+
SubEnv extends Env,
|
|
211
|
+
SubSchema extends Schema,
|
|
212
|
+
SubBasePath extends string,
|
|
213
|
+
SubCurrentPath extends string,
|
|
214
|
+
>(
|
|
215
|
+
path: SubPath,
|
|
216
|
+
app: Hono<SubEnv, SubSchema, SubBasePath, SubCurrentPath>
|
|
217
|
+
): Hono<E, MergeSchemaPath<SubSchema, MergePath<BasePath, SubPath>> | S, BasePath, CurrentPath> {
|
|
218
|
+
const subApp = this.basePath(path)
|
|
219
|
+
app.routes.map((r) => {
|
|
220
|
+
let handler
|
|
221
|
+
if (app.errorHandler === errorHandler) {
|
|
222
|
+
handler = r.handler
|
|
223
|
+
} else {
|
|
224
|
+
handler = async (c: Context, next: Next) =>
|
|
225
|
+
(await compose([], app.errorHandler)(c, () => r.handler(c, next))).res
|
|
226
|
+
;(handler as any)[COMPOSED_HANDLER] = r.handler
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
subApp.#addRoute(r.method, r.path, handler)
|
|
230
|
+
})
|
|
231
|
+
return this
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* `.basePath()` allows base paths to be specified.
|
|
236
|
+
*
|
|
237
|
+
* @see {@link https://hono.dev/docs/api/routing#base-path}
|
|
238
|
+
*
|
|
239
|
+
* @param {string} path - base Path
|
|
240
|
+
* @returns {Hono} changed Hono instance
|
|
241
|
+
*
|
|
242
|
+
* @example
|
|
243
|
+
* ```ts
|
|
244
|
+
* const api = new Hono().basePath('/api')
|
|
245
|
+
* ```
|
|
246
|
+
*/
|
|
247
|
+
basePath<SubPath extends string>(
|
|
248
|
+
path: SubPath
|
|
249
|
+
): Hono<E, S, MergePath<BasePath, SubPath>, MergePath<BasePath, SubPath>> {
|
|
250
|
+
const subApp = this.#clone()
|
|
251
|
+
subApp._basePath = mergePath(this._basePath, path)
|
|
252
|
+
return subApp
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* `.onError()` handles an error and returns a customized Response.
|
|
257
|
+
*
|
|
258
|
+
* @see {@link https://hono.dev/docs/api/hono#error-handling}
|
|
259
|
+
*
|
|
260
|
+
* @param {ErrorHandler} handler - request Handler for error
|
|
261
|
+
* @returns {Hono} changed Hono instance
|
|
262
|
+
*
|
|
263
|
+
* @example
|
|
264
|
+
* ```ts
|
|
265
|
+
* app.onError((err, c) => {
|
|
266
|
+
* console.error(`${err}`)
|
|
267
|
+
* return c.text('Custom Error Message', 500)
|
|
268
|
+
* })
|
|
269
|
+
* ```
|
|
270
|
+
*/
|
|
271
|
+
onError = (handler: ErrorHandler<E>): Hono<E, S, BasePath, CurrentPath> => {
|
|
272
|
+
this.errorHandler = handler
|
|
273
|
+
return this
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* `.notFound()` allows you to customize a Not Found Response.
|
|
278
|
+
*
|
|
279
|
+
* @see {@link https://hono.dev/docs/api/hono#not-found}
|
|
280
|
+
*
|
|
281
|
+
* @param {NotFoundHandler} handler - request handler for not-found
|
|
282
|
+
* @returns {Hono} changed Hono instance
|
|
283
|
+
*
|
|
284
|
+
* @example
|
|
285
|
+
* ```ts
|
|
286
|
+
* app.notFound((c) => {
|
|
287
|
+
* return c.text('Custom 404 Message', 404)
|
|
288
|
+
* })
|
|
289
|
+
* ```
|
|
290
|
+
*/
|
|
291
|
+
notFound = (handler: NotFoundHandler<E>): Hono<E, S, BasePath, CurrentPath> => {
|
|
292
|
+
this.#notFoundHandler = handler
|
|
293
|
+
return this
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* `.mount()` allows you to mount applications built with other frameworks into your Hono application.
|
|
298
|
+
*
|
|
299
|
+
* @see {@link https://hono.dev/docs/api/hono#mount}
|
|
300
|
+
*
|
|
301
|
+
* @param {string} path - base Path
|
|
302
|
+
* @param {Function} applicationHandler - other Request Handler
|
|
303
|
+
* @param {MountOptions} [options] - options of `.mount()`
|
|
304
|
+
* @returns {Hono} mounted Hono instance
|
|
305
|
+
*
|
|
306
|
+
* @example
|
|
307
|
+
* ```ts
|
|
308
|
+
* import { Router as IttyRouter } from 'itty-router'
|
|
309
|
+
* import { Hono } from 'hono'
|
|
310
|
+
* // Create itty-router application
|
|
311
|
+
* const ittyRouter = IttyRouter()
|
|
312
|
+
* // GET /itty-router/hello
|
|
313
|
+
* ittyRouter.get('/hello', () => new Response('Hello from itty-router'))
|
|
314
|
+
*
|
|
315
|
+
* const app = new Hono()
|
|
316
|
+
* app.mount('/itty-router', ittyRouter.handle)
|
|
317
|
+
* ```
|
|
318
|
+
*
|
|
319
|
+
* @example
|
|
320
|
+
* ```ts
|
|
321
|
+
* const app = new Hono()
|
|
322
|
+
* // Send the request to another application without modification.
|
|
323
|
+
* app.mount('/app', anotherApp, {
|
|
324
|
+
* replaceRequest: (req) => req,
|
|
325
|
+
* })
|
|
326
|
+
* ```
|
|
327
|
+
*/
|
|
328
|
+
mount(
|
|
329
|
+
path: string,
|
|
330
|
+
applicationHandler: (request: Request, ...args: any) => Response | Promise<Response>,
|
|
331
|
+
options?: MountOptions
|
|
332
|
+
): Hono<E, S, BasePath, CurrentPath> {
|
|
333
|
+
// handle options
|
|
334
|
+
let replaceRequest: MountReplaceRequest | undefined
|
|
335
|
+
let optionHandler: MountOptionHandler | undefined
|
|
336
|
+
if (options) {
|
|
337
|
+
if (typeof options === 'function') {
|
|
338
|
+
optionHandler = options
|
|
339
|
+
} else {
|
|
340
|
+
optionHandler = options.optionHandler
|
|
341
|
+
if (options.replaceRequest === false) {
|
|
342
|
+
replaceRequest = (request) => request
|
|
343
|
+
} else {
|
|
344
|
+
replaceRequest = options.replaceRequest
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// prepare handlers for request
|
|
350
|
+
const getOptions: (c: Context) => unknown[] = optionHandler
|
|
351
|
+
? (c) => {
|
|
352
|
+
const options = optionHandler!(c)
|
|
353
|
+
return Array.isArray(options) ? options : [options]
|
|
354
|
+
}
|
|
355
|
+
: (c) => {
|
|
356
|
+
let executionContext: ExecutionContext | undefined = undefined
|
|
357
|
+
try {
|
|
358
|
+
executionContext = c.executionCtx
|
|
359
|
+
} catch {} // Do nothing
|
|
360
|
+
return [c.env, executionContext]
|
|
361
|
+
}
|
|
362
|
+
replaceRequest ||= (() => {
|
|
363
|
+
const mergedPath = mergePath(this._basePath, path)
|
|
364
|
+
const pathPrefixLength = mergedPath === '/' ? 0 : mergedPath.length
|
|
365
|
+
return (request) => {
|
|
366
|
+
const url = new URL(request.url)
|
|
367
|
+
url.pathname = url.pathname.slice(pathPrefixLength) || '/'
|
|
368
|
+
return new Request(url, request)
|
|
369
|
+
}
|
|
370
|
+
})()
|
|
371
|
+
|
|
372
|
+
const handler: MiddlewareHandler = async (c, next) => {
|
|
373
|
+
const res = await applicationHandler(replaceRequest(c.req.raw), ...getOptions(c))
|
|
374
|
+
|
|
375
|
+
if (res) {
|
|
376
|
+
return res
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
await next()
|
|
380
|
+
}
|
|
381
|
+
this.#addRoute(METHOD_NAME_ALL, mergePath(path, '*'), handler)
|
|
382
|
+
return this
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
#addRoute(method: string, path: string, handler: H): void {
|
|
386
|
+
method = method.toUpperCase()
|
|
387
|
+
path = mergePath(this._basePath, path)
|
|
388
|
+
const r: RouterRoute = { basePath: this._basePath, path, method, handler }
|
|
389
|
+
this.router.add(method, path, [handler, r])
|
|
390
|
+
this.routes.push(r)
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
#handleError(err: unknown, c: Context<E>): Response | Promise<Response> {
|
|
394
|
+
if (err instanceof Error) {
|
|
395
|
+
return this.errorHandler(err, c)
|
|
396
|
+
}
|
|
397
|
+
throw err
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
#dispatch(
|
|
401
|
+
request: Request,
|
|
402
|
+
executionCtx: ExecutionContext | FetchEventLike | undefined,
|
|
403
|
+
env: E['Bindings'],
|
|
404
|
+
method: string
|
|
405
|
+
): Response | Promise<Response> {
|
|
406
|
+
// Handle HEAD method
|
|
407
|
+
if (method === 'HEAD') {
|
|
408
|
+
return (async () =>
|
|
409
|
+
new Response(null, await this.#dispatch(request, executionCtx, env, 'GET')))()
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
const path = this.getPath(request, { env })
|
|
413
|
+
const matchResult = this.router.match(method, path)
|
|
414
|
+
|
|
415
|
+
const c = new Context(request, {
|
|
416
|
+
path,
|
|
417
|
+
matchResult,
|
|
418
|
+
env,
|
|
419
|
+
executionCtx,
|
|
420
|
+
notFoundHandler: this.#notFoundHandler,
|
|
421
|
+
})
|
|
422
|
+
|
|
423
|
+
// Do not `compose` if it has only one handler
|
|
424
|
+
if (matchResult[0].length === 1) {
|
|
425
|
+
let res: ReturnType<H>
|
|
426
|
+
try {
|
|
427
|
+
res = matchResult[0][0][0][0](c, async () => {
|
|
428
|
+
c.res = await this.#notFoundHandler(c)
|
|
429
|
+
})
|
|
430
|
+
} catch (err) {
|
|
431
|
+
return this.#handleError(err, c)
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
return res instanceof Promise
|
|
435
|
+
? res
|
|
436
|
+
.then(
|
|
437
|
+
(resolved: Response | undefined) =>
|
|
438
|
+
resolved || (c.finalized ? c.res : this.#notFoundHandler(c))
|
|
439
|
+
)
|
|
440
|
+
.catch((err: Error) => this.#handleError(err, c))
|
|
441
|
+
: (res ?? this.#notFoundHandler(c))
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
const composed = compose(matchResult[0], this.errorHandler, this.#notFoundHandler)
|
|
445
|
+
|
|
446
|
+
return (async () => {
|
|
447
|
+
try {
|
|
448
|
+
const context = await composed(c)
|
|
449
|
+
if (!context.finalized) {
|
|
450
|
+
throw new Error(
|
|
451
|
+
'Context is not finalized. Did you forget to return a Response object or `await next()`?'
|
|
452
|
+
)
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
return context.res
|
|
456
|
+
} catch (err) {
|
|
457
|
+
return this.#handleError(err, c)
|
|
458
|
+
}
|
|
459
|
+
})()
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
/**
|
|
463
|
+
* `.fetch()` will be entry point of your app.
|
|
464
|
+
*
|
|
465
|
+
* @see {@link https://hono.dev/docs/api/hono#fetch}
|
|
466
|
+
*
|
|
467
|
+
* @param {Request} request - request Object of request
|
|
468
|
+
* @param {Env} Env - env Object
|
|
469
|
+
* @param {ExecutionContext} - context of execution
|
|
470
|
+
* @returns {Response | Promise<Response>} response of request
|
|
471
|
+
*
|
|
472
|
+
*/
|
|
473
|
+
fetch: (
|
|
474
|
+
request: Request,
|
|
475
|
+
Env?: E['Bindings'] | {},
|
|
476
|
+
executionCtx?: ExecutionContext
|
|
477
|
+
) => Response | Promise<Response> = (request, ...rest) => {
|
|
478
|
+
return this.#dispatch(request, rest[1], rest[0], request.method)
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
/**
|
|
482
|
+
* `.request()` is a useful method for testing.
|
|
483
|
+
* You can pass a URL or pathname to send a GET request.
|
|
484
|
+
* app will return a Response object.
|
|
485
|
+
* ```ts
|
|
486
|
+
* test('GET /hello is ok', async () => {
|
|
487
|
+
* const res = await app.request('/hello')
|
|
488
|
+
* expect(res.status).toBe(200)
|
|
489
|
+
* })
|
|
490
|
+
* ```
|
|
491
|
+
* @see https://hono.dev/docs/api/hono#request
|
|
492
|
+
*/
|
|
493
|
+
request = (
|
|
494
|
+
input: RequestInfo | URL,
|
|
495
|
+
requestInit?: RequestInit,
|
|
496
|
+
Env?: E['Bindings'] | {},
|
|
497
|
+
executionCtx?: ExecutionContext
|
|
498
|
+
): Response | Promise<Response> => {
|
|
499
|
+
if (input instanceof Request) {
|
|
500
|
+
return this.fetch(requestInit ? new Request(input, requestInit) : input, Env, executionCtx)
|
|
501
|
+
}
|
|
502
|
+
input = input.toString()
|
|
503
|
+
return this.fetch(
|
|
504
|
+
new Request(
|
|
505
|
+
/^https?:\/\//.test(input) ? input : `http://localhost${mergePath('/', input)}`,
|
|
506
|
+
requestInit
|
|
507
|
+
),
|
|
508
|
+
Env,
|
|
509
|
+
executionCtx
|
|
510
|
+
)
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
/**
|
|
514
|
+
* `.fire()` automatically adds a global fetch event listener.
|
|
515
|
+
* This can be useful for environments that adhere to the Service Worker API, such as non-ES module Cloudflare Workers.
|
|
516
|
+
* @deprecated
|
|
517
|
+
* Use `fire` from `hono/service-worker` instead.
|
|
518
|
+
* ```ts
|
|
519
|
+
* import { Hono } from 'hono'
|
|
520
|
+
* import { fire } from 'hono/service-worker'
|
|
521
|
+
*
|
|
522
|
+
* const app = new Hono()
|
|
523
|
+
* // ...
|
|
524
|
+
* fire(app)
|
|
525
|
+
* ```
|
|
526
|
+
* @see https://hono.dev/docs/api/hono#fire
|
|
527
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API
|
|
528
|
+
* @see https://developers.cloudflare.com/workers/reference/migrate-to-module-workers/
|
|
529
|
+
*/
|
|
530
|
+
fire = (): void => {
|
|
531
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
532
|
+
// @ts-ignore
|
|
533
|
+
addEventListener('fetch', (event: FetchEventLike): void => {
|
|
534
|
+
event.respondWith(this.#dispatch(event.request, event, undefined, event.request.method))
|
|
535
|
+
})
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
export { Hono as HonoBase }
|