h3 1.0.2 → 1.2.0

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
@@ -39,28 +39,32 @@ pnpm add h3
39
39
  ## Usage
40
40
 
41
41
  ```ts
42
- import { createServer } from 'http'
43
- import { createApp, eventHandler, toNodeListener } from 'h3'
42
+ import { createServer } from "node:http";
43
+ import { createApp, eventHandler, toNodeListener } from "h3";
44
44
 
45
- const app = createApp()
46
- app.use('/', eventHandler(() => 'Hello world!'))
45
+ const app = createApp();
46
+ app.use(
47
+ "/",
48
+ eventHandler(() => "Hello world!")
49
+ );
47
50
 
48
- createServer(toNodeListener(app)).listen(process.env.PORT || 3000)
51
+ createServer(toNodeListener(app)).listen(process.env.PORT || 3000);
49
52
  ```
50
53
 
51
- <details>
52
- <summary>Example using <a href="https://github.com/unjs/listhen">listhen</a> for an elegant listener.</summary>
54
+ Example using <a href="https://github.com/unjs/listhen">listhen</a> for an elegant listener:
53
55
 
54
56
  ```ts
55
- import { createApp, toNodeListener } from 'h3'
56
- import { listen } from 'listhen'
57
+ import { createApp, eventHandler, toNodeListener } from "h3";
58
+ import { listen } from "listhen";
57
59
 
58
- const app = createApp()
59
- app.use('/', eventHandler(() => 'Hello world!'))
60
+ const app = createApp();
61
+ app.use(
62
+ "/",
63
+ eventHandler(() => "Hello world!")
64
+ );
60
65
 
61
- listen(toNodeListener(app))
66
+ listen(toNodeListener(app));
62
67
  ```
63
- </details>
64
68
 
65
69
  ## Router
66
70
 
