@wooksjs/event-http 0.4.26 → 0.4.28

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