routup 3.2.0 → 4.0.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/dist/index.mjs CHANGED
@@ -4,7 +4,7 @@ import { subtle } from 'uncrypto';
4
4
  import { compile, all } from 'proxy-addr';
5
5
  import { getType, get } from 'mime-explorer';
6
6
  import Negotiator from 'negotiator';
7
- import { Readable, Writable } from 'readable-stream';
7
+ import { Readable, PassThrough, Writable } from 'readable-stream';
8
8
  import { HTTPError } from '@ebec/http';
9
9
  import { pathToRegexp } from 'path-to-regexp';
10
10
 
@@ -32,6 +32,7 @@ var HeaderName;
32
32
  HeaderName["CONTENT_LENGTH"] = "content-length";
33
33
  HeaderName["CONTENT_RANGE"] = "content-range";
34
34
  HeaderName["CONTENT_TYPE"] = "content-type";
35
+ HeaderName["CONNECTION"] = "connection";
35
36
  HeaderName["COOKIE"] = "cookie";
36
37
  HeaderName["ETag"] = "etag";
37
38
  HeaderName["HOST"] = "host";
@@ -46,6 +47,7 @@ var HeaderName;
46
47
  HeaderName["RETRY_AFTER"] = "retry-after";
47
48
  HeaderName["SET_COOKIE"] = "set-cookie";
48
49
  HeaderName["TRANSFER_ENCODING"] = "transfer-encoding";
50
+ HeaderName["X_ACCEL_BUFFERING"] = "x-accel-buffering";
49
51
  HeaderName["X_FORWARDED_HOST"] = "x-forwarded-host";
50
52
  HeaderName["X_FORWARDED_FOR"] = "x-forwarded-for";
51
53
  HeaderName["X_FORWARDED_PROTO"] = "x-forwarded-proto";
@@ -60,55 +62,6 @@ function isRequestCacheable(req, modifiedTime) {
60
62
  return new Date(modifiedSince) >= modifiedTime;
61
63
  }
62
64
 