@@ -69,18 +73,24 @@ The `app` instance created by `h3` uses a middleware stack (see [how it works](#
69
73
  To opt-in using a more advanced and convenient routing system, we can create a router instance and register it to app instance.
70
74
 
71
75
  ```ts
72
- import { createApp, eventHandler, createRouter } from 'h3'
76
+ import { createApp, eventHandler, createRouter } from "h3";
73
77
 
74
- const app = createApp()
78
+ const app = createApp();
75
79
 
76
80
  const router = createRouter()
77
- .get('/', eventHandler(() => 'Hello World!'))
78
- .get('/hello/:name', eventHandler(event => `Hello ${event.context.params.name}!`))
79
-
80
- app.use(router)
81
+ .get(
82
+ "/",
83
+ eventHandler(() => "Hello World!")
84
+ )
85
+ .get(
86
+ "/hello/:name",
87
+ eventHandler((event) => `Hello ${event.context.params.name}!`)
88
+ );
89
+
90
+ app.use(router);
81
91
  ```
82
92
 
83
- **Tip:** We can register same route more than once with different methods.
93
+ **Tip:** We can register the same route more than once with different methods.
84
94
 
85
95
  Routes are internally stored in a [Radix Tree](https://en.wikipedia.org/wiki/Radix_tree) and matched using [unjs/radix3](https://github.com/unjs/radix3).
86
96
 
@@ -109,7 +119,7 @@ app.use('/big', () => import('./big-handler'), { lazy: true })
109
119
 
110
120
  ## Utilities
111
121
 
112
- H3 has concept of compasable utilities that accept `event` (from `eventHandler((event) => {})`) as their first argument. This has several performance benefits over injecting them to `event` or `app` instances and global middleware commonly used in Node.js frameworks such as Express, which Only required code is evaluated and bundled and rest of utils can be tree-shaken when not used.
122
+ H3 has a concept of composable utilities that accept `event` (from `eventHandler((event) => {})`) as their first argument. This has several performance benefits over injecting them to `event` or `app` instances in global middleware commonly used in Node.js frameworks, such as Express. This concept means only required code is evaluated and bundled, and the rest of the utilities can be tree-shaken when not used.
113
123
 
114
124
  ### Built-in
115
125
 
@@ -138,12 +148,21 @@ H3 has concept of compasable utilities that accept `event` (from `eventHandler((
138
148
  - `createError({ statusCode, statusMessage, data? })`
139
149
  - `sendProxy(event, { target, headers?, fetchOptions?, fetch?, sendStream? })`
140
150
  - `proxyRequest(event, { target, headers?, fetchOptions?, fetch?, sendStream? })`
151
+ - `sendNoContent(event, code = 204)`
152
+ - `setResponseStatus(event, status)`
153
+ - `getResponseStatus(event)`
154
+ - `getResponseStatusText(event)`
155
+ - `readMultipartFormData(event)`
156
+ - `useSession(event, { password, name?, cookie?, seal?, crypto? })`
157
+ - `getSession(event, { password, name?, cookie?, seal?, crypto? })`
158
+ - `updateSession(event, { password, name?, cookie?, seal?, crypto? }), update)`
159
+ - `clearSession(event, { password, name?, cookie?, seal?, crypto? }))`
141
160
 
142
161
  👉 You can learn more about usage in [JSDocs Documentation](https://www.jsdocs.io/package/h3#package-functions).
143
162
 
144
163
  ## Community Packages
145
164
 
146
- You can use more h3 event utilities made by the community.
165
+ You can use more H3 event utilities made by the community.
147
166
 
148
167
  Please check their READMEs for more details.
149
168
 
package/dist/index.cjs CHANGED
@@ -4,6 +4,8 @@ const ufo = require('ufo');
4
4
  const radix3 = require('radix3');
5
5
  const destr = require('destr');
6
6
  const cookieEs = require('cookie-es');
7
+ const ironWebcrypto = require('iron-webcrypto');
8
+ const crypto = require('uncrypto');
7
9
 
8
10
  function useBase(base, handler) {
9
11
  base = ufo.withoutTrailingSlash(base);
@@ -17,6 +19,83 @@ function useBase(base, handler) {
17
19
  });
18
20
  }
19
21
 
22
+ function parse(multipartBodyBuffer, boundary) {
23
+ let lastline = "";
24
+ let state = 0 /* INIT */;
25
+ let buffer = [];
26
+ const allParts = [];
27
+ let currentPartHeaders = [];
28
+ for (let i = 0; i < multipartBodyBuffer.length; i++) {
29
+ const prevByte = i > 0 ? multipartBodyBuffer[i - 1] : null;
30
+ const currByte = multipartBodyBuffer[i];
31
+ const newLineChar = currByte === 10 || currByte === 13;
32
+ if (!newLineChar) {
33
+ lastline += String.fromCodePoint(currByte);
34
+ }
35
+ const newLineDetected = currByte === 10 && prevByte === 13;
36
+ if (0 /* INIT */ === state && newLineDetected) {
37
+ if ("--" + boundary === lastline) {
38
+ state = 1 /* READING_HEADERS */;
39
+ }
40
+ lastline = "";
41
+ } else if (1 /* READING_HEADERS */ === state && newLineDetected) {
42
+ if (lastline.length > 0) {
43
+ const i2 = lastline.indexOf(":");
44
+ if (i2 > 0) {
45
+ const name = lastline.slice(0, i2).toLowerCase();
46
+ const value = lastline.slice(i2 + 1).trim();
47
+ currentPartHeaders.push([name, value]);
48
+ }
49
+ } else {
50
+ state = 2 /* READING_DATA */;
51
+ buffer = [];
52
+ }
53
+ lastline = "";
54
+ } else if (2 /* READING_DATA */ === state) {
55
+ if (lastline.length > boundary.length + 4) {
56
+ lastline = "";
57
+ }
58
+ if ("--" + boundary === lastline) {
59
+ const j = buffer.length - lastline.length;
60
+ const part = buffer.slice(0, j - 1);
61
+ allParts.push(process(part, currentPartHeaders));
62
+ buffer = [];
63
+ currentPartHeaders = [];
64
+ lastline = "";
65
+ state = 3 /* READING_PART_SEPARATOR */;
66
+ } else {
67
+ buffer.push(currByte);
68
+ }
69
+ if (newLineDetected) {
70
+ lastline = "";
71
+ }
72
+ } else if (3 /* READING_PART_SEPARATOR */ === state && newLineDetected) {
73
+ state = 1 /* READING_HEADERS */;
74
+ }
75
+ }
76
+ return allParts;
77
+ }
78
+ function process(data, headers) {
79
+ const dataObj = {};
80
+ const contentDispositionHeader = headers.find((h) => h[0] === "content-disposition")?.[1] || "";
81
+ for (const i of contentDispositionHeader.split(";")) {
82
+ const s = i.split("=");
83
+ if (s.length !== 2) {
84
+ continue;
85
+ }
86
+ const key = (s[0] || "").trim();
87
+ if (key === "name" || key === "filename") {
88
+ dataObj[key] = (s[1] || "").trim().replace(/"/g, "");
89
+ }
90
+ }
91
+ const contentType = headers.find((h) => h[0] === "content-type")?.[1] || "";
92
+ if (contentType) {
93
+ dataObj.type = contentType;
94
+ }
95
+ dataObj.data = Buffer.from(data);
96
+ return dataObj;
97
+ }
98
+
20
99
  class H3Error extends Error {
21
100
  constructor() {
22
101
  super(...arguments);
@@ -49,6 +128,7 @@ function createError(input) {
49
128
  }
50
129
  const err = new H3Error(
51
130
  input.message ?? input.statusMessage,
131
+ // @ts-ignore
52
132
  input.cause ? { cause: input.cause } : void 0
53
133
  );
54
134
  if ("stack" in input) {
@@ -222,6 +302,21 @@ async function readBody(event) {
222
302
  event.node.req[ParsedBodySymbol] = json;
223
303
  return json;
224
304
  }
305
+ async function readMultipartFormData(event) {
306
+ const contentType = getRequestHeader(event, "content-type");
307
+ if (!contentType || !contentType.startsWith("multipart/form-data")) {
308
+ return;
309
+ }
310
+ const boundary = contentType.match(/boundary=([^;]*)(;|$)/i)?.[1];
311
+ if (!boundary) {
312
+ return;
313
+ }
314
+ const body = await readRawBody(event, false);
315
+ if (!body) {
316
+ return;
317
+ }
318
+ return parse(body, boundary);
319
+ }
225
320
 
226
321
  function handleCacheHeaders(event, opts) {
227
322
  const cacheControls = ["public", ...opts.cacheControls || []];
@@ -258,6 +353,111 @@ const MIMES = {
258
353
  json: "application/json"
259
354
  };
260
355
 
356
+ function parseCookies(event) {
357
+ return cookieEs.parse(event.node.req.headers.cookie || "");
358
+ }
359
+ function getCookie(event, name) {
360
+ return parseCookies(event)[name];
361
+ }
362
+ function setCookie(event, name, value, serializeOptions) {
363
+ const cookieStr = cookieEs.serialize(name, value, {
364
+ path: "/",
365
+ ...serializeOptions
366
+ });
367
+ let setCookies = event.node.res.getHeader("set-cookie");
368
+ if (!Array.isArray(setCookies)) {
369
+ setCookies = [setCookies];
370
+ }
371
+ setCookies = setCookies.filter((cookieValue) => {
372
+ return cookieValue && !cookieValue.startsWith(name + "=");
373
+ });
374
+ event.node.res.setHeader("set-cookie", [...setCookies, cookieStr]);
375
+ }
376
+ function deleteCookie(event, name, serializeOptions) {
377
+ setCookie(event, name, "", {
378
+ ...serializeOptions,
379
+ maxAge: 0
380
+ });
381
+ }
382
+
383
+ const PayloadMethods = /* @__PURE__ */ new Set(["PATCH", "POST", "PUT", "DELETE"]);
384
+ const ignoredHeaders = /* @__PURE__ */ new Set([
385
+ "transfer-encoding",
386
+ "connection",
387
+ "keep-alive",
388
+ "upgrade",
389
+ "expect",
390
+ "host"
391
+ ]);
392
+ async function proxyRequest(event, target, opts = {}) {
393
+ const method = getMethod(event);
394
+ let body;
395
+ if (PayloadMethods.has(method)) {
396
+ body = await readRawBody(event).catch(() => void 0);
397
+ }
398
+ const headers = /* @__PURE__ */ Object.create(null);
399
+ const reqHeaders = getRequestHeaders(event);
400
+ for (const name in reqHeaders) {
401
+ if (!ignoredHeaders.has(name)) {
402
+ headers[name] = reqHeaders[name];
403
+ }
404
+ }
405
+ if (opts.fetchOptions?.headers) {
406
+ Object.assign(headers, opts.fetchOptions.headers);
407
+ }
408
+ if (opts.headers) {
409
+ Object.assign(headers, opts.headers);
410
+ }
411
+ return sendProxy(event, target, {
412
+ ...opts,
413
+ fetchOptions: {
414
+ headers,
415
+ method,
416
+ body,
417
+ ...opts.fetchOptions
418
+ }
419
+ });
420
+ }
421
+ async function sendProxy(event, target, opts = {}) {
422
+ const _fetch = opts.fetch || globalThis.fetch;
423
+ if (!_fetch) {
424
+ throw new Error(
425
+ "fetch is not available. Try importing `node-fetch-native/polyfill` for Node.js."
426
+ );
427
+ }
428
+ const response = await _fetch(target, {
429
+ headers: opts.headers,
430
+ ...opts.fetchOptions
431
+ });
432
+ event.node.res.statusCode = response.status;
433
+ event.node.res.statusMessage = response.statusText;
434
+ for (const [key, value] of response.headers.entries()) {
435
+ if (key === "content-encoding") {
436
+ continue;
437
+ }
438
+ if (key === "content-length") {
439
+ continue;
440
+ }
441
+ event.node.res.setHeader(key, value);
442
+ }
443
+ try {
444
+ if (response.body) {
445
+ if (opts.sendStream === false) {
446
+ const data = new Uint8Array(await response.arrayBuffer());
447
+ event.node.res.end(data);
448
+ } else {
449
+ for await (const chunk of response.body) {
450
+ event.node.res.write(chunk);
451
+ }
452
+ event.node.res.end();
453
+ }
454
+ }
455
+ } catch (error) {
456
+ event.node.res.end();
457
+ throw error;
458
+ }
459
+ }
460
+
261
461
  const defer = typeof setImmediate !== "undefined" ? setImmediate : (fn) => fn();
262
462
  function send(event, data, type) {
263
463
  if (type) {
@@ -270,6 +470,25 @@ function send(event, data, type) {
270
470
  });
271
471
  });
272
472
  }
473
+ function sendNoContent(event, code = 204) {
474
+ event.node.res.statusCode = code;
475
+ if (event.node.res.statusCode === 204) {
476
+ event.node.res.removeHeader("content-length");
477
+ }
478
+ event.node.res.end();
479
+ }
480
+ function setResponseStatus(event, code, text) {
481
+ event.node.res.statusCode = code;
482
+ if (text) {
483
+ event.node.res.statusMessage = text;
484
+ }
485
+ }
486
+ function getResponseStatus(event) {
487
+ return event.node.res.statusCode;
488
+ }
489
+ function getResponseStatusText(event) {
490
+ return event.node.res.statusMessage;
491
+ }
273
492
  function defaultContentType(event, type) {
274
493
  if (type && !event.node.res.getHeader("content-type")) {
275
494
  event.node.res.setHeader("content-type", type);
@@ -371,101 +590,82 @@ ${header}: ${value}`;
371
590
  }
372
591
  }
