h3 0.8.4 → 0.8.6

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
@@ -136,6 +136,7 @@ H3 has concept of compasable utilities that accept `event` (from `eventHandler((
136
136
  - `isMethod(event, expected, allowHead?)`
137
137
  - `assertMethod(event, expected, allowHead?)`
138
138
  - `createError({ statusCode, statusMessage, data? })`
139
+ - `sendProxy(event, { target, headers?, fetchOptions?, fetch?, sendStream? })`
139
140
 
140
141
  👉 You can learn more about usage in [JSDocs Documentation](https://www.jsdocs.io/package/h3#package-functions).
141
142
 
package/dist/index.cjs CHANGED
@@ -25,6 +25,19 @@ class H3Error extends Error {
25
25
  this.unhandled = false;
26
26
  this.statusMessage = void 0;
27
27
  }
28
+ toJSON() {
29
+ const obj = {
30
+ message: this.message,
31
+ statusCode: this.statusCode
32
+ };
33
+ if (this.statusMessage) {
34
+ obj.statusMessage = this.statusMessage;
35
+ }
36
+ if (this.data !== void 0) {
37
+ obj.data = this.data;
38
+ }
39
+ return obj;
40
+ }
28
41
  }
29
42
  H3Error.__h3_error__ = true;
30
43
  function createError(input) {
@@ -47,14 +60,18 @@ function createError(input) {
47
60
  }
48
61
  }
49
62
  }
63
+ if (input.data) {
64
+ err.data = input.data;
65
+ }
50
66
  if (input.statusCode) {
51
67
  err.statusCode = input.statusCode;
68
+ } else if (input.status) {
69
+ err.statusCode = input.status;
52
70
  }
53
71
  if (input.statusMessage) {
54
72
  err.statusMessage = input.statusMessage;
55
- }
56
- if (input.data) {
57
- err.data = input.data;
73
+ } else if (input.statusText) {
74
+ err.statusMessage = input.statusText;
58
75
  }
59
76
  if (input.fatal !== void 0) {
60
77
  err.fatal = input.fatal;
@@ -133,7 +150,12 @@ function assertMethod(event, expected, allowHead) {
133
150
  }
134
151
  }
135
152
  function getRequestHeaders(event) {
136
- return event.req.headers;
153
+ const _headers = {};
154
+ for (const key in event.req.headers) {
155
+ const val = event.req.headers[key];
156
+ _headers[key] = Array.isArray(val) ? val.filter(Boolean).join(", ") : val;
157
+ }
158
+ return _headers;
137
159
  }
138
160
  const getHeaders = getRequestHeaders;
139
161
  function getRequestHeader(event, name) {
@@ -290,7 +312,7 @@ function sendStream(event, data) {
290
312
  const noop = () => {
291
313
  };
292
314
  function writeEarlyHints(event, hints, cb = noop) {
293
- if (!event.res.socket && !("writeEarlyHints" in event.res)) {
315
+ if (!event.res.socket) {
294
316
  cb();
295
317
  return;
296
318
  }
@@ -299,10 +321,6 @@ function writeEarlyHints(event, hints, cb = noop) {
299
321
  }
300
322
  if (hints.link) {
301
323
  hints.link = Array.isArray(hints.link) ? hints.link : hints.link.split(",");
302
- hints.link = hints.link.map((l) => l.trim().replace(/; crossorigin/g, ""));
303
- }
304
- if ("writeEarlyHints" in event.res) {
305
- return event.res.writeEarlyHints(hints, cb);
306
324
  }
307
325
  const headers = Object.entries(hints).map((e) => [e[0].toLowerCase(), e[1]]);
308
326
  if (!headers.length) {
@@ -348,6 +366,44 @@ function deleteCookie(event, name, serializeOptions) {
348
366
  });
349
367
  }
350
368
 
369
+ async function sendProxy(event, target, opts = {}) {
370
+ const _fetch = opts.fetch || globalThis.fetch;
371
+ if (!_fetch) {
372
+ throw new Error("fetch is not available. Try importing `node-fetch-native/polyfill` for Node.js.");
373
+ }
374
+ const response = await _fetch(target, {
375
+ headers: opts.headers,
376
+ ...opts.fetchOptions
377
+ });
378
+ event.res.statusCode = response.status;
379
+ event.res.statusMessage = response.statusText;
380
+ for (const [key, value] of response.headers.entries()) {
381
+ if (key === "content-encoding") {
382
+ continue;
383
+ }
384
+ if (key === "content-length") {
385
+ continue;
386
+ }
387
+ event.res.setHeader(key, value);
388
+ }
389
+ try {
390
+ if (response.body) {
391
+ if (opts.sendStream === false) {
392
+ const data = new Uint8Array(await response.arrayBuffer());
393
+ event.res.end(data);
394
+ } else {
395
+ for await (const chunk of response.body) {
396
+ event.res.write(chunk);
397
+ }
398
+ event.res.end();
399
+ }
400
+ }
401
+ } catch (err) {
402
+ event.res.end();
403
+ throw err;
404
+ }
405
+ }
406
+
351
407
  class H3Headers {
352
408
  constructor(init) {
353
409
  if (!init) {
@@ -360,6 +416,18 @@ class H3Headers {
360
416
  this._headers = Object.fromEntries(Object.entries(init).map(([key, value]) => [key.toLowerCase(), value]));
361
417
  }
362
418
  }
419
+ [Symbol.iterator]() {
420
+ return this.entries();
421
+ }
422
+ entries() {
423
+ throw Object.entries(this._headers)[Symbol.iterator]();
424
+ }
425
+ keys() {
426
+ return Object.keys(this._headers)[Symbol.iterator]();
427
+ }
428
+ values() {
429
+ throw Object.values(this._headers)[Symbol.iterator]();
430
+ }
363
431
  append(name, value) {
364
432
  const _name = name.toLowerCase();
365
433
  this.set(_name, [this.get(_name), value].filter(Boolean).join(", "));
@@ -699,7 +767,11 @@ function createRouter(opts = {}) {
699
767
  router[method] = (path, handle) => router.add(path, handle, method);
700
768
  }
701
769
  router.handler = eventHandler((event) => {
702
- const path = new URL(event.req.url || "/", "http://localhost").pathname;
770
+ let path = event.req.url || "/";
771
+ const qIndex = path.indexOf("?");
772
+ if (qIndex !== -1) {
773
+ path = path.substring(0, qIndex);
774
+ }
703
775
  const matched = _router.lookup(path);
704
776
  if (!matched) {
705
777
  if (opts.preemtive) {
@@ -777,6 +849,7 @@ exports.readBody = readBody;
777
849
  exports.readRawBody = readRawBody;
778
850
  exports.send = send;
779
851
  exports.sendError = sendError;
852
+ exports.sendProxy = sendProxy;
780
853
  exports.sendRedirect = sendRedirect;
781
854
  exports.sendStream = sendStream;
782
855
  exports.setCookie = setCookie;
package/dist/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { IncomingMessage, ServerResponse, OutgoingMessage } from 'http';
2
2
  export { IncomingMessage as NodeIncomingMessage, ServerResponse as NodeServerResponse } from 'http';
3
3
  import { CookieSerializeOptions } from 'cookie-es';
4
+ import { RequestHeaders as RequestHeaders$1 } from 'src/types';
4
5
  import * as ufo from 'ufo';
5
6
 
6
7
  declare type HTTPMethod = 'GET' | 'HEAD' | 'PATCH' | 'POST' | 'PUT' | 'DELETE' | 'CONNECT' | 'OPTIONS' | 'TRACE';
@@ -13,6 +14,9 @@ interface EventHandler<T = any> {
13
14
  (event: H3Event): EventHandlerResponse<T>;
14
15
  }
15
16
  declare type LazyEventHandler = () => EventHandler | Promise<EventHandler>;
17
+ declare type RequestHeaders = {
18
+ [name: string]: string | undefined;
19
+ };
16
20
 
17
21
  declare type NodeListener = (req: IncomingMessage, res: ServerResponse) => void;
18
22
  declare type NodePromisifiedHandler = (req: IncomingMessage, res: ServerResponse) => Promise<any>;
@@ -27,6 +31,10 @@ declare function callNodeListener(handler: NodeMiddleware, req: IncomingMessage,
27
31
  declare class H3Headers implements Headers {
28
32
  _headers: Record<string, string>;
29
33
  constructor(init?: HeadersInit);
34
+ [Symbol.iterator](): IterableIterator<[string, string]>;
35
+ entries(): IterableIterator<[string, string]>;
36
+ keys(): IterableIterator<string>;
37
+ values(): IterableIterator<string>;
30
38
  append(name: string, value: string): void;
31
39
  delete(name: string): void;
32
40
  get(name: string): string | null;
@@ -124,6 +132,7 @@ declare function createAppEventHandler(stack: Stack, options: AppOptions): Event
124
132
  */
125
133
  declare class H3Error extends Error {
126
134
  static __h3_error__: boolean;
135
+ toJSON(): Pick<H3Error, "statusCode" | "statusMessage" | "data" | "message">;
127
136
  statusCode: number;
128
137
  fatal: boolean;
129
138
  unhandled: boolean;
@@ -136,7 +145,10 @@ declare class H3Error extends Error {
136
145
  * @param input {Partial<H3Error>}
137
146
  * @return {H3Error} An instance of the H3Error
138
147
  */
139
- declare function createError(input: string | Partial<H3Error>): H3Error;
148
+ declare function createError(input: string | Partial<H3Error> & {
149
+ status?: number;
150
+ statusText?: string;
151
+ }): H3Error;
140
152
  /**
141
153
  * Receive an error and return the corresponding response.<br>
142
154
  * H3 internally uses this function to handle unhandled errors.<br>
@@ -240,6 +252,14 @@ declare function setCookie(event: H3Event, name: string, value: string, serializ
240
252
  */
241
253
  declare function deleteCookie(event: H3Event, name: string, serializeOptions?: CookieSerializeOptions): void;
242
254
 
255
+ interface SendProxyOptions {
256
+ headers?: RequestHeaders$1 | HeadersInit;
257
+ fetchOptions?: RequestInit;
258
+ fetch?: typeof fetch;
259
+ sendStream?: boolean;
260
+ }
261
+ declare function sendProxy(event: H3Event, target: string, opts?: SendProxyOptions): Promise<void>;
262
+
243
263
  declare function getQuery(event: H3Event): ufo.QueryObject;
244
264
  /** @deprecated Use `h3.getQuery` */
245
265
  declare const useQuery: typeof getQuery;
@@ -250,9 +270,9 @@ declare function getMethod(event: H3Event, defaultMethod?: HTTPMethod): HTTPMeth
250
270
  declare const useMethod: typeof getMethod;
251
271
  declare function isMethod(event: H3Event, expected: HTTPMethod | HTTPMethod[], allowHead?: boolean): boolean;
252
272
  declare function assertMethod(event: H3Event, expected: HTTPMethod | HTTPMethod[], allowHead?: boolean): void;
253
- declare function getRequestHeaders(event: H3Event): H3Event['req']['headers'];
273
+ declare function getRequestHeaders(event: H3Event): RequestHeaders;
254
274
  declare const getHeaders: typeof getRequestHeaders;
255
- declare function getRequestHeader(event: H3Event, name: string): H3Event['req']['headers'][string];
275
+ declare function getRequestHeader(event: H3Event, name: string): RequestHeaders[string];
256
276
  declare const getHeader: typeof getRequestHeader;
257
277
 
258
278
  declare function send(event: H3Event, data?: any, type?: string): Promise<void>;
@@ -285,4 +305,4 @@ interface CreateRouterOptions {
285
305
  }
286
306
  declare function createRouter(opts?: CreateRouterOptions): Router;
287
307
 
288
- export { AddRouteShortcuts, App, AppOptions, AppUse, CacheConditions, CreateRouterOptions, DynamicEventHandler, Encoding, EventHandler, EventHandlerResponse, H3Error, H3Event, H3EventContext, H3Headers, H3Response, HTTPMethod, InputLayer, InputStack, Layer, LazyEventHandler, MIMES, Matcher, NodeListener, NodeMiddleware, NodePromisifiedHandler, 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, readBody, readRawBody, send, sendError, sendRedirect, sendStream, setCookie, setHeader, setHeaders, setResponseHeader, setResponseHeaders, toEventHandler, toNodeListener, use, useBase, useBody, useCookie, useCookies, useMethod, useQuery, useRawBody, writeEarlyHints };
308
+ export { AddRouteShortcuts, App, AppOptions, AppUse, CacheConditions, CreateRouterOptions, DynamicEventHandler, Encoding, EventHandler, EventHandlerResponse, H3Error, H3Event, H3EventContext, H3Headers, H3Response, HTTPMethod, InputLayer, InputStack, Layer, LazyEventHandler, MIMES, Matcher, NodeListener, NodeMiddleware, NodePromisifiedHandler, RequestHeaders, Router, RouterMethod, RouterUse, SendProxyOptions, 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, readBody, readRawBody, send, sendError, sendProxy, sendRedirect, sendStream, setCookie, setHeader, setHeaders, setResponseHeader, setResponseHeaders, toEventHandler, toNodeListener, use, useBase, useBody, useCookie, useCookies, useMethod, useQuery, useRawBody, writeEarlyHints };
package/dist/index.mjs CHANGED
@@ -23,6 +23,19 @@ class H3Error extends Error {
23
23
  this.unhandled = false;
24
24
  this.statusMessage = void 0;
25
25
  }
26
+ toJSON() {
27
+ const obj = {
28
+ message: this.message,
29
+ statusCode: this.statusCode
30
+ };
31
+ if (this.statusMessage) {
32
+ obj.statusMessage = this.statusMessage;
33
+ }
34
+ if (this.data !== void 0) {
35
+ obj.data = this.data;
36
+ }
37
+ return obj;
38
+ }
26
39
  }
27
40
  H3Error.__h3_error__ = true;
28
41
  function createError(input) {
@@ -45,14 +58,18 @@ function createError(input) {
45
58
  }
46
59
  }
47
60
  }
61
+ if (input.data) {
62
+ err.data = input.data;
63
+ }
48
64
  if (input.statusCode) {
49
65
  err.statusCode = input.statusCode;
66
+ } else if (input.status) {
67
+ err.statusCode = input.status;
50
68
  }
51
69
  if (input.statusMessage) {
52
70
  err.statusMessage = input.statusMessage;
53
- }
54
- if (input.data) {
55
- err.data = input.data;
71
+ } else if (input.statusText) {
72
+ err.statusMessage = input.statusText;
56
73
  }
57
74
  if (input.fatal !== void 0) {
58
75
  err.fatal = input.fatal;
@@ -131,7 +148,12 @@ function assertMethod(event, expected, allowHead) {
131
148
  }
132
149
  }
133
150
  function getRequestHeaders(event) {
134
- return event.req.headers;
151
+ const _headers = {};
152
+ for (const key in event.req.headers) {
153
+ const val = event.req.headers[key];
154
+ _headers[key] = Array.isArray(val) ? val.filter(Boolean).join(", ") : val;
155
+ }
156
+ return _headers;
135
157
  }
136
158
  const getHeaders = getRequestHeaders;
137
159
  function getRequestHeader(event, name) {
@@ -288,7 +310,7 @@ function sendStream(event, data) {
288
310
  const noop = () => {
289
311
  };
290
312
  function writeEarlyHints(event, hints, cb = noop) {
291
- if (!event.res.socket && !("writeEarlyHints" in event.res)) {
313
+ if (!event.res.socket) {
292
314
  cb();
293
315
  return;
294
316
  }
@@ -297,10 +319,6 @@ function writeEarlyHints(event, hints, cb = noop) {
297
319
  }
298
320
  if (hints.link) {
299
321
  hints.link = Array.isArray(hints.link) ? hints.link : hints.link.split(",");
300
- hints.link = hints.link.map((l) => l.trim().replace(/; crossorigin/g, ""));
301
- }
302
- if ("writeEarlyHints" in event.res) {
303
- return event.res.writeEarlyHints(hints, cb);
304
322
  }
305
323
  const headers = Object.entries(hints).map((e) => [e[0].toLowerCase(), e[1]]);
306
324
  if (!headers.length) {
@@ -346,6 +364,44 @@ function deleteCookie(event, name, serializeOptions) {
346
364
  });
347
365
  }
348
366
 
367
+ async function sendProxy(event, target, opts = {}) {
368
+ const _fetch = opts.fetch || globalThis.fetch;
369
+ if (!_fetch) {
370
+ throw new Error("fetch is not available. Try importing `node-fetch-native/polyfill` for Node.js.");
371
+ }
372
+ const response = await _fetch(target, {
373
+ headers: opts.headers,
374
+ ...opts.fetchOptions
375
+ });
376
+ event.res.statusCode = response.status;
377
+ event.res.statusMessage = response.statusText;
378
+ for (const [key, value] of response.headers.entries()) {
379
+ if (key === "content-encoding") {
380
+ continue;
381
+ }
382
+ if (key === "content-length") {
383
+ continue;
384
+ }
385
+ event.res.setHeader(key, value);
386
+ }
387
+ try {
388
+ if (response.body) {
389
+ if (opts.sendStream === false) {
390
+ const data = new Uint8Array(await response.arrayBuffer());
391
+ event.res.end(data);
392
+ } else {
393
+ for await (const chunk of response.body) {
394
+ event.res.write(chunk);
395
+ }
396
+ event.res.end();
397
+ }
398
+ }
399
+ } catch (err) {
400
+ event.res.end();
401
+ throw err;
402
+ }
403
+ }
404
+
349
405
  class H3Headers {
350
406
  constructor(init) {
351
407
  if (!init) {
@@ -358,6 +414,18 @@ class H3Headers {
358
414
  this._headers = Object.fromEntries(Object.entries(init).map(([key, value]) => [key.toLowerCase(), value]));
359
415
  }
360
416
  }
417
+ [Symbol.iterator]() {
418
+ return this.entries();
419
+ }
420
+ entries() {
421
+ throw Object.entries(this._headers)[Symbol.iterator]();
422
+ }
423
+ keys() {
424
+ return Object.keys(this._headers)[Symbol.iterator]();
425
+ }
426
+ values() {
427
+ throw Object.values(this._headers)[Symbol.iterator]();
428
+ }
361
429
  append(name, value) {
362
430
  const _name = name.toLowerCase();
363
431
  this.set(_name, [this.get(_name), value].filter(Boolean).join(", "));
@@ -697,7 +765,11 @@ function createRouter(opts = {}) {
697
765
  router[method] = (path, handle) => router.add(path, handle, method);
698
766
  }
699
767
  router.handler = eventHandler((event) => {
700
- const path = new URL(event.req.url || "/", "http://localhost").pathname;
768
+ let path = event.req.url || "/";
769
+ const qIndex = path.indexOf("?");
770
+ if (qIndex !== -1) {
771
+ path = path.substring(0, qIndex);
772
+ }
701
773
  const matched = _router.lookup(path);
702
774
  if (!matched) {
703
775
  if (opts.preemtive) {
@@ -726,4 +798,4 @@ function createRouter(opts = {}) {
726
798
  return router;
727
799
  }
728
800
 
729
- 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, readBody, readRawBody, send, sendError, sendRedirect, sendStream, setCookie, setHeader, setHeaders, setResponseHeader, setResponseHeaders, toEventHandler, toNodeListener, use, useBase, useBody, useCookie, useCookies, useMethod, useQuery, useRawBody, writeEarlyHints };
801
+ 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, readBody, readRawBody, send, sendError, sendProxy, sendRedirect, sendStream, setCookie, setHeader, setHeaders, setResponseHeader, setResponseHeaders, toEventHandler, toNodeListener, use, useBase, useBody, useCookie, useCookies, useMethod, useQuery, useRawBody, writeEarlyHints };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "h3",
3
- "version": "0.8.4",
3
+ "version": "0.8.6",
4
4
  "description": "Tiny JavaScript Server",
5
5
  "repository": "unjs/h3",
6
6
  "license": "MIT",
@@ -21,7 +21,7 @@
21
21
  ],
22
22
  "dependencies": {
23
23
  "cookie-es": "^0.5.0",
24
- "destr": "^1.1.1",
24
+ "destr": "^1.2.0",
25
25
  "radix3": "^0.2.1",
26
26
  "ufo": "^0.8.6"
27
27
  },
@@ -29,30 +29,31 @@
29
29
  "0x": "^5.4.1",
30
30
  "@nuxtjs/eslint-config-typescript": "^11.0.0",
31
31
  "@types/express": "^4.17.14",
32
- "@types/node": "^18.11.0",
32
+ "@types/node": "^18.11.7",
33
33
  "@types/supertest": "^2.0.12",
34
34
  "@vitest/coverage-c8": "^0.24.3",
35
35
  "autocannon": "^7.10.0",
36
36
  "changelogen": "^0.3.5",
37
37
  "connect": "^3.7.0",
38
- "eslint": "^8.25.0",
38
+ "eslint": "^8.26.0",
39
39
  "express": "^4.18.2",
40
40
  "get-port": "^6.1.2",
41
41
  "jiti": "^1.16.0",
42
42
  "listhen": "^0.3.4",
43
- "supertest": "^6.3.0",
43
+ "node-fetch-native": "^0.1.8",
44
+ "supertest": "^6.3.1",
44
45
  "typescript": "^4.8.4",
45
46
  "unbuild": "^0.9.4",
46
47
  "vitest": "^0.24.3"
47
48
  },
48
- "packageManager": "pnpm@7.13.5",
49
+ "packageManager": "pnpm@7.14.0",
49
50
  "scripts": {
50
51
  "build": "unbuild",
51
52
  "dev": "vitest",
52
53
  "lint": "eslint --ext ts,mjs,cjs .",
53
54
  "play": "jiti ./playground/index.ts",
54
55
  "profile": "0x -o -D .profile -P 'autocannon -c 100 -p 10 -d 40 http://localhost:$PORT' ./playground/server.cjs",
55
- "release": "pnpm test && pnpm test && pnpm build && changelogen --release && pnpm publish && git push --follow-tags",
56
+ "release": "pnpm test && pnpm build && changelogen --release && pnpm publish && git push --follow-tags",
56
57
  "test": "pnpm lint && vitest run --coverage"
57
58
  }
58
59
  }