63
- const envSymbol = Symbol.for('ReqEnv');
64
- function setRequestEnv(req, key, value) {
65
- if (envSymbol in req) {
66
- if (typeof key === 'object') {
67
- if (value) {
68
- req[envSymbol] = merge(req[envSymbol], key);
69
- } else {
70
- req[envSymbol] = key;
71
- }
72
- } else {
73
- req[envSymbol][key] = value;
74
- }
75
- return;
76
- }
77
- if (typeof key === 'object') {
78
- req[envSymbol] = key;
79
- return;
80
- }
81
- req[envSymbol] = {
82
- [key]: value
83
- };
84
- }
85
- function useRequestEnv(req, key) {
86
- if (envSymbol in req) {
87
- if (typeof key === 'string') {
88
- return req[envSymbol][key];
89
- }
90
- return req[envSymbol];
91
- }
92
- if (typeof key === 'string') {
93
- return undefined;
94
- }
95
- return {};
96
- }
97
- function unsetRequestEnv(req, key) {
98
- if (envSymbol in req) {
99
- if (hasOwnProperty(req[envSymbol], key)) {
100
- delete req[envSymbol][key];
101
- }
102
- }
103
- }
104
-
105
- function getRequestHeader(req, name) {
106
- return req.headers[name];
107
- }
108
- function setRequestHeader(req, name, value) {
109
- req.headers[name] = value;
110
- }
111
-
112
65
  /*
113
66
  Set-Cookie header field-values are sometimes comma joined in one string. This splits them without choking on commas
114
67
  that are within a single set-cookie field-value, such as in the Expires portion.
@@ -184,6 +137,12 @@ function setRequestHeader(req, name, value) {
184
137
  function isObject(item) {
185
138
  return !!item && typeof item === 'object' && !Array.isArray(item);
186
139
  }
140
+ function setProperty(record, property, value) {
141
+ record[property] = value;
142
+ }
143
+ function getProperty(req, property) {
144
+ return req[property];
145
+ }
187
146
 
188
147
  /**
189
148
  * Determine if object is a Stats object.
@@ -320,7 +279,7 @@ function basename(input, extension) {
320
279
  if (!lastSegment) {
321
280
  return input;
322
281
  }
323
- return extension && lastSegment.endsWith(extension) ? lastSegment.slice(0, -extension.length) : lastSegment;
282
+ return lastSegment;
324
283
  }
325
284
 
326
285
  function isPromise(p) {
@@ -376,12 +335,66 @@ function isWebResponse(input) {
376
335
  return typeof Response !== 'undefined' && input instanceof Response;
377
336
  }
378
337
 
379
- const NegotiatorSymbol = Symbol.for('ReqNegotiator');
338
+ const symbol$4 = Symbol.for('ReqEnv');
339
+ function setRequestEnv(req, key, value) {
340
+ const propertyValue = getProperty(req, symbol$4);
341
+ if (propertyValue) {
342
+ if (typeof key === 'object') {
343
+ if (value) {
344
+ setProperty(req, symbol$4, merge(propertyValue, key));
345
+ } else {
346
+ setProperty(req, symbol$4, key);
347
+ }
348
+ } else {
349
+ propertyValue[key] = value;
350
+ setProperty(req, symbol$4, propertyValue);
351
+ }
352
+ return;
353
+ }
354
+ if (typeof key === 'object') {
355
+ setProperty(req, symbol$4, key);
356
+ return;
357
+ }
358
+ setProperty(req, symbol$4, {
359
+ [key]: value
360
+ });
361
+ }
362
+ function useRequestEnv(req, key) {
363
+ const propertyValue = getProperty(req, symbol$4);
364
+ if (propertyValue) {
365
+ if (typeof key !== 'undefined') {
366
+ return propertyValue[key];
367
+ }
368
+ return propertyValue;
369
+ }
370
+ if (typeof key !== 'undefined') {
371
+ return undefined;
372
+ }
373
+ return {};
374
+ }
375
+ function unsetRequestEnv(req, key) {
376
+ const propertyValue = getProperty(req, symbol$4);
377
+ if (hasOwnProperty(propertyValue, key)) {
378
+ delete propertyValue[key];
379
+ }
380
+ }
381
+
382
+ function getRequestHeader(req, name) {
383
+ return req.headers[name];
384
+ }
385
+ function setRequestHeader(req, name, value) {
386
+ req.headers[name] = value;
387
+ }
388
+
389
+ const symbol$3 = Symbol.for('ReqNegotiator');
380
390
  function useRequestNegotiator(req) {
381
- if (NegotiatorSymbol in req) {
382
- return req[NegotiatorSymbol];
391
+ let value = getProperty(req, symbol$3);
392
+ if (value) {
393
+ return value;
383
394
  }
384
- return new Negotiator(req);
395
+ value = new Negotiator(req);
396
+ setProperty(req, symbol$3, value);
397
+ return value;
385
398
  }
386
399
 
387
400
  function getRequestAcceptableContentTypes(req) {
@@ -509,13 +522,10 @@ function findRouterOption(key, path) {
509
522
 
510
523
  const routerSymbol = Symbol.for('ReqRouterID');
511
524
  function setRequestRouterPath(req, path) {
512
- req[routerSymbol] = path;
525
+ setProperty(req, routerSymbol, path);
513
526
  }
514
527
  function useRequestRouterPath(req) {
515
- if (routerSymbol in req) {
516
- return req[routerSymbol];
517
- }
518
- return undefined;
528
+ return getProperty(req, routerSymbol);
519
529
  }
520
530
 
521
531
  function getRequestHostName(req, options) {
@@ -544,6 +554,10 @@ function getRequestHostName(req, options) {
544
554
  return index !== -1 ? hostname.substring(0, index) : hostname;
545
555
  }
546
556
 
557
+ function isRequestHTTP2(req) {
558
+ return typeof getRequestHeader(req, ':path') !== 'undefined' && typeof getRequestHeader(req, ':method') !== 'undefined';
559
+ }
560
+
547
561
  function getRequestIP(req, options) {
548
562
  options = options || {};
549
563
  let trustProxy;
@@ -556,32 +570,23 @@ function getRequestIP(req, options) {
556
570
  return addrs[addrs.length - 1];
557
571
  }
558
572
 
559
- const ReqMountPathSymbol = Symbol.for('ReqMountPath');
573
+ const symbol$2 = Symbol.for('ReqMountPath');
560
574
  function useRequestMountPath(req) {
561
- if (ReqMountPathSymbol in req) {
562
- return req[ReqMountPathSymbol];
563
- }
564
- return '/';
575
+ return getProperty(req, symbol$2) || '/';
565
576
  }
566
577
  function setRequestMountPath(req, basePath) {
567
- req[ReqMountPathSymbol] = basePath;
578
+ setProperty(req, symbol$2, basePath);
568
579
  }
569
580
 
570
- const ParamsSymbol = Symbol.for('ReqParams');
581
+ const symbol$1 = Symbol.for('ReqParams');
571
582
  function useRequestParams(req) {
572
- /* istanbul ignore next */ if ('params' in req) {
573
- return req.params;
574
- }
575
- if (ParamsSymbol in req) {
576
- return req[ParamsSymbol];
577
- }
578
- return {};
583
+ return getProperty(req, symbol$1) || getProperty(req, 'params') || {};
579
584
  }
