@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.
Files changed (58) hide show
  1. package/esm/HonoYjsMemAdapter.js +1 -0
  2. package/esm/HonoYjsMemAdapter.js.map +1 -0
  3. package/esm/deps/jsr.io/@hono/hono/4.11.3/src/compose.js +1 -0
  4. package/esm/deps/jsr.io/@hono/hono/4.11.3/src/compose.js.map +1 -0
  5. package/esm/deps/jsr.io/@hono/hono/4.11.3/src/context.js +1 -0
  6. package/esm/deps/jsr.io/@hono/hono/4.11.3/src/context.js.map +1 -0
  7. package/esm/deps/jsr.io/@hono/hono/4.11.3/src/helper/websocket/index.js +1 -0
  8. package/esm/deps/jsr.io/@hono/hono/4.11.3/src/helper/websocket/index.js.map +1 -0
  9. package/esm/deps/jsr.io/@hono/hono/4.11.3/src/hono-base.js +1 -0
  10. package/esm/deps/jsr.io/@hono/hono/4.11.3/src/hono-base.js.map +1 -0
  11. package/esm/deps/jsr.io/@hono/hono/4.11.3/src/http-exception.js +1 -0
  12. package/esm/deps/jsr.io/@hono/hono/4.11.3/src/http-exception.js.map +1 -0
  13. package/esm/deps/jsr.io/@hono/hono/4.11.3/src/request/constants.js +1 -0
  14. package/esm/deps/jsr.io/@hono/hono/4.11.3/src/request/constants.js.map +1 -0
  15. package/esm/deps/jsr.io/@hono/hono/4.11.3/src/request.js +1 -0
  16. package/esm/deps/jsr.io/@hono/hono/4.11.3/src/request.js.map +1 -0
  17. package/esm/deps/jsr.io/@hono/hono/4.11.3/src/router.js +1 -0
  18. package/esm/deps/jsr.io/@hono/hono/4.11.3/src/router.js.map +1 -0
  19. package/esm/deps/jsr.io/@hono/hono/4.11.3/src/types.js +1 -0
  20. package/esm/deps/jsr.io/@hono/hono/4.11.3/src/types.js.map +1 -0
  21. package/esm/deps/jsr.io/@hono/hono/4.11.3/src/utils/body.js +1 -0
  22. package/esm/deps/jsr.io/@hono/hono/4.11.3/src/utils/body.js.map +1 -0
  23. package/esm/deps/jsr.io/@hono/hono/4.11.3/src/utils/constants.js +1 -0
  24. package/esm/deps/jsr.io/@hono/hono/4.11.3/src/utils/constants.js.map +1 -0
  25. package/esm/deps/jsr.io/@hono/hono/4.11.3/src/utils/headers.js +1 -0
  26. package/esm/deps/jsr.io/@hono/hono/4.11.3/src/utils/headers.js.map +1 -0
  27. package/esm/deps/jsr.io/@hono/hono/4.11.3/src/utils/html.js +1 -0
  28. package/esm/deps/jsr.io/@hono/hono/4.11.3/src/utils/html.js.map +1 -0
  29. package/esm/deps/jsr.io/@hono/hono/4.11.3/src/utils/http-status.js +1 -0
  30. package/esm/deps/jsr.io/@hono/hono/4.11.3/src/utils/http-status.js.map +1 -0
  31. package/esm/deps/jsr.io/@hono/hono/4.11.3/src/utils/mime.js +1 -0
  32. package/esm/deps/jsr.io/@hono/hono/4.11.3/src/utils/mime.js.map +1 -0
  33. package/esm/deps/jsr.io/@hono/hono/4.11.3/src/utils/types.js +1 -0
  34. package/esm/deps/jsr.io/@hono/hono/4.11.3/src/utils/types.js.map +1 -0
  35. package/esm/deps/jsr.io/@hono/hono/4.11.3/src/utils/url.js +1 -0
  36. package/esm/deps/jsr.io/@hono/hono/4.11.3/src/utils/url.js.map +1 -0
  37. package/esm/mod.js +1 -0
  38. package/esm/mod.js.map +1 -0
  39. package/package.json +5 -1
  40. package/src/HonoYjsMemAdapter.ts +264 -0
  41. package/src/deps/jsr.io/@hono/hono/4.11.3/src/compose.ts +73 -0
  42. package/src/deps/jsr.io/@hono/hono/4.11.3/src/context.ts +770 -0
  43. package/src/deps/jsr.io/@hono/hono/4.11.3/src/helper/websocket/index.ts +140 -0
  44. package/src/deps/jsr.io/@hono/hono/4.11.3/src/hono-base.ts +539 -0
  45. package/src/deps/jsr.io/@hono/hono/4.11.3/src/http-exception.ts +78 -0
  46. package/src/deps/jsr.io/@hono/hono/4.11.3/src/request/constants.ts +1 -0
  47. package/src/deps/jsr.io/@hono/hono/4.11.3/src/request.ts +487 -0
  48. package/src/deps/jsr.io/@hono/hono/4.11.3/src/router.ts +103 -0
  49. package/src/deps/jsr.io/@hono/hono/4.11.3/src/types.ts +2489 -0
  50. package/src/deps/jsr.io/@hono/hono/4.11.3/src/utils/body.ts +229 -0
  51. package/src/deps/jsr.io/@hono/hono/4.11.3/src/utils/constants.ts +4 -0
  52. package/src/deps/jsr.io/@hono/hono/4.11.3/src/utils/headers.ts +333 -0
  53. package/src/deps/jsr.io/@hono/hono/4.11.3/src/utils/html.ts +182 -0
  54. package/src/deps/jsr.io/@hono/hono/4.11.3/src/utils/http-status.ts +72 -0
  55. package/src/deps/jsr.io/@hono/hono/4.11.3/src/utils/mime.ts +96 -0
  56. package/src/deps/jsr.io/@hono/hono/4.11.3/src/utils/types.ts +116 -0
  57. package/src/deps/jsr.io/@hono/hono/4.11.3/src/utils/url.ts +310 -0
  58. package/src/mod.ts +5 -0
