h3 0.2.12 → 0.3.3

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
@@ -1,24 +1,25 @@
1
- [![d](https://img.shields.io/npm/dm/h3.svg?style=flat-square)](https://npmjs.com/package/h3)
2
- [![v](https://img.shields.io/npm/v/h3/latest.svg?style=flat-square)](https://npmjs.com/package/h3)
3
- [![b](https://img.shields.io/bundlephobia/min/h3/latest.svg?style=flat-square)](https://bundlephobia.com/result?p=h3)
4
- [![a](https://img.shields.io/github/workflow/status/unjs/h3/ci/main?style=flat-square)](https://github.com/unjs/h3/actions)
5
- [![c](https://img.shields.io/codecov/c/gh/unjs/h3/main?style=flat-square)](https://codecov.io/gh/unjs/h3)
1
+ [![npm downloads](https://img.shields.io/npm/dm/h3.svg?style=flat-square)](https://npmjs.com/package/h3)
2
+ [![version](https://img.shields.io/npm/v/h3/latest.svg?style=flat-square)](https://npmjs.com/package/h3)
3
+ [![bundlephobia](https://img.shields.io/bundlephobia/min/h3/latest.svg?style=flat-square)](https://bundlephobia.com/result?p=h3)
4
+ [![build status](https://img.shields.io/github/workflow/status/unjs/h3/ci/main?style=flat-square)](https://github.com/unjs/h3/actions)
5
+ [![coverage](https://img.shields.io/codecov/c/gh/unjs/h3/main?style=flat-square)](https://codecov.io/gh/unjs/h3)
6
+ [![jsDocs.io](https://img.shields.io/badge/jsDocs.io-reference-blue?style=flat-square)](https://www.jsdocs.io/package/h3)
6
7
 
7
8
  > H3 is a minimal h(ttp) framework built for high performance and portability
8
9
 
9
10
  <!-- ![h3 - Tiny JavaScript Server](.github/banner.svg) -->
10
11
 
11
- **Features**
12
+ ## Features
12
13
 
13
- ✔️ **Portable:** Works perfectly in Serverless, Workers, and Node.js
14
+ ✔️ &nbsp;**Portable:** Works perfectly in Serverless, Workers, and Node.js
14
15
 
15
- ✔️ **Compatible:** Support connect/express middleware
16
+ ✔️ &nbsp;**Compatible:** Support connect/express middleware
16
17
 
17
- ✔️ **Minimal:** Small, tree-shakable and zero-dependency
18
+ ✔️ &nbsp;**Minimal:** Small, tree-shakable and zero-dependency
18
19
 
19
- ✔️ **Modern:** Native promise support
20
+ ✔️ &nbsp;**Modern:** Native promise support
20
21
 
21
- ✔️**Extendable:** Ships with a set of composable utilities but can be extended
22
+ ✔️ &nbsp;**Extendable:** Ships with a set of composable utilities but can be extended
22
23
 
23
24
  ## Install
24
25
 
@@ -37,31 +38,46 @@ import { createServer } from 'http'
37
38
  import { createApp } from 'h3'
38
39
 
39
40
  const app = createApp()
40
- app.useAsync('/', () => 'Hello world!')
41
+ app.use('/', () => 'Hello world!')
41
42
 
42
- listen(app)
43
+ createServer(app).listen(process.env.PORT || 3000)
43
44
  ```
44
45
 
45
- **Tip:** you may try [listhen](https://github.com/unjs/listhen) for a more elegant and advanced listener.
46
+ <details>
47
+ <summary>Example using <a href="https://github.com/unjs/listhen">listhen</a> for an elegant listener.</summary>
48
+
49
+ ```ts
50
+ import { createApp } from 'h3'
51
+ import { listen } from 'listhen'
52
+
53
+ const app = createApp()
54
+ app.use('/', () => 'Hello world!')
55
+
56
+ listen(app)
57
+ ```
58
+ </details>
46
59
 
47
60
  ## Examples
48
61
 
49
62
  ```js
50
63
  // Handle can directly return object or Promise<object> for JSON response
51
- app.useAsync('/api', (req) => ({ url: req.url }))
64
+ app.use('/api', (req) => ({ url: req.url }))
52
65
 
53
66
  // We can have better matching other than quick prefix match
54
- app.useAsync('/odd', () => 'Is odd!', { match: url => url.substr(1) % 2 })
67
+ app.use('/odd', () => 'Is odd!', { match: url => url.substr(1) % 2 })
55
68
 
56
69
  // Handle can directly return string for HTML response
57
- app.useAsync(() => '<h1>Hello world!</h1>')
70
+ app.use(() => '<h1>Hello world!</h1>')
58
71
 
59
72
  // We can chain calls to .use()
60
- app.useAsync('/1', () => '<h1>Hello world!</h1>')
61
- .useAsync('/2', () => '<h1>Goodbye!</h1>')
73
+ app.use('/1', () => '<h1>Hello world!</h1>')
74
+ .use('/2', () => '<h1>Goodbye!</h1>')
62
75
 
63
- // Promisify middleware before register: (supporting (req, res, next) format)
64
- // app.use(async () => {})
76
+ // Legacy middleware with 3rd argument are automatically promisified
77
+ app.use((req, res, next) => { req.setHeader('X-Foo', 'bar'); next() })
78
+
79
+ // Force promisify a legacy middleware
80
+ // app.use(someMiddleware, { promisify: true })
65
81
 
66
82
  // Lazy loaded routes using { lazy: true }
67
83
  // app.use('/big', () => import('./big'), { lazy: true })
@@ -75,7 +91,7 @@ Instead of adding helpers to `req` and `res`, h3 exposes them as composable util
75
91
  - `useBody(req)`
76
92
  - `useCookies(req)`
77
93
  - `useCookie(req, name)`
78
- - `setCookie(req, name, value, opts?)`
94
+ - `setCookie(res, name, value, opts?)`
79
95
  - `useQuery(req)`
80
96
  - `send(res, data, type?)`
81
97
  - `sendRedirect(res, location, code=302)`
@@ -83,18 +99,20 @@ Instead of adding helpers to `req` and `res`, h3 exposes them as composable util
83
99
  - `createError({ statusCode, statusMessage, data? }`
84
100
  - `sendError(res, error, debug?)`
85
101
 
102
+ 👉 You can learn more about usage in [JSDocs Documentation](https://www.jsdocs.io/package/h3#package-functions).
103
+
86
104
  ## How it works?
87
105
 
88
- Using `createApp`, it returns a standard `(req, res)` handler function and internally an array called middleware stack. `useAsync()` and `use()` methods are helpers to add an item to this internal stack.
106
+ Using `createApp`, it returns a standard `(req, res)` handler function and internally an array called middleware stack. using`use()` method we can add an item to this internal stack.
89
107
 
90
108
  When a request comes, each stack item that matches the route will be called and resolved until [`res.writableEnded`](https://nodejs.org/api/http.html#http_response_writableended) flag is set, which means the response is sent. If `writableEnded` is not set after all middleware, a `404` error will be thrown. And if one of the stack items resolves to a value, it will be serialized and sent as response as a shorthand method to sending responses.
91
109
 
92
- For maximum compatibility with connect/express middleware (`req, res, next?` signature), when using `use` instead of `useAsync`, it converts classic middleware into a promisified version ready to use with stack runner:
110
+ For maximum compatibility with connect/express middleware (`req, res, next?` signature), h3 converts classic middleware into a promisified version ready to use with stack runner:
93
111
 
94
- - If middleware has 3rd next/callback param, promise will `resolve/reject` when called
112
+ - If middleware has 3rd next/callback param, the promise will `resolve/reject` when called
95
113
  - If middleware returns a promise, it will be **chained** to the main promise
96
- - If calling middleware throws an immediate error, promise will be rejected
97
- - On `close` and `error` events of res, promise will `resolve/reject` (to ensure if middleware simply calls `res.end`)
114
+ - If calling middleware throws an immediate error, the promise will be rejected
115
+ - On `close` and `error` events of res, the promise will `resolve/reject` (to ensure if middleware simply calls `res.end`)
98
116
 
99
117
  ## License
100
118
 
package/dist/index.cjs CHANGED
@@ -124,7 +124,7 @@ function callHandle(handle, req, res) {
124
124
  }
125
125
  });
126
126
  }
127
- function lazyHandle(handle, promisify = true) {
127
+ function lazyHandle(handle, promisify) {
128
128
  let _promise;
129
129
  const resolve = () => {
130
130
  if (!_promise) {
@@ -447,7 +447,7 @@ function defaultContentType(res, type) {
447
447
  function sendRedirect(res, location, code = 302) {
448
448
  res.statusCode = code;
449
449
  res.setHeader("Location", location);
450
- send(res, "Redirecting to " + location, MIMES.html);
450
+ return send(res, "Redirecting to " + location, MIMES.html);
451
451
  }
452
452
  function appendHeader(res, name, value) {
453
453
  let current = res.getHeader(name);
@@ -528,7 +528,7 @@ function sendError(res, error, debug) {
528
528
 
529
529
  function createApp(options = {}) {
530
530
  const stack = [];
531
- const _handle = createHandle(stack);
531
+ const _handle = createHandle(stack, options);
532
532
  const app = function(req, res) {
533
533
  return _handle(req, res).catch((error) => {
534
534
  if (options.onError) {
@@ -540,7 +540,6 @@ function createApp(options = {}) {
540
540
  app.stack = stack;
541
541
  app._handle = _handle;
542
542
  app.use = (arg1, arg2, arg3) => use(app, arg1, arg2, arg3);
543
- app.useAsync = (arg1, arg2, arg3) => use(app, arg1, arg2 !== void 0 ? arg2 : { ...arg3, promisify: false }, { ...arg3, promisify: false });
544
543
  return app;
545
544
  }
546
545
  function use(app, arg1, arg2, arg3) {
@@ -557,7 +556,8 @@ function use(app, arg1, arg2, arg3) {
557
556
  }
558
557
  return app;
559
558
  }
560
- function createHandle(stack) {
559
+ function createHandle(stack, options) {
560
+ const spacing = options.debug ? 2 : void 0;
561
561
  return async function handle(req, res) {
562
562
  req.originalUrl = req.originalUrl || req.url || "/";
563
563
  const reqUrl = req.url || "/";
@@ -581,10 +581,12 @@ function createHandle(stack) {
581
581
  if (type === "string") {
582
582
  return send(res, val, MIMES.html);
583
583
  } else if (type === "object" && val !== void 0) {
584
- if (val.buffer) {
584
+ if (val && val.buffer) {
585
585
  return send(res, val);
586
+ } else if (val instanceof Error) {
587
+ throw createError(val);
586
588
  } else {
587
- return send(res, JSON.stringify(val, null, 2), MIMES.json);
589
+ return send(res, JSON.stringify(val, null, spacing), MIMES.json);
588
590
  }
589
591
  }
590
592
  }
@@ -594,10 +596,13 @@ function createHandle(stack) {
594
596
  };
595
597
  }
596
598
  function normalizeLayer(layer) {
599
+ if (layer.promisify === void 0) {
600
+ layer.promisify = layer.handle.length > 2;
601
+ }
597
602
  return {
598
603
  route: withoutTrailingSlash(layer.route).toLocaleLowerCase(),
599
604
  match: layer.match,
600
- handle: layer.lazy ? lazyHandle(layer.handle, layer.promisify) : layer.promisify !== false ? promisifyHandle(layer.handle) : layer.handle
605
+ handle: layer.lazy ? lazyHandle(layer.handle, layer.promisify) : layer.promisify ? promisifyHandle(layer.handle) : layer.handle
601
606
  };
602
607
  }
603
608
 
package/dist/index.d.ts CHANGED
@@ -3,8 +3,8 @@ import * as ufo from 'ufo';
3
3
 
4
4
  declare type Encoding = false | 'ascii' | 'utf8' | 'utf-8' | 'utf16le' | 'ucs2' | 'ucs-2' | 'base64' | 'latin1' | 'binary' | 'hex';
5
5
 
6
- declare type Handle = (req: IncomingMessage, res: ServerResponse) => any;
7
- declare type PHandle = (req: IncomingMessage, res: ServerResponse) => Promise<any>;
6
+ declare type Handle<T = any> = (req: IncomingMessage, res: ServerResponse) => T;
7
+ declare type PHandle = Handle<Promise<any>>;
8
8
  declare type Middleware = (req: IncomingMessage, res: ServerResponse, next: (err?: Error) => any) => any;
9
9
  declare type LazyHandle = () => Handle | Promise<Handle>;
10
10
  declare function promisifyHandle(handle: Handle | Middleware): PHandle;
@@ -28,13 +28,9 @@ interface InputLayer {
28
28
  declare type InputStack = InputLayer[];
29
29
  declare type Matcher = (url: string, req?: IncomingMessage) => boolean;
30
30
  interface AppUse {
31
- (route: string | string[], handle: Middleware | Middleware[], options?: Partial<InputLayer & {
32
- promisify: true;
33
- }>): App;
31
+ (route: string | string[], handle: Middleware | Middleware[], options?: Partial<InputLayer>): App;
34
32
  (route: string | string[], handle: Handle | Handle[], options?: Partial<InputLayer>): App;
35
- (handle: Middleware | Middleware[], options?: Partial<InputLayer & {
36
- promisify: true;
37
- }>): App;
33
+ (handle: Middleware | Middleware[], options?: Partial<InputLayer>): App;
38
34
  (handle: Handle | Handle[], options?: Partial<InputLayer>): App;
39
35
  (options: InputLayer): App;
40
36
  }
@@ -43,15 +39,14 @@ interface App {
43
39
  stack: Stack;
44
40
  _handle: PHandle;
45
41
  use: AppUse;
46
- useAsync: AppUse;
47
42
  }
48
43
  interface AppOptions {
49
44
  debug?: boolean;
50
45
  onError?: (error: Error, req: IncomingMessage, res: ServerResponse) => any;
51
46
  }
52
47
  declare function createApp(options?: AppOptions): App;
53
- declare function use(app: App, arg1: string | Handle | InputLayer | InputLayer[], arg2?: Handle | Partial<InputLayer> | Handle[], arg3?: Partial<InputLayer>): App;
54
- declare function createHandle(stack: Stack): PHandle;
48
+ declare function use(app: App, arg1: string | Handle | InputLayer | InputLayer[], arg2?: Handle | Partial<InputLayer> | Handle[] | Middleware | Middleware[], arg3?: Partial<InputLayer>): App;
49
+ declare function createHandle(stack: Stack, options: AppOptions): PHandle;
55
50
 
56
51
  /**
57
52
  * H3 Runtime Error
@@ -89,20 +84,22 @@ declare function sendError(res: ServerResponse, error: Error | H3Error, debug?:
89
84
 
90
85
  /**
91
86
  * Reads body of the request and returns encoded raw string (default) or `Buffer` if encoding if falsy.
92
- * @param req {IncomingMessage} An IncomingMessage object is created by
93
- * <a href="https://nodejs.org/api/http.html#http_class_http_server">http.Server</a>
87
+ * @param req {IncomingMessage} An IncomingMessage object is created by [http.Server](https://nodejs.org/api/http.html#http_class_http_server)
94
88
  * @param encoding {Encoding} encoding="utf-8" - The character encoding to use.
95
89
  *
96
90
  * @return {String|Buffer} Encoded raw string or raw Buffer of the body
97
91
  */
98
92
  declare function useRawBody(req: IncomingMessage, encoding?: Encoding): Encoding extends false ? Buffer : Promise<string>;
99
93
  /**
100
- * Reads request body and try to safely parse using {@link https://github.com/unjs/destr destr}
101
- * @param req {IncomingMessage} An IncomingMessage object is created by
102
- * <a href="https://nodejs.org/api/http.html#http_class_http_server">http.Server</a>
94
+ * Reads request body and try to safely parse using [destr](https://github.com/unjs/destr)
95
+ * @param req {IncomingMessage} An IncomingMessage object created by [http.Server](https://nodejs.org/api/http.html#http_class_http_server)
103
96
  * @param encoding {Encoding} encoding="utf-8" - The character encoding to use.
104
97
  *
105
- * @return {*} The Object, Array, string, number, boolean, or null value corresponding to the request JSON body
98
+ * @return {*} The `Object`, `Array`, `String`, `Number`, `Boolean`, or `null` value corresponding to the request JSON body
99
+ *
100
+ * ```ts
101
+ * const body = await useBody(req)
102
+ * ```
106
103
  */
107
104
  declare function useBody<T = any>(req: IncomingMessage): Promise<T>;
108
105
 
@@ -197,15 +194,42 @@ interface CookieSerializeOptions {
197
194
  secure?: boolean;
198
195
  }
199
196
 
197
+ /**
198
+ * Parse the request to get HTTP Cookie header string and returning an object of all cookie name-value pairs.
199
+ * @param req {IncomingMessage} An IncomingMessage object created by [http.Server](https://nodejs.org/api/http.html#http_class_http_server)
200
+ * @returns Object of cookie name-value pairs
201
+ * ```ts
202
+ * const cookies = useCookies(req)
203
+ * ```
204
+ */
200
205
  declare function useCookies(req: IncomingMessage): Record<string, string>;
206
+ /**
207
+ * Get a cookie value by name.
208
+ * @param req {IncomingMessage} An IncomingMessage object created by [http.Server](https://nodejs.org/api/http.html#http_class_http_server)
209
+ * @param name Name of the cookie to get
210
+ * @returns {*} Value of the cookie (String or undefined)
211
+ * ```ts
212
+ * const authorization = useCookie(request, 'Authorization')
213
+ * ```
214
+ */
201
215
  declare function useCookie(req: IncomingMessage, name: string): string | undefined;
202
- declare function setCookie(res: ServerResponse, name: string, value: string, serializeOptions: CookieSerializeOptions): void;
216
+ /**
217
+ * Set a cookie value by name.
218
+ * @param res {ServerResponse} A ServerResponse object created by [http.Server](https://nodejs.org/api/http.html#http_class_http_server)
219
+ * @param name Name of the cookie to set
220
+ * @param value Value of the cookie to set
221
+ * @param serializeOptions {CookieSerializeOptions} Options for serializing the cookie
222
+ * ```ts
223
+ * setCookie(res, 'Authorization', '1234567')
224
+ * ```
225
+ */
226
+ declare function setCookie(res: ServerResponse, name: string, value: string, serializeOptions?: CookieSerializeOptions): void;
203
227
 
204
228
  declare function useQuery(req: IncomingMessage): ufo.QueryObject;
205
229
 
206
230
  declare function send(res: ServerResponse, data: any, type?: string): Promise<unknown>;
207
231
  declare function defaultContentType(res: ServerResponse, type?: string): void;
208
- declare function sendRedirect(res: ServerResponse, location: string, code?: number): void;
232
+ declare function sendRedirect(res: ServerResponse, location: string, code?: number): Promise<unknown>;
209
233
  declare function appendHeader(res: ServerResponse, name: string, value: string): void;
210
234
 
211
235
  export { App, AppOptions, AppUse, H3Error, Handle, InputLayer, InputStack, Layer, LazyHandle, MIMES, Matcher, Middleware, PHandle, Stack, appendHeader, callHandle, createApp, createError, createHandle, defaultContentType, lazyHandle, promisifyHandle, send, sendError, sendRedirect, setCookie, use, useBase, useBody, useCookie, useCookies, useQuery, useRawBody };
package/dist/index.mjs CHANGED
@@ -120,7 +120,7 @@ function callHandle(handle, req, res) {
120
120
  }
121
121
  });
122
122
  }
123
- function lazyHandle(handle, promisify = true) {
123
+ function lazyHandle(handle, promisify) {
124
124
  let _promise;
125
125
  const resolve = () => {
126
126
  if (!_promise) {
@@ -443,7 +443,7 @@ function defaultContentType(res, type) {
443
443
  function sendRedirect(res, location, code = 302) {
444
444
  res.statusCode = code;
445
445
  res.setHeader("Location", location);
446
- send(res, "Redirecting to " + location, MIMES.html);
446
+ return send(res, "Redirecting to " + location, MIMES.html);
447
447
  }
448
448
  function appendHeader(res, name, value) {
449
449
  let current = res.getHeader(name);
@@ -524,7 +524,7 @@ function sendError(res, error, debug) {
524
524
 
525
525
  function createApp(options = {}) {
526
526
  const stack = [];
527
- const _handle = createHandle(stack);
527
+ const _handle = createHandle(stack, options);
528
528
  const app = function(req, res) {
529
529
  return _handle(req, res).catch((error) => {
530
530
  if (options.onError) {
@@ -536,7 +536,6 @@ function createApp(options = {}) {
536
536
  app.stack = stack;
537
537
  app._handle = _handle;
538
538
  app.use = (arg1, arg2, arg3) => use(app, arg1, arg2, arg3);
539
- app.useAsync = (arg1, arg2, arg3) => use(app, arg1, arg2 !== void 0 ? arg2 : { ...arg3, promisify: false }, { ...arg3, promisify: false });
540
539
  return app;
541
540
  }
542
541
  function use(app, arg1, arg2, arg3) {
@@ -553,7 +552,8 @@ function use(app, arg1, arg2, arg3) {
553
552
  }
554
553
  return app;
555
554
  }
556
- function createHandle(stack) {
555
+ function createHandle(stack, options) {
556
+ const spacing = options.debug ? 2 : void 0;
557
557
  return async function handle(req, res) {
558
558
  req.originalUrl = req.originalUrl || req.url || "/";
559
559
  const reqUrl = req.url || "/";
@@ -577,10 +577,12 @@ function createHandle(stack) {
577
577
  if (type === "string") {
578
578
  return send(res, val, MIMES.html);
579
579
  } else if (type === "object" && val !== void 0) {
580
- if (val.buffer) {
580
+ if (val && val.buffer) {
581
581
  return send(res, val);
582
+ } else if (val instanceof Error) {
583
+ throw createError(val);
582
584
  } else {
583
- return send(res, JSON.stringify(val, null, 2), MIMES.json);
585
+ return send(res, JSON.stringify(val, null, spacing), MIMES.json);
584
586
  }
585
587
  }
586
588
  }
@@ -590,10 +592,13 @@ function createHandle(stack) {
590
592
  };
591
593
  }
592
594
  function normalizeLayer(layer) {
595
+ if (layer.promisify === void 0) {
596
+ layer.promisify = layer.handle.length > 2;
597
+ }
593
598
  return {
594
599
  route: withoutTrailingSlash(layer.route).toLocaleLowerCase(),
595
600
  match: layer.match,
596
- handle: layer.lazy ? lazyHandle(layer.handle, layer.promisify) : layer.promisify !== false ? promisifyHandle(layer.handle) : layer.handle
601
+ handle: layer.lazy ? lazyHandle(layer.handle, layer.promisify) : layer.promisify ? promisifyHandle(layer.handle) : layer.handle
597
602
  };
598
603
  }
599
604
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "h3",
3
- "version": "0.2.12",
3
+ "version": "0.3.3",
4
4
  "description": "Tiny JavaScript Server",
5
5
  "repository": "unjs/h3",
6
6
  "license": "MIT",