373
592
 
374
- function parseCookies(event) {
375
- return cookieEs.parse(event.node.req.headers.cookie || "");
376
- }
377
- function getCookie(event, name) {
378
- return parseCookies(event)[name];
379
- }
380
- function setCookie(event, name, value, serializeOptions) {
381
- const cookieStr = cookieEs.serialize(name, value, {
382
- path: "/",
383
- ...serializeOptions
384
- });
385
- appendHeader(event, "Set-Cookie", cookieStr);
386
- }
387
- function deleteCookie(event, name, serializeOptions) {
388
- setCookie(event, name, "", {
389
- ...serializeOptions,
390
- maxAge: 0
391
- });
392
- }
393
-
394
- const PayloadMethods = /* @__PURE__ */ new Set(["PATCH", "POST", "PUT", "DELETE"]);
395
- const ignoredHeaders = /* @__PURE__ */ new Set([
396
- "transfer-encoding",
397
- "connection",
398
- "keep-alive",
399
- "upgrade",
400
- "expect"
401
- ]);
402
- async function proxyRequest(event, target, opts = {}) {
403
- const method = getMethod(event);
404
- let body;
405
- if (PayloadMethods.has(method)) {
406
- body = await readRawBody(event).catch(() => void 0);
407
- }
408
- const headers = /* @__PURE__ */ Object.create(null);
409
- const reqHeaders = getRequestHeaders(event);
410
- for (const name in reqHeaders) {
411
- if (!ignoredHeaders.has(name)) {
412
- headers[name] = reqHeaders[name];
413
- }
414
- }
415
- if (opts.fetchOptions?.headers) {
416
- Object.assign(headers, opts.fetchOptions.headers);
417
- }
418
- if (opts.headers) {
419
- Object.assign(headers, opts.headers);
420
- }
421
- return sendProxy(event, target, {
422
- ...opts,
423
- fetchOptions: {
424
- headers,
425
- method,
426
- body,
427
- ...opts.fetchOptions
593
+ const DEFAULT_NAME = "h3";
594
+ const DEFAULT_COOKIE = {
595
+ path: "/",
596
+ secure: true,
597
+ httpOnly: true
598
+ };
599
+ async function useSession(event, config) {
600
+ const sessionName = config.name || DEFAULT_NAME;
601
+ await getSession(event, config);
602
+ const sessionManager = {
603
+ get id() {
604
+ return event.context.sessions?.[sessionName]?.id;
605
+ },
606
+ get data() {
607
+ return event.context.sessions?.[sessionName]?.data || {};
608
+ },
609
+ update: async (update) => {
610
+ await updateSession(event, config, update);
611
+ return sessionManager;
612
+ },
613
+ clear: async () => {
614
+ await clearSession(event, config);
615
+ return sessionManager;
428
616
  }
429
- });
617
+ };
618
+ return sessionManager;
430
619
  }
431
- async function sendProxy(event, target, opts = {}) {
432
- const _fetch = opts.fetch || globalThis.fetch;
433
- if (!_fetch) {
434
- throw new Error(
435
- "fetch is not available. Try importing `node-fetch-native/polyfill` for Node.js."
620
+ async function getSession(event, config) {
621
+ const sessionName = config.name || DEFAULT_NAME;
622
+ if (!event.context.sessions) {
623
+ event.context.sessions = /* @__PURE__ */ Object.create(null);
624
+ }
625
+ if (event.context.sessions[sessionName]) {
626
+ return event.context.sessions[sessionName];
627
+ }
628
+ const session = { id: "", data: /* @__PURE__ */ Object.create(null) };
629
+ event.context.sessions[sessionName] = session;
630
+ const reqCookie = getCookie(event, sessionName);
631
+ if (!reqCookie) {
632
+ session.id = (config.crypto || crypto).randomUUID();
633
+ await updateSession(event, config);
634
+ } else {
635
+ const unsealed = await ironWebcrypto.unseal(
636
+ config.crypto || crypto,
637
+ reqCookie,
638
+ config.password,
639
+ config.seal || ironWebcrypto.defaults
436
640
  );
641
+ Object.assign(session, unsealed);
437
642
  }
438
- const response = await _fetch(target, {
439
- headers: opts.headers,
440
- ...opts.fetchOptions
441
- });
442
- event.node.res.statusCode = response.status;
443
- event.node.res.statusMessage = response.statusText;
444
- for (const [key, value] of response.headers.entries()) {
445
- if (key === "content-encoding") {
446
- continue;
447
- }
448
- if (key === "content-length") {
449
- continue;
450
- }
451
- event.node.res.setHeader(key, value);
452
- }
453
- try {
454
- if (response.body) {
455
- if (opts.sendStream === false) {
456
- const data = new Uint8Array(await response.arrayBuffer());
457
- event.node.res.end(data);
458
- } else {
459
- for await (const chunk of response.body) {
460
- event.node.res.write(chunk);
461
- }
462
- event.node.res.end();
463
- }
464
- }
465
- } catch (error) {
466
- event.node.res.end();
467
- throw error;
643
+ return session;
644
+ }
645
+ async function updateSession(event, config, update) {
646
+ const sessionName = config.name || DEFAULT_NAME;
647
+ const session = event.context.sessions?.[sessionName] || await getSession(event, config);
648
+ if (typeof update === "function") {
649
+ update = update(session.data);
650
+ }
651
+ if (update) {
652
+ Object.assign(session.data, update);
653
+ }
654
+ const sealed = await ironWebcrypto.seal(
655
+ config.crypto || crypto,
656
+ session,
657
+ config.password,
658
+ config.seal || ironWebcrypto.defaults
659
+ );
660
+ setCookie(event, sessionName, sealed, config.cookie || DEFAULT_COOKIE);
661
+ return session;
662
+ }
663
+ async function clearSession(event, config) {
664
+ const sessionName = config.name || DEFAULT_NAME;
665
+ if (event.context.sessions?.[sessionName]) {
666
+ delete event.context.sessions[sessionName];
468
667
  }
668
+ await setCookie(event, sessionName, "", config.cookie || DEFAULT_COOKIE);
469
669
  }
470
670
 
471
671
  class H3Headers {
@@ -521,6 +721,7 @@ class H3Headers {
521
721
 
522
722
  class H3Response {
523
723
  constructor(body = null, init = {}) {
724
+ // TODO: yet to implement
524
725
  this.body = null;
525
726
  this.type = "default";
526
727
  this.bodyUsed = false;
@@ -565,12 +766,15 @@ class H3Event {
565
766
  get path() {
566
767
  return this.req.url;
567
768
  }
769
+ /** @deprecated Please use `event.node.req` instead. **/
568
770
  get req() {
569
771
  return this.node.req;
570
772
  }
773
+ /** @deprecated Please use `event.node.res` instead. **/
571
774
  get res() {
572
775
  return this.node.res;
573
776
  }
777
+ // Implementation of FetchEvent
574
778
  respondWith(r) {
575
779
  Promise.resolve(r).then((_response) => {
576
780
  if (this.res.writableEnded) {
@@ -676,6 +880,7 @@ function createApp(options = {}) {
676
880
  const stack = [];
677
881
  const handler = createAppEventHandler(stack, options);
678
882
  const app = {
883
+ // @ts-ignore
679
884
  use: (arg1, arg2, arg3) => use(app, arg1, arg2, arg3),
680
885
  handler,
681
886
  stack,
@@ -923,6 +1128,7 @@ exports.appendResponseHeader = appendResponseHeader;
923
1128
  exports.appendResponseHeaders = appendResponseHeaders;
924
1129
  exports.assertMethod = assertMethod;
925
1130
  exports.callNodeListener = callNodeListener;
1131
+ exports.clearSession = clearSession;
926
1132
  exports.createApp = createApp;
927
1133
  exports.createAppEventHandler = createAppEventHandler;
928
1134
  exports.createError = createError;
@@ -946,8 +1152,11 @@ exports.getRequestHeader = getRequestHeader;
946
1152
  exports.getRequestHeaders = getRequestHeaders;
947
1153
  exports.getResponseHeader = getResponseHeader;
948
1154
  exports.getResponseHeaders = getResponseHeaders;
1155
+ exports.getResponseStatus = getResponseStatus;
1156
+ exports.getResponseStatusText = getResponseStatusText;
949
1157
  exports.getRouterParam = getRouterParam;
950
1158
  exports.getRouterParams = getRouterParams;
1159
+ exports.getSession = getSession;
951
1160
  exports.handleCacheHeaders = handleCacheHeaders;
952
1161
  exports.isError = isError;
953
1162
  exports.isEvent = isEvent;
@@ -959,9 +1168,11 @@ exports.parseCookies = parseCookies;
959
1168
  exports.promisifyNodeListener = promisifyNodeListener;
960
1169
  exports.proxyRequest = proxyRequest;
961
1170
  exports.readBody = readBody;
1171
+ exports.readMultipartFormData = readMultipartFormData;
962
1172
  exports.readRawBody = readRawBody;
963
1173
  exports.send = send;
964
1174
  exports.sendError = sendError;
1175
+ exports.sendNoContent = sendNoContent;
965
1176
  exports.sendProxy = sendProxy;
966
1177
  exports.sendRedirect = sendRedirect;
967
1178
  exports.sendStream = sendStream;
@@ -970,8 +1181,11 @@ exports.setHeader = setHeader;
970
1181
  exports.setHeaders = setHeaders;
971
1182
  exports.setResponseHeader = setResponseHeader;
972
1183
  exports.setResponseHeaders = setResponseHeaders;
1184
+ exports.setResponseStatus = setResponseStatus;
973
1185
  exports.toEventHandler = toEventHandler;
974
1186
  exports.toNodeListener = toNodeListener;
1187
+ exports.updateSession = updateSession;
975
1188
  exports.use = use;
976
1189
  exports.useBase = useBase;
1190
+ exports.useSession = useSession;
977
1191
  exports.writeEarlyHints = writeEarlyHints;
package/dist/index.d.ts CHANGED
@@ -1,11 +1,38 @@
1
+ import { SealOptions } from 'iron-webcrypto';
2
+ import { CookieSerializeOptions } from 'cookie-es';
1
3
  import { IncomingMessage, ServerResponse, OutgoingMessage } from 'node:http';
2
4
  export { IncomingMessage as NodeIncomingMessage, ServerResponse as NodeServerResponse } from 'node:http';
3
- import { CookieSerializeOptions } from 'cookie-es';
4
5
  import * as ufo from 'ufo';
5
6
 
7
+ type SessionDataT = Record<string, string | number | boolean>;
8
+ type SessionData<T extends SessionDataT = SessionDataT> = T;
9
+ interface Session<T extends SessionDataT = SessionDataT> {
10
+ id: string;
11
+ data: SessionData<T>;
12
+ }
13
+ interface SessionConfig {
14
+ password: string;
15
+ name?: string;
16
+ cookie?: CookieSerializeOptions;
17
+ seal?: SealOptions;
18
+ crypto?: Crypto;
19
+ }
20
+ declare function useSession<T extends SessionDataT = SessionDataT>(event: H3Event, config: SessionConfig): Promise<{
21
+ readonly id: string | undefined;
22
+ readonly data: SessionDataT;
23
+ update: (update: SessionUpdate<T>) => Promise<any>;
24
+ clear: () => Promise<any>;
25
+ }>;
26
+ declare function getSession<T extends SessionDataT = SessionDataT>(event: H3Event, config: SessionConfig): Promise<Session<T>>;
27
+ type SessionUpdate<T extends SessionDataT = SessionDataT> = Partial<SessionData<T>> | ((oldData: SessionData<T>) => Partial<SessionData<T>> | undefined);
28
+ declare function updateSession<T extends SessionDataT = SessionDataT>(event: H3Event, config: SessionConfig, update?: SessionUpdate<T>): Promise<Session<T>>;
29
+ declare function clearSession(event: H3Event, config: SessionConfig): Promise<void>;
30
+
6
31
  type HTTPMethod = "GET" | "HEAD" | "PATCH" | "POST" | "PUT" | "DELETE" | "CONNECT" | "OPTIONS" | "TRACE";
7
32
  type Encoding = false | "ascii" | "utf8" | "utf-8" | "utf16le" | "ucs2" | "ucs-2" | "base64" | "latin1" | "binary" | "hex";
8
33
  interface H3EventContext extends Record<string, any> {
34
+ params?: Record<string, string>;
35
+ sessions?: Record<string, Session>;
9
36
  }
10
37
  type EventHandlerResponse<T = any> = T | Promise<T>;
11
38
  interface EventHandler<T = any> {
@@ -139,7 +166,7 @@ declare function createAppEventHandler(stack: Stack, options: AppOptions): Event
139
166
  */
140
167
  declare class H3Error extends Error {
141
168
  static __h3_error__: boolean;
142
- toJSON(): Pick<H3Error, "statusCode" | "statusMessage" | "data" | "message">;
169
+ toJSON(): Pick<H3Error, "data" | "statusCode" | "statusMessage" | "message">;
143
170
  statusCode: number;
144
171
  fatal: boolean;
145
172
  unhandled: boolean;
@@ -171,6 +198,13 @@ declare function isError(input: any): input is H3Error;
171
198
 
172
199
  declare function useBase(base: string, handler: EventHandler): EventHandler;
173
200
 
201
+ interface MultiPartData {
202
+ data: Buffer;
203
+ name?: string;
204
+ filename?: string;
205
+ type?: string;
206
+ }
207
+
174
208
  /**
175
209
  * Reads body of the request and returns encoded raw string (default) or `Buffer` if encoding if falsy.
176
210
  * @param event {H3Event} H3 event or req passed by h3 handler
@@ -191,6 +225,7 @@ declare function readRawBody<E extends Encoding = "utf8">(event: H3Event, encodi
191
225
  * ```
192
226
  */
193
227
  declare function readBody<T = any>(event: H3Event): Promise<T>;
228
+ declare function readMultipartFormData(event: H3Event): Promise<MultiPartData[] | undefined>;
194
229
 
195
230
  interface CacheConditions {
196
231
  modifiedTime?: string | Date;
@@ -272,6 +307,17 @@ declare function getRequestHeader(event: H3Event, name: string): RequestHeaders[
272
307
  declare const getHeader: typeof getRequestHeader;
273
308
 
274
309
  declare function send(event: H3Event, data?: any, type?: string): Promise<void>;
310
+ /**
311
+ * Respond with an empty payload.<br>
312
+ * Note that calling this function will close the connection and no other data can be sent to the client afterwards.
313
+ *
314
+ * @param event H3 event
315
+ * @param code status code to be send. By default, it is `204 No Content`.
316
+ */
317
+ declare function sendNoContent(event: H3Event, code?: number): void;
318
+ declare function setResponseStatus(event: H3Event, code: number, text?: string): void;
319
+ declare function getResponseStatus(event: H3Event): number;
320
+ declare function getResponseStatusText(event: H3Event): string;
275
321
  declare function defaultContentType(event: H3Event, type?: string): void;
276
322
  declare function sendRedirect(event: H3Event, location: string, code?: number): Promise<void>;
277
323
  declare function getResponseHeaders(event: H3Event): ReturnType<H3Event["res"]["getHeaders"]>;
@@ -303,4 +349,4 @@ interface CreateRouterOptions {
303
349
  }
304
350
  declare function createRouter(opts?: CreateRouterOptions): Router;
305
351
 
306
- export { AddRouteShortcuts, App, AppOptions, AppUse, CacheConditions, CreateRouterOptions, DynamicEventHandler, Encoding, EventHandler, EventHandlerResponse, H3Error, H3Event, H3EventContext, H3Headers, H3Response, HTTPMethod, InputLayer, InputStack, Layer, LazyEventHandler, MIMES, Matcher, NodeEventContext, NodeListener, NodeMiddleware, NodePromisifiedHandler, ProxyOptions, RequestHeaders, Router, RouterMethod, RouterUse, Stack, appendHeader, appendHeaders, appendResponseHeader, appendResponseHeaders, assertMethod, callNodeListener, createApp, createAppEventHandler, createError, createEvent, createRouter, defaultContentType, defineEventHandler, defineLazyEventHandler, defineNodeListener, defineNodeMiddleware, deleteCookie, dynamicEventHandler, eventHandler, fromNodeMiddleware, getCookie, getHeader, getHeaders, getMethod, getQuery, getRequestHeader, getRequestHeaders, getResponseHeader, getResponseHeaders, getRouterParam, getRouterParams, handleCacheHeaders, isError, isEvent, isEventHandler, isMethod, isStream, lazyEventHandler, parseCookies, promisifyNodeListener, proxyRequest, readBody, readRawBody, send, sendError, sendProxy, sendRedirect, sendStream, setCookie, setHeader, setHeaders, setResponseHeader, setResponseHeaders, toEventHandler, toNodeListener, use, useBase, writeEarlyHints };
352
+ export { AddRouteShortcuts, App, AppOptions, AppUse, CacheConditions, CreateRouterOptions, DynamicEventHandler, Encoding, EventHandler, EventHandlerResponse, H3Error, H3Event, H3EventContext, H3Headers, H3Response, HTTPMethod, InputLayer, InputStack, Layer, LazyEventHandler, MIMES, Matcher, NodeEventContext, NodeListener, NodeMiddleware, NodePromisifiedHandler, ProxyOptions, RequestHeaders, Router, RouterMethod, RouterUse, Session, SessionConfig, SessionData, Stack, appendHeader, appendHeaders, appendResponseHeader, appendResponseHeaders, assertMethod, callNodeListener, clearSession, createApp, createAppEventHandler, createError, createEvent, createRouter, defaultContentType, defineEventHandler, defineLazyEventHandler, defineNodeListener, defineNodeMiddleware, deleteCookie, dynamicEventHandler, eventHandler, fromNodeMiddleware, getCookie, getHeader, getHeaders, getMethod, getQuery, getRequestHeader, getRequestHeaders, getResponseHeader, getResponseHeaders, getResponseStatus, getResponseStatusText, getRouterParam, getRouterParams, getSession, handleCacheHeaders, isError, isEvent, isEventHandler, isMethod, isStream, lazyEventHandler, parseCookies, promisifyNodeListener, proxyRequest, readBody, readMultipartFormData, readRawBody, send, sendError, sendNoContent, sendProxy, sendRedirect, sendStream, setCookie, setHeader, setHeaders, setResponseHeader, setResponseHeaders, setResponseStatus, toEventHandler, toNodeListener, updateSession, use, useBase, useSession, writeEarlyHints };
package/dist/index.mjs CHANGED
@@ -1,7 +1,9 @@
1
1
  import { withoutTrailingSlash, withoutBase, getQuery as getQuery$1 } from 'ufo';
2
2
  import { createRouter as createRouter$1 } from 'radix3';
3
3
  import destr from 'destr';
4
- import { parse, serialize } from 'cookie-es';
4
+ import { parse as parse$1, serialize } from 'cookie-es';
5
+ import { unseal, defaults, seal } from 'iron-webcrypto';
6
+ import crypto from 'uncrypto';
5
7
 
6
8
  function useBase(base, handler) {
7
9
  base = withoutTrailingSlash(base);
@@ -15,6 +17,83 @@ function useBase(base, handler) {
15
17
  });
16
18
  }
17
19
 
20
+ function parse(multipartBodyBuffer, boundary) {
21
+ let lastline = "";
22
+ let state = 0 /* INIT */;
23
+ let buffer = [];
24
+ const allParts = [];
25
+ let currentPartHeaders = [];
26
+ for (let i = 0; i < multipartBodyBuffer.length; i++) {
27
+ const prevByte = i > 0 ? multipartBodyBuffer[i - 1] : null;
28
+ const currByte = multipartBodyBuffer[i];
29
+ const newLineChar = currByte === 10 || currByte === 13;
30
+ if (!newLineChar) {
31
+ lastline += String.fromCodePoint(currByte);
32
+ }
33
+ const newLineDetected = currByte === 10 && prevByte === 13;
34
+ if (0 /* INIT */ === state && newLineDetected) {
35
+ if ("--" + boundary === lastline) {
36
+ state = 1 /* READING_HEADERS */;
37
+ }
38
+ lastline = "";
39
+ } else if (1 /* READING_HEADERS */ === state && newLineDetected) {
40
+ if (lastline.length > 0) {
41
+ const i2 = lastline.indexOf(":");
42
+ if (i2 > 0) {
43
+ const name = lastline.slice(0, i2).toLowerCase();
44
+ const value = lastline.slice(i2 + 1).trim();
45
+ currentPartHeaders.push([name, value]);
46
+ }
47
+ } else {
48
+ state = 2 /* READING_DATA */;
49
+ buffer = [];
50
+ }
51
+ lastline = "";
52
+ } else if (2 /* READING_DATA */ === state) {
53
+ if (lastline.length > boundary.length + 4) {
54
+ lastline = "";
55
+ }
56
+ if ("--" + boundary === lastline) {
57
+ const j = buffer.length - lastline.length;
58
+ const part = buffer.slice(0, j - 1);
59
+ allParts.push(process(part, currentPartHeaders));
60
+ buffer = [];
61
+ currentPartHeaders = [];
62
+ lastline = "";
63
+ state = 3 /* READING_PART_SEPARATOR */;
64
+ } else {
65
+ buffer.push(currByte);
66
+ }
67
+ if (newLineDetected) {
68
+ lastline = "";
69
+ }
70
+ } else if (3 /* READING_PART_SEPARATOR */ === state && newLineDetected) {
71
+ state = 1 /* READING_HEADERS */;
72
+ }
73
+ }
74
+ return allParts;
75
+ }
76
+ function process(data, headers) {
77
+ const dataObj = {};
78
+ const contentDispositionHeader = headers.find((h) => h[0] === "content-disposition")?.[1] || "";
79
+ for (const i of contentDispositionHeader.split(";")) {
80
+ const s = i.split("=");
81
+ if (s.length !== 2) {
82
+ continue;
83
+ }
84
+ const key = (s[0] || "").trim();
85
+ if (key === "name" || key === "filename") {
86
+ dataObj[key] = (s[1] || "").trim().replace(/"/g, "");
87
+ }
88
+ }
89
+ const contentType = headers.find((h) => h[0] === "content-type")?.[1] || "";
90
+ if (contentType) {
91
+ dataObj.type = contentType;
92
+ }
93
+ dataObj.data = Buffer.from(data);
94
+ return dataObj;
95
+ }
96
+
18
97
  class H3Error extends Error {
19
98
  constructor() {
20
99
  super(...arguments);
@@ -47,6 +126,7 @@ function createError(input) {
47
126
  }
48
127
  const err = new H3Error(
49
128
  input.message ?? input.statusMessage,
129
+ // @ts-ignore
50
130
  input.cause ? { cause: input.cause } : void 0
51
131
  );
52
132
  if ("stack" in input) {
@@ -220,6 +300,21 @@ async function readBody(event) {
220
300
  event.node.req[ParsedBodySymbol] = json;
221
301
  return json;
222
302
  }
303
+ async function readMultipartFormData(event) {
304
+ const contentType = getRequestHeader(event, "content-type");
305
+ if (!contentType || !contentType.startsWith("multipart/form-data")) {
306
+ return;
307
+ }
308
+ const boundary = contentType.match(/boundary=([^;]*)(;|$)/i)?.[1];
309
+ if (!boundary) {
310
+ return;
311
+ }
312
+ const body = await readRawBody(event, false);
313
+ if (!body) {
314
+ return;
315
+ }
316
+ return parse(body, boundary);
317
+ }
223
318
 
224
319
  function handleCacheHeaders(event, opts) {
225
320
  const cacheControls = ["public", ...opts.cacheControls || []];
@@ -256,6 +351,111 @@ const MIMES = {
256
351
  json: "application/json"
257
352
  };
258
353
 
354
+ function parseCookies(event) {
355
+ return parse$1(event.node.req.headers.cookie || "");
356
+ }
357
+ function getCookie(event, name) {
358
+ return parseCookies(event)[name];
359
+ }
360
+ function setCookie(event, name, value, serializeOptions) {
361
+ const cookieStr = serialize(name, value, {
362
+ path: "/",
363
+ ...serializeOptions
364
+ });
365
+ let setCookies = event.node.res.getHeader("set-cookie");
366
+ if (!Array.isArray(setCookies)) {
367
+ setCookies = [setCookies];
368
+ }
369
+ setCookies = setCookies.filter((cookieValue) => {
370
+ return cookieValue && !cookieValue.startsWith(name + "=");
371
+ });
372
+ event.node.res.setHeader("set-cookie", [...setCookies, cookieStr]);
373
+ }
374
+ function deleteCookie(event, name, serializeOptions) {
375
+ setCookie(event, name, "", {
376
+ ...serializeOptions,
377
+ maxAge: 0
378
+ });
379
+ }
380
+
381
+ const PayloadMethods = /* @__PURE__ */ new Set(["PATCH", "POST", "PUT", "DELETE"]);
382
+ const ignoredHeaders = /* @__PURE__ */ new Set([
383
+ "transfer-encoding",
384
+ "connection",
385
+ "keep-alive",
386
+ "upgrade",
387
+ "expect",
388
+ "host"
389
+ ]);
390
+ async function proxyRequest(event, target, opts = {}) {
391
+ const method = getMethod(event);
392
+ let body;
393
+ if (PayloadMethods.has(method)) {
394
+ body = await readRawBody(event).catch(() => void 0);
395
+ }
396
+ const headers = /* @__PURE__ */ Object.create(null);
397
+ const reqHeaders = getRequestHeaders(event);
398
+ for (const name in reqHeaders) {
399
+ if (!ignoredHeaders.has(name)) {
400
+ headers[name] = reqHeaders[name];
401
+ }
402
+ }
403
+ if (opts.fetchOptions?.headers) {
404
+ Object.assign(headers, opts.fetchOptions.headers);
405
+ }
406
+ if (opts.headers) {
407
+ Object.assign(headers, opts.headers);
408
+ }
409
+ return sendProxy(event, target, {
410
+ ...opts,
411
+ fetchOptions: {
412
+ headers,
413
+ method,
414
+ body,
415
+ ...opts.fetchOptions
416
+ }
417
+ });
418
+ }
419
+ async function sendProxy(event, target, opts = {}) {
420
+ const _fetch = opts.fetch || globalThis.fetch;
421
+ if (!_fetch) {
422
+ throw new Error(
423
+ "fetch is not available. Try importing `node-fetch-native/polyfill` for Node.js."
424
+ );
425
+ }
426
+ const response = await _fetch(target, {
427
+ headers: opts.headers,
428
+ ...opts.fetchOptions
429
+ });
430
+ event.node.res.statusCode = response.status;
431
+ event.node.res.statusMessage = response.statusText;
432
+ for (const [key, value] of response.headers.entries()) {
433
+ if (key === "content-encoding") {
434
+ continue;
435
+ }
436
+ if (key === "content-length") {
437
+ continue;
438
+ }
439
+ event.node.res.setHeader(key, value);
440
+ }
441
+ try {
442
+ if (response.body) {
443
+ if (opts.sendStream === false) {
444
+ const data = new Uint8Array(await response.arrayBuffer());
445
+ event.node.res.end(data);
446
+ } else {
447
+ for await (const chunk of response.body) {
448
+ event.node.res.write(chunk);
449
+ }
450
+ event.node.res.end();
451
+ }
452
+ }
453
+ } catch (error) {
454
+ event.node.res.end();
455
+ throw error;
456
+ }
457
+ }
458
+
259
459
  const defer = typeof setImmediate !== "undefined" ? setImmediate : (fn) => fn();
260
460
  function send(event, data, type) {
261
461
  if (type) {
@@ -268,6 +468,25 @@ function send(event, data, type) {
268
468
  });
269
469
  });
270
470
  }
471
+ function sendNoContent(event, code = 204) {
472
+ event.node.res.statusCode = code;
473
+ if (event.node.res.statusCode === 204) {
474
+ event.node.res.removeHeader("content-length");
475
+ }
476
+ event.node.res.end();
477
+ }
478
+ function setResponseStatus(event, code, text) {
479
+ event.node.res.statusCode = code;
480
+ if (text) {
481
+ event.node.res.statusMessage = text;
482
+ }
483
+ }
484
+ function getResponseStatus(event) {
485
+ return event.node.res.statusCode;
486
+ }
487
+ function getResponseStatusText(event) {
488
+ return event.node.res.statusMessage;
489
+ }
271
490
  function defaultContentType(event, type) {
272
491
  if (type && !event.node.res.getHeader("content-type")) {
273
492
  event.node.res.setHeader("content-type", type);
@@ -369,101 +588,82 @@ ${header}: ${value}`;
369
588
  }
370
589
  }
371
590
 
372
- function parseCookies(event) {
373
- return parse(event.node.req.headers.cookie || "");
374
- }
375
- function getCookie(event, name) {
376
- return parseCookies(event)[name];
377
- }
378
- function setCookie(event, name, value, serializeOptions) {
379
- const cookieStr = serialize(name, value, {
380
- path: "/",
381
- ...serializeOptions
382
- });
383
- appendHeader(event, "Set-Cookie", cookieStr);
384
- }
385
- function deleteCookie(event, name, serializeOptions) {
386
- setCookie(event, name, "", {
387
- ...serializeOptions,
388
- maxAge: 0
389
- });
390
- }
391
-
392
- const PayloadMethods = /* @__PURE__ */ new Set(["PATCH", "POST", "PUT", "DELETE"]);
393
- const ignoredHeaders = /* @__PURE__ */ new Set([
394
- "transfer-encoding",
395
- "connection",
396
- "keep-alive",
397
- "upgrade",
398
- "expect"
399
- ]);
400
- async function proxyRequest(event, target, opts = {}) {
401
- const method = getMethod(event);
402
- let body;
403
- if (PayloadMethods.has(method)) {
404
- body = await readRawBody(event).catch(() => void 0);
405
- }
406
- const headers = /* @__PURE__ */ Object.create(null);
407
- const reqHeaders = getRequestHeaders(event);
408
- for (const name in reqHeaders) {
409
- if (!ignoredHeaders.has(name)) {
410
- headers[name] = reqHeaders[name];
411
- }
412
- }
413
- if (opts.fetchOptions?.headers) {
414
- Object.assign(headers, opts.fetchOptions.headers);
415
- }
416
- if (opts.headers) {
417
- Object.assign(headers, opts.headers);
418
- }
419
- return sendProxy(event, target, {
420
- ...opts,
421
- fetchOptions: {
422
- headers,
423
- method,
424
- body,
425
- ...opts.fetchOptions
591
+ const DEFAULT_NAME = "h3";
592
+ const DEFAULT_COOKIE = {
593
+ path: "/",
594
+ secure: true,
595
+ httpOnly: true
596
+ };
597
+ async function useSession(event, config) {
598
+ const sessionName = config.name || DEFAULT_NAME;
599
+ await getSession(event, config);
600
+ const sessionManager = {
601
+ get id() {
602
+ return event.context.sessions?.[sessionName]?.id;
603
+ },
604
+ get data() {
605
+ return event.context.sessions?.[sessionName]?.data || {};
606
+ },
607
+ update: async (update) => {
608
+ await updateSession(event, config, update);
609
+ return sessionManager;
610
+ },
611
+ clear: async () => {
612
+ await clearSession(event, config);
613
+ return sessionManager;
426
614
  }
427
- });
615
+ };
616
+ return sessionManager;
428
617
  }
429
- async function sendProxy(event, target, opts = {}) {
430
- const _fetch = opts.fetch || globalThis.fetch;
431
- if (!_fetch) {
432
- throw new Error(
433
- "fetch is not available. Try importing `node-fetch-native/polyfill` for Node.js."
618
+ async function getSession(event, config) {
619
+ const sessionName = config.name || DEFAULT_NAME;
620
+ if (!event.context.sessions) {
621
+ event.context.sessions = /* @__PURE__ */ Object.create(null);
622
+ }
623
+ if (event.context.sessions[sessionName]) {
624
+ return event.context.sessions[sessionName];
625
+ }
626
+ const session = { id: "", data: /* @__PURE__ */ Object.create(null) };
627
+ event.context.sessions[sessionName] = session;
628
+ const reqCookie = getCookie(event, sessionName);
629
+ if (!reqCookie) {
630
+ session.id = (config.crypto || crypto).randomUUID();
631
+ await updateSession(event, config);
632
+ } else {
633
+ const unsealed = await unseal(
634
+ config.crypto || crypto,
635
+ reqCookie,
636
+ config.password,
637
+ config.seal || defaults
434
638
  );
639
+ Object.assign(session, unsealed);
435
640
  }
436
- const response = await _fetch(target, {
437
- headers: opts.headers,
438
- ...opts.fetchOptions
439
- });
440
- event.node.res.statusCode = response.status;
441
- event.node.res.statusMessage = response.statusText;
442
- for (const [key, value] of response.headers.entries()) {
443
- if (key === "content-encoding") {
444
- continue;
445
- }
446
- if (key === "content-length") {
447
- continue;
448
- }
449
- event.node.res.setHeader(key, value);
450
- }
451
- try {
452
- if (response.body) {
453
- if (opts.sendStream === false) {
454
- const data = new Uint8Array(await response.arrayBuffer());
455
- event.node.res.end(data);
456
- } else {
457
- for await (const chunk of response.body) {
458
- event.node.res.write(chunk);
459
- }
460
- event.node.res.end();
461
- }
462
- }
463
- } catch (error) {
464
- event.node.res.end();
465
- throw error;
641
+ return session;
642
+ }
643
+ async function updateSession(event, config, update) {
644
+ const sessionName = config.name || DEFAULT_NAME;
645
+ const session = event.context.sessions?.[sessionName] || await getSession(event, config);
646
+ if (typeof update === "function") {
647
+ update = update(session.data);
648
+ }
649
+ if (update) {
650
+ Object.assign(session.data, update);
651
+ }
652
+ const sealed = await seal(
653
+ config.crypto || crypto,
654
+ session,
655
+ config.password,
656
+ config.seal || defaults
657
+ );
658
+ setCookie(event, sessionName, sealed, config.cookie || DEFAULT_COOKIE);
659
+ return session;
660
+ }
661
+ async function clearSession(event, config) {
662
+ const sessionName = config.name || DEFAULT_NAME;
663
+ if (event.context.sessions?.[sessionName]) {
664
+ delete event.context.sessions[sessionName];
466
665
  }
666
+ await setCookie(event, sessionName, "", config.cookie || DEFAULT_COOKIE);
467
667
  }
468
668
 
469
669
  class H3Headers {
@@ -519,6 +719,7 @@ class H3Headers {
519
719
 
520
720
  class H3Response {
521
721
  constructor(body = null, init = {}) {
722
+ // TODO: yet to implement
522
723
  this.body = null;
523
724
  this.type = "default";
524
725
  this.bodyUsed = false;
@@ -563,12 +764,15 @@ class H3Event {
563
764
  get path() {
564
765
  return this.req.url;
565
766
  }
767
+ /** @deprecated Please use `event.node.req` instead. **/
566
768
  get req() {
567
769
  return this.node.req;
568
770
  }
771
+ /** @deprecated Please use `event.node.res` instead. **/
569
772
  get res() {
570
773
  return this.node.res;
571
774
  }
775
+ // Implementation of FetchEvent
572
776
  respondWith(r) {
573
777
  Promise.resolve(r).then((_response) => {
574
778
  if (this.res.writableEnded) {
@@ -674,6 +878,7 @@ function createApp(options = {}) {
674
878
  const stack = [];
675
879
  const handler = createAppEventHandler(stack, options);
676
880
  const app = {
881
+ // @ts-ignore
677
882
  use: (arg1, arg2, arg3) => use(app, arg1, arg2, arg3),
678
883
  handler,
679
884
  stack,
@@ -910,4 +1115,4 @@ function createRouter(opts = {}) {
910
1115
  return router;
911
1116
  }
912
1117
 
913
- export { H3Error, H3Event, H3Headers, H3Response, MIMES, appendHeader, appendHeaders, appendResponseHeader, appendResponseHeaders, assertMethod, callNodeListener, createApp, createAppEventHandler, createError, createEvent, createRouter, defaultContentType, defineEventHandler, defineLazyEventHandler, defineNodeListener, defineNodeMiddleware, deleteCookie, dynamicEventHandler, eventHandler, fromNodeMiddleware, getCookie, getHeader, getHeaders, getMethod, getQuery, getRequestHeader, getRequestHeaders, getResponseHeader, getResponseHeaders, getRouterParam, getRouterParams, handleCacheHeaders, isError, isEvent, isEventHandler, isMethod, isStream, lazyEventHandler, parseCookies, promisifyNodeListener, proxyRequest, readBody, readRawBody, send, sendError, sendProxy, sendRedirect, sendStream, setCookie, setHeader, setHeaders, setResponseHeader, setResponseHeaders, toEventHandler, toNodeListener, use, useBase, writeEarlyHints };
1118
+ export { H3Error, H3Event, H3Headers, H3Response, MIMES, appendHeader, appendHeaders, appendResponseHeader, appendResponseHeaders, assertMethod, callNodeListener, clearSession, createApp, createAppEventHandler, createError, createEvent, createRouter, defaultContentType, defineEventHandler, defineLazyEventHandler, defineNodeListener, defineNodeMiddleware, deleteCookie, dynamicEventHandler, eventHandler, fromNodeMiddleware, getCookie, getHeader, getHeaders, getMethod, getQuery, getRequestHeader, getRequestHeaders, getResponseHeader, getResponseHeaders, getResponseStatus, getResponseStatusText, getRouterParam, getRouterParams, getSession, handleCacheHeaders, isError, isEvent, isEventHandler, isMethod, isStream, lazyEventHandler, parseCookies, promisifyNodeListener, proxyRequest, readBody, readMultipartFormData, readRawBody, send, sendError, sendNoContent, sendProxy, sendRedirect, sendStream, setCookie, setHeader, setHeaders, setResponseHeader, setResponseHeaders, setResponseStatus, toEventHandler, toNodeListener, updateSession, use, useBase, useSession, writeEarlyHints };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "h3",
3
- "version": "1.0.2",
3
+ "version": "1.2.0",
4
4
  "description": "Tiny JavaScript Server",
5
5
  "repository": "unjs/h3",
6
6
  "license": "MIT",
@@ -22,32 +22,34 @@
22
22
  "dependencies": {
23
23
  "cookie-es": "^0.5.0",
24
24
  "destr": "^1.2.2",
25
+ "iron-webcrypto": "^0.2.7",
25
26
  "radix3": "^1.0.0",
26
- "ufo": "^1.0.1"
27
+ "ufo": "^1.0.1",
28
+ "uncrypto": "^0.1.2"
27
29
  },
28
30
  "devDependencies": {
29
31
  "0x": "^5.4.1",
30
- "@types/express": "^4.17.14",
31
- "@types/node": "^18.11.14",
32
+ "@types/express": "^4.17.16",
33
+ "@types/node": "^18.11.18",
32
34
  "@types/supertest": "^2.0.12",
33
- "@vitest/coverage-c8": "^0.25.8",
35
+ "@vitest/coverage-c8": "^0.28.3",
34
36
  "autocannon": "^7.10.0",
35
- "changelogen": "^0.4.0",
37
+ "changelogen": "^0.4.1",
36
38
  "connect": "^3.7.0",
37
- "eslint": "^8.29.0",
38
- "eslint-config-unjs": "^0.0.3",
39
+ "eslint": "^8.33.0",
40
+ "eslint-config-unjs": "^0.1.0",
39
41
  "express": "^4.18.2",
40
42
  "get-port": "^6.1.2",
41
- "jiti": "^1.16.0",
42
- "listhen": "^1.0.1",
43
+ "jiti": "^1.16.2",
44
+ "listhen": "^1.0.2",
43
45
  "node-fetch-native": "^1.0.1",
44
- "prettier": "^2.8.1",
46
+ "prettier": "^2.8.3",
45
47
  "supertest": "^6.3.3",
46
- "typescript": "^4.9.4",
47
- "unbuild": "^1.0.2",
48
- "vitest": "^0.25.8"
48
+ "typescript": "^4.9.5",
49
+ "unbuild": "^1.1.1",
50
+ "vitest": "^0.28.3"
49
51
  },
50
- "packageManager": "pnpm@7.18.2",
52
+ "packageManager": "pnpm@7.26.3",
51
53
  "scripts": {
52
54
  "build": "unbuild",
53
55
  "dev": "vitest",