@@ -0,0 +1,770 @@
1
+ import { HonoRequest } from './request.js'
2
+ import type { Result } from './router.js'
3
+ import type {
4
+ Env,
5
+ FetchEventLike,
6
+ H,
7
+ Input,
8
+ NotFoundHandler,
9
+ RouterRoute,
10
+ TypedResponse,
11
+ } from './types.js'
12
+ import type { ResponseHeader } from './utils/headers.js'
13
+ import { HtmlEscapedCallbackPhase, resolveCallback } from './utils/html.js'
14
+ import type { ContentfulStatusCode, RedirectStatusCode, StatusCode } from './utils/http-status.js'
15
+ import type { BaseMime } from './utils/mime.js'
16
+ import type { InvalidJSONValue, IsAny, JSONParsed, JSONValue } from './utils/types.js'
17
+
18
+ type HeaderRecord =
19
+ | Record<'Content-Type', BaseMime>
20
+ | Record<ResponseHeader, string | string[]>
21
+ | Record<string, string | string[]>
22
+
23
+ /**
24
+ * Data type can be a string, ArrayBuffer, Uint8Array (buffer), or ReadableStream.
25
+ */
26
+ export type Data = string | ArrayBuffer | ReadableStream | Uint8Array<ArrayBuffer>
27
+
28
+ /**
29
+ * Interface for the execution context in a web worker or similar environment.
30
+ */
31
+ export interface ExecutionContext {
32
+ /**
33
+ * Extends the lifetime of the event callback until the promise is settled.
34
+ *
35
+ * @param promise - A promise to wait for.
36
+ */
37
+ waitUntil(promise: Promise<unknown>): void
38
+ /**
39
+ * Allows the event to be passed through to subsequent event listeners.
40
+ */
41
+ passThroughOnException(): void
42
+ /**
43
+ * For compatibility with Wrangler 4.x.
44
+ */
45
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
46
+ props: any
47
+ }
48
+
49
+ /**
50
+ * Interface for context variable mapping.
51
+ */
52
+ export interface ContextVariableMap {}
53
+
54
+ /**
55
+ * Interface for context renderer.
56
+ */
57
+ export interface ContextRenderer {}
58
+
59
+ /**
60
+ * Interface representing a renderer for content.
61
+ *
62
+ * @interface DefaultRenderer
63
+ * @param {string | Promise<string>} content - The content to be rendered, which can be either a string or a Promise resolving to a string.
64
+ * @returns {Response | Promise<Response>} - The response after rendering the content, which can be either a Response or a Promise resolving to a Response.
65
+ */
66
+ interface DefaultRenderer {
67
+ (content: string | Promise<string>): Response | Promise<Response>
68
+ }
69
+
70
+ /**
71
+ * Renderer type which can either be a ContextRenderer or DefaultRenderer.
72
+ */
73
+ export type Renderer = ContextRenderer extends Function ? ContextRenderer : DefaultRenderer
74
+
75
+ /**
76
+ * Extracts the props for the renderer.
77
+ */
78
+ export type PropsForRenderer = [...Required<Parameters<Renderer>>] extends [unknown, infer Props]
79
+ ? Props
80
+ : unknown
81
+
82
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
83
+ export type Layout<T = Record<string, any>> = (props: T) => any
84
+
85
+ /**
86
+ * Interface for getting context variables.
87
+ *
88
+ * @template E - Environment type.
89
+ */
90
+ interface Get<E extends Env> {
91
+ <Key extends keyof E['Variables']>(key: Key): E['Variables'][Key]
92
+ <Key extends keyof ContextVariableMap>(key: Key): ContextVariableMap[Key]
93
+ }
94
+
95
+ /**
96
+ * Interface for setting context variables.
97
+ *
98
+ * @template E - Environment type.
99
+ */
100
+ interface Set<E extends Env> {
101
+ <Key extends keyof E['Variables']>(key: Key, value: E['Variables'][Key]): void
102
+ <Key extends keyof ContextVariableMap>(key: Key, value: ContextVariableMap[Key]): void
103
+ }
104
+
105
+ /**
106
+ * Interface for creating a new response.
107
+ */
108
+ interface NewResponse {
109
+ (data: Data | null, status?: StatusCode, headers?: HeaderRecord): Response
110
+ (data: Data | null, init?: ResponseOrInit): Response
111
+ }
112
+
113
+ /**
114
+ * Interface for responding with a body.
115
+ */
116
+ interface BodyRespond {
117
+ // if we return content, only allow the status codes that allow for returning the body
118
+ <T extends Data, U extends ContentfulStatusCode>(
119
+ data: T,
120
+ status?: U,
121
+ headers?: HeaderRecord
122
+ ): Response & TypedResponse<T, U, 'body'>
123
+ <T extends Data, U extends ContentfulStatusCode>(
124
+ data: T,
125
+ init?: ResponseOrInit<U>
126
+ ): Response & TypedResponse<T, U, 'body'>
127
+ <T extends null, U extends StatusCode>(
128
+ data: T,
129
+ status?: U,
130
+ headers?: HeaderRecord
131
+ ): Response & TypedResponse<null, U, 'body'>
132
+ <T extends null, U extends StatusCode>(
133
+ data: T,
134
+ init?: ResponseOrInit<U>
135
+ ): Response & TypedResponse<null, U, 'body'>
136
+ }
137
+
138
+ /**
139
+ * Interface for responding with text.
140
+ *
141
+ * @interface TextRespond
142
+ * @template T - The type of the text content.
143
+ * @template U - The type of the status code.
144
+ *
145
+ * @param {T} text - The text content to be included in the response.
146
+ * @param {U} [status] - An optional status code for the response.
147
+ * @param {HeaderRecord} [headers] - An optional record of headers to include in the response.
148
+ *
149
+ * @returns {Response & TypedResponse<T, U, 'text'>} - The response after rendering the text content, typed with the provided text and status code types.
150
+ */
151
+ interface TextRespond {
152
+ <T extends string, U extends ContentfulStatusCode = ContentfulStatusCode>(
153
+ text: T,
154
+ status?: U,
155
+ headers?: HeaderRecord
156
+ ): Response & TypedResponse<T, U, 'text'>
157
+ <T extends string, U extends ContentfulStatusCode = ContentfulStatusCode>(
158
+ text: T,
159
+ init?: ResponseOrInit<U>
160
+ ): Response & TypedResponse<T, U, 'text'>
161
+ }
162
+
163
+ /**
164
+ * Interface for responding with JSON.
165
+ *
166
+ * @interface JSONRespond
167
+ * @template T - The type of the JSON value or simplified unknown type.
168
+ * @template U - The type of the status code.
169
+ *
170
+ * @param {T} object - The JSON object to be included in the response.
171
+ * @param {U} [status] - An optional status code for the response.
172
+ * @param {HeaderRecord} [headers] - An optional record of headers to include in the response.
173
+ *
174
+ * @returns {JSONRespondReturn<T, U>} - The response after rendering the JSON object, typed with the provided object and status code types.
175
+ */
176
+ interface JSONRespond {
177
+ <
178
+ T extends JSONValue | {} | InvalidJSONValue,
179
+ U extends ContentfulStatusCode = ContentfulStatusCode,
180
+ >(
181
+ object: T,
182
+ status?: U,
183
+ headers?: HeaderRecord
184
+ ): JSONRespondReturn<T, U>
185
+ <
186
+ T extends JSONValue | {} | InvalidJSONValue,
187
+ U extends ContentfulStatusCode = ContentfulStatusCode,
188
+ >(
189
+ object: T,
190
+ init?: ResponseOrInit<U>
191
+ ): JSONRespondReturn<T, U>
192
+ }
193
+
194
+ /**
195
+ * @template T - The type of the JSON value or simplified unknown type.
196
+ * @template U - The type of the status code.
197
+ *
198
+ * @returns {Response & TypedResponse<JSONParsed<T>, U, 'json'>} - The response after rendering the JSON object, typed with the provided object and status code types.
199
+ */
200
+ type JSONRespondReturn<
201
+ T extends JSONValue | {} | InvalidJSONValue,
202
+ U extends ContentfulStatusCode,
203
+ > = Response & TypedResponse<JSONParsed<T>, U, 'json'>
204
+
205
+ /**
206
+ * Interface representing a function that responds with HTML content.
207
+ *
208
+ * @param html - The HTML content to respond with, which can be a string or a Promise that resolves to a string.
209
+ * @param status - (Optional) The HTTP status code for the response.
210
+ * @param headers - (Optional) A record of headers to include in the response.
211
+ * @param init - (Optional) The response initialization object.
212
+ *
213
+ * @returns A Response object or a Promise that resolves to a Response object.
214
+ */
215
+ interface HTMLRespond {
216
+ <T extends string | Promise<string>>(
217
+ html: T,
218
+ status?: ContentfulStatusCode,
219
+ headers?: HeaderRecord
220
+ ): T extends string ? Response : Promise<Response>
221
+ <T extends string | Promise<string>>(
222
+ html: T,
223
+ init?: ResponseOrInit<ContentfulStatusCode>
224
+ ): T extends string ? Response : Promise<Response>
225
+ }
226
+
227
+ /**
228
+ * Options for configuring the context.
229
+ *
230
+ * @template E - Environment type.
231
+ */
232
+ type ContextOptions<E extends Env> = {
233
+ /**
234
+ * Bindings for the environment.
235
+ */
236
+ env: E['Bindings']
237
+ /**
238
+ * Execution context for the request.
239
+ */
240
+ executionCtx?: FetchEventLike | ExecutionContext | undefined
241
+ /**
242
+ * Handler for not found responses.
243
+ */
244
+ notFoundHandler?: NotFoundHandler<E>
245
+ matchResult?: Result<[H, RouterRoute]>
246
+ path?: string
247
+ }
248
+
249
+ interface SetHeadersOptions {
250
+ append?: boolean
251
+ }
252
+
253
+ interface SetHeaders {
254
+ (name: 'Content-Type', value?: BaseMime, options?: SetHeadersOptions): void
255
+ (name: ResponseHeader, value?: string, options?: SetHeadersOptions): void
256
+ (name: string, value?: string, options?: SetHeadersOptions): void
257
+ }
258
+
259
+ type ResponseHeadersInit =
260
+ | [string, string][]
261
+ | Record<'Content-Type', BaseMime>
262
+ | Record<ResponseHeader, string>
263
+ | Record<string, string>
264
+ | Headers
265
+
266
+ interface ResponseInit<T extends StatusCode = StatusCode> {
267
+ headers?: ResponseHeadersInit
268
+ status?: T
269
+ statusText?: string
270
+ }
271
+
272
+ type ResponseOrInit<T extends StatusCode = StatusCode> = ResponseInit<T> | Response
273
+
274
+ export const TEXT_PLAIN = 'text/plain; charset=UTF-8'
275
+
276
+ const setDefaultContentType = (contentType: string, headers?: HeaderRecord): HeaderRecord => {
277
+ return {
278
+ 'Content-Type': contentType,
279
+ ...headers,
280
+ }
281
+ }
282
+
283
+ export class Context<
284
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
285
+ E extends Env = any,
286
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
287
+ P extends string = any,
288
+ I extends Input = {},
289
+ > {
290
+ #rawRequest: Request
291
+ #req: HonoRequest<P, I['out']> | undefined
292
+ /**
293
+ * `.env` can get bindings (environment variables, secrets, KV namespaces, D1 database, R2 bucket etc.) in Cloudflare Workers.
294
+ *
295
+ * @see {@link https://hono.dev/docs/api/context#env}
296
+ *
297
+ * @example
298
+ * ```ts
299
+ * // Environment object for Cloudflare Workers
300
+ * app.get('*', async c => {
301
+ * const counter = c.env.COUNTER
302
+ * })
303
+ * ```
304
+ */
305
+ env: E['Bindings'] = {}
306
+ #var: Map<unknown, unknown> | undefined
307
+ finalized: boolean = false
308
+ /**
309
+ * `.error` can get the error object from the middleware if the Handler throws an error.
310
+ *
311
+ * @see {@link https://hono.dev/docs/api/context#error}
312
+ *
313
+ * @example
314
+ * ```ts
315
+ * app.use('*', async (c, next) => {
316
+ * await next()
317
+ * if (c.error) {
318
+ * // do something...
319
+ * }
320
+ * })
321
+ * ```
322
+ */
323
+ error: Error | undefined
324
+
325
+ #status: StatusCode | undefined
326
+ #executionCtx: FetchEventLike | ExecutionContext | undefined
327
+ #res: Response | undefined
328
+ #layout: Layout<PropsForRenderer & { Layout: Layout }> | undefined
329
+ #renderer: Renderer | undefined
330
+ #notFoundHandler: NotFoundHandler<E> | undefined
331
+ #preparedHeaders: Headers | undefined
332
+
333
+ #matchResult: Result<[H, RouterRoute]> | undefined
334
+ #path: string | undefined
335
+
336
+ /**
337
+ * Creates an instance of the Context class.
338
+ *
339
+ * @param req - The Request object.
340
+ * @param options - Optional configuration options for the context.
341
+ */
342
+ constructor(req: Request, options?: ContextOptions<E>) {
343
+ this.#rawRequest = req
344
+ if (options) {
345
+ this.#executionCtx = options.executionCtx
346
+ this.env = options.env
347
+ this.#notFoundHandler = options.notFoundHandler
348
+ this.#path = options.path
349
+ this.#matchResult = options.matchResult
350
+ }
351
+ }
352
+
353
+ /**
354
+ * `.req` is the instance of {@link HonoRequest}.
355
+ */
356
+ get req(): HonoRequest<P, I['out']> {
357
+ this.#req ??= new HonoRequest(this.#rawRequest, this.#path, this.#matchResult)
358
+ return this.#req
359
+ }
360
+
361
+ /**
362
+ * @see {@link https://hono.dev/docs/api/context#event}
363
+ * The FetchEvent associated with the current request.
364
+ *
365
+ * @throws Will throw an error if the context does not have a FetchEvent.
366
+ */
367
+ get event(): FetchEventLike {
368
+ if (this.#executionCtx && 'respondWith' in this.#executionCtx) {
369
+ return this.#executionCtx
370
+ } else {
371
+ throw Error('This context has no FetchEvent')
372
+ }
373
+ }
374
+
375
+ /**
376
+ * @see {@link https://hono.dev/docs/api/context#executionctx}
377
+ * The ExecutionContext associated with the current request.
378
+ *
379
+ * @throws Will throw an error if the context does not have an ExecutionContext.
380
+ */
381
+ get executionCtx(): ExecutionContext {
382
+ if (this.#executionCtx) {
383
+ return this.#executionCtx as ExecutionContext
384
+ } else {
385
+ throw Error('This context has no ExecutionContext')
386
+ }
387
+ }
388
+
389
+ /**
390
+ * @see {@link https://hono.dev/docs/api/context#res}
391
+ * The Response object for the current request.
392
+ */
393
+ get res(): Response {
394
+ return (this.#res ||= new Response(null, {
395
+ headers: (this.#preparedHeaders ??= new Headers()),
396
+ }))
397
+ }
398
+
399
+ /**
400
+ * Sets the Response object for the current request.
401
+ *
402
+ * @param _res - The Response object to set.
403
+ */
404
+ set res(_res: Response | undefined) {
405
+ if (this.#res && _res) {
406
+ _res = new Response(_res.body, _res)
407
+ for (const [k, v] of this.#res.headers.entries()) {
408
+ if (k === 'content-type') {
409
+ continue
410
+ }
411
+ if (k === 'set-cookie') {
412
+ const cookies = this.#res.headers.getSetCookie()
413
+ _res.headers.delete('set-cookie')
414
+ for (const cookie of cookies) {
415
+ _res.headers.append('set-cookie', cookie)
416
+ }
417
+ } else {
418
+ _res.headers.set(k, v)
419
+ }
420
+ }
421
+ }
422
+ this.#res = _res
423
+ this.finalized = true
424
+ }
425
+
426
+ /**
427
+ * `.render()` can create a response within a layout.
428
+ *
429
+ * @see {@link https://hono.dev/docs/api/context#render-setrenderer}
430
+ *
431
+ * @example
432
+ * ```ts
433
+ * app.get('/', (c) => {
434
+ * return c.render('Hello!')
435
+ * })
436
+ * ```
437
+ */
438
+ render: Renderer = (...args) => {
439
+ this.#renderer ??= (content: string | Promise<string>) => this.html(content)
440
+ return this.#renderer(...args)
441
+ }
442
+
443
+ /**
444
+ * Sets the layout for the response.
445
+ *
446
+ * @param layout - The layout to set.
447
+ * @returns The layout function.
448
+ */
449
+ setLayout = (
450
+ layout: Layout<PropsForRenderer & { Layout: Layout }>
451
+ ): Layout<
452
+ PropsForRenderer & {
453
+ Layout: Layout
454
+ }
455
+ > => (this.#layout = layout)
456
+
457
+ /**
458
+ * Gets the current layout for the response.
459
+ *
460
+ * @returns The current layout function.
461
+ */
462
+ getLayout = (): Layout<PropsForRenderer & { Layout: Layout }> | undefined => this.#layout
463
+
464
+ /**
465
+ * `.setRenderer()` can set the layout in the custom middleware.
466
+ *
467
+ * @see {@link https://hono.dev/docs/api/context#render-setrenderer}
468
+ *
469
+ * @example
470
+ * ```tsx
471
+ * app.use('*', async (c, next) => {
472
+ * c.setRenderer((content) => {
473
+ * return c.html(
474
+ * <html>
475
+ * <body>
476
+ * <p>{content}</p>
477
+ * </body>
478
+ * </html>
479
+ * )
480
+ * })
481
+ * await next()
482
+ * })
483
+ * ```
484
+ */
485
+ setRenderer = (renderer: Renderer): void => {
486
+ this.#renderer = renderer
487
+ }
488
+
489
+ /**
490
+ * `.header()` can set headers.
491
+ *
492
+ * @see {@link https://hono.dev/docs/api/context#header}
493
+ *
494
+ * @example
495
+ * ```ts
496
+ * app.get('/welcome', (c) => {
497
+ * // Set headers
498
+ * c.header('X-Message', 'Hello!')
499
+ * c.header('Content-Type', 'text/plain')
500
+ *
501
+ * return c.body('Thank you for coming')
502
+ * })
503
+ * ```
504
+ */
505
+ header: SetHeaders = (name, value, options): void => {
506
+ if (this.finalized) {
507
+ this.#res = new Response((this.#res as Response).body, this.#res)
508
+ }
509
+ const headers = this.#res ? this.#res.headers : (this.#preparedHeaders ??= new Headers())
510
+ if (value === undefined) {
511
+ headers.delete(name)
512
+ } else if (options?.append) {
513
+ headers.append(name, value)
514
+ } else {
515
+ headers.set(name, value)
516
+ }
517
+ }
518
+
519
+ status = (status: StatusCode): void => {
520
+ this.#status = status
521
+ }
522
+
523
+ /**
524
+ * `.set()` can set the value specified by the key.
525
+ *
526
+ * @see {@link https://hono.dev/docs/api/context#set-get}
527
+ *
528
+ * @example
529
+ * ```ts
530
+ * app.use('*', async (c, next) => {
531
+ * c.set('message', 'Hono is hot!!')
532
+ * await next()
533
+ * })
534
+ * ```
535
+ */
536
+ set: Set<
537
+ IsAny<E> extends true
538
+ ? {
539
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
540
+ Variables: ContextVariableMap & Record<string, any>
541
+ }
542
+ : E
543
+ > = (key: string, value: unknown) => {
544
+ this.#var ??= new Map()
545
+ this.#var.set(key, value)
546
+ }
547
+
548
+ /**
549
+ * `.get()` can use the value specified by the key.
550
+ *
551
+ * @see {@link https://hono.dev/docs/api/context#set-get}
552
+ *
553
+ * @example
554
+ * ```ts
555
+ * app.get('/', (c) => {
556
+ * const message = c.get('message')
557
+ * return c.text(`The message is "${message}"`)
558
+ * })
559
+ * ```
560
+ */
561
+ get: Get<
562
+ IsAny<E> extends true
563
+ ? {
564
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
565
+ Variables: ContextVariableMap & Record<string, any>
566
+ }
567
+ : E
568
+ > = (key: string) => {
569
+ return this.#var ? this.#var.get(key) : undefined
570
+ }
571
+
572
+ /**
573
+ * `.var` can access the value of a variable.
574
+ *
575
+ * @see {@link https://hono.dev/docs/api/context#var}
576
+ *
577
+ * @example
578
+ * ```ts
579
+ * const result = c.var.client.oneMethod()
580
+ * ```
581
+ */
582
+ // c.var.propName is a read-only
583
+ get var(): Readonly<
584
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
585
+ ContextVariableMap & (IsAny<E['Variables']> extends true ? Record<string, any> : E['Variables'])
586
+ > {
587
+ if (!this.#var) {
588
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
589
+ return {} as any
590
+ }
591
+ return Object.fromEntries(this.#var)
592
+ }
593
+
594
+ #newResponse(
595
+ data: Data | null,
596
+ arg?: StatusCode | ResponseOrInit,
597
+ headers?: HeaderRecord
598
+ ): Response {
599
+ const responseHeaders = this.#res
600
+ ? new Headers(this.#res.headers)
601
+ : (this.#preparedHeaders ?? new Headers())
602
+
603
+ if (typeof arg === 'object' && 'headers' in arg) {
604
+ const argHeaders = arg.headers instanceof Headers ? arg.headers : new Headers(arg.headers)
605
+ for (const [key, value] of argHeaders) {
606
+ if (key.toLowerCase() === 'set-cookie') {
607
+ responseHeaders.append(key, value)
608
+ } else {
609
+ responseHeaders.set(key, value)
610
+ }
611
+ }
612
+ }
613
+
614
+ if (headers) {
615
+ for (const [k, v] of Object.entries(headers)) {
616
+ if (typeof v === 'string') {
617
+ responseHeaders.set(k, v)
618
+ } else {
619
+ responseHeaders.delete(k)
620
+ for (const v2 of v) {
621
+ responseHeaders.append(k, v2)
622
+ }
623
+ }
624
+ }
625
+ }
626
+
627
+ const status = typeof arg === 'number' ? arg : (arg?.status ?? this.#status)
628
+ return new Response(data, { status, headers: responseHeaders })
629
+ }
630
+
631
+ newResponse: NewResponse = (...args) => this.#newResponse(...(args as Parameters<NewResponse>))
632
+
633
+ /**
634
+ * `.body()` can return the HTTP response.
635
+ * You can set headers with `.header()` and set HTTP status code with `.status`.
636
+ * This can also be set in `.text()`, `.json()` and so on.
637
+ *
638
+ * @see {@link https://hono.dev/docs/api/context#body}
639
+ *
640
+ * @example
641
+ * ```ts
642
+ * app.get('/welcome', (c) => {
643
+ * // Set headers
644
+ * c.header('X-Message', 'Hello!')
645
+ * c.header('Content-Type', 'text/plain')
646
+ * // Set HTTP status code
647
+ * c.status(201)
648
+ *
649
+ * // Return the response body
650
+ * return c.body('Thank you for coming')
651
+ * })
652
+ * ```
653
+ */
654
+ body: BodyRespond = (
655
+ data: Data | null,
656
+ arg?: StatusCode | RequestInit,
657
+ headers?: HeaderRecord
658
+ ): ReturnType<BodyRespond> => this.#newResponse(data, arg, headers) as ReturnType<BodyRespond>
659
+
660
+ /**
661
+ * `.text()` can render text as `Content-Type:text/plain`.
662
+ *
663
+ * @see {@link https://hono.dev/docs/api/context#text}
664
+ *
665
+ * @example
666
+ * ```ts
667
+ * app.get('/say', (c) => {
668
+ * return c.text('Hello!')
669
+ * })
670
+ * ```
671
+ */
672
+ text: TextRespond = (
673
+ text: string,
674
+ arg?: ContentfulStatusCode | ResponseOrInit,
675
+ headers?: HeaderRecord
676
+ ): ReturnType<TextRespond> => {
677
+ return !this.#preparedHeaders && !this.#status && !arg && !headers && !this.finalized
678
+ ? (new Response(text) as ReturnType<TextRespond>)
679
+ : (this.#newResponse(
680
+ text,
681
+ arg,
682
+ setDefaultContentType(TEXT_PLAIN, headers)
683
+ ) as ReturnType<TextRespond>)
684
+ }
685
+
686
+ /**
687
+ * `.json()` can render JSON as `Content-Type:application/json`.
688
+ *
689
+ * @see {@link https://hono.dev/docs/api/context#json}
690
+ *
691
+ * @example
692
+ * ```ts
693
+ * app.get('/api', (c) => {
694
+ * return c.json({ message: 'Hello!' })
695
+ * })
696
+ * ```
697
+ */
698
+ json: JSONRespond = <
699
+ T extends JSONValue | {} | InvalidJSONValue,
700
+ U extends ContentfulStatusCode = ContentfulStatusCode,
701
+ >(
702
+ object: T,
703
+ arg?: U | ResponseOrInit<U>,
704
+ headers?: HeaderRecord
705
+ ): JSONRespondReturn<T, U> => {
706
+ return this.#newResponse(
707
+ JSON.stringify(object),
708
+ arg,
709
+ setDefaultContentType('application/json', headers)
710
+ ) /* eslint-disable @typescript-eslint/no-explicit-any */ as any
711
+ }
712
+
713
+ html: HTMLRespond = (
714
+ html: string | Promise<string>,
715
+ arg?: ContentfulStatusCode | ResponseOrInit<ContentfulStatusCode>,
716
+ headers?: HeaderRecord
717
+ ): Response | Promise<Response> => {
718
+ const res = (html: string) =>
719
+ this.#newResponse(html, arg, setDefaultContentType('text/html; charset=UTF-8', headers))
720
+ return typeof html === 'object'
721
+ ? resolveCallback(html, HtmlEscapedCallbackPhase.Stringify, false, {}).then(res)
722
+ : res(html)
723
+ }
724
+
725
+ /**
726
+ * `.redirect()` can Redirect, default status code is 302.
727
+ *
728
+ * @see {@link https://hono.dev/docs/api/context#redirect}
729
+ *
730
+ * @example
731
+ * ```ts
732
+ * app.get('/redirect', (c) => {
733
+ * return c.redirect('/')
734
+ * })
735
+ * app.get('/redirect-permanently', (c) => {
736
+ * return c.redirect('/', 301)
737
+ * })
738
+ * ```
739
+ */
740
+ redirect = <T extends RedirectStatusCode = 302>(
741
+ location: string | URL,
742
+ status?: T
743
+ ): Response & TypedResponse<undefined, T, 'redirect'> => {
744
+ const locationString = String(location)
745
+ this.header(
746
+ 'Location',
747
+ // Multibyes should be encoded
748
+ // eslint-disable-next-line no-control-regex
749
+ !/[^\x00-\xFF]/.test(locationString) ? locationString : encodeURI(locationString)
750
+ )
751
+ return this.newResponse(null, status ?? 302) as any
752
+ }
753
+
754
+ /**
755
+ * `.notFound()` can return the Not Found Response.
756
+ *
757
+ * @see {@link https://hono.dev/docs/api/context#notfound}
758
+ *
759
+ * @example
760
+ * ```ts
761
+ * app.get('/notfound', (c) => {
762
+ * return c.notFound()
763
+ * })
764
+ * ```
765
+ */
766
+ notFound = (): ReturnType<NotFoundHandler> => {
767
+ this.#notFoundHandler ??= () => new Response()
768
+ return this.#notFoundHandler(this)
769
+ }
770
+ }