580
585
  function useRequestParam(req, key) {
581
586
  return useRequestParams(req)[key];
582
587
  }
583
588
  function setRequestParams(req, data) {
584
- req[ParamsSymbol] = data;
589
+ setProperty(req, symbol$1, data);
585
590
  }
586
591
  function setRequestParam(req, key, value) {
587
592
  const params = useRequestParams(req);
@@ -591,18 +596,16 @@ function setRequestParam(req, key, value) {
591
596
 
592
597
  const PathSymbol = Symbol.for('ReqPath');
593
598
  function useRequestPath(req) {
594
- if ('path' in req) {
595
- return req.path;
596
- }
597
- if (PathSymbol in req) {
598
- return req[PathSymbol];
599
+ const path = getProperty(req, 'path') || getProperty(req, PathSymbol);
600
+ if (path) {
601
+ return path;
599
602
  }
600
603
  if (typeof req.url === 'undefined') {
601
604
  return '/';
602
605
  }
603
606
  const parsed = new URL(req.url, 'http://localhost/');
604
- req[PathSymbol] = parsed.pathname;
605
- return req[PathSymbol];
607
+ setProperty(req, PathSymbol, parsed.pathname);
608
+ return parsed.pathname;
606
609
  }
607
610
 
608
611
  function getRequestProtocol(req, options) {
@@ -749,15 +752,102 @@ function setResponseCacheHeaders(res, options) {
749
752
  res.setHeader('cache-control', cacheControls.join(', '));
750
753
  }
751
754
 
752
- const GoneSymbol = Symbol.for('ResGone');
755
+ const symbol = Symbol.for('ResGone');
753
756
  function isResponseGone(res) {
754
757
  if (res.headersSent || res.writableEnded) {
755
758
  return true;
756
759
  }
757
- if (GoneSymbol in res) {
758
- return res[GoneSymbol];
760
+ return getProperty(res, symbol) ?? false;
761
+ }
762
+ function setResponseGone(res, value) {
763
+ setProperty(res, symbol, value);
764
+ }
765
+
766
+ function serializeEventStreamMessage(message) {
767
+ let result = '';
768
+ if (message.id) {
769
+ result += `id: ${message.id}\n`;
759
770
  }
760
- return false;
771
+ if (message.event) {
772
+ result += `event: ${message.event}\n`;
773
+ }
774
+ if (typeof message.retry === 'number' && Number.isInteger(message.retry)) {
775
+ result += `retry: ${message.retry}\n`;
776
+ }
777
+ result += `data: ${message.data}\n\n`;
778
+ return result;
779
+ }
780
+
781
+ class EventStream {
782
+ open() {
783
+ this.response.req.on('close', ()=>this.end());
784
+ this.response.req.on('error', (err)=>{
785
+ this.emit('error', err);
786
+ this.end();
787
+ });
788
+ this.passThrough.on('data', (chunk)=>this.response.write(chunk));
789
+ this.passThrough.on('error', (err)=>{
790
+ this.emit('error', err);
791
+ this.end();
792
+ });
793
+ this.passThrough.on('close', ()=>this.end());
794
+ this.response.setHeader(HeaderName.CONTENT_TYPE, 'text/event-stream');
795
+ this.response.setHeader(HeaderName.CACHE_CONTROL, 'private, no-cache, no-store, no-transform, must-revalidate, max-age=0');
796
+ this.response.setHeader(HeaderName.X_ACCEL_BUFFERING, 'no');
797
+ if (!isRequestHTTP2(this.response.req)) {
798
+ this.response.setHeader(HeaderName.CONNECTION, 'keep-alive');
799
+ }
800
+ this.response.statusCode = 200;
801
+ }
802
+ write(message) {
803
+ if (typeof message === 'string') {
804
+ this.write({
805
+ data: message
806
+ });
807
+ return;
808
+ }
809
+ if (!this.passThrough.closed && this.passThrough.writable) {
810
+ this.passThrough.write(serializeEventStreamMessage(message));
811
+ }
812
+ }
813
+ end() {
814
+ if (this.flushed) return;
815
+ this.flushed = true;
816
+ if (!this.passThrough.closed) {
817
+ this.passThrough.end();
818
+ }
819
+ this.emit('close');
820
+ setResponseGone(this.response, true);
821
+ this.response.end();
822
+ }
823
+ on(event, listener) {
824
+ if (typeof this.eventHandlers[event] === 'undefined') {
825
+ this.eventHandlers[event] = [];
826
+ }
827
+ this.eventHandlers[event].push(listener);
828
+ }
829
+ emit(event, ...args) {
830
+ if (typeof this.eventHandlers[event] === 'undefined') {
831
+ return;
832
+ }
833
+ const listeners = this.eventHandlers[event].slice();
834
+ for(let i = 0; i < listeners.length; i++){
835
+ listeners[i].apply(this, args);
836
+ }
837
+ }
838
+ constructor(response){
839
+ this.response = response;
840
+ this.passThrough = new PassThrough({
841
+ encoding: 'utf-8'
842
+ });
843
+ this.flushed = false;
844
+ this.eventHandlers = {};
845
+ this.open();
846
+ }
847
+ }
848
+
849
+ function createEventStream(response) {
850
+ return new EventStream(response);
761
851
  }
762
852
 
763
853
  function appendResponseHeader(res, name, value) {
@@ -1632,12 +1722,14 @@ class PathMatcher {
1632
1722
  this.regexpKeys = [];
1633
1723
  this.path = path;
1634
1724
  this.regexpOptions = options || {};
1635
- this.regexp = pathToRegexp(path, this.regexpKeys, options);
1725
+ const regexp = pathToRegexp(path, options);
1726
+ this.regexp = regexp;
1727
+ this.regexpKeys = regexp.keys;
1636
1728
  }
1637
1729
  }
1638
1730
 
1639
1731
  function isPath(input) {
1640
- return typeof input === 'string' || input instanceof RegExp;
1732
+ return typeof input === 'string';
1641
1733
  }
1642
1734
 
1643
1735
  class Handler {
@@ -1830,15 +1922,9 @@ class Router {
1830
1922
  this.pathMatcher = undefined;
1831
1923
  return;
1832
1924
  }
1833
- if (typeof value === 'string') {
1834
- this.pathMatcher = new PathMatcher(withLeadingSlash(withoutTrailingSlash(`${value}`)), {
1835
- end: false
1836
- });
1837
- } else {
1838
- this.pathMatcher = new PathMatcher(value, {
1839
- end: false
1840
- });
1841
- }
1925
+ this.pathMatcher = new PathMatcher(withLeadingSlash(withoutTrailingSlash(`${value}`)), {
1926
+ end: false
1927
+ });
1842
1928
  }
1843
1929
  // --------------------------------------------------
1844
1930
  async executePipelineStep(context) {
@@ -2060,11 +2146,7 @@ class Router {
2060
2146
  for(let i = 0; i < input.length; i++){
2061
2147
  const item = input[i];
2062
2148
  if (isPath(item)) {
2063
- if (typeof item === 'string') {
2064
- path = withLeadingSlash(item);
2065
- } else {
2066
- path = item;
2067
- }
2149
+ path = withLeadingSlash(item);
2068
2150
  continue;
2069
2151
  }
2070
2152
  if (isRouterInstance(item)) {
@@ -2137,5 +2219,5 @@ class Router {
2137
2219
  }
2138
2220
  }
2139
2221
 
2140
- export { DispatchErrorEvent, DispatchEvent, Handler, HandlerSymbol, HandlerType, HeaderName, MethodName, PathMatcher, Router, RoutupError, appendResponseHeader, appendResponseHeaderDirective, coreHandler, createError, createNodeDispatcher, createRawDispatcher, createRequest, createResponse, createWebDispatcher, dispatch, dispatchNodeRequest, dispatchRawRequest, dispatchWebRequest, errorHandler, getRequestAcceptableCharset, getRequestAcceptableCharsets, getRequestAcceptableContentType, getRequestAcceptableContentTypes, getRequestAcceptableEncoding, getRequestAcceptableEncodings, getRequestAcceptableLanguage, getRequestAcceptableLanguages, getRequestHeader, getRequestHostName, getRequestIP, getRequestProtocol, isDispatcherErrorEvent, isError, isHandler, isHandlerConfig, isPath, isPlugin, isRequestCacheable, isResponseGone, matchRequestContentType, send, sendAccepted, sendCreated, sendFile, sendFormat, sendRedirect, sendStream, sendWebBlob, sendWebResponse, setRequestEnv, setRequestHeader, setRequestMountPath, setRequestParam, setRequestParams, setRequestRouterPath, setResponseCacheHeaders, setResponseContentTypeByFileName, setResponseHeaderAttachment, setResponseHeaderContentType, transformHeaderToTuples, transformHeadersToTuples, unsetRequestEnv, useRequestEnv, useRequestMountPath, useRequestNegotiator, useRequestParam, useRequestParams, useRequestPath, useRequestRouterPath };
2222
+ export { DispatchErrorEvent, DispatchEvent, EventStream, Handler, HandlerSymbol, HandlerType, HeaderName, MethodName, PathMatcher, Router, RoutupError, appendResponseHeader, appendResponseHeaderDirective, coreHandler, createError, createEventStream, createNodeDispatcher, createRawDispatcher, createRequest, createResponse, createWebDispatcher, dispatch, dispatchNodeRequest, dispatchRawRequest, dispatchWebRequest, errorHandler, getRequestAcceptableCharset, getRequestAcceptableCharsets, getRequestAcceptableContentType, getRequestAcceptableContentTypes, getRequestAcceptableEncoding, getRequestAcceptableEncodings, getRequestAcceptableLanguage, getRequestAcceptableLanguages, getRequestHeader, getRequestHostName, getRequestIP, getRequestProtocol, isDispatcherErrorEvent, isError, isHandler, isHandlerConfig, isPath, isPlugin, isRequestCacheable, isRequestHTTP2, isResponseGone, matchRequestContentType, send, sendAccepted, sendCreated, sendFile, sendFormat, sendRedirect, sendStream, sendWebBlob, sendWebResponse, setRequestEnv, setRequestHeader, setRequestMountPath, setRequestParam, setRequestParams, setRequestRouterPath, setResponseCacheHeaders, setResponseContentTypeByFileName, setResponseGone, setResponseHeaderAttachment, setResponseHeaderContentType, transformHeaderToTuples, transformHeadersToTuples, unsetRequestEnv, useRequestEnv, useRequestMountPath, useRequestNegotiator, useRequestParam, useRequestParams, useRequestPath, useRequestRouterPath };
2141
2223
  //# sourceMappingURL=index.mjs.map