h3 0.4.2 → 0.5.2

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
@@ -29,8 +29,8 @@
29
29
  # Using npm
30
30
  npm install h3
31
31
 
32
- # Using pnpm
33
- pnpm add h3
32
+ # Using yarn
33
+ yarn add h3
34
34
 
35
35
  # Using pnpm
36
36
  pnpm add h3
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);
58
55
  };
59
56
  }
60
57
 
61
- function useQuery(req) {
62
- return ufo.getQuery(req.url || "");
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
+ });
63
87
  }
64
- function useMethod(req, defaultMethod = "GET") {
65
- return (req.method || defaultMethod).toUpperCase();
88
+ function isEventHandler(input) {
89
+ return "__is_handler__" in input;
66
90
  }
67
- function isMethod(req, expected, allowHead) {
68
- const method = useMethod(req);
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
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;
120
+ }
121
+
122
+ function useQuery(event) {
123
+ return ufo.getQuery(event.req.url || "");
124
+ }
125
+ function useMethod(event, defaultMethod = "GET") {
126
+ return (event.req.method || defaultMethod).toUpperCase();
127
+ }
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,61 +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));
161
222
  }
162
223
  function isStream(data) {
163
- return typeof data === "object" && typeof data.pipe === "function" && typeof data.on === "function";
224
+ return data && typeof data === "object" && typeof data.pipe === "function" && typeof data.on === "function";
164
225
  }
165
- function sendStream(res, data) {
226
+ function sendStream(event, data) {
166
227
  return new Promise((resolve, reject) => {
167
- data.pipe(res);
228
+ data.pipe(event.res);
168
229
  data.on("end", () => resolve(void 0));
169
230
  data.on("error", (error) => reject(createError(error)));
170
231
  });
171
232
  }
172
233
 
173
- function useCookies(req) {
174
- return cookieEs.parse(req.headers.cookie || "");
234
+ function useCookies(event) {
235
+ return cookieEs.parse(event.req.headers.cookie || "");
175
236
  }
176
- function useCookie(req, name) {
177
- return useCookies(req)[name];
237
+ function useCookie(event, name) {
238
+ return useCookies(event)[name];
178
239
  }
179
- function setCookie(res, name, value, serializeOptions) {
240
+ function setCookie(event, name, value, serializeOptions) {
180
241
  const cookieStr = cookieEs.serialize(name, value, serializeOptions);
181
- appendHeader(res, "Set-Cookie", cookieStr);
242
+ appendHeader(event, "Set-Cookie", cookieStr);
182
243
  }
183
- function deleteCookie(res, name, serializeOptions) {
184
- setCookie(res, name, "", {
244
+ function deleteCookie(event, name, serializeOptions) {
245
+ setCookie(event, name, "", {
185
246
  ...serializeOptions,
186
247
  maxAge: 0
187
248
  });
@@ -210,45 +271,54 @@ function createError(input) {
210
271
  }
211
272
  return err;
212
273
  }
213
- function sendError(res, error, debug) {
214
- let h3Error;
215
- if (error instanceof H3Error) {
216
- h3Error = error;
217
- } else {
218
- console.error(error);
219
- h3Error = createError(error);
220
- }
221
- if (res.writableEnded) {
274
+ function sendError(event, error, debug) {
275
+ if (event.res.writableEnded) {
222
276
  return;
223
277
  }
224
- res.statusCode = h3Error.statusCode;
225
- res.statusMessage = h3Error.statusMessage;
278
+ const h3Error = isError(error) ? error : createError(error);
226
279
  const responseBody = {
227
- statusCode: res.statusCode,
228
- statusMessage: res.statusMessage,
280
+ statusCode: h3Error.statusCode,
281
+ statusMessage: h3Error.statusMessage,
229
282
  stack: [],
230
283
  data: h3Error.data
231
284
  };
232
285
  if (debug) {
233
286
  responseBody.stack = (h3Error.stack || "").split("\n").map((l) => l.trim());
234
287
  }
235
- res.setHeader("Content-Type", MIMES.json);
236
- res.end(JSON.stringify(responseBody, null, 2));
288
+ if (event.res.writableEnded) {
289
+ return;
290
+ }
291
+ event.res.statusCode = h3Error.statusCode;
292
+ event.res.statusMessage = h3Error.statusMessage;
293
+ event.res.setHeader("Content-Type", MIMES.json);
294
+ event.res.end(JSON.stringify(responseBody, null, 2));
295
+ }
296
+ function isError(input) {
297
+ return input instanceof H3Error;
237
298
  }
238
299
 
239
300
  function createApp(options = {}) {
240
301
  const stack = [];
241
- const _handle = createHandle(stack, options);
242
- const app = function(req, res) {
243
- 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) {
244
308
  if (options.onError) {
245
- return options.onError(error, req, res);
309
+ await options.onError(err, event);
310
+ } else {
311
+ if (!isError(err)) {
312
+ console.error("[h3]", err);
313
+ }
314
+ await sendError(event, err, !!options.debug);
246
315
  }
247
- return sendError(res, error, !!options.debug);
248
- });
316
+ }
249
317
  };
318
+ const app = nodeHandler;
319
+ app.nodeHandler = nodeHandler;
250
320
  app.stack = stack;
251
- app._handle = _handle;
321
+ app.handler = handler;
252
322
  app.use = (arg1, arg2, arg3) => use(app, arg1, arg2, arg3);
253
323
  return app;
254
324
  }
@@ -258,63 +328,69 @@ function use(app, arg1, arg2, arg3) {
258
328
  } else if (Array.isArray(arg2)) {
259
329
  arg2.forEach((i) => use(app, arg1, i, arg3));
260
330
  } else if (typeof arg1 === "string") {
261
- app.stack.push(normalizeLayer({ ...arg3, route: arg1, handle: arg2 }));
331
+ app.stack.push(normalizeLayer({ ...arg3, route: arg1, handler: arg2 }));
262
332
  } else if (typeof arg1 === "function") {
263
- app.stack.push(normalizeLayer({ ...arg2, route: "/", handle: arg1 }));
333
+ app.stack.push(normalizeLayer({ ...arg2, route: "/", handler: arg1 }));
264
334
  } else {
265
335
  app.stack.push(normalizeLayer({ ...arg1 }));
266
336
  }
267
337
  return app;
268
338
  }
269
- function createHandle(stack, options) {
339
+ function createAppEventHandler(stack, options) {
270
340
  const spacing = options.debug ? 2 : void 0;
271
- return async function handle(req, res) {
272
- req.originalUrl = req.originalUrl || req.url || "/";
273
- const reqUrl = req.url || "/";
341
+ return defineEventHandler(async (event) => {
342
+ event.req.originalUrl = event.req.originalUrl || event.req.url || "/";
343
+ const reqUrl = event.req.url || "/";
274
344
  for (const layer of stack) {
275
345
  if (layer.route.length > 1) {
276
346
  if (!reqUrl.startsWith(layer.route)) {
277
347
  continue;
278
348
  }
279
- req.url = reqUrl.slice(layer.route.length) || "/";
349
+ event.req.url = reqUrl.slice(layer.route.length) || "/";
280
350
  } else {
281
- req.url = reqUrl;
351
+ event.req.url = reqUrl;
282
352
  }
283
- if (layer.match && !layer.match(req.url, req)) {
353
+ if (layer.match && !layer.match(event.req.url, event)) {
284
354
  continue;
285
355
  }
286
- const val = await layer.handle(req, res);
287
- if (res.writableEnded) {
356
+ const val = await layer.handler(event);
357
+ if (event.res.writableEnded) {
288
358
  return;
289
359
  }
290
360
  const type = typeof val;
291
361
  if (type === "string") {
292
- return send(res, val, MIMES.html);
362
+ return send(event, val, MIMES.html);
293
363
  } else if (isStream(val)) {
294
- return sendStream(res, val);
364
+ return sendStream(event, val);
295
365
  } else if (type === "object" || type === "boolean" || type === "number") {
296
366
  if (val && val.buffer) {
297
- return send(res, val);
367
+ return send(event, val);
298
368
  } else if (val instanceof Error) {
299
369
  throw createError(val);
300
370
  } else {
301
- return send(res, JSON.stringify(val, null, spacing), MIMES.json);
371
+ return send(event, JSON.stringify(val, null, spacing), MIMES.json);
302
372
  }
303
373
  }
304
374
  }
305
- if (!res.writableEnded) {
375
+ if (!event.res.writableEnded) {
306
376
  throw createError({ statusCode: 404, statusMessage: "Not Found" });
307
377
  }
308
- };
378
+ });
309
379
  }
310
- function normalizeLayer(layer) {
311
- if (layer.promisify === void 0) {
312
- layer.promisify = layer.handle.length > 2;
380
+ function normalizeLayer(input) {
381
+ let handler = input.handler || input.handle;
382
+ if (handler.handler) {
383
+ handler = handler.handler;
384
+ }
385
+ if (input.lazy) {
386
+ handler = defineLazyEventHandler(handler);
387
+ } else if (!isEventHandler(handler)) {
388
+ handler = toEventHandler(handler);
313
389
  }
314
390
  return {
315
- route: ufo.withoutTrailingSlash(layer.route),
316
- match: layer.match,
317
- handle: layer.lazy ? lazyHandle(layer.handle, layer.promisify) : layer.promisify ? promisifyHandle(layer.handle) : layer.handle
391
+ route: ufo.withoutTrailingSlash(input.route),
392
+ match: input.match,
393
+ handler
318
394
  };
319
395
  }
320
396
 
@@ -323,28 +399,34 @@ function createRouter() {
323
399
  const _router = radix3.createRouter({});
324
400
  const routes = {};
325
401
  const router = {};
326
- router.add = (path, handle, method = "all") => {
402
+ const addRoute = (path, handler, method) => {
327
403
  let route = routes[path];
328
404
  if (!route) {
329
405
  routes[path] = route = { handlers: {} };
330
406
  _router.insert(path, route);
331
407
  }
332
- route.handlers[method] = handle;
408
+ route.handlers[method] = toEventHandler(handler);
333
409
  return router;
334
410
  };
411
+ router.use = router.add = (path, handler, method) => addRoute(path, handler, method || "all");
335
412
  for (const method of RouterMethods) {
336
413
  router[method] = (path, handle) => router.add(path, handle, method);
337
414
  }
338
- router.handle = (req, res) => {
339
- const matched = _router.lookup(req.url || "/");
415
+ router.handler = defineEventHandler((event) => {
416
+ let path = event.req.url || "/";
417
+ const queryUrlIndex = path.lastIndexOf("?");
418
+ if (queryUrlIndex > -1) {
419
+ path = path.substring(0, queryUrlIndex);
420
+ }
421
+ const matched = _router.lookup(path);
340
422
  if (!matched) {
341
423
  throw createError({
342
424
  statusCode: 404,
343
425
  name: "Not Found",
344
- statusMessage: `Cannot find any route matching ${req.url || "/"}.`
426
+ statusMessage: `Cannot find any route matching ${event.req.url || "/"}.`
345
427
  });
346
428
  }
347
- const method = (req.method || "get").toLowerCase();
429
+ const method = (event.req.method || "get").toLowerCase();
348
430
  const handler = matched.handlers[method] || matched.handlers.all;
349
431
  if (!handler) {
350
432
  throw createError({
@@ -353,9 +435,10 @@ function createRouter() {
353
435
  statusMessage: `Method ${method} is not allowed on this route.`
354
436
  });
355
437
  }
356
- req.params = matched.params || {};
357
- return handler(req, res);
358
- };
438
+ event.event.params = matched.params || {};
439
+ event.req.params = event.event.params;
440
+ return handler(event);
441
+ });
359
442
  return router;
360
443
  }
361
444
 
@@ -363,24 +446,34 @@ exports.H3Error = H3Error;
363
446
  exports.MIMES = MIMES;
364
447
  exports.appendHeader = appendHeader;
365
448
  exports.assertMethod = assertMethod;
366
- exports.callHandle = callHandle;
449
+ exports.callHandler = callHandler;
367
450
  exports.createApp = createApp;
451
+ exports.createAppEventHandler = createAppEventHandler;
368
452
  exports.createError = createError;
369
- exports.createHandle = createHandle;
453
+ exports.createEvent = createEvent;
370
454
  exports.createRouter = createRouter;
371
455
  exports.defaultContentType = defaultContentType;
456
+ exports.defineEventHandler = defineEventHandler;
372
457
  exports.defineHandle = defineHandle;
458
+ exports.defineHandler = defineHandler;
459
+ exports.defineLazyEventHandler = defineLazyEventHandler;
460
+ exports.defineLazyHandler = defineLazyHandler;
373
461
  exports.defineMiddleware = defineMiddleware;
374
462
  exports.deleteCookie = deleteCookie;
463
+ exports.isError = isError;
464
+ exports.isEvent = isEvent;
465
+ exports.isEventHandler = isEventHandler;
375
466
  exports.isMethod = isMethod;
376
467
  exports.isStream = isStream;
377
468
  exports.lazyHandle = lazyHandle;
378
469
  exports.promisifyHandle = promisifyHandle;
470
+ exports.promisifyHandler = promisifyHandler;
379
471
  exports.send = send;
380
472
  exports.sendError = sendError;
381
473
  exports.sendRedirect = sendRedirect;
382
474
  exports.sendStream = sendStream;
383
475
  exports.setCookie = setCookie;
476
+ exports.toEventHandler = toEventHandler;
384
477
  exports.use = use;
385
478
  exports.useBase = useBase;
386
479
  exports.useBody = useBody;