@wooksjs/event-http 0.4.26 → 0.4.27

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/dist/index.mjs CHANGED
@@ -1,8 +1,8 @@
1
1
  import { createEventContext, useEventContext, useEventId, attachHook, useEventLogger } from '@wooksjs/event-core';
2
2
  import { URLSearchParams } from 'url';
3
- import { Readable } from 'stream';
4
- import { WooksAdapterBase } from 'wooks';
5
3
  import http from 'http';
4
+ import { WooksAdapterBase } from 'wooks';
5
+ import { Readable } from 'stream';
6
6
 
7
7
  function createHttpContext(data, options) {
8
8
  return createEventContext({
@@ -17,58 +17,106 @@ function useHttpContext() {
17
17
  return useEventContext('HTTP');
18
18
  }
19
19
 
20
+ function escapeRegex(s) {
21
+ return s.replace(/[$()*+./?[\\\]^{|}-]/g, '\\$&');
22
+ }
23
+ function safeDecode(f, v) {
24
+ try {
25
+ return f(v);
26
+ }
27
+ catch (error) {
28
+ return v;
29
+ }
30
+ }
31
+ function safeDecodeURIComponent(uri) {
32
+ if (!uri.includes('%')) {
33
+ return uri;
34
+ }
35
+ return safeDecode(decodeURIComponent, uri);
36
+ }
37
+
38
+ function convertTime(time, unit = 'ms') {
39
+ if (typeof time === 'number') {
40
+ return time / units[unit];
41
+ }
42
+ const rg = /(\d+)(\w+)/g;
43
+ let t = 0;
44
+ let r;
45
+ while ((r = rg.exec(time))) {
46
+ t += Number(r[1]) * (units[r[2]] || 0);
47
+ }
48
+ return t / units[unit];
49
+ }
50
+ const units = {
51
+ ms: 1,
52
+ s: 1000,
53
+ m: 1000 * 60,
54
+ h: 1000 * 60 * 60,
55
+ d: 1000 * 60 * 60 * 24,
56
+ w: 1000 * 60 * 60 * 24 * 7,
57
+ M: 1000 * 60 * 60 * 24 * 30,
58
+ Y: 1000 * 60 * 60 * 24 * 365,
59
+ };
60
+
61
+ function renderCookie(key, data) {
62
+ let attrs = '';
63
+ for (const [a, v] of Object.entries(data.attrs)) {
64
+ const func = cookieAttrFunc[a];
65
+ if (typeof func === 'function') {
66
+ const val = func(v);
67
+ attrs += val ? `; ${val}` : '';
68
+ }
69
+ else {
70
+ throw new TypeError(`Unknown Set-Cookie attribute ${a}`);
71
+ }
72
+ }
73
+ return `${key}=${encodeURIComponent(data.value)}${attrs}`;
74
+ }
75
+ const cookieAttrFunc = {
76
+ expires: (v) => `Expires=${typeof v === 'string' || typeof v === 'number' ? new Date(v).toUTCString() : v.toUTCString()}`,
77
+ maxAge: (v) => `Max-Age=${convertTime(v, 's').toString()}`,
78
+ domain: (v) => `Domain=${v}`,
79
+ path: (v) => `Path=${v}`,
80
+ secure: (v) => (v ? 'Secure' : ''),
81
+ httpOnly: (v) => (v ? 'HttpOnly' : ''),
82
+ sameSite: (v) => v ? `SameSite=${typeof v === 'string' ? v : 'Strict'}` : '',
83
+ };
84
+
20
85
  const xForwardedFor = 'x-forwarded-for';
21
86
  function useRequest() {
22
87
  const { store } = useHttpContext();
23
88
  const { init } = store('request');
24
89
  const event = store('event');
25
- const { req } = event.value;
26
- const rawBody = () => init('rawBody', () => {
27
- return new Promise((resolve, reject) => {
28
- let body = Buffer.from('');
29
- req.on('data', function (chunk) {
30
- body = Buffer.concat([body, chunk]);
31
- });
32
- req.on('error', function (err) {
33
- reject(err);
34
- });
35
- req.on('end', function () {
36
- resolve(body);
37
- });
90
+ const req = event.get('req');
91
+ const rawBody = async () => init('rawBody', async () => new Promise((resolve, reject) => {
92
+ let body = Buffer.from('');
93
+ req.on('data', chunk => {
94
+ body = Buffer.concat([body, chunk]);
38
95
  });
39
- });
96
+ req.on('error', err => {
97
+ reject(err);
98
+ });
99
+ req.on('end', () => {
100
+ resolve(body);
101
+ });
102
+ }));
40
103
  const reqId = useEventId().getId;
41
104
  const forwardedIp = () => init('forwardedIp', () => {
42
- if (typeof req.headers[xForwardedFor] === 'string' &&
43
- req.headers[xForwardedFor]) {
44
- return req.headers[xForwardedFor]
45
- .split(',')
46
- .shift()
47
- ?.trim();
105
+ if (typeof req.headers[xForwardedFor] === 'string' && req.headers[xForwardedFor]) {
106
+ return req.headers[xForwardedFor].split(',').shift()?.trim();
48
107
  }
49
108
  else {
50
109
  return '';
51
110
  }
52
111
  });
53
- const remoteIp = () => init('remoteIp', () => req.socket?.remoteAddress || req.connection?.remoteAddress || '');
112
+ const remoteIp = () => init('remoteIp', () => req.socket.remoteAddress || req.connection.remoteAddress || '');
54
113
  function getIp(options) {
55
- if (options?.trustProxy) {
56
- return forwardedIp() || getIp();
57
- }
58
- else {
59
- return remoteIp();
60
- }
114
+ return options?.trustProxy ? forwardedIp() || getIp() : remoteIp();
61
115
  }
62
- const getIpList = () => init('ipList', () => {
63
- return {
64
- remoteIp: req.socket?.remoteAddress ||
65
- req.connection?.remoteAddress ||
66
- '',
67
- forwarded: (req.headers[xForwardedFor] || '')
68
- .split(',')
69
- .map((s) => s.trim()),
70
- };
71
- });
116
+ const getIpList = () => init('ipList', () => ({
117
+ remoteIp: req.socket.remoteAddress || req.connection.remoteAddress || '',
118
+ forwarded: (req.headers[xForwardedFor] || '').split(',').map(s => s.trim()),
119
+ }));
72
120
  return {
73
121
  rawRequest: req,
74
122
  url: req.url,
@@ -111,13 +159,73 @@ function useSetHeader(name) {
111
159
  return hook(name);
112
160
  }
113
161
 
162
+ function useCookies() {
163
+ const { store } = useHttpContext();
164
+ const { cookie } = useHeaders();
165
+ const { init } = store('cookies');
166
+ const getCookie = (name) => init(name, () => {
167
+ if (cookie) {
168
+ const result = new RegExp(`(?:^|; )${escapeRegex(name)}=(.*?)(?:;?$|; )`, 'i').exec(cookie);
169
+ return result?.[1] ? safeDecodeURIComponent(result[1]) : null;
170
+ }
171
+ else {
172
+ return null;
173
+ }
174
+ });
175
+ return {
176
+ rawCookies: cookie,
177
+ getCookie,
178
+ };
179
+ }
180
+ function useSetCookies() {
181
+ const { store } = useHttpContext();
182
+ const cookiesStore = store('setCookies');
183
+ function setCookie(name, value, attrs) {
184
+ cookiesStore.set(name, {
185
+ value,
186
+ attrs: attrs || {},
187
+ });
188
+ }
189
+ function cookies() {
190
+ return cookiesStore
191
+ .entries()
192
+ .filter(a => Boolean(a[1]))
193
+ .map(([key, value]) => renderCookie(key, value));
194
+ }
195
+ return {
196
+ setCookie,
197
+ getCookie: cookiesStore.get,
198
+ removeCookie: cookiesStore.del,
199
+ clearCookies: cookiesStore.clear,
200
+ cookies,
201
+ };
202
+ }
203
+ function useSetCookie(name) {
204
+ const { setCookie, getCookie } = useSetCookies();
205
+ const valueHook = attachHook({
206
+ name,
207
+ type: 'cookie',
208
+ }, {
209
+ get: () => getCookie(name).value,
210
+ set: (value) => {
211
+ setCookie(name, value, getCookie(name).attrs);
212
+ },
213
+ });
214
+ return attachHook(valueHook, {
215
+ get: () => getCookie(name).attrs,
216
+ set: (attrs) => {
217
+ setCookie(name, getCookie(name).value || '', attrs);
218
+ },
219
+ }, 'attrs');
220
+ }
221
+
114
222
  function useAccept() {
115
223
  const { store } = useHttpContext();
116
224
  const { accept } = useHeaders();
117
225
  const accepts = (mime) => {
118
226
  const { set, get, has } = store('accept');
119
227
  if (!has(mime)) {
120
- return set(mime, !!(accept && (accept === '*/*' || accept.indexOf(mime) >= 0)));
228
+ return set(mime, Boolean(accept && (accept === '*/*' || accept.includes(mime))));
121
229
  }
122
230
  return get(mime);
123
231
  };
@@ -169,62 +277,39 @@ function useAuthorization() {
169
277
  };
170
278
  }
171
279
 
172
- function convertTime(time, unit = 'ms') {
173
- if (typeof time === 'number')
174
- return time / units[unit];
175
- const rg = /(\d+)(\w+)/g;
176
- let t = 0;
177
- let r;
178
- while ((r = rg.exec(time))) {
179
- t += Number(r[1]) * (units[r[2]] || 0);
180
- }
181
- return t / units[unit];
182
- }
183
- const units = {
184
- ms: 1,
185
- s: 1000,
186
- m: 1000 * 60,
187
- h: 1000 * 60 * 60,
188
- d: 1000 * 60 * 60 * 24,
189
- w: 1000 * 60 * 60 * 24 * 7,
190
- M: 1000 * 60 * 60 * 24 * 30,
191
- Y: 1000 * 60 * 60 * 24 * 365,
192
- };
193
-
194
280
  function renderCacheControl(data) {
195
281
  let attrs = '';
196
282
  for (const [a, v] of Object.entries(data)) {
197
- if (typeof v === 'undefined')
283
+ if (v === undefined) {
198
284
  continue;
285
+ }
199
286
  const func = cacheControlFunc[a];
200
287
  if (typeof func === 'function') {
201
288
  const val = func(v);
202
289
  if (val) {
203
- attrs += attrs ? ', ' + val : val;
290
+ attrs += attrs ? `, ${val}` : val;
204
291
  }
205
292
  }
206
293
  else {
207
- throw new Error('Unknown Cache-Control attribute ' + a);
294
+ throw new TypeError(`Unknown Cache-Control attribute ${a}`);
208
295
  }
209
296
  }
210
297
  return attrs;
211
298
  }
212
299
  const cacheControlFunc = {
213
- mustRevalidate: (v) => v ? 'must-revalidate' : '',
300
+ mustRevalidate: (v) => (v ? 'must-revalidate' : ''),
214
301
  noCache: (v) => v ? (typeof v === 'string' ? `no-cache="${v}"` : 'no-cache') : '',
215
302
  noStore: (v) => (v ? 'no-store' : ''),
216
303
  noTransform: (v) => (v ? 'no-transform' : ''),
217
304
  public: (v) => (v ? 'public' : ''),
218
305
  private: (v) => v ? (typeof v === 'string' ? `private="${v}"` : 'private') : '',
219
- proxyRevalidate: (v) => v ? 'proxy-revalidate' : '',
220
- maxAge: (v) => 'max-age=' + convertTime(v, 's').toString(),
221
- sMaxage: (v) => 's-maxage=' + convertTime(v, 's').toString(),
306
+ proxyRevalidate: (v) => (v ? 'proxy-revalidate' : ''),
307
+ maxAge: (v) => `max-age=${convertTime(v, 's').toString()}`,
308
+ sMaxage: (v) => `s-maxage=${convertTime(v, 's').toString()}`,
222
309
  };
223
310
 
224
311
  const renderAge = (v) => convertTime(v, 's').toString();
225
- const renderExpires = (v) => typeof v === 'string' || typeof v === 'number'
226
- ? new Date(v).toUTCString()
227
- : v.toUTCString();
312
+ const renderExpires = (v) => typeof v === 'string' || typeof v === 'number' ? new Date(v).toUTCString() : v.toUTCString();
228
313
  const renderPragmaNoCache = (v) => (v ? 'no-cache' : '');
229
314
  function useSetCacheControl() {
230
315
  const { setHeader } = useSetHeaders();
@@ -251,15 +336,16 @@ function useSetCacheControl() {
251
336
  function useResponse() {
252
337
  const { store } = useHttpContext();
253
338
  const event = store('event');
254
- const { res } = event.value;
339
+ const res = event.get('res');
255
340
  const responded = store('response').hook('responded');
256
341
  const statusCode = store('status').hook('code');
257
342
  function status(code) {
258
- return (statusCode.value = code ? code : statusCode.value);
343
+ return (statusCode.value = code || statusCode.value);
259
344
  }
260
345
  const rawResponse = (options) => {
261
- if (!options || !options.passthrough)
346
+ if (!options || !options.passthrough) {
262
347
  responded.value = true;
348
+ }
263
349
  return res;
264
350
  };
265
351
  return {
@@ -276,108 +362,6 @@ function useStatus() {
276
362
  return store('status').hook('code');
277
363
  }
278
364
 
279
- function renderCookie(key, data) {
280
- let attrs = '';
281
- for (const [a, v] of Object.entries(data.attrs)) {
282
- const func = cookieAttrFunc[a];
283
- if (typeof func === 'function') {
284
- const val = func(v);
285
- attrs += val ? '; ' + val : '';
286
- }
287
- else {
288
- throw new Error('Unknown Set-Cookie attribute ' + a);
289
- }
290
- }
291
- return `${key}=${encodeURIComponent(data.value)}${attrs}`;
292
- }
293
- const cookieAttrFunc = {
294
- expires: (v) => 'Expires=' +
295
- (typeof v === 'string' || typeof v === 'number'
296
- ? new Date(v).toUTCString()
297
- : v.toUTCString()),
298
- maxAge: (v) => 'Max-Age=' + convertTime(v, 's').toString(),
299
- domain: (v) => 'Domain=' + v,
300
- path: (v) => 'Path=' + v,
301
- secure: (v) => (v ? 'Secure' : ''),
302
- httpOnly: (v) => (v ? 'HttpOnly' : ''),
303
- sameSite: (v) => v ? 'SameSite=' + (typeof v === 'string' ? v : 'Strict') : '',
304
- };
305
-
306
- function escapeRegex(s) {
307
- return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
308
- }
309
- function safeDecode(f, v) {
310
- try {
311
- return f(v);
312
- }
313
- catch (e) {
314
- return v;
315
- }
316
- }
317
- function safeDecodeURIComponent(uri) {
318
- if (uri.indexOf('%') < 0)
319
- return uri;
320
- return safeDecode(decodeURIComponent, uri);
321
- }
322
-
323
- function useCookies() {
324
- const { store } = useHttpContext();
325
- const { cookie } = useHeaders();
326
- const { init } = store('cookies');
327
- const getCookie = (name) => init(name, () => {
328
- if (cookie) {
329
- const result = new RegExp(`(?:^|; )${escapeRegex(name)}=(.*?)(?:;?$|; )`, 'i').exec(cookie);
330
- return result && result[1]
331
- ? safeDecodeURIComponent(result[1])
332
- : null;
333
- }
334
- else {
335
- return null;
336
- }
337
- });
338
- return {
339
- rawCookies: cookie,
340
- getCookie,
341
- };
342
- }
343
- function useSetCookies() {
344
- const { store } = useHttpContext();
345
- const cookiesStore = store('setCookies');
346
- function setCookie(name, value, attrs) {
347
- cookiesStore.set(name, {
348
- value,
349
- attrs: attrs || {},
350
- });
351
- }
352
- function cookies() {
353
- return cookiesStore
354
- .entries()
355
- .filter((a) => !!a[1])
356
- .map(([key, value]) => renderCookie(key, value));
357
- }
358
- return {
359
- setCookie,
360
- getCookie: cookiesStore.get,
361
- removeCookie: cookiesStore.del,
362
- clearCookies: cookiesStore.clear,
363
- cookies,
364
- };
365
- }
366
- function useSetCookie(name) {
367
- const { setCookie, getCookie } = useSetCookies();
368
- const valueHook = attachHook({
369
- name,
370
- type: 'cookie',
371
- }, {
372
- get: () => getCookie(name)?.value,
373
- set: (value) => setCookie(name, value, getCookie(name)?.attrs),
374
- });
375
- return attachHook(valueHook, {
376
- get: () => getCookie(name)?.attrs,
377
- set: (attrs) => setCookie(name, getCookie(name)?.value || '', attrs),
378
- }, 'attrs');
379
- }
380
-
381
365
  class WooksURLSearchParams extends URLSearchParams {
382
366
  toJson() {
383
367
  const json = {};
@@ -418,89 +402,91 @@ class BaseHttpResponseRenderer {
418
402
  if (typeof response.body === 'string' ||
419
403
  typeof response.body === 'boolean' ||
420
404
  typeof response.body === 'number') {
421
- if (!response.getContentType())
405
+ if (!response.getContentType()) {
422
406
  response.setContentType('text/plain');
407
+ }
423
408
  return response.body.toString();
424
409
  }
425
- if (typeof response.body === 'undefined') {
410
+ if (response.body === undefined) {
426
411
  return '';
427
412
  }
428
413
  if (response.body instanceof Uint8Array) {
429
414
  return response.body;
430
415
  }
431
416
  if (typeof response.body === 'object') {
432
- if (!response.getContentType())
417
+ if (!response.getContentType()) {
433
418
  response.setContentType('application/json');
419
+ }
434
420
  return JSON.stringify(response.body);
435
421
  }
436
- throw new Error('Unsupported body format "' + typeof response.body + '"');
422
+ throw new Error(`Unsupported body format "${typeof response.body}"`);
437
423
  }
438
424
  }
439
425
 
440
426
  const httpStatusCodes = {
441
- [100]: 'Continue',
442
- [101]: 'Switching protocols',
443
- [102]: 'Processing',
444
- [103]: 'Early Hints',
445
- [200]: 'OK',
446
- [201]: 'Created',
447
- [202]: 'Accepted',
448
- [203]: 'Non-Authoritative Information',
449
- [204]: 'No Content',
450
- [205]: 'Reset Content',
451
- [206]: 'Partial Content',
452
- [207]: 'Multi-Status',
453
- [208]: 'Already Reported',
454
- [226]: 'IM Used',
455
- [300]: 'Multiple Choices',
456
- [301]: 'Moved Permanently',
457
- [302]: 'Found (Previously "Moved Temporarily")',
458
- [303]: 'See Other',
459
- [304]: 'Not Modified',
460
- [305]: 'Use Proxy',
461
- [306]: 'Switch Proxy',
462
- [307]: 'Temporary Redirect',
463
- [308]: 'Permanent Redirect',
464
- [400]: 'Bad Request',
465
- [401]: 'Unauthorized',
466
- [402]: 'Payment Required',
467
- [403]: 'Forbidden',
468
- [404]: 'Not Found',
469
- [405]: 'Method Not Allowed',
470
- [406]: 'Not Acceptable',
471
- [407]: 'Proxy Authentication Required',
472
- [408]: 'Request Timeout',
473
- [409]: 'Conflict',
474
- [410]: 'Gone',
475
- [411]: 'Length Required',
476
- [412]: 'Precondition Failed',
477
- [413]: 'Payload Too Large',
478
- [414]: 'URI Too Long',
479
- [415]: 'Unsupported Media Type',
480
- [416]: 'Range Not Satisfiable',
481
- [417]: 'Expectation Failed',
482
- [418]: 'I\'m a Teapot',
483
- [421]: 'Misdirected Request',
484
- [422]: 'Unprocessable Entity',
485
- [423]: 'Locked',
486
- [424]: 'Failed Dependency',
487
- [425]: 'Too Early',
488
- [426]: 'Upgrade Required',
489
- [428]: 'Precondition Required',
490
- [429]: 'Too Many Requests',
491
- [431]: 'Request Header Fields Too Large',
492
- [451]: 'Unavailable For Legal Reasons',
493
- [500]: 'Internal Server Error',
494
- [501]: 'Not Implemented',
495
- [502]: 'Bad Gateway',
496
- [503]: 'Service Unavailable',
497
- [504]: 'Gateway Timeout',
498
- [505]: 'HTTP Version Not Supported',
499
- [506]: 'Variant Also Negotiates',
500
- [507]: 'Insufficient Storage',
501
- [508]: 'Loop Detected',
502
- [510]: 'Not Extended',
503
- [511]: 'Network Authentication Required',
427
+ 100: 'Continue',
428
+ 101: 'Switching protocols',
429
+ 102: 'Processing',
430
+ 103: 'Early Hints',
431
+ 200: 'OK',
432
+ 201: 'Created',
433
+ 202: 'Accepted',
434
+ 203: 'Non-Authoritative Information',
435
+ 204: 'No Content',
436
+ 205: 'Reset Content',
437
+ 206: 'Partial Content',
438
+ 207: 'Multi-Status',
439
+ 208: 'Already Reported',
440
+ 226: 'IM Used',
441
+ 300: 'Multiple Choices',
442
+ 301: 'Moved Permanently',
443
+ 302: 'Found (Previously "Moved Temporarily")',
444
+ 303: 'See Other',
445
+ 304: 'Not Modified',
446
+ 305: 'Use Proxy',
447
+ 306: 'Switch Proxy',
448
+ 307: 'Temporary Redirect',
449
+ 308: 'Permanent Redirect',
450
+ 400: 'Bad Request',
451
+ 401: 'Unauthorized',
452
+ 402: 'Payment Required',
453
+ 403: 'Forbidden',
454
+ 404: 'Not Found',
455
+ 405: 'Method Not Allowed',
456
+ 406: 'Not Acceptable',
457
+ 407: 'Proxy Authentication Required',
458
+ 408: 'Request Timeout',
459
+ 409: 'Conflict',
460
+ 410: 'Gone',
461
+ 411: 'Length Required',
462
+ 412: 'Precondition Failed',
463
+ 413: 'Payload Too Large',
464
+ 414: 'URI Too Long',
465
+ 415: 'Unsupported Media Type',
466
+ 416: 'Range Not Satisfiable',
467
+ 417: 'Expectation Failed',
468
+ 418: "I'm a Teapot",
469
+ 421: 'Misdirected Request',
470
+ 422: 'Unprocessable Entity',
471
+ 423: 'Locked',
472
+ 424: 'Failed Dependency',
473
+ 425: 'Too Early',
474
+ 426: 'Upgrade Required',
475
+ 428: 'Precondition Required',
476
+ 429: 'Too Many Requests',
477
+ 431: 'Request Header Fields Too Large',
478
+ 451: 'Unavailable For Legal Reasons',
479
+ 500: 'Internal Server Error',
480
+ 501: 'Not Implemented',
481
+ 502: 'Bad Gateway',
482
+ 503: 'Service Unavailable',
483
+ 504: 'Gateway Timeout',
484
+ 505: 'HTTP Version Not Supported',
485
+ 506: 'Variant Also Negotiates',
486
+ 507: 'Insufficient Storage',
487
+ 508: 'Loop Detected',
488
+ 510: 'Not Extended',
489
+ 511: 'Network Authentication Required',
504
490
  };
505
491
  var EHttpStatusCode;
506
492
  (function (EHttpStatusCode) {
@@ -569,6 +555,110 @@ var EHttpStatusCode;
569
555
  EHttpStatusCode[EHttpStatusCode["NetworkAuthenticationRequired"] = 511] = "NetworkAuthenticationRequired";
570
556
  })(EHttpStatusCode || (EHttpStatusCode = {}));
571
557
 
558
+ const preStyles = 'font-family: monospace;' +
559
+ 'width: 100%;' +
560
+ 'max-width: 900px;' +
561
+ 'padding: 10px;' +
562
+ 'margin: 20px auto;' +
563
+ 'border-radius: 8px;' +
564
+ 'background-color: #494949;' +
565
+ 'box-shadow: 0px 0px 3px 2px rgb(255 255 255 / 20%);';
566
+ class HttpErrorRenderer extends BaseHttpResponseRenderer {
567
+ renderHtml(response) {
568
+ const data = response.body || {};
569
+ response.setContentType('text/html');
570
+ const keys = Object.keys(data).filter(key => !['statusCode', 'error', 'message'].includes(key));
571
+ return ('<html style="background-color: #333; color: #bbb;">' +
572
+ `<head><title>${data.statusCode} ${httpStatusCodes[data.statusCode]}</title></head>` +
573
+ `<body><center><h1>${data.statusCode} ${httpStatusCodes[data.statusCode]}</h1></center>` +
574
+ `<center><h4>${data.message}</h1></center><hr color="#666">` +
575
+ `<center style="color: #666;"> Wooks v${"0.4.27"} </center>` +
576
+ `${keys.length > 0
577
+ ? `<pre style="${preStyles}">${JSON.stringify({
578
+ ...data,
579
+ statusCode: undefined,
580
+ message: undefined,
581
+ error: undefined,
582
+ }, null, ' ')}</pre>`
583
+ : ''}` +
584
+ '</body></html>');
585
+ }
586
+ renderText(response) {
587
+ const data = response.body || {};
588
+ response.setContentType('text/plain');
589
+ const keys = Object.keys(data).filter(key => !['statusCode', 'error', 'message'].includes(key));
590
+ return (`${data.statusCode} ${httpStatusCodes[data.statusCode]}\n${data.message}` +
591
+ `\n\n${keys.length > 0
592
+ ? `${JSON.stringify({
593
+ ...data,
594
+ statusCode: undefined,
595
+ message: undefined,
596
+ error: undefined,
597
+ }, null, ' ')}`
598
+ : ''}`);
599
+ }
600
+ renderJson(response) {
601
+ const data = response.body || {};
602
+ response.setContentType('application/json');
603
+ const keys = Object.keys(data).filter(key => !['statusCode', 'error', 'message'].includes(key));
604
+ return (`{"statusCode":${escapeQuotes(data.statusCode)},` +
605
+ `"error":"${escapeQuotes(data.error)}",` +
606
+ `"message":"${escapeQuotes(data.message)}"` +
607
+ `${keys.length > 0
608
+ ?
609
+ `,${keys.map(k => `"${escapeQuotes(k)}":${JSON.stringify(data[k])}`).join(',')}`
610
+ : ''}}`);
611
+ }
612
+ render(response) {
613
+ const { acceptsJson, acceptsText, acceptsHtml } = useAccept();
614
+ response.status = response.body?.statusCode || 500;
615
+ if (acceptsJson()) {
616
+ return this.renderJson(response);
617
+ }
618
+ else if (acceptsHtml()) {
619
+ return this.renderHtml(response);
620
+ }
621
+ else if (acceptsText()) {
622
+ return this.renderText(response);
623
+ }
624
+ else {
625
+ return this.renderJson(response);
626
+ }
627
+ }
628
+ }
629
+ function escapeQuotes(s) {
630
+ return (typeof s === 'number' ? s : s || '').toString().replace(/"/g, '\\"');
631
+ }
632
+
633
+ class HttpError extends Error {
634
+ constructor(code = 500, _body = '') {
635
+ super(typeof _body === 'string' ? _body : _body.message);
636
+ this.code = code;
637
+ this._body = _body;
638
+ this.name = 'HttpError';
639
+ }
640
+ get body() {
641
+ return typeof this._body === 'string'
642
+ ? {
643
+ statusCode: this.code,
644
+ message: this.message,
645
+ error: httpStatusCodes[this.code],
646
+ }
647
+ : {
648
+ ...this._body,
649
+ statusCode: this.code,
650
+ message: this.message,
651
+ error: httpStatusCodes[this.code],
652
+ };
653
+ }
654
+ attachRenderer(renderer) {
655
+ this.renderer = renderer;
656
+ }
657
+ getRenderer() {
658
+ return this.renderer;
659
+ }
660
+ }
661
+
572
662
  const defaultStatus = {
573
663
  GET: EHttpStatusCode.OK,
574
664
  POST: EHttpStatusCode.Created,
@@ -649,7 +739,7 @@ class BaseHttpResponse {
649
739
  ...this._headers,
650
740
  };
651
741
  const setCookie = [...newCookies, ...cookies()];
652
- if (setCookie && setCookie.length) {
742
+ if (setCookie.length > 0) {
653
743
  this._headers['set-cookie'] = setCookie;
654
744
  }
655
745
  return this;
@@ -696,7 +786,7 @@ class BaseHttpResponse {
696
786
  }
697
787
  else {
698
788
  return new Promise((resolve, reject) => {
699
- stream.on('error', (e) => {
789
+ stream.on('error', e => {
700
790
  stream.destroy();
701
791
  res.end();
702
792
  reject(e);
@@ -709,8 +799,7 @@ class BaseHttpResponse {
709
799
  });
710
800
  }
711
801
  }
712
- else if (globalThis.Response &&
713
- this.body instanceof Response) {
802
+ else if (globalThis.Response && this.body instanceof Response) {
714
803
  this.mergeFetchStatus(this.body.status);
715
804
  if (method === 'HEAD') {
716
805
  res.end();
@@ -733,10 +822,12 @@ class BaseHttpResponse {
733
822
  else {
734
823
  const renderedBody = this.renderer.render(this);
735
824
  this.mergeStatus(renderedBody);
736
- res.writeHead(this.status, {
825
+ res
826
+ .writeHead(this.status, {
737
827
  'content-length': Buffer.byteLength(renderedBody),
738
828
  ...this._headers,
739
- }).end(method !== 'HEAD' ? renderedBody : '');
829
+ })
830
+ .end(method === 'HEAD' ? '' : renderedBody);
740
831
  }
741
832
  }
742
833
  }
@@ -747,133 +838,21 @@ async function respondWithFetch(fetchBody, res) {
747
838
  res.write(chunk);
748
839
  }
749
840
  }
750
- catch (e) {
841
+ catch (error) {
751
842
  }
752
843
  }
753
844
  res.end();
754
845
  }
755
846
 
756
- const preStyles = 'font-family: monospace;' +
757
- 'width: 100%;' +
758
- 'max-width: 900px;' +
759
- 'padding: 10px;' +
760
- 'margin: 20px auto;' +
761
- 'border-radius: 8px;' +
762
- 'background-color: #494949;' +
763
- 'box-shadow: 0px 0px 3px 2px rgb(255 255 255 / 20%);';
764
- class HttpErrorRenderer extends BaseHttpResponseRenderer {
765
- renderHtml(response) {
766
- const data = response.body || {};
767
- response.setContentType('text/html');
768
- const keys = Object.keys(data).filter((key) => !['statusCode', 'error', 'message'].includes(key));
769
- return ('<html style="background-color: #333; color: #bbb;">' +
770
- `<head><title>${data.statusCode} ${httpStatusCodes[data.statusCode]}</title></head>` +
771
- `<body><center><h1>${data.statusCode} ${httpStatusCodes[data.statusCode]}</h1></center>` +
772
- `<center><h4>${data.message}</h1></center><hr color="#666">` +
773
- `<center style="color: #666;"> Wooks v${"0.4.26"} </center>` +
774
- `${keys.length
775
- ? `<pre style="${preStyles}">${JSON.stringify({
776
- ...data,
777
- statusCode: undefined,
778
- message: undefined,
779
- error: undefined,
780
- }, null, ' ')}</pre>`
781
- : ''}` +
782
- '</body></html>');
783
- }
784
- renderText(response) {
785
- const data = response.body || {};
786
- response.setContentType('text/plain');
787
- const keys = Object.keys(data).filter((key) => !['statusCode', 'error', 'message'].includes(key));
788
- return (`${data.statusCode} ${httpStatusCodes[data.statusCode]}\n${data.message}` +
789
- `\n\n${keys.length
790
- ? `${JSON.stringify({
791
- ...data,
792
- statusCode: undefined,
793
- message: undefined,
794
- error: undefined,
795
- }, null, ' ')}`
796
- : ''}`);
797
- }
798
- renderJson(response) {
799
- const data = response.body || {};
800
- response.setContentType('application/json');
801
- const keys = Object.keys(data).filter((key) => !['statusCode', 'error', 'message'].includes(key));
802
- return (`{"statusCode":${escapeQuotes(data.statusCode)},` +
803
- `"error":"${escapeQuotes(data.error)}",` +
804
- `"message":"${escapeQuotes(data.message)}"` +
805
- `${keys.length
806
- ? ',' +
807
- keys
808
- .map((k) => `"${escapeQuotes(k)}":${JSON.stringify(data[k])}`)
809
- .join(',')
810
- : ''}}`);
811
- }
812
- render(response) {
813
- const { acceptsJson, acceptsText, acceptsHtml } = useAccept();
814
- response.status = response.body?.statusCode || 500;
815
- if (acceptsJson()) {
816
- return this.renderJson(response);
817
- }
818
- else if (acceptsHtml()) {
819
- return this.renderHtml(response);
820
- }
821
- else if (acceptsText()) {
822
- return this.renderText(response);
823
- }
824
- else {
825
- return this.renderJson(response);
826
- }
827
- }
828
- }
829
- function escapeQuotes(s) {
830
- return (typeof s === 'number' ? s : s || '')
831
- .toString()
832
- .replace(/[\""]/g, '\\"');
833
- }
834
-
835
- class HttpError extends Error {
836
- constructor(code = 500, _body = '') {
837
- super(typeof _body === 'string' ? _body : _body.message);
838
- this.code = code;
839
- this._body = _body;
840
- }
841
- get body() {
842
- return typeof this._body === 'string'
843
- ? {
844
- statusCode: this.code,
845
- message: this.message,
846
- error: httpStatusCodes[this.code],
847
- }
848
- : {
849
- ...this._body,
850
- statusCode: this.code,
851
- message: this.message,
852
- error: httpStatusCodes[this.code],
853
- };
854
- }
855
- attachRenderer(renderer) {
856
- this.renderer = renderer;
857
- }
858
- getRenderer() {
859
- return this.renderer;
860
- }
861
- }
862
-
863
847
  function createWooksResponder(renderer = new BaseHttpResponseRenderer(), errorRenderer = new HttpErrorRenderer()) {
864
848
  function createResponse(data) {
865
849
  const { hasResponded } = useResponse();
866
- if (hasResponded())
850
+ if (hasResponded()) {
867
851
  return null;
852
+ }
868
853
  if (data instanceof Error) {
869
854
  const r = new BaseHttpResponse(errorRenderer);
870
- let httpError;
871
- if (data instanceof HttpError) {
872
- httpError = data;
873
- }
874
- else {
875
- httpError = new HttpError(500, data.message);
876
- }
855
+ const httpError = data instanceof HttpError ? data : new HttpError(500, data.message);
877
856
  r.setBody(httpError.body);
878
857
  return r;
879
858
  }
@@ -886,7 +865,7 @@ function createWooksResponder(renderer = new BaseHttpResponseRenderer(), errorRe
886
865
  }
887
866
  return {
888
867
  createResponse,
889
- respond: (data) => createResponse(data)?.respond(),
868
+ respond: async (data) => createResponse(data)?.respond(),
890
869
  };
891
870
  }
892
871
 
@@ -929,12 +908,14 @@ class WooksHttp extends WooksAdapterBase {
929
908
  server.listen(...args);
930
909
  });
931
910
  }
932
- close(server) {
911
+ async close(server) {
933
912
  const srv = server || this.server;
934
913
  return new Promise((resolve, reject) => {
935
- srv?.close((err) => {
936
- if (err)
937
- return reject(err);
914
+ srv?.close(err => {
915
+ if (err) {
916
+ reject(err);
917
+ return;
918
+ }
938
919
  resolve(srv);
939
920
  });
940
921
  });
@@ -946,8 +927,8 @@ class WooksHttp extends WooksAdapterBase {
946
927
  this.server = server;
947
928
  }
948
929
  respond(data) {
949
- void this.responder.respond(data)?.catch((e) => {
950
- this.logger.error('Uncought response exception', e);
930
+ void this.responder.respond(data).catch(error => {
931
+ this.logger.error('Uncought response exception', error);
951
932
  });
952
933
  }
953
934
  getServerCb() {
@@ -958,10 +939,10 @@ class WooksHttp extends WooksAdapterBase {
958
939
  try {
959
940
  await this.processHandlers(handlers || [this.opts?.onNotFound]);
960
941
  }
961
- catch (e) {
962
- this.logger.error('Internal error, please report', e);
942
+ catch (error) {
943
+ this.logger.error('Internal error, please report', error);
963
944
  restoreCtx();
964
- this.respond(e);
945
+ this.respond(error);
965
946
  clearCtx();
966
947
  }
967
948
  }
@@ -986,11 +967,11 @@ class WooksHttp extends WooksAdapterBase {
986
967
  clearCtx();
987
968
  break;
988
969
  }
989
- catch (e) {
990
- this.logger.error(`Uncought route handler exception: ${store('event').get('req').url || ''}`, e);
970
+ catch (error) {
971
+ this.logger.error(`Uncought route handler exception: ${store('event').get('req').url || ''}`, error);
991
972
  if (isLastHandler) {
992
973
  restoreCtx();
993
- this.respond(e);
974
+ this.respond(error);
994
975
  clearCtx();
995
976
  }
996
977
  }