hono 3.7.3 → 3.8.0-rc.1

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.
@@ -1,15 +1,6 @@
1
1
  // src/adapter/vercel/handler.ts
2
2
  var handle = (app) => (req, requestContext) => {
3
- return app.fetch(
4
- req,
5
- {},
6
- {
7
- waitUntil: requestContext?.waitUntil,
8
- passThroughOnException: () => {
9
- throw new Error("`passThroughOnException` is not implemented in the Vercel");
10
- }
11
- }
12
- );
3
+ return app.fetch(req, {}, requestContext);
13
4
  };
14
5
  export {
15
6
  handle
@@ -22,16 +22,7 @@ __export(handler_exports, {
22
22
  });
23
23
  module.exports = __toCommonJS(handler_exports);
24
24
  const handle = (app) => (req, requestContext) => {
25
- return app.fetch(
26
- req,
27
- {},
28
- {
29
- waitUntil: requestContext?.waitUntil,
30
- passThroughOnException: () => {
31
- throw new Error("`passThroughOnException` is not implemented in the Vercel");
32
- }
33
- }
34
- );
25
+ return app.fetch(req, {}, requestContext);
35
26
  };
36
27
  // Annotate the CommonJS export names for ESM import in node:
37
28
  0 && (module.exports = {
@@ -21,7 +21,6 @@ __export(context_exports, {
21
21
  Context: () => Context
22
22
  });
23
23
  module.exports = __toCommonJS(context_exports);
24
- var import_types = require("./types");
25
24
  var import_cookie = require("./utils/cookie");
26
25
  var import_stream = require("./utils/stream");
27
26
  const TEXT_PLAIN = "text/plain; charset=UTF-8";
@@ -200,7 +199,7 @@ class Context {
200
199
  }
201
200
  }
202
201
  get event() {
203
- if (this._exCtx instanceof import_types.FetchEventLike) {
202
+ if (this._exCtx instanceof FetchEvent) {
204
203
  return this._exCtx;
205
204
  } else {
206
205
  throw Error("This context has no FetchEvent");
@@ -18,11 +18,11 @@ var __copyProps = (to, from, except, desc) => {
18
18
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
19
  var factory_exports = {};
20
20
  __export(factory_exports, {
21
- middleware: () => middleware
21
+ createMiddleware: () => createMiddleware
22
22
  });
23
23
  module.exports = __toCommonJS(factory_exports);
24
- const middleware = (middleware2) => middleware2;
24
+ const createMiddleware = (middleware) => middleware;
25
25
  // Annotate the CommonJS export names for ESM import in node:
26
26
  0 && (module.exports = {
27
- middleware
27
+ createMiddleware
28
28
  });
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var streaming_exports = {};
20
+ __export(streaming_exports, {
21
+ streamSSE: () => streamSSE
22
+ });
23
+ module.exports = __toCommonJS(streaming_exports);
24
+ var import_stream = require("../../utils/stream");
25
+ class SSEStreamingApi extends import_stream.StreamingApi {
26
+ constructor(writable) {
27
+ super(writable);
28
+ }
29
+ async writeSSE(message) {
30
+ const sseData = [
31
+ message.event && `event: ${message.event}`,
32
+ message.id && `id: ${message.id}`,
33
+ `data: ${message.data}`
34
+ ].filter(Boolean).join("\n") + "\n";
35
+ await this.write(sseData);
36
+ }
37
+ }
38
+ const setSSEHeaders = (context) => {
39
+ context.header("Transfer-Encoding", "chunked");
40
+ context.header("Content-Type", "text/event-stream");
41
+ context.header("Cache-Control", "no-cache");
42
+ context.header("Connection", "keep-alive");
43
+ };
44
+ const streamSSE = (c, cb) => {
45
+ return c.stream(async (originalStream) => {
46
+ const { readable, writable } = new TransformStream();
47
+ const stream = new SSEStreamingApi(writable);
48
+ originalStream.pipe(readable);
49
+ setSSEHeaders(c);
50
+ try {
51
+ await cb(stream);
52
+ } catch (err) {
53
+ console.error("Error during streaming: ", err);
54
+ stream.close();
55
+ }
56
+ });
57
+ };
58
+ // Annotate the CommonJS export names for ESM import in node:
59
+ 0 && (module.exports = {
60
+ streamSSE
61
+ });
@@ -15,7 +15,8 @@ var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "defau
15
15
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
16
16
  var helper_exports = {};
17
17
  module.exports = __toCommonJS(helper_exports);
18
+ __reExport(helper_exports, require("./helper/adapter"), module.exports);
18
19
  __reExport(helper_exports, require("./helper/cookie"), module.exports);
19
20
  __reExport(helper_exports, require("./helper/html"), module.exports);
20
- __reExport(helper_exports, require("./helper/adapter"), module.exports);
21
+ __reExport(helper_exports, require("./helper/streaming"), module.exports);
21
22
  __reExport(helper_exports, require("./helper/testing"), module.exports);
@@ -209,10 +209,10 @@ class Hono extends defineDynamicClass() {
209
209
  throw err;
210
210
  }
211
211
  dispatch(request, executionCtx, env, method) {
212
- const path = this.getPath(request, { env });
213
212
  if (method === "HEAD") {
214
213
  return (async () => new Response(null, await this.dispatch(request, executionCtx, env, "GET")))();
215
214
  }
215
+ const path = this.getPath(request, { env });
216
216
  const { handlers, params } = this.matchRoute(method, path);
217
217
  const c = new import_context.Context(new import_request.HonoRequest(request, path, params), {
218
218
  env,
@@ -20,8 +20,10 @@ var jsx_exports = {};
20
20
  __export(jsx_exports, {
21
21
  Fragment: () => Fragment,
22
22
  JSXNode: () => JSXNode,
23
+ createContext: () => createContext,
23
24
  jsx: () => jsxFn,
24
- memo: () => memo
25
+ memo: () => memo,
26
+ useContext: () => useContext
25
27
  });
26
28
  module.exports = __toCommonJS(jsx_exports);
27
29
  var import_html = require("../utils/html");
@@ -198,12 +200,32 @@ const memo = (component, propsAreEqual = shallowEqual) => {
198
200
  };
199
201
  };
200
202
  const Fragment = (props) => {
201
- return new JSXFragmentNode("", {}, props.children || []);
203
+ return new JSXFragmentNode("", {}, props.children ? [props.children] : []);
204
+ };
205
+ const createContext = (defaultValue) => {
206
+ const values = [defaultValue];
207
+ return {
208
+ values,
209
+ Provider(props) {
210
+ values.push(props.value);
211
+ const res = new String(
212
+ props.children ? (Array.isArray(props.children) ? new JSXFragmentNode("", {}, props.children) : props.children).toString() : ""
213
+ );
214
+ res.isEscaped = true;
215
+ values.pop();
216
+ return res;
217
+ }
218
+ };
219
+ };
220
+ const useContext = (context) => {
221
+ return context.values[context.values.length - 1];
202
222
  };
203
223
  // Annotate the CommonJS export names for ESM import in node:
204
224
  0 && (module.exports = {
205
225
  Fragment,
206
226
  JSXNode,
227
+ createContext,
207
228
  jsx,
208
- memo
229
+ memo,
230
+ useContext
209
231
  });
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var jsx_renderer_exports = {};
20
+ __export(jsx_renderer_exports, {
21
+ RequestContext: () => RequestContext,
22
+ jsxRenderer: () => jsxRenderer,
23
+ useRequestContext: () => useRequestContext
24
+ });
25
+ module.exports = __toCommonJS(jsx_renderer_exports);
26
+ var import_jsx = require("../../jsx");
27
+ const RequestContext = (0, import_jsx.createContext)(null);
28
+ const createRenderer = (c, component) => (children, props) => c.html(
29
+ (0, import_jsx.jsx)(
30
+ RequestContext.Provider,
31
+ { value: c },
32
+ component ? component({ children, ...props || {} }) : children
33
+ )
34
+ );
35
+ const jsxRenderer = (component) => (c, next) => {
36
+ c.setRenderer(createRenderer(c, component));
37
+ return next();
38
+ };
39
+ const useRequestContext = () => {
40
+ const c = (0, import_jsx.useContext)(RequestContext);
41
+ if (!c) {
42
+ throw new Error("RequestContext is not provided.");
43
+ }
44
+ return c;
45
+ };
46
+ // Annotate the CommonJS export names for ESM import in node:
47
+ 0 && (module.exports = {
48
+ RequestContext,
49
+ jsxRenderer,
50
+ useRequestContext
51
+ });
@@ -88,10 +88,10 @@ class HonoRequest {
88
88
  return obj;
89
89
  }
90
90
  }
91
- async parseBody() {
91
+ async parseBody(options) {
92
92
  if (this.bodyCache.parsedBody)
93
93
  return this.bodyCache.parsedBody;
94
- const parsedBody = await (0, import_body.parseBody)(this);
94
+ const parsedBody = await (0, import_body.parseBody)(this, options);
95
95
  this.bodyCache.parsedBody = parsedBody;
96
96
  return parsedBody;
97
97
  }
@@ -21,7 +21,12 @@ __export(body_exports, {
21
21
  parseBody: () => parseBody
22
22
  });
23
23
  module.exports = __toCommonJS(body_exports);
24
- const parseBody = async (request) => {
24
+ const isArrayField = (value) => {
25
+ return Array.isArray(value);
26
+ };
27
+ const parseBody = async (request, options = {
28
+ all: false
29
+ }) => {
25
30
  let body = {};
26
31
  const contentType = request.headers.get("Content-Type");
27
32
  if (contentType && (contentType.startsWith("multipart/form-data") || contentType.startsWith("application/x-www-form-urlencoded"))) {
@@ -29,18 +34,21 @@ const parseBody = async (request) => {
29
34
  if (formData) {
30
35
  const form = {};
31
36
  formData.forEach((value, key) => {
32
- if (key.slice(-2) === "[]") {
33
- if (!form[key]) {
34
- form[key] = [value.toString()];
35
- } else {
36
- if (Array.isArray(form[key])) {
37
- ;
38
- form[key].push(value.toString());
39
- }
40
- }
41
- } else {
37
+ const shouldParseAllValues = options.all || key.slice(-2) === "[]";
38
+ if (!shouldParseAllValues) {
42
39
  form[key] = value;
40
+ return;
41
+ }
42
+ if (form[key] && isArrayField(form[key])) {
43
+ ;
44
+ form[key].push(value);
45
+ return;
46
+ }
47
+ if (form[key]) {
48
+ form[key] = [form[key], value];
49
+ return;
43
50
  }
51
+ form[key] = value;
44
52
  });
45
53
  body = form;
46
54
  }
package/dist/context.js CHANGED
@@ -1,5 +1,4 @@
1
1
  // src/context.ts
2
- import { FetchEventLike } from "./types.js";
3
2
  import { serialize } from "./utils/cookie.js";
4
3
  import { StreamingApi } from "./utils/stream.js";
5
4
  var TEXT_PLAIN = "text/plain; charset=UTF-8";
@@ -178,7 +177,7 @@ var Context = class {
178
177
  }
179
178
  }
180
179
  get event() {
181
- if (this._exCtx instanceof FetchEventLike) {
180
+ if (this._exCtx instanceof FetchEvent) {
182
181
  return this._exCtx;
183
182
  } else {
184
183
  throw Error("This context has no FetchEvent");
@@ -1,5 +1,5 @@
1
1
  // src/helper/factory/index.ts
2
- var middleware = (middleware2) => middleware2;
2
+ var createMiddleware = (middleware) => middleware;
3
3
  export {
4
- middleware
4
+ createMiddleware
5
5
  };
@@ -0,0 +1,38 @@
1
+ // src/helper/streaming/index.ts
2
+ import { StreamingApi } from "../../utils/stream.js";
3
+ var SSEStreamingApi = class extends StreamingApi {
4
+ constructor(writable) {
5
+ super(writable);
6
+ }
7
+ async writeSSE(message) {
8
+ const sseData = [
9
+ message.event && `event: ${message.event}`,
10
+ message.id && `id: ${message.id}`,
11
+ `data: ${message.data}`
12
+ ].filter(Boolean).join("\n") + "\n";
13
+ await this.write(sseData);
14
+ }
15
+ };
16
+ var setSSEHeaders = (context) => {
17
+ context.header("Transfer-Encoding", "chunked");
18
+ context.header("Content-Type", "text/event-stream");
19
+ context.header("Cache-Control", "no-cache");
20
+ context.header("Connection", "keep-alive");
21
+ };
22
+ var streamSSE = (c, cb) => {
23
+ return c.stream(async (originalStream) => {
24
+ const { readable, writable } = new TransformStream();
25
+ const stream = new SSEStreamingApi(writable);
26
+ originalStream.pipe(readable);
27
+ setSSEHeaders(c);
28
+ try {
29
+ await cb(stream);
30
+ } catch (err) {
31
+ console.error("Error during streaming: ", err);
32
+ stream.close();
33
+ }
34
+ });
35
+ };
36
+ export {
37
+ streamSSE
38
+ };
package/dist/helper.js CHANGED
@@ -1,5 +1,6 @@
1
1
  // src/helper.ts
2
+ export * from "./helper/adapter/index.js";
2
3
  export * from "./helper/cookie/index.js";
3
4
  export * from "./helper/html/index.js";
4
- export * from "./helper/adapter/index.js";
5
+ export * from "./helper/streaming/index.js";
5
6
  export * from "./helper/testing/index.js";
package/dist/hono-base.js CHANGED
@@ -187,10 +187,10 @@ var Hono = class extends defineDynamicClass() {
187
187
  throw err;
188
188
  }
189
189
  dispatch(request, executionCtx, env, method) {
190
- const path = this.getPath(request, { env });
191
190
  if (method === "HEAD") {
192
191
  return (async () => new Response(null, await this.dispatch(request, executionCtx, env, "GET")))();
193
192
  }
193
+ const path = this.getPath(request, { env });
194
194
  const { handlers, params } = this.matchRoute(method, path);
195
195
  const c = new Context(new HonoRequest(request, path, params), {
196
196
  env,
package/dist/jsx/index.js CHANGED
@@ -173,11 +173,31 @@ var memo = (component, propsAreEqual = shallowEqual) => {
173
173
  };
174
174
  };
175
175
  var Fragment = (props) => {
176
- return new JSXFragmentNode("", {}, props.children || []);
176
+ return new JSXFragmentNode("", {}, props.children ? [props.children] : []);
177
+ };
178
+ var createContext = (defaultValue) => {
179
+ const values = [defaultValue];
180
+ return {
181
+ values,
182
+ Provider(props) {
183
+ values.push(props.value);
184
+ const res = new String(
185
+ props.children ? (Array.isArray(props.children) ? new JSXFragmentNode("", {}, props.children) : props.children).toString() : ""
186
+ );
187
+ res.isEscaped = true;
188
+ values.pop();
189
+ return res;
190
+ }
191
+ };
192
+ };
193
+ var useContext = (context) => {
194
+ return context.values[context.values.length - 1];
177
195
  };
178
196
  export {
179
197
  Fragment,
180
198
  JSXNode,
199
+ createContext,
181
200
  jsxFn as jsx,
182
- memo
201
+ memo,
202
+ useContext
183
203
  };
@@ -0,0 +1,26 @@
1
+ // src/middleware/jsx-renderer/index.ts
2
+ import { jsx, createContext, useContext } from "../../jsx/index.js";
3
+ var RequestContext = createContext(null);
4
+ var createRenderer = (c, component) => (children, props) => c.html(
5
+ jsx(
6
+ RequestContext.Provider,
7
+ { value: c },
8
+ component ? component({ children, ...props || {} }) : children
9
+ )
10
+ );
11
+ var jsxRenderer = (component) => (c, next) => {
12
+ c.setRenderer(createRenderer(c, component));
13
+ return next();
14
+ };
15
+ var useRequestContext = () => {
16
+ const c = useContext(RequestContext);
17
+ if (!c) {
18
+ throw new Error("RequestContext is not provided.");
19
+ }
20
+ return c;
21
+ };
22
+ export {
23
+ RequestContext,
24
+ jsxRenderer,
25
+ useRequestContext
26
+ };
package/dist/request.js CHANGED
@@ -66,10 +66,10 @@ var HonoRequest = class {
66
66
  return obj;
67
67
  }
68
68
  }
69
- async parseBody() {
69
+ async parseBody(options) {
70
70
  if (this.bodyCache.parsedBody)
71
71
  return this.bodyCache.parsedBody;
72
- const parsedBody = await parseBody(this);
72
+ const parsedBody = await parseBody(this, options);
73
73
  this.bodyCache.parsedBody = parsedBody;
74
74
  return parsedBody;
75
75
  }
@@ -1,2 +1,2 @@
1
1
  import type { Hono } from '../../hono';
2
- export declare const handle: (app: Hono<any, any, any>) => (req: Request, requestContext: Omit<ExecutionContext, 'passThroughOnException'>) => Response | Promise<Response>;
2
+ export declare const handle: (app: Hono<any, any, any>) => (req: Request, requestContext: FetchEvent) => Response | Promise<Response>;
@@ -1,5 +1,6 @@
1
+ /// <reference lib="es2022" />
2
+ /// <reference lib="webworker" />
1
3
  import type { HonoRequest } from './request';
2
- import { FetchEventLike } from './types';
3
4
  import type { Env, NotFoundHandler, Input, TypedResponse } from './types';
4
5
  import type { CookieOptions } from './utils/cookie';
5
6
  import type { StatusCode } from './utils/http-status';
@@ -19,7 +20,7 @@ export interface ContextRenderer {
19
20
  interface DefaultRenderer {
20
21
  (content: string): Response | Promise<Response>;
21
22
  }
22
- declare type Renderer = ContextRenderer extends Function ? ContextRenderer : DefaultRenderer;
23
+ export declare type Renderer = ContextRenderer extends Function ? ContextRenderer : DefaultRenderer;
23
24
  interface Get<E extends Env> {
24
25
  <Key extends keyof ContextVariableMap>(key: Key): ContextVariableMap[Key];
25
26
  <Key extends keyof E['Variables']>(key: Key): E['Variables'][Key];
@@ -52,7 +53,7 @@ interface HTMLRespond {
52
53
  }
53
54
  declare type ContextOptions<E extends Env> = {
54
55
  env: E['Bindings'];
55
- executionCtx?: FetchEventLike | ExecutionContext | undefined;
56
+ executionCtx?: FetchEvent | ExecutionContext | undefined;
56
57
  notFoundHandler?: NotFoundHandler<E>;
57
58
  };
58
59
  export declare class Context<E extends Env = any, P extends string = any, I extends Input = {}> {
@@ -70,7 +71,7 @@ export declare class Context<E extends Env = any, P extends string = any, I exte
70
71
  private _renderer;
71
72
  private notFoundHandler;
72
73
  constructor(req: HonoRequest<P, I['out']>, options?: ContextOptions<E>);
73
- get event(): FetchEventLike;
74
+ get event(): FetchEvent;
74
75
  get executionCtx(): ExecutionContext;
75
76
  get res(): Response;
76
77
  set res(_res: Response | undefined);
@@ -4,4 +4,4 @@ import type { Env, Input, MiddlewareHandler } from '../../types';
4
4
  * `middleware()` is an experimental feature.
5
5
  * The API might be changed.
6
6
  */
7
- export declare const middleware: <E extends Env = any, P extends string = any, I extends Input = {}>(middleware: MiddlewareHandler<E, P, I>) => MiddlewareHandler<E, P, I>;
7
+ export declare const createMiddleware: <E extends Env = any, P extends string = any, I extends Input = {}>(middleware: MiddlewareHandler<E, P, I>) => MiddlewareHandler<E, P, I>;
@@ -0,0 +1,13 @@
1
+ import type { Context } from '../../context';
2
+ import { StreamingApi } from '../../utils/stream';
3
+ interface SSEMessage {
4
+ data: string;
5
+ event?: string;
6
+ id?: string;
7
+ }
8
+ declare class SSEStreamingApi extends StreamingApi {
9
+ constructor(writable: WritableStream);
10
+ writeSSE(message: SSEMessage): Promise<void>;
11
+ }
12
+ export declare const streamSSE: (c: Context, cb: (stream: SSEStreamingApi) => Promise<void>) => Response;
13
+ export {};
@@ -1,4 +1,5 @@
1
+ export * from './helper/adapter';
1
2
  export * from './helper/cookie';
2
3
  export * from './helper/html';
3
- export * from './helper/adapter';
4
+ export * from './helper/streaming';
4
5
  export * from './helper/testing';
@@ -10,11 +10,11 @@ interface RouterRoute {
10
10
  }
11
11
  declare const Hono_base: new <E_1 extends Env = Env, S_1 extends Schema = {}, BasePath_1 extends string = "/">() => {
12
12
  all: HandlerInterface<E_1, "all", S_1, BasePath_1>;
13
- options: HandlerInterface<E_1, "options", S_1, BasePath_1>;
14
13
  get: HandlerInterface<E_1, "get", S_1, BasePath_1>;
15
14
  post: HandlerInterface<E_1, "post", S_1, BasePath_1>;
16
15
  put: HandlerInterface<E_1, "put", S_1, BasePath_1>;
17
16
  delete: HandlerInterface<E_1, "delete", S_1, BasePath_1>;
17
+ options: HandlerInterface<E_1, "options", S_1, BasePath_1>;
18
18
  patch: HandlerInterface<E_1, "patch", S_1, BasePath_1>;
19
19
  } & {
20
20
  on: OnHandlerInterface<E_1, S_1, BasePath_1>;
@@ -29,5 +29,13 @@ export declare type FC<T = Props> = (props: T & {
29
29
  export declare const memo: <T>(component: FC<T>, propsAreEqual?: (prevProps: Readonly<T>, nextProps: Readonly<T>) => boolean) => FC<T>;
30
30
  export declare const Fragment: (props: {
31
31
  key?: string;
32
- children?: Child[];
32
+ children?: Child | HtmlEscapedString;
33
33
  }) => HtmlEscapedString;
34
+ export interface Context<T> {
35
+ values: T[];
36
+ Provider: FC<{
37
+ value: T;
38
+ }>;
39
+ }
40
+ export declare const createContext: <T>(defaultValue: T) => Context<T>;
41
+ export declare const useContext: <T>(context: Context<T>) => T;
@@ -0,0 +1,8 @@
1
+ import type { Context, Renderer } from '../../context';
2
+ import type { FC } from '../../jsx';
3
+ import type { Env, Input, MiddlewareHandler } from '../../types';
4
+ export declare const RequestContext: import("../../jsx").Context<Context<any, any, {}> | null>;
5
+ declare type PropsForRenderer = [...Parameters<Renderer>] extends [unknown, infer Props] ? Props : unknown;
6
+ export declare const jsxRenderer: (component?: FC<PropsForRenderer>) => MiddlewareHandler;
7
+ export declare const useRequestContext: <E extends Env = any, P extends string = any, I extends Input = {}>() => Context<E, P, I>;
8
+ export {};
@@ -1,5 +1,5 @@
1
1
  import type { Input, InputToDataByTarget, ParamKeys, ParamKeyToRecord, RemoveQuestion, UndefinedIfHavingQuestion, ValidationTargets } from './types';
2
- import type { BodyData } from './utils/body';
2
+ import type { BodyData, ParseBodyOptions } from './utils/body';
3
3
  import type { Cookie } from './utils/cookie';
4
4
  import type { UnionToIntersection } from './utils/types';
5
5
  declare type Body = {
@@ -47,7 +47,7 @@ export declare class HonoRequest<P extends string = '/', I extends Input['out']
47
47
  * app.get('/', (c) => c.json(getCookie(c)))
48
48
  */
49
49
  cookie(): Cookie;
50
- parseBody<T extends BodyData = BodyData>(): Promise<T>;
50
+ parseBody<T extends BodyData = BodyData>(options?: ParseBodyOptions): Promise<T>;
51
51
  private cachedBody;
52
52
  json<T = any>(): Promise<T>;
53
53
  text(): Promise<string>;
@@ -1,3 +1,23 @@
1
1
  import type { HonoRequest } from '../request';
2
- export declare type BodyData = Record<string, string | string[] | File>;
3
- export declare const parseBody: <T extends BodyData = BodyData>(request: HonoRequest | Request) => Promise<T>;
2
+ export declare type BodyData = Record<string, string | File | (string | File)[]>;
3
+ export declare type ParseBodyOptions = {
4
+ /**
5
+ * Parse all fields with multiple values should be parsed as an array.
6
+ * @default false
7
+ * @example
8
+ * ```ts
9
+ * const data = new FormData()
10
+ * data.append('file', 'aaa')
11
+ * data.append('file', 'bbb')
12
+ * data.append('message', 'hello')
13
+ * ```
14
+ *
15
+ * If `all` is `false`:
16
+ * parseBody should return `{ file: 'bbb', message: 'hello' }`
17
+ *
18
+ * If `all` is `true`:
19
+ * parseBody should return `{ file: ['aaa', 'bbb'], message: 'hello' }`
20
+ */
21
+ all?: boolean;
22
+ };
23
+ export declare const parseBody: <T extends BodyData = BodyData>(request: HonoRequest | Request, options?: ParseBodyOptions) => Promise<T>;
@@ -1,5 +1,10 @@
1
1
  // src/utils/body.ts
2
- var parseBody = async (request) => {
2
+ var isArrayField = (value) => {
3
+ return Array.isArray(value);
4
+ };
5
+ var parseBody = async (request, options = {
6
+ all: false
7
+ }) => {
3
8
  let body = {};
4
9
  const contentType = request.headers.get("Content-Type");
5
10
  if (contentType && (contentType.startsWith("multipart/form-data") || contentType.startsWith("application/x-www-form-urlencoded"))) {
@@ -7,18 +12,21 @@ var parseBody = async (request) => {
7
12
  if (formData) {
8
13
  const form = {};
9
14
  formData.forEach((value, key) => {
10
- if (key.slice(-2) === "[]") {
11
- if (!form[key]) {
12
- form[key] = [value.toString()];
13
- } else {
14
- if (Array.isArray(form[key])) {
15
- ;
16
- form[key].push(value.toString());
17
- }
18
- }
19
- } else {
15
+ const shouldParseAllValues = options.all || key.slice(-2) === "[]";
16
+ if (!shouldParseAllValues) {
20
17
  form[key] = value;
18
+ return;
19
+ }
20
+ if (form[key] && isArrayField(form[key])) {
21
+ ;
22
+ form[key].push(value);
23
+ return;
24
+ }
25
+ if (form[key]) {
26
+ form[key] = [form[key], value];
27
+ return;
21
28
  }
29
+ form[key] = value;
22
30
  });
23
31
  body = form;
24
32
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hono",
3
- "version": "3.7.3",
3
+ "version": "3.8.0-rc.1",
4
4
  "description": "Ultrafast web framework for the Edges",
5
5
  "main": "dist/cjs/index.js",
6
6
  "type": "module",
@@ -24,7 +24,7 @@
24
24
  "lint:fix": "eslint --ext js,ts src runtime_tests .eslintrc.cjs --fix",
25
25
  "format": "prettier --check 'src/**/*.{js,ts}' 'runtime_tests/**/*.{js,ts}'",
26
26
  "format:fix": "prettier --write 'src/**/*.{js,ts}' 'runtime_tests/**/*.{js,ts}'",
27
- "denoify": "rimraf deno_dist && denoify && rimraf 'deno_dist/**/*.test.ts'",
27
+ "denoify": "rimraf deno_dist && denoify && rimraf 'deno_dist/**/*.test.{ts,tsx}'",
28
28
  "copy:package.cjs.json": "cp ./package.cjs.json ./dist/cjs/package.json && cp ./package.cjs.json ./dist/types/package.json ",
29
29
  "build": "rimraf dist && tsx ./build.ts && yarn copy:package.cjs.json",
30
30
  "postbuild": "publint",
@@ -109,6 +109,11 @@
109
109
  "import": "./dist/jsx/jsx-runtime.js",
110
110
  "require": "./dist/cjs/jsx/jsx-runtime.js"
111
111
  },
112
+ "./jsx-renderer": {
113
+ "types": "./dist/types/middleware/jsx-renderer/index.d.ts",
114
+ "import": "./dist/middleware/jsx-renderer/index.js",
115
+ "require": "./dist/cjs/middleware/jsx-renderer/index.js"
116
+ },
112
117
  "./jwt": {
113
118
  "types": "./dist/types/middleware/jwt/index.d.ts",
114
119
  "import": "./dist/middleware/jwt/index.js",
@@ -284,6 +289,9 @@
284
289
  "jsx/jsx-dev-runtime": [
285
290
  "./dist/types/jsx/jsx-dev-runtime.d.ts"
286
291
  ],
292
+ "jsx-renderer": [
293
+ "./dist/types/middleware/jsx-renderer"
294
+ ],
287
295
  "jwt": [
288
296
  "./dist/types/middleware/jwt"
289
297
  ],
@@ -398,7 +406,7 @@
398
406
  "@types/crypto-js": "^4.1.1",
399
407
  "@types/glob": "^8.0.0",
400
408
  "@types/jest": "^29.4.0",
401
- "@types/node": "^17.0.29",
409
+ "@types/node": "^20.8.2",
402
410
  "@types/node-fetch": "^2.6.2",
403
411
  "@types/supertest": "^2.0.12",
404
412
  "@typescript-eslint/eslint-plugin": "^5.59.2",