h3 0.4.1 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -31,6 +31,9 @@ npm install h3
31
31
 
32
32
  # Using yarn
33
33
  yarn add h3
34
+
35
+ # Using pnpm
36
+ pnpm add h3
34
37
  ```
35
38
 
36
39
  ## Usage
package/dist/index.cjs CHANGED
@@ -11,34 +11,30 @@ function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'defau
11
11
 
12
12
  const destr__default = /*#__PURE__*/_interopDefaultLegacy(destr);
13
13
 
14
- const defineHandle = (handler) => handler;
14
+ const defineHandler = (handler) => handler;
15
+ const defineHandle = defineHandler;
15
16
  const defineMiddleware = (middleware) => middleware;
16
- function promisifyHandle(handle) {
17
+ function promisifyHandler(handler) {
17
18
  return function(req, res) {
18
- return callHandle(handle, req, res);
19
+ return callHandler(handler, req, res);
19
20
  };
20
21
  }
21
- function callHandle(handle, req, res) {
22
+ const promisifyHandle = promisifyHandler;
23
+ function callHandler(handler, req, res) {
22
24
  return new Promise((resolve, reject) => {
23
25
  const next = (err) => err ? reject(err) : resolve(void 0);
24
26
  try {
25
- const returned = handle(req, res, next);
26
- if (returned !== void 0) {
27
- resolve(returned);
28
- } else {
29
- res.once("close", next);
30
- res.once("error", next);
31
- }
27
+ return resolve(handler(req, res, next));
32
28
  } catch (err) {
33
29
  next(err);
34
30
  }
35
31
  });
36
32
  }
37
- function lazyHandle(handle, promisify) {
33
+ function defineLazyHandler(handler, promisify) {
38
34
  let _promise;
39
35
  const resolve = () => {
40
36
  if (!_promise) {
41
- _promise = Promise.resolve(handle()).then((r) => promisify ? promisifyHandle(r.default || r) : r.default || r);
37
+ _promise = Promise.resolve(handler()).then((r) => promisify ? promisifyHandler(r.default || r) : r.default || r);
42
38
  }
43
39
  return _promise;
44
40
  };
@@ -46,26 +42,91 @@ function lazyHandle(handle, promisify) {
46
42
  return resolve().then((h) => h(req, res));
47
43
  };
48
44
  }
49
- function useBase(base, handle) {
45
+ const lazyHandle = defineLazyHandler;
46
+ function useBase(base, handler) {
50
47
  base = ufo.withoutTrailingSlash(base);
51
48
  if (!base) {
52
- return handle;
49
+ return handler;
53
50
  }
54
51
  return function(req, res) {
55
52
  req.originalUrl = req.originalUrl || req.url || "/";
56
53
  req.url = ufo.withoutBase(req.url || "/", base);
57
- return handle(req, res);
54
+ return handler(req, res);
55
+ };
56
+ }
57
+
58
+ function defineEventHandler(handler) {
59
+ handler.__is_handler__ = true;
60
+ return handler;
61
+ }
62
+ function defineLazyEventHandler(factory) {
63
+ let _promise;
64
+ let _resolved;
65
+ const resolveHandler = () => {
66
+ if (_resolved) {
67
+ return Promise.resolve(_resolved);
68
+ }
69
+ if (!_promise) {
70
+ _promise = Promise.resolve(factory()).then((r) => {
71
+ const handler = r.default || r;
72
+ if (typeof handler !== "function") {
73
+ throw new TypeError("Invalid lazy handler result. It should be a function:", handler);
74
+ }
75
+ _resolved = toEventHandler(r.default || r);
76
+ return _resolved;
77
+ });
78
+ }
79
+ return _promise;
80
+ };
81
+ return defineEventHandler((event) => {
82
+ if (_resolved) {
83
+ return _resolved(event);
84
+ }
85
+ return resolveHandler().then((handler) => handler(event));
86
+ });
87
+ }
88
+ function isEventHandler(input) {
89
+ return "__is_handler__" in input;
90
+ }
91
+ function toEventHandler(handler) {
92
+ if (isEventHandler(handler)) {
93
+ return handler;
94
+ }
95
+ if (typeof handler !== "function") {
96
+ throw new TypeError("Invalid handler. It should be a function:", handler);
97
+ }
98
+ return defineEventHandler((event) => {
99
+ return callHandler(handler, event.req, event.res);
100
+ });
101
+ }
102
+ function createEvent(req, res) {
103
+ const event = {
104
+ __is_event__: true,
105
+ req,
106
+ res
58
107
  };
108
+ event.event = event;
109
+ req.event = event;
110
+ req.req = req;
111
+ req.res = res;
112
+ res.event = event;
113
+ res.res = res;
114
+ res.req.res = res;
115
+ res.req.req = req;
116
+ return event;
117
+ }
118
+ function isEvent(input) {
119
+ return "__is_event__" in input;
59
120
  }
60
121
 
61
- function useQuery(req) {
62
- return ufo.getQuery(req.url || "");
122
+ function useQuery(event) {
123
+ return ufo.getQuery(event.req.url || "");
63
124
  }
64
- function useMethod(req, defaultMethod = "GET") {
65
- return (req.method || defaultMethod).toUpperCase();
125
+ function useMethod(event, defaultMethod = "GET") {
126
+ return (event.req.method || defaultMethod).toUpperCase();
66
127
  }
67
- function isMethod(req, expected, allowHead) {
68
- const method = useMethod(req);
128
+ function isMethod(event, expected, allowHead) {
129
+ const method = useMethod(event);
69
130
  if (allowHead && method === "HEAD") {
70
131
  return true;
71
132
  }
@@ -78,8 +139,8 @@ function isMethod(req, expected, allowHead) {
78
139
  }
79
140
  return false;
80
141
  }
81
- function assertMethod(req, expected, allowHead) {
82
- if (!isMethod(req, expected, allowHead)) {
142
+ function assertMethod(event, expected, allowHead) {
143
+ if (!isMethod(event, expected, allowHead)) {
83
144
  throw createError({
84
145
  statusCode: 405,
85
146
  statusMessage: "HTTP method is not allowed."
@@ -90,18 +151,18 @@ function assertMethod(req, expected, allowHead) {
90
151
  const RawBodySymbol = Symbol("h3RawBody");
91
152
  const ParsedBodySymbol = Symbol("h3RawBody");
92
153
  const PayloadMethods = ["PATCH", "POST", "PUT", "DELETE"];
93
- function useRawBody(req, encoding = "utf-8") {
94
- assertMethod(req, PayloadMethods);
95
- if (RawBodySymbol in req) {
96
- const promise2 = Promise.resolve(req[RawBodySymbol]);
154
+ function useRawBody(event, encoding = "utf-8") {
155
+ assertMethod(event, PayloadMethods);
156
+ if (RawBodySymbol in event.req) {
157
+ const promise2 = Promise.resolve(event.req[RawBodySymbol]);
97
158
  return encoding ? promise2.then((buff) => buff.toString(encoding)) : promise2;
98
159
  }
99
- if ("body" in req) {
100
- return Promise.resolve(req.body);
160
+ if ("body" in event.req) {
161
+ return Promise.resolve(event.req.body);
101
162
  }
102
- const promise = req[RawBodySymbol] = new Promise((resolve, reject) => {
163
+ const promise = event.req[RawBodySymbol] = new Promise((resolve, reject) => {
103
164
  const bodyData = [];
104
- req.on("error", (err) => {
165
+ event.req.on("error", (err) => {
105
166
  reject(err);
106
167
  }).on("data", (chunk) => {
107
168
  bodyData.push(chunk);
@@ -111,13 +172,13 @@ function useRawBody(req, encoding = "utf-8") {
111
172
  });
112
173
  return encoding ? promise.then((buff) => buff.toString(encoding)) : promise;
113
174
  }
114
- async function useBody(req) {
115
- if (ParsedBodySymbol in req) {
116
- return req[ParsedBodySymbol];
175
+ async function useBody(event) {
176
+ if (ParsedBodySymbol in event.req) {
177
+ return event.req[ParsedBodySymbol];
117
178
  }
118
- const body = await useRawBody(req);
179
+ const body = await useRawBody(event);
119
180
  const json = destr__default(body);
120
- req[ParsedBodySymbol] = json;
181
+ event.req[ParsedBodySymbol] = json;
121
182
  return json;
122
183
  }
123
184
 
@@ -127,51 +188,61 @@ const MIMES = {
127
188
  };
128
189
 
129
190
  const defer = typeof setImmediate !== "undefined" ? setImmediate : (fn) => fn();
130
- function send(res, data, type) {
191
+ function send(event, data, type) {
131
192
  if (type) {
132
- defaultContentType(res, type);
193
+ defaultContentType(event, type);
133
194
  }
134
195
  return new Promise((resolve) => {
135
196
  defer(() => {
136
- res.end(data);
197
+ event.res.end(data);
137
198
  resolve(void 0);
138
199
  });
139
200
  });
140
201
  }
141
- function defaultContentType(res, type) {
142
- if (type && !res.getHeader("Content-Type")) {
143
- res.setHeader("Content-Type", type);
202
+ function defaultContentType(event, type) {
203
+ if (type && !event.res.getHeader("Content-Type")) {
204
+ event.res.setHeader("Content-Type", type);
144
205
  }
145
206
  }
146
- function sendRedirect(res, location, code = 302) {
147
- res.statusCode = code;
148
- res.setHeader("Location", location);
149
- return send(res, "Redirecting to " + location, MIMES.html);
207
+ function sendRedirect(event, location, code = 302) {
208
+ event.res.statusCode = code;
209
+ event.res.setHeader("Location", location);
210
+ return send(event, "Redirecting to " + location, MIMES.html);
150
211
  }
151
- function appendHeader(res, name, value) {
152
- let current = res.getHeader(name);
212
+ function appendHeader(event, name, value) {
213
+ let current = event.res.getHeader(name);
153
214
  if (!current) {
154
- res.setHeader(name, value);
215
+ event.res.setHeader(name, value);
155
216
  return;
156
217
  }
157
218
  if (!Array.isArray(current)) {
158
219
  current = [current.toString()];
159
220
  }
160
- res.setHeader(name, current.concat(value));
221
+ event.res.setHeader(name, current.concat(value));
222
+ }
223
+ function isStream(data) {
224
+ return data && typeof data === "object" && typeof data.pipe === "function" && typeof data.on === "function";
225
+ }
226
+ function sendStream(event, data) {
227
+ return new Promise((resolve, reject) => {
228
+ data.pipe(event.res);
229
+ data.on("end", () => resolve(void 0));
230
+ data.on("error", (error) => reject(createError(error)));
231
+ });
161
232
  }
162
233
 
163
- function useCookies(req) {
164
- return cookieEs.parse(req.headers.cookie || "");
234
+ function useCookies(event) {
235
+ return cookieEs.parse(event.req.headers.cookie || "");
165
236
  }
166
- function useCookie(req, name) {
167
- return useCookies(req)[name];
237
+ function useCookie(event, name) {
238
+ return useCookies(event)[name];
168
239
  }
169
- function setCookie(res, name, value, serializeOptions) {
240
+ function setCookie(event, name, value, serializeOptions) {
170
241
  const cookieStr = cookieEs.serialize(name, value, serializeOptions);
171
- appendHeader(res, "Set-Cookie", cookieStr);
242
+ appendHeader(event, "Set-Cookie", cookieStr);
172
243
  }
173
- function deleteCookie(res, name, serializeOptions) {
174
- setCookie(res, name, "", {
244
+ function deleteCookie(event, name, serializeOptions) {
245
+ setCookie(event, name, "", {
175
246
  ...serializeOptions,
176
247
  maxAge: 0
177
248
  });
@@ -200,7 +271,7 @@ function createError(input) {
200
271
  }
201
272
  return err;
202
273
  }
203
- function sendError(res, error, debug) {
274
+ function sendError(event, error, debug) {
204
275
  let h3Error;
205
276
  if (error instanceof H3Error) {
206
277
  h3Error = error;
@@ -208,37 +279,42 @@ function sendError(res, error, debug) {
208
279
  console.error(error);
209
280
  h3Error = createError(error);
210
281
  }
211
- if (res.writableEnded) {
282
+ if (event.res.writableEnded) {
212
283
  return;
213
284
  }
214
- res.statusCode = h3Error.statusCode;
215
- res.statusMessage = h3Error.statusMessage;
285
+ event.res.statusCode = h3Error.statusCode;
286
+ event.res.statusMessage = h3Error.statusMessage;
216
287
  const responseBody = {
217
- statusCode: res.statusCode,
218
- statusMessage: res.statusMessage,
288
+ statusCode: event.res.statusCode,
289
+ statusMessage: event.res.statusMessage,
219
290
  stack: [],
220
291
  data: h3Error.data
221
292
  };
222
293
  if (debug) {
223
294
  responseBody.stack = (h3Error.stack || "").split("\n").map((l) => l.trim());
224
295
  }
225
- res.setHeader("Content-Type", MIMES.json);
226
- res.end(JSON.stringify(responseBody, null, 2));
296
+ event.res.setHeader("Content-Type", MIMES.json);
297
+ event.res.end(JSON.stringify(responseBody, null, 2));
227
298
  }
228
299
 
229
300
  function createApp(options = {}) {
230
301
  const stack = [];
231
- const _handle = createHandle(stack, options);
232
- const app = function(req, res) {
233
- return _handle(req, res).catch((error) => {
302
+ const handler = createAppEventHandler(stack, options);
303
+ const nodeHandler = async function(req, res) {
304
+ const event = createEvent(req, res);
305
+ try {
306
+ await handler(event);
307
+ } catch (err) {
234
308
  if (options.onError) {
235
- return options.onError(error, req, res);
309
+ await options.onError(err, event);
236
310
  }
237
- return sendError(res, error, !!options.debug);
238
- });
311
+ await sendError(event, err, !!options.debug);
312
+ }
239
313
  };
314
+ const app = nodeHandler;
315
+ app.nodeHandler = nodeHandler;
240
316
  app.stack = stack;
241
- app._handle = _handle;
317
+ app.handler = handler;
242
318
  app.use = (arg1, arg2, arg3) => use(app, arg1, arg2, arg3);
243
319
  return app;
244
320
  }
@@ -248,61 +324,69 @@ function use(app, arg1, arg2, arg3) {
248
324
  } else if (Array.isArray(arg2)) {
249
325
  arg2.forEach((i) => use(app, arg1, i, arg3));
250
326
  } else if (typeof arg1 === "string") {
251
- app.stack.push(normalizeLayer({ ...arg3, route: arg1, handle: arg2 }));
327
+ app.stack.push(normalizeLayer({ ...arg3, route: arg1, handler: arg2 }));
252
328
  } else if (typeof arg1 === "function") {
253
- app.stack.push(normalizeLayer({ ...arg2, route: "/", handle: arg1 }));
329
+ app.stack.push(normalizeLayer({ ...arg2, route: "/", handler: arg1 }));
254
330
  } else {
255
331
  app.stack.push(normalizeLayer({ ...arg1 }));
256
332
  }
257
333
  return app;
258
334
  }
259
- function createHandle(stack, options) {
335
+ function createAppEventHandler(stack, options) {
260
336
  const spacing = options.debug ? 2 : void 0;
261
- return async function handle(req, res) {
262
- req.originalUrl = req.originalUrl || req.url || "/";
263
- const reqUrl = req.url || "/";
337
+ return defineEventHandler(async (event) => {
338
+ event.req.originalUrl = event.req.originalUrl || event.req.url || "/";
339
+ const reqUrl = event.req.url || "/";
264
340
  for (const layer of stack) {
265
341
  if (layer.route.length > 1) {
266
342
  if (!reqUrl.startsWith(layer.route)) {
267
343
  continue;
268
344
  }
269
- req.url = reqUrl.slice(layer.route.length) || "/";
345
+ event.req.url = reqUrl.slice(layer.route.length) || "/";
270
346
  } else {
271
- req.url = reqUrl;
347
+ event.req.url = reqUrl;
272
348
  }
273
- if (layer.match && !layer.match(req.url, req)) {
349
+ if (layer.match && !layer.match(event.req.url, event)) {
274
350
  continue;
275
351
  }
276
- const val = await layer.handle(req, res);
277
- if (res.writableEnded) {
352
+ const val = await layer.handler(event);
353
+ if (event.res.writableEnded) {
278
354
  return;
279
355
  }
280
356
  const type = typeof val;
281
357
  if (type === "string") {
282
- return send(res, val, MIMES.html);
358
+ return send(event, val, MIMES.html);
359
+ } else if (isStream(val)) {
360
+ return sendStream(event, val);
283
361
  } else if (type === "object" || type === "boolean" || type === "number") {
284
362
  if (val && val.buffer) {
285
- return send(res, val);
363
+ return send(event, val);
286
364
  } else if (val instanceof Error) {
287
365
  throw createError(val);
288
366
  } else {
289
- return send(res, JSON.stringify(val, null, spacing), MIMES.json);
367
+ return send(event, JSON.stringify(val, null, spacing), MIMES.json);
290
368
  }
291
369
  }
292
370
  }
293
- if (!res.writableEnded) {
371
+ if (!event.res.writableEnded) {
294
372
  throw createError({ statusCode: 404, statusMessage: "Not Found" });
295
373
  }
296
- };
374
+ });
297
375
  }
298
- function normalizeLayer(layer) {
299
- if (layer.promisify === void 0) {
300
- layer.promisify = layer.handle.length > 2;
376
+ function normalizeLayer(input) {
377
+ let handler = input.handler || input.handle;
378
+ if (handler.handler) {
379
+ handler = handler.handler;
380
+ }
381
+ if (input.lazy) {
382
+ handler = defineLazyEventHandler(handler);
383
+ } else if (!isEventHandler(handler)) {
384
+ handler = toEventHandler(handler);
301
385
  }
302
386
  return {
303
- route: ufo.withoutTrailingSlash(layer.route),
304
- match: layer.match,
305
- handle: layer.lazy ? lazyHandle(layer.handle, layer.promisify) : layer.promisify ? promisifyHandle(layer.handle) : layer.handle
387
+ route: ufo.withoutTrailingSlash(input.route),
388
+ match: input.match,
389
+ handler
306
390
  };
307
391
  }
308
392
 
@@ -311,28 +395,34 @@ function createRouter() {
311
395
  const _router = radix3.createRouter({});
312
396
  const routes = {};
313
397
  const router = {};
314
- router.add = (path, handle, method = "all") => {
398
+ const addRoute = (path, handler, method) => {
315
399
  let route = routes[path];
316
400
  if (!route) {
317
401
  routes[path] = route = { handlers: {} };
318
402
  _router.insert(path, route);
319
403
  }
320
- route.handlers[method] = handle;
404
+ route.handlers[method] = toEventHandler(handler);
321
405
  return router;
322
406
  };
407
+ router.use = router.add = (path, handler, method) => addRoute(path, handler, method || "all");
323
408
  for (const method of RouterMethods) {
324
409
  router[method] = (path, handle) => router.add(path, handle, method);
325
410
  }
326
- router.handle = (req, res) => {
327
- const matched = _router.lookup(req.url || "/");
411
+ router.handler = defineEventHandler((event) => {
412
+ let path = event.req.url || "/";
413
+ const queryUrlIndex = path.lastIndexOf("?");
414
+ if (queryUrlIndex > -1) {
415
+ path = path.substring(0, queryUrlIndex);
416
+ }
417
+ const matched = _router.lookup(path);
328
418
  if (!matched) {
329
419
  throw createError({
330
420
  statusCode: 404,
331
421
  name: "Not Found",
332
- statusMessage: `Cannot find any route matching ${req.url || "/"}.`
422
+ statusMessage: `Cannot find any route matching ${event.req.url || "/"}.`
333
423
  });
334
424
  }
335
- const method = (req.method || "get").toLowerCase();
425
+ const method = (event.req.method || "get").toLowerCase();
336
426
  const handler = matched.handlers[method] || matched.handlers.all;
337
427
  if (!handler) {
338
428
  throw createError({
@@ -341,9 +431,10 @@ function createRouter() {
341
431
  statusMessage: `Method ${method} is not allowed on this route.`
342
432
  });
343
433
  }
344
- req.params = matched.params || {};
345
- return handler(req, res);
346
- };
434
+ event.event.params = matched.params || {};
435
+ event.req.params = event.event.params;
436
+ return handler(event);
437
+ });
347
438
  return router;
348
439
  }
349
440
 
@@ -351,22 +442,33 @@ exports.H3Error = H3Error;
351
442
  exports.MIMES = MIMES;
352
443
  exports.appendHeader = appendHeader;
353
444
  exports.assertMethod = assertMethod;
354
- exports.callHandle = callHandle;
445
+ exports.callHandler = callHandler;
355
446
  exports.createApp = createApp;
447
+ exports.createAppEventHandler = createAppEventHandler;
356
448
  exports.createError = createError;
357
- exports.createHandle = createHandle;
449
+ exports.createEvent = createEvent;
358
450
  exports.createRouter = createRouter;
359
451
  exports.defaultContentType = defaultContentType;
452
+ exports.defineEventHandler = defineEventHandler;
360
453
  exports.defineHandle = defineHandle;
454
+ exports.defineHandler = defineHandler;
455
+ exports.defineLazyEventHandler = defineLazyEventHandler;
456
+ exports.defineLazyHandler = defineLazyHandler;
361
457
  exports.defineMiddleware = defineMiddleware;
362
458
  exports.deleteCookie = deleteCookie;
459
+ exports.isEvent = isEvent;
460
+ exports.isEventHandler = isEventHandler;
363
461
  exports.isMethod = isMethod;
462
+ exports.isStream = isStream;
364
463
  exports.lazyHandle = lazyHandle;
365
464
  exports.promisifyHandle = promisifyHandle;
465
+ exports.promisifyHandler = promisifyHandler;
366
466
  exports.send = send;
367
467
  exports.sendError = sendError;
368
468
  exports.sendRedirect = sendRedirect;
469
+ exports.sendStream = sendStream;
369
470
  exports.setCookie = setCookie;
471
+ exports.toEventHandler = toEventHandler;
370
472
  exports.use = use;
371
473
  exports.useBase = useBase;
372
474
  exports.useBody = useBody;