h3 1.0.2 → 1.1.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 +34 -19
- package/dist/index.cjs +122 -0
- package/dist/index.d.ts +21 -2
- package/dist/index.mjs +120 -3
- package/package.json +13 -13
package/README.md
CHANGED
|
@@ -39,28 +39,32 @@ pnpm add h3
|
|
|
39
39
|
## Usage
|
|
40
40
|
|
|
41
41
|
```ts
|
|
42
|
-
import { createServer } from
|
|
43
|
-
import { createApp, eventHandler, toNodeListener } from
|
|
42
|
+
import { createServer } from "node:http";
|
|
43
|
+
import { createApp, eventHandler, toNodeListener } from "h3";
|
|
44
44
|
|
|
45
|
-
const app = createApp()
|
|
46
|
-
app.use(
|
|
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
|
-
<
|
|
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
|
|
56
|
-
import { listen } from
|
|
57
|
+
import { createApp, eventHandler, toNodeListener } from "h3";
|
|
58
|
+
import { listen } from "listhen";
|
|
57
59
|
|
|
58
|
-
const app = createApp()
|
|
59
|
-
app.use(
|
|
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,15 +73,21 @@ 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
|
|
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
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
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
93
|
**Tip:** We can register same route more than once with different methods.
|
|
@@ -138,6 +148,11 @@ 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)`
|
|
141
156
|
|
|
142
157
|
👉 You can learn more about usage in [JSDocs Documentation](https://www.jsdocs.io/package/h3#package-functions).
|
|
143
158
|
|
package/dist/index.cjs
CHANGED
|
@@ -17,6 +17,83 @@ function useBase(base, handler) {
|
|
|
17
17
|
});
|
|
18
18
|
}
|
|
19
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
|
+
|
|
20
97
|
class H3Error extends Error {
|
|
21
98
|
constructor() {
|
|
22
99
|
super(...arguments);
|
|
@@ -49,6 +126,7 @@ function createError(input) {
|
|
|
49
126
|
}
|
|
50
127
|
const err = new H3Error(
|
|
51
128
|
input.message ?? input.statusMessage,
|
|
129
|
+
// @ts-ignore
|
|
52
130
|
input.cause ? { cause: input.cause } : void 0
|
|
53
131
|
);
|
|
54
132
|
if ("stack" in input) {
|
|
@@ -222,6 +300,21 @@ async function readBody(event) {
|
|
|
222
300
|
event.node.req[ParsedBodySymbol] = json;
|
|
223
301
|
return json;
|
|
224
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
|
+
}
|
|
225
318
|
|
|
226
319
|
function handleCacheHeaders(event, opts) {
|
|
227
320
|
const cacheControls = ["public", ...opts.cacheControls || []];
|
|
@@ -270,6 +363,25 @@ function send(event, data, type) {
|
|
|
270
363
|
});
|
|
271
364
|
});
|
|
272
365
|
}
|
|
366
|
+
function sendNoContent(event, code = 204) {
|
|
367
|
+
event.node.res.statusCode = code;
|
|
368
|
+
if (event.node.res.statusCode === 204) {
|
|
369
|
+
event.node.res.removeHeader("content-length");
|
|
370
|
+
}
|
|
371
|
+
event.node.res.end();
|
|
372
|
+
}
|
|
373
|
+
function setResponseStatus(event, code, text) {
|
|
374
|
+
event.node.res.statusCode = code;
|
|
375
|
+
if (text) {
|
|
376
|
+
event.node.res.statusMessage = text;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
function getResponseStatus(event) {
|
|
380
|
+
return event.node.res.statusCode;
|
|
381
|
+
}
|
|
382
|
+
function getResponseStatusText(event) {
|
|
383
|
+
return event.node.res.statusMessage;
|
|
384
|
+
}
|
|
273
385
|
function defaultContentType(event, type) {
|
|
274
386
|
if (type && !event.node.res.getHeader("content-type")) {
|
|
275
387
|
event.node.res.setHeader("content-type", type);
|
|
@@ -521,6 +633,7 @@ class H3Headers {
|
|
|
521
633
|
|
|
522
634
|
class H3Response {
|
|
523
635
|
constructor(body = null, init = {}) {
|
|
636
|
+
// TODO: yet to implement
|
|
524
637
|
this.body = null;
|
|
525
638
|
this.type = "default";
|
|
526
639
|
this.bodyUsed = false;
|
|
@@ -565,12 +678,15 @@ class H3Event {
|
|
|
565
678
|
get path() {
|
|
566
679
|
return this.req.url;
|
|
567
680
|
}
|
|
681
|
+
/** @deprecated Please use `event.node.req` instead. **/
|
|
568
682
|
get req() {
|
|
569
683
|
return this.node.req;
|
|
570
684
|
}
|
|
685
|
+
/** @deprecated Please use `event.node.res` instead. **/
|
|
571
686
|
get res() {
|
|
572
687
|
return this.node.res;
|
|
573
688
|
}
|
|
689
|
+
// Implementation of FetchEvent
|
|
574
690
|
respondWith(r) {
|
|
575
691
|
Promise.resolve(r).then((_response) => {
|
|
576
692
|
if (this.res.writableEnded) {
|
|
@@ -676,6 +792,7 @@ function createApp(options = {}) {
|
|
|
676
792
|
const stack = [];
|
|
677
793
|
const handler = createAppEventHandler(stack, options);
|
|
678
794
|
const app = {
|
|
795
|
+
// @ts-ignore
|
|
679
796
|
use: (arg1, arg2, arg3) => use(app, arg1, arg2, arg3),
|
|
680
797
|
handler,
|
|
681
798
|
stack,
|
|
@@ -946,6 +1063,8 @@ exports.getRequestHeader = getRequestHeader;
|
|
|
946
1063
|
exports.getRequestHeaders = getRequestHeaders;
|
|
947
1064
|
exports.getResponseHeader = getResponseHeader;
|
|
948
1065
|
exports.getResponseHeaders = getResponseHeaders;
|
|
1066
|
+
exports.getResponseStatus = getResponseStatus;
|
|
1067
|
+
exports.getResponseStatusText = getResponseStatusText;
|
|
949
1068
|
exports.getRouterParam = getRouterParam;
|
|
950
1069
|
exports.getRouterParams = getRouterParams;
|
|
951
1070
|
exports.handleCacheHeaders = handleCacheHeaders;
|
|
@@ -959,9 +1078,11 @@ exports.parseCookies = parseCookies;
|
|
|
959
1078
|
exports.promisifyNodeListener = promisifyNodeListener;
|
|
960
1079
|
exports.proxyRequest = proxyRequest;
|
|
961
1080
|
exports.readBody = readBody;
|
|
1081
|
+
exports.readMultipartFormData = readMultipartFormData;
|
|
962
1082
|
exports.readRawBody = readRawBody;
|
|
963
1083
|
exports.send = send;
|
|
964
1084
|
exports.sendError = sendError;
|
|
1085
|
+
exports.sendNoContent = sendNoContent;
|
|
965
1086
|
exports.sendProxy = sendProxy;
|
|
966
1087
|
exports.sendRedirect = sendRedirect;
|
|
967
1088
|
exports.sendStream = sendStream;
|
|
@@ -970,6 +1091,7 @@ exports.setHeader = setHeader;
|
|
|
970
1091
|
exports.setHeaders = setHeaders;
|
|
971
1092
|
exports.setResponseHeader = setResponseHeader;
|
|
972
1093
|
exports.setResponseHeaders = setResponseHeaders;
|
|
1094
|
+
exports.setResponseStatus = setResponseStatus;
|
|
973
1095
|
exports.toEventHandler = toEventHandler;
|
|
974
1096
|
exports.toNodeListener = toNodeListener;
|
|
975
1097
|
exports.use = use;
|
package/dist/index.d.ts
CHANGED
|
@@ -139,7 +139,7 @@ declare function createAppEventHandler(stack: Stack, options: AppOptions): Event
|
|
|
139
139
|
*/
|
|
140
140
|
declare class H3Error extends Error {
|
|
141
141
|
static __h3_error__: boolean;
|
|
142
|
-
toJSON(): Pick<H3Error, "
|
|
142
|
+
toJSON(): Pick<H3Error, "data" | "statusCode" | "statusMessage" | "message">;
|
|
143
143
|
statusCode: number;
|
|
144
144
|
fatal: boolean;
|
|
145
145
|
unhandled: boolean;
|
|
@@ -171,6 +171,13 @@ declare function isError(input: any): input is H3Error;
|
|
|
171
171
|
|
|
172
172
|
declare function useBase(base: string, handler: EventHandler): EventHandler;
|
|
173
173
|
|
|
174
|
+
interface MultiPartData {
|
|
175
|
+
data: Buffer;
|
|
176
|
+
name?: string;
|
|
177
|
+
filename?: string;
|
|
178
|
+
type?: string;
|
|
179
|
+
}
|
|
180
|
+
|
|
174
181
|
/**
|
|
175
182
|
* Reads body of the request and returns encoded raw string (default) or `Buffer` if encoding if falsy.
|
|
176
183
|
* @param event {H3Event} H3 event or req passed by h3 handler
|
|
@@ -191,6 +198,7 @@ declare function readRawBody<E extends Encoding = "utf8">(event: H3Event, encodi
|
|
|
191
198
|
* ```
|
|
192
199
|
*/
|
|
193
200
|
declare function readBody<T = any>(event: H3Event): Promise<T>;
|
|
201
|
+
declare function readMultipartFormData(event: H3Event): Promise<MultiPartData[] | undefined>;
|
|
194
202
|
|
|
195
203
|
interface CacheConditions {
|
|
196
204
|
modifiedTime?: string | Date;
|
|
@@ -272,6 +280,17 @@ declare function getRequestHeader(event: H3Event, name: string): RequestHeaders[
|
|
|
272
280
|
declare const getHeader: typeof getRequestHeader;
|
|
273
281
|
|
|
274
282
|
declare function send(event: H3Event, data?: any, type?: string): Promise<void>;
|
|
283
|
+
/**
|
|
284
|
+
* Respond with an empty payload.<br>
|
|
285
|
+
* Note that calling this function will close the connection and no other data can be sent to the client afterwards.
|
|
286
|
+
*
|
|
287
|
+
* @param event H3 event
|
|
288
|
+
* @param code status code to be send. By default, it is `204 No Content`.
|
|
289
|
+
*/
|
|
290
|
+
declare function sendNoContent(event: H3Event, code?: number): void;
|
|
291
|
+
declare function setResponseStatus(event: H3Event, code: number, text?: string): void;
|
|
292
|
+
declare function getResponseStatus(event: H3Event): number;
|
|
293
|
+
declare function getResponseStatusText(event: H3Event): string;
|
|
275
294
|
declare function defaultContentType(event: H3Event, type?: string): void;
|
|
276
295
|
declare function sendRedirect(event: H3Event, location: string, code?: number): Promise<void>;
|
|
277
296
|
declare function getResponseHeaders(event: H3Event): ReturnType<H3Event["res"]["getHeaders"]>;
|
|
@@ -303,4 +322,4 @@ interface CreateRouterOptions {
|
|
|
303
322
|
}
|
|
304
323
|
declare function createRouter(opts?: CreateRouterOptions): Router;
|
|
305
324
|
|
|
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 };
|
|
325
|
+
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, getResponseStatus, getResponseStatusText, getRouterParam, getRouterParams, 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, use, useBase, writeEarlyHints };
|
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
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
5
|
|
|
6
6
|
function useBase(base, handler) {
|
|
7
7
|
base = withoutTrailingSlash(base);
|
|
@@ -15,6 +15,83 @@ function useBase(base, handler) {
|
|
|
15
15
|
});
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
+
function parse(multipartBodyBuffer, boundary) {
|
|
19
|
+
let lastline = "";
|
|
20
|
+
let state = 0 /* INIT */;
|
|
21
|
+
let buffer = [];
|
|
22
|
+
const allParts = [];
|
|
23
|
+
let currentPartHeaders = [];
|
|
24
|
+
for (let i = 0; i < multipartBodyBuffer.length; i++) {
|
|
25
|
+
const prevByte = i > 0 ? multipartBodyBuffer[i - 1] : null;
|
|
26
|
+
const currByte = multipartBodyBuffer[i];
|
|
27
|
+
const newLineChar = currByte === 10 || currByte === 13;
|
|
28
|
+
if (!newLineChar) {
|
|
29
|
+
lastline += String.fromCodePoint(currByte);
|
|
30
|
+
}
|
|
31
|
+
const newLineDetected = currByte === 10 && prevByte === 13;
|
|
32
|
+
if (0 /* INIT */ === state && newLineDetected) {
|
|
33
|
+
if ("--" + boundary === lastline) {
|
|
34
|
+
state = 1 /* READING_HEADERS */;
|
|
35
|
+
}
|
|
36
|
+
lastline = "";
|
|
37
|
+
} else if (1 /* READING_HEADERS */ === state && newLineDetected) {
|
|
38
|
+
if (lastline.length > 0) {
|
|
39
|
+
const i2 = lastline.indexOf(":");
|
|
40
|
+
if (i2 > 0) {
|
|
41
|
+
const name = lastline.slice(0, i2).toLowerCase();
|
|
42
|
+
const value = lastline.slice(i2 + 1).trim();
|
|
43
|
+
currentPartHeaders.push([name, value]);
|
|
44
|
+
}
|
|
45
|
+
} else {
|
|
46
|
+
state = 2 /* READING_DATA */;
|
|
47
|
+
buffer = [];
|
|
48
|
+
}
|
|
49
|
+
lastline = "";
|
|
50
|
+
} else if (2 /* READING_DATA */ === state) {
|
|
51
|
+
if (lastline.length > boundary.length + 4) {
|
|
52
|
+
lastline = "";
|
|
53
|
+
}
|
|
54
|
+
if ("--" + boundary === lastline) {
|
|
55
|
+
const j = buffer.length - lastline.length;
|
|
56
|
+
const part = buffer.slice(0, j - 1);
|
|
57
|
+
allParts.push(process(part, currentPartHeaders));
|
|
58
|
+
buffer = [];
|
|
59
|
+
currentPartHeaders = [];
|
|
60
|
+
lastline = "";
|
|
61
|
+
state = 3 /* READING_PART_SEPARATOR */;
|
|
62
|
+
} else {
|
|
63
|
+
buffer.push(currByte);
|
|
64
|
+
}
|
|
65
|
+
if (newLineDetected) {
|
|
66
|
+
lastline = "";
|
|
67
|
+
}
|
|
68
|
+
} else if (3 /* READING_PART_SEPARATOR */ === state && newLineDetected) {
|
|
69
|
+
state = 1 /* READING_HEADERS */;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return allParts;
|
|
73
|
+
}
|
|
74
|
+
function process(data, headers) {
|
|
75
|
+
const dataObj = {};
|
|
76
|
+
const contentDispositionHeader = headers.find((h) => h[0] === "content-disposition")?.[1] || "";
|
|
77
|
+
for (const i of contentDispositionHeader.split(";")) {
|
|
78
|
+
const s = i.split("=");
|
|
79
|
+
if (s.length !== 2) {
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
const key = (s[0] || "").trim();
|
|
83
|
+
if (key === "name" || key === "filename") {
|
|
84
|
+
dataObj[key] = (s[1] || "").trim().replace(/"/g, "");
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
const contentType = headers.find((h) => h[0] === "content-type")?.[1] || "";
|
|
88
|
+
if (contentType) {
|
|
89
|
+
dataObj.type = contentType;
|
|
90
|
+
}
|
|
91
|
+
dataObj.data = Buffer.from(data);
|
|
92
|
+
return dataObj;
|
|
93
|
+
}
|
|
94
|
+
|
|
18
95
|
class H3Error extends Error {
|
|
19
96
|
constructor() {
|
|
20
97
|
super(...arguments);
|
|
@@ -47,6 +124,7 @@ function createError(input) {
|
|
|
47
124
|
}
|
|
48
125
|
const err = new H3Error(
|
|
49
126
|
input.message ?? input.statusMessage,
|
|
127
|
+
// @ts-ignore
|
|
50
128
|
input.cause ? { cause: input.cause } : void 0
|
|
51
129
|
);
|
|
52
130
|
if ("stack" in input) {
|
|
@@ -220,6 +298,21 @@ async function readBody(event) {
|
|
|
220
298
|
event.node.req[ParsedBodySymbol] = json;
|
|
221
299
|
return json;
|
|
222
300
|
}
|
|
301
|
+
async function readMultipartFormData(event) {
|
|
302
|
+
const contentType = getRequestHeader(event, "content-type");
|
|
303
|
+
if (!contentType || !contentType.startsWith("multipart/form-data")) {
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
const boundary = contentType.match(/boundary=([^;]*)(;|$)/i)?.[1];
|
|
307
|
+
if (!boundary) {
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
const body = await readRawBody(event, false);
|
|
311
|
+
if (!body) {
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
return parse(body, boundary);
|
|
315
|
+
}
|
|
223
316
|
|
|
224
317
|
function handleCacheHeaders(event, opts) {
|
|
225
318
|
const cacheControls = ["public", ...opts.cacheControls || []];
|
|
@@ -268,6 +361,25 @@ function send(event, data, type) {
|
|
|
268
361
|
});
|
|
269
362
|
});
|
|
270
363
|
}
|
|
364
|
+
function sendNoContent(event, code = 204) {
|
|
365
|
+
event.node.res.statusCode = code;
|
|
366
|
+
if (event.node.res.statusCode === 204) {
|
|
367
|
+
event.node.res.removeHeader("content-length");
|
|
368
|
+
}
|
|
369
|
+
event.node.res.end();
|
|
370
|
+
}
|
|
371
|
+
function setResponseStatus(event, code, text) {
|
|
372
|
+
event.node.res.statusCode = code;
|
|
373
|
+
if (text) {
|
|
374
|
+
event.node.res.statusMessage = text;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
function getResponseStatus(event) {
|
|
378
|
+
return event.node.res.statusCode;
|
|
379
|
+
}
|
|
380
|
+
function getResponseStatusText(event) {
|
|
381
|
+
return event.node.res.statusMessage;
|
|
382
|
+
}
|
|
271
383
|
function defaultContentType(event, type) {
|
|
272
384
|
if (type && !event.node.res.getHeader("content-type")) {
|
|
273
385
|
event.node.res.setHeader("content-type", type);
|
|
@@ -370,7 +482,7 @@ ${header}: ${value}`;
|
|
|
370
482
|
}
|
|
371
483
|
|
|
372
484
|
function parseCookies(event) {
|
|
373
|
-
return parse(event.node.req.headers.cookie || "");
|
|
485
|
+
return parse$1(event.node.req.headers.cookie || "");
|
|
374
486
|
}
|
|
375
487
|
function getCookie(event, name) {
|
|
376
488
|
return parseCookies(event)[name];
|
|
@@ -519,6 +631,7 @@ class H3Headers {
|
|
|
519
631
|
|
|
520
632
|
class H3Response {
|
|
521
633
|
constructor(body = null, init = {}) {
|
|
634
|
+
// TODO: yet to implement
|
|
522
635
|
this.body = null;
|
|
523
636
|
this.type = "default";
|
|
524
637
|
this.bodyUsed = false;
|
|
@@ -563,12 +676,15 @@ class H3Event {
|
|
|
563
676
|
get path() {
|
|
564
677
|
return this.req.url;
|
|
565
678
|
}
|
|
679
|
+
/** @deprecated Please use `event.node.req` instead. **/
|
|
566
680
|
get req() {
|
|
567
681
|
return this.node.req;
|
|
568
682
|
}
|
|
683
|
+
/** @deprecated Please use `event.node.res` instead. **/
|
|
569
684
|
get res() {
|
|
570
685
|
return this.node.res;
|
|
571
686
|
}
|
|
687
|
+
// Implementation of FetchEvent
|
|
572
688
|
respondWith(r) {
|
|
573
689
|
Promise.resolve(r).then((_response) => {
|
|
574
690
|
if (this.res.writableEnded) {
|
|
@@ -674,6 +790,7 @@ function createApp(options = {}) {
|
|
|
674
790
|
const stack = [];
|
|
675
791
|
const handler = createAppEventHandler(stack, options);
|
|
676
792
|
const app = {
|
|
793
|
+
// @ts-ignore
|
|
677
794
|
use: (arg1, arg2, arg3) => use(app, arg1, arg2, arg3),
|
|
678
795
|
handler,
|
|
679
796
|
stack,
|
|
@@ -910,4 +1027,4 @@ function createRouter(opts = {}) {
|
|
|
910
1027
|
return router;
|
|
911
1028
|
}
|
|
912
1029
|
|
|
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 };
|
|
1030
|
+
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, getResponseStatus, getResponseStatusText, getRouterParam, getRouterParams, 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, use, useBase, writeEarlyHints };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "h3",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Tiny JavaScript Server",
|
|
5
5
|
"repository": "unjs/h3",
|
|
6
6
|
"license": "MIT",
|
|
@@ -27,27 +27,27 @@
|
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
29
|
"0x": "^5.4.1",
|
|
30
|
-
"@types/express": "^4.17.
|
|
31
|
-
"@types/node": "^18.11.
|
|
30
|
+
"@types/express": "^4.17.16",
|
|
31
|
+
"@types/node": "^18.11.18",
|
|
32
32
|
"@types/supertest": "^2.0.12",
|
|
33
|
-
"@vitest/coverage-c8": "^0.
|
|
33
|
+
"@vitest/coverage-c8": "^0.28.2",
|
|
34
34
|
"autocannon": "^7.10.0",
|
|
35
|
-
"changelogen": "^0.4.
|
|
35
|
+
"changelogen": "^0.4.1",
|
|
36
36
|
"connect": "^3.7.0",
|
|
37
|
-
"eslint": "^8.
|
|
38
|
-
"eslint-config-unjs": "^0.0
|
|
37
|
+
"eslint": "^8.32.0",
|
|
38
|
+
"eslint-config-unjs": "^0.1.0",
|
|
39
39
|
"express": "^4.18.2",
|
|
40
40
|
"get-port": "^6.1.2",
|
|
41
|
-
"jiti": "^1.16.
|
|
42
|
-
"listhen": "^1.0.
|
|
41
|
+
"jiti": "^1.16.2",
|
|
42
|
+
"listhen": "^1.0.2",
|
|
43
43
|
"node-fetch-native": "^1.0.1",
|
|
44
|
-
"prettier": "^2.8.
|
|
44
|
+
"prettier": "^2.8.3",
|
|
45
45
|
"supertest": "^6.3.3",
|
|
46
46
|
"typescript": "^4.9.4",
|
|
47
|
-
"unbuild": "^1.
|
|
48
|
-
"vitest": "^0.
|
|
47
|
+
"unbuild": "^1.1.1",
|
|
48
|
+
"vitest": "^0.28.2"
|
|
49
49
|
},
|
|
50
|
-
"packageManager": "pnpm@7.
|
|
50
|
+
"packageManager": "pnpm@7.26.0",
|
|
51
51
|
"scripts": {
|
|
52
52
|
"build": "unbuild",
|
|
53
53
|
"dev": "vitest",
|