hono 1.4.6 → 1.5.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.
package/README.md CHANGED
@@ -73,6 +73,7 @@ Built-in middleware make _"**Write Less, do more**"_ in reality. You can use a l
73
73
  - [CORS](https://github.com/honojs/hono/tree/master/src/middleware/cors/)
74
74
  - [ETag](https://github.com/honojs/hono/tree/master/src/middleware/etag/)
75
75
  - [GraphQL Server](https://github.com/honojs/hono/tree/master/src/middleware/graphql-server/)
76
+ - [html](https://github.com/honojs/hono/tree/master/src/middleware/html/)
76
77
  - [JSX](https://github.com/honojs/hono/tree/master/src/middleware/jsx/)
77
78
  - [JWT Authentication](https://github.com/honojs/hono/tree/master/src/middleware/jwt/)
78
79
  - [Logger](https://github.com/honojs/hono/tree/master/src/middleware/logger/)
@@ -482,10 +483,22 @@ app.use('/', async (c, next) => {
482
483
  })
483
484
  ```
484
485
 
486
+ ### c.executionCtx
487
+
488
+ ```ts
489
+ // ExecutionContext object
490
+ app.get('/foo', async (c) => {
491
+ c.executionCtx.waitUntil(
492
+ c.env.KV.put(key, data)
493
+ )
494
+ ...
495
+ })
496
+ ```
497
+
485
498
  ### c.event
486
499
 
487
500
  ```ts
488
- // FetchEvent object
501
+ // FetchEvent object (only set when using Service Worker syntax)
489
502
  app.get('/foo', async (c) => {
490
503
  c.event.waitUntil(
491
504
  c.env.KV.put(key, data)
@@ -522,8 +535,8 @@ addEventListener('fetch', (event) => {
522
535
 
523
536
  ```ts
524
537
  export default {
525
- fetch(request: Request, env: Env, event: FetchEvent) {
526
- return app.fetch(request, env, event)
538
+ fetch(request: Request, env: Env, ctx: ExecutionContext) {
539
+ return app.fetch(request, env, ctx)
527
540
  },
528
541
  }
529
542
  ```
@@ -555,9 +568,9 @@ import { RegExpRouter } from 'hono/router/reg-exp-router'
555
568
  const app = new Hono({ router: new RegExpRouter() })
556
569
  ```
557
570
 
558
- ## Routing Ordering
571
+ ## Routing priority
559
572
 
560
- The routing priority is decided by the order of registration. Only one handler will be dispatched.
573
+ Handlers or middleware will be executed in registration order.
561
574
 
562
575
  ```ts
563
576
  app.get('/book/a', (c) => c.text('a')) // a
@@ -565,27 +578,37 @@ app.get('/book/:slug', (c) => c.text('common')) // common
565
578
  ```
566
579
 
567
580
  ```http
568
- GET /book/a ---> `a` // common will not be dispatched
569
- GET /book/b ---> `common` // a will not be dispatched
581
+ GET /book/a ---> `a`
582
+ GET /book/b ---> `common`
570
583
  ```
571
584
 
572
- All scoring rules:
585
+ When a handler is executed, the process will be stopped.
573
586
 
574
587
  ```ts
575
- app.get('/api/*', 'c') // score 1.1 <--- `/*` is special wildcard
576
- app.get('/api/:type/:id', 'd') // score 3.2
577
- app.get('/api/posts/:id', 'e') // score 3.3
578
- app.get('/api/posts/123', 'f') // score 3.4
579
- app.get('/*/*/:id', 'g') // score 3.5
580
- app.get('/api/posts/*/comment', 'h') // score 4.6 - not match
581
- app.get('*', 'a') // score 0.7
582
- app.get('*', 'b') // score 0.8
588
+ app.get('*', (c) => c.text('common')) // common
589
+ app.get('/foo', (c) => c.text('foo')) // foo
583
590
  ```
584
591
 
585
- ```plain
586
- GET /api/posts/123
587
- ---> will match => c, d, e, f, b, a, b
588
- ---> sort by score => a, b, c, d, e, f, g
592
+ ```http
593
+ GET /foo ---> `common` // foo will not be dispatched
594
+ ```
595
+
596
+ If you have the middleware that you want to execute, write the code above the handler.
597
+
598
+ ```ts
599
+ app.use('*', logger())
600
+ app.get('/foo', (c) => c.text('foo'))
601
+ ```
602
+
603
+ If you want a "_fallback_" handler, write the code below the other handler.
604
+
605
+ ```ts
606
+ app.get('/foo', (c) => c.text('foo')) // foo
607
+ app.get('*', (c) => c.text('fallback')) // fallback
608
+ ```
609
+
610
+ ```http
611
+ GET /bar ---> `fallback`
589
612
  ```
590
613
 
591
614
  ## Cloudflare Workers with Hono
package/dist/context.d.ts CHANGED
@@ -8,6 +8,7 @@ export declare class Context<RequestParamKeyType extends string = string, E = En
8
8
  req: Request<RequestParamKeyType>;
9
9
  env: E;
10
10
  event: FetchEvent | undefined;
11
+ executionCtx: ExecutionContext | undefined;
11
12
  finalized: boolean;
12
13
  private _status;
13
14
  private _pretty;
@@ -17,7 +18,7 @@ export declare class Context<RequestParamKeyType extends string = string, E = En
17
18
  private _res;
18
19
  private notFoundHandler;
19
20
  render: (content: string, params?: object, options?: object) => Response | Promise<Response>;
20
- constructor(req: Request<RequestParamKeyType>, env?: E | undefined, event?: FetchEvent | undefined, notFoundHandler?: NotFoundHandler);
21
+ constructor(req: Request<RequestParamKeyType>, env?: E | undefined, eventOrExecutionCtx?: FetchEvent | ExecutionContext | undefined, notFoundHandler?: NotFoundHandler);
21
22
  get res(): Response;
22
23
  set res(_res: Response);
23
24
  header(name: string, value: string): void;
package/dist/context.js CHANGED
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Context = void 0;
4
4
  const url_1 = require("./utils/url");
5
5
  class Context {
6
- constructor(req, env = undefined, event = undefined, notFoundHandler = () => new Response()) {
6
+ constructor(req, env = undefined, eventOrExecutionCtx = undefined, notFoundHandler = () => new Response()) {
7
7
  this._status = 200;
8
8
  this._pretty = false;
9
9
  this._prettySpace = 2;
@@ -11,7 +11,10 @@ class Context {
11
11
  if (env) {
12
12
  this.env = env;
13
13
  }
14
- this.event = event;
14
+ this.executionCtx = eventOrExecutionCtx;
15
+ if (eventOrExecutionCtx && 'respondWith' in eventOrExecutionCtx) {
16
+ this.event = eventOrExecutionCtx;
17
+ }
15
18
  this.notFoundHandler = notFoundHandler;
16
19
  this.finalized = false;
17
20
  }
package/dist/hono.d.ts CHANGED
@@ -48,7 +48,7 @@ export declare class Hono<E extends Env = Env, P extends string = '/'> extends H
48
48
  private matchRoute;
49
49
  private dispatch;
50
50
  handleEvent(event: FetchEvent): Promise<Response>;
51
- fetch(request: Request, env?: E, event?: FetchEvent): Promise<Response>;
51
+ fetch(request: Request, env?: E, executionCtx?: ExecutionContext): Promise<Response>;
52
52
  request(input: RequestInfo, requestInit?: RequestInit): Promise<Response>;
53
53
  fire(): void;
54
54
  }
package/dist/hono.js CHANGED
@@ -91,13 +91,13 @@ class Hono extends defineDynamicClass() {
91
91
  matchRoute(method, path) {
92
92
  return this.router.match(method, path);
93
93
  }
94
- async dispatch(request, event, env) {
94
+ async dispatch(request, executionCtx, env) {
95
95
  const path = (0, url_1.getPathFromURL)(request.url, this.strict);
96
96
  const method = request.method;
97
97
  const result = this.matchRoute(method, path);
98
98
  request.paramData = result?.params;
99
99
  const handlers = result ? result.handlers : [this.notFoundHandler];
100
- const c = new context_1.Context(request, env, event, this.notFoundHandler);
100
+ const c = new context_1.Context(request, env, executionCtx, this.notFoundHandler);
101
101
  const composed = (0, compose_1.compose)(handlers, this.errorHandler, this.notFoundHandler);
102
102
  let context;
103
103
  try {
@@ -117,8 +117,8 @@ class Hono extends defineDynamicClass() {
117
117
  async handleEvent(event) {
118
118
  return this.dispatch(event.request, event);
119
119
  }
120
- async fetch(request, env, event) {
121
- return this.dispatch(request, event, env);
120
+ async fetch(request, env, executionCtx) {
121
+ return this.dispatch(request, executionCtx, env);
122
122
  }
123
123
  request(input, requestInit) {
124
124
  const req = input instanceof Request ? input : new Request(input, requestInit);
@@ -0,0 +1,5 @@
1
+ export declare type HtmlEscapedString = string & {
2
+ isEscaped: true;
3
+ };
4
+ export declare const raw: (value: any) => HtmlEscapedString;
5
+ export declare const html: (strings: TemplateStringsArray, ...values: any[]) => HtmlEscapedString;
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.html = exports.raw = void 0;
4
+ const html_1 = require("../../utils/html");
5
+ const raw = (value) => {
6
+ const escapedString = new String(value);
7
+ escapedString.isEscaped = true;
8
+ return escapedString;
9
+ };
10
+ exports.raw = raw;
11
+ const html = (strings, ...values) => {
12
+ let result = '';
13
+ for (let i = 0, len = strings.length - 1; i < len; i++) {
14
+ result += strings[i];
15
+ const children = values[i] instanceof Array ? values[i].flat(Infinity) : [values[i]];
16
+ for (let i = 0, len = children.length; i < len; i++) {
17
+ const child = children[i];
18
+ if (typeof child === 'boolean' || child === null || child === undefined) {
19
+ continue;
20
+ }
21
+ else if (typeof child === 'object' && child.isEscaped) {
22
+ result += child;
23
+ }
24
+ else {
25
+ result += (0, html_1.escape)(child.toString());
26
+ }
27
+ }
28
+ }
29
+ result += strings[strings.length - 1];
30
+ return (0, exports.raw)(result);
31
+ };
32
+ exports.html = html;
@@ -1,17 +1,16 @@
1
- import type { Context } from '../../context';
2
- import type { Next } from '../../hono';
1
+ import type { HtmlEscapedString } from '../../utils/html';
3
2
  declare global {
4
- namespace h.JSX {
3
+ namespace jsx.JSX {
5
4
  interface IntrinsicElements {
6
5
  [tagName: string]: Record<string, any>;
7
6
  }
8
7
  }
9
8
  }
10
- export declare const jsx: () => (c: Context, next: Next) => Promise<void>;
11
- declare type EscapedString = string & {
12
- isEscaped: true;
13
- };
14
- export declare const h: (tag: string | Function, props: Record<string, any>, ...children: (string | EscapedString)[]) => EscapedString;
15
- declare type FC<T = Record<string, any>> = (props: T) => EscapedString;
9
+ export declare const jsx: (tag: string | Function, props: Record<string, any>, ...children: (string | HtmlEscapedString)[]) => HtmlEscapedString;
10
+ declare type FC<T = Record<string, any>> = (props: T) => HtmlEscapedString;
16
11
  export declare const memo: <T>(component: FC<T>, propsAreEqual?: (prevProps: Readonly<T>, nextProps: Readonly<T>) => boolean) => FC<T>;
12
+ export declare const Fragment: (props: {
13
+ key?: string;
14
+ children?: any;
15
+ }) => HtmlEscapedString;
17
16
  export {};
@@ -1,22 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.memo = exports.h = exports.jsx = void 0;
3
+ exports.Fragment = exports.memo = exports.jsx = void 0;
4
4
  const html_1 = require("../../utils/html");
5
- const jsx = () => {
6
- return async (c, next) => {
7
- c.render = (content) => {
8
- const output = `<!doctype html>${content.toString()}`;
9
- return c.html(output);
10
- };
11
- await next();
12
- };
13
- };
14
- exports.jsx = jsx;
15
- const h = (tag, props, ...children) => {
5
+ const jsx = (tag, props, ...children) => {
16
6
  if (typeof tag === 'function') {
17
7
  return tag.call(null, { ...props, children: children.length <= 1 ? children[0] : children });
18
8
  }
19
- let result = `<${tag}`;
9
+ let result = tag !== '' ? `<${tag}` : '';
20
10
  const propsKeys = Object.keys(props || {});
21
11
  for (let i = 0, len = propsKeys.length; i < len; i++) {
22
12
  const v = props[propsKeys[i]];
@@ -34,7 +24,9 @@ const h = (tag, props, ...children) => {
34
24
  }
35
25
  result += ` ${propsKeys[i]}="${(0, html_1.escape)(v.toString())}"`;
36
26
  }
37
- result += '>';
27
+ if (tag !== '') {
28
+ result += '>';
29
+ }
38
30
  const flattenChildren = children.flat(Infinity);
39
31
  for (let i = 0, len = flattenChildren.length; i < len; i++) {
40
32
  const child = flattenChildren[i];
@@ -48,12 +40,14 @@ const h = (tag, props, ...children) => {
48
40
  result += (0, html_1.escape)(child.toString());
49
41
  }
50
42
  }
51
- result += `</${tag}>`;
43
+ if (tag !== '') {
44
+ result += `</${tag}>`;
45
+ }
52
46
  const escapedString = new String(result);
53
47
  escapedString.isEscaped = true;
54
48
  return escapedString;
55
49
  };
56
- exports.h = h;
50
+ exports.jsx = jsx;
57
51
  const shallowEqual = (a, b) => {
58
52
  if (a === b) {
59
53
  return true;
@@ -82,3 +76,7 @@ const memo = (component, propsAreEqual = shallowEqual) => {
82
76
  });
83
77
  };
84
78
  exports.memo = memo;
79
+ const Fragment = (props) => {
80
+ return (0, exports.jsx)('', {}, ...(props.children || []));
81
+ };
82
+ exports.Fragment = Fragment;
@@ -11,7 +11,6 @@ interface Hint {
11
11
  interface HandlerWithSortIndex<T> {
12
12
  handler: T;
13
13
  index: number;
14
- componentsLength: number;
15
14
  }
16
15
  interface Route<T> {
17
16
  method: string;
@@ -65,9 +65,7 @@ function compareRoute(a, b) {
65
65
  return i === b.hint.regExpComponents.length || a.hint.endWithWildcard ? 1 : 0;
66
66
  }
67
67
  function compareHandler(a, b) {
68
- return a.componentsLength !== b.componentsLength
69
- ? a.componentsLength - b.componentsLength
70
- : a.index - b.index;
68
+ return a.index - b.index;
71
69
  }
72
70
  function getSortedHandlers(handlers) {
73
71
  return [...handlers].sort(compareHandler).map((h) => h.handler);
@@ -158,7 +156,6 @@ class RegExpRouter {
158
156
  const handlerWithSortIndex = {
159
157
  index,
160
158
  handler,
161
- componentsLength: hint.components.length || 1,
162
159
  };
163
160
  for (let i = 0, len = routes.length; i < len; i++) {
164
161
  if (routes[i].method === method && routes[i].path === path) {
@@ -318,9 +315,7 @@ class RegExpRouter {
318
315
  }
319
316
  }
320
317
  if (routes[j].hint.components.length < routes[i].hint.components.length) {
321
- const componentsLength = routes[j].hint.components.length || 1;
322
318
  routes[j].middleware.push(...routes[i].handlers.map((h) => ({
323
- componentsLength,
324
319
  index: h.index,
325
320
  handler: h.handler,
326
321
  })));
@@ -66,18 +66,11 @@ class Node {
66
66
  parentPatterns.push(...curNode.patterns);
67
67
  curNode = curNode.children[p];
68
68
  }
69
- let score = 1;
70
- if (path === '*') {
71
- score = score + this.order * 0.01;
72
- }
73
- else {
74
- score = parts.length + this.order * 0.01;
75
- }
76
69
  if (!curNode.methods.length) {
77
70
  curNode.methods = [];
78
71
  }
79
72
  const m = {};
80
- const handlerSet = { handler: handler, name: this.name, score: score };
73
+ const handlerSet = { handler: handler, name: this.name, score: this.order };
81
74
  m[method] = handlerSet;
82
75
  curNode.methods.push(m);
83
76
  return curNode;
@@ -90,9 +83,6 @@ class Node {
90
83
  const handlerSet = m[method] || m[router_1.METHOD_NAME_ALL];
91
84
  if (handlerSet !== undefined) {
92
85
  const hs = { ...handlerSet };
93
- if (wildcard) {
94
- hs.score = handlerSet.score - 1;
95
- }
96
86
  handlerSets.push(hs);
97
87
  return;
98
88
  }
@@ -1 +1,4 @@
1
+ export declare type HtmlEscapedString = string & {
2
+ isEscaped: true;
3
+ };
1
4
  export declare const escape: (str: string) => string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hono",
3
- "version": "1.4.6",
3
+ "version": "1.5.1",
4
4
  "description": "Ultrafast web framework for Cloudflare Workers.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -24,6 +24,7 @@
24
24
  "./cors": "./dist/middleware/cors/index.js",
25
25
  "./etag": "./dist/middleware/etag/index.js",
26
26
  "./graphql-server": "./dist/middleware/graphql-server/index.js",
27
+ "./html": "./dist/middleware/html/index.js",
27
28
  "./jsx": "./dist/middleware/jsx/index.js",
28
29
  "./jwt": "./dist/middleware/jwt/index.js",
29
30
  "./logger": "./dist/middleware/logger/index.js",
@@ -61,6 +62,9 @@
61
62
  "graphql-server": [
62
63
  "./dist/middleware/graphql-server"
63
64
  ],
65
+ "html": [
66
+ "./dist/middleware/html"
67
+ ],
64
68
  "jsx": [
65
69
  "./dist/middleware/jsx"
66
70
  ],
@@ -1,3 +0,0 @@
1
- export interface Context {
2
- render(template: string, params?: object, options?: object): Promise<Response>;
3
- }
@@ -1,2 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });