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