dx-server 0.12.2 → 0.13.0-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. package/README.md +417 -293
  2. package/{cjs → lib}/body.d.ts +2 -3
  3. package/lib/body.js +10 -0
  4. package/lib/body.js.map +1 -0
  5. package/{esm → lib}/bodyHelpers.d.ts +2 -4
  6. package/lib/bodyHelpers.js +102 -0
  7. package/lib/bodyHelpers.js.map +1 -0
  8. package/{esm → lib}/dx.d.ts +6 -9
  9. package/lib/dx.js +133 -0
  10. package/lib/dx.js.map +1 -0
  11. package/{cjs → lib}/dxHelpers.d.ts +2 -5
  12. package/lib/dxHelpers.js +135 -0
  13. package/lib/dxHelpers.js.map +1 -0
  14. package/lib/helpers.js.map +1 -0
  15. package/{cjs → lib}/index.d.ts +1 -2
  16. package/{esm/index.d.ts → lib/index.js} +1 -2
  17. package/lib/index.js.map +1 -0
  18. package/lib/logger.d.ts +4 -0
  19. package/lib/logger.js +64 -0
  20. package/lib/logger.js.map +1 -0
  21. package/lib/router.d.ts +42 -0
  22. package/lib/router.js +43 -0
  23. package/lib/router.js.map +1 -0
  24. package/lib/static.js +23 -0
  25. package/lib/static.js.map +1 -0
  26. package/{cjs → lib}/staticHelpers.d.ts +5 -3
  27. package/lib/staticHelpers.js +202 -0
  28. package/lib/staticHelpers.js.map +1 -0
  29. package/{cjs → lib}/stream.d.ts +3 -8
  30. package/lib/stream.js +96 -0
  31. package/lib/stream.js.map +1 -0
  32. package/lib/vendors/contentType.js +64 -0
  33. package/lib/vendors/contentType.js.map +1 -0
  34. package/{cjs → lib}/vendors/etag.d.ts +2 -5
  35. package/lib/vendors/etag.js +94 -0
  36. package/lib/vendors/etag.js.map +1 -0
  37. package/{cjs → lib}/vendors/fresh.d.ts +2 -2
  38. package/lib/vendors/fresh.js +88 -0
  39. package/lib/vendors/fresh.js.map +1 -0
  40. package/lib/vendors/mime.d.ts +1 -0
  41. package/lib/vendors/mime.js +35 -0
  42. package/lib/vendors/mime.js.map +1 -0
  43. package/{cjs → lib}/vendors/mimeDb.d.ts +2544 -2544
  44. package/lib/vendors/mimeDb.js +9435 -0
  45. package/lib/vendors/mimeDb.js.map +1 -0
  46. package/{cjs → lib}/vendors/mimeScore.d.ts +1 -1
  47. package/lib/vendors/mimeScore.js +44 -0
  48. package/lib/vendors/mimeScore.js.map +1 -0
  49. package/{cjs → lib}/vendors/onFinished.d.ts +1 -1
  50. package/lib/vendors/onFinished.js +231 -0
  51. package/lib/vendors/rangeParser.d.ts +12 -0
  52. package/lib/vendors/rangeParser.js +108 -0
  53. package/lib/vendors/rangeParser.js.map +1 -0
  54. package/package.json +32 -36
  55. package/cjs/body.js +0 -14
  56. package/cjs/bodyHelpers.d.ts +0 -16
  57. package/cjs/bodyHelpers.js +0 -101
  58. package/cjs/connect.d.ts +0 -5
  59. package/cjs/connect.js +0 -44
  60. package/cjs/dx.d.ts +0 -46
  61. package/cjs/dx.js +0 -144
  62. package/cjs/dxHelpers.js +0 -123
  63. package/cjs/express.d.ts +0 -4
  64. package/cjs/express.js +0 -43
  65. package/cjs/helpers.js +0 -14
  66. package/cjs/index.js +0 -38
  67. package/cjs/logger.d.ts +0 -3
  68. package/cjs/logger.js +0 -61
  69. package/cjs/package.json +0 -3
  70. package/cjs/polyfillWithResolvers.d.ts +0 -1
  71. package/cjs/polyfillWithResolvers.js +0 -17
  72. package/cjs/router.js +0 -47
  73. package/cjs/static.js +0 -27
  74. package/cjs/staticHelpers.js +0 -195
  75. package/cjs/stream.js +0 -97
  76. package/cjs/vendors/contentType.js +0 -92
  77. package/cjs/vendors/etag.js +0 -136
  78. package/cjs/vendors/fresh.js +0 -102
  79. package/cjs/vendors/mime.d.ts +0 -1
  80. package/cjs/vendors/mime.js +0 -42
  81. package/cjs/vendors/mimeDb.js +0 -9417
  82. package/cjs/vendors/mimeScore.js +0 -50
  83. package/cjs/vendors/onFinished.js +0 -245
  84. package/cjs/vendors/rangeParser.d.ts +0 -10
  85. package/cjs/vendors/rangeParser.js +0 -126
  86. package/esm/body.d.ts +0 -8
  87. package/esm/body.js +0 -11
  88. package/esm/bodyHelpers.js +0 -90
  89. package/esm/connect.d.ts +0 -5
  90. package/esm/connect.js +0 -40
  91. package/esm/dx.js +0 -128
  92. package/esm/dxHelpers.d.ts +0 -49
  93. package/esm/dxHelpers.js +0 -119
  94. package/esm/express.d.ts +0 -4
  95. package/esm/express.js +0 -35
  96. package/esm/helpers.js +0 -3
  97. package/esm/index.js +0 -9
  98. package/esm/logger.d.ts +0 -3
  99. package/esm/logger.js +0 -57
  100. package/esm/polyfillWithResolvers.d.ts +0 -1
  101. package/esm/polyfillWithResolvers.js +0 -16
  102. package/esm/router.js +0 -44
  103. package/esm/static.d.ts +0 -5
  104. package/esm/static.js +0 -23
  105. package/esm/staticHelpers.d.ts +0 -18
  106. package/esm/staticHelpers.js +0 -188
  107. package/esm/stream.d.ts +0 -12
  108. package/esm/stream.js +0 -92
  109. package/esm/vendors/contentType.d.ts +0 -4
  110. package/esm/vendors/contentType.js +0 -88
  111. package/esm/vendors/etag.d.ts +0 -10
  112. package/esm/vendors/etag.js +0 -105
  113. package/esm/vendors/fresh.d.ts +0 -23
  114. package/esm/vendors/fresh.js +0 -96
  115. package/esm/vendors/mime.d.ts +0 -1
  116. package/esm/vendors/mime.js +0 -35
  117. package/esm/vendors/mimeDb.d.ts +0 -9413
  118. package/esm/vendors/mimeDb.js +0 -9415
  119. package/esm/vendors/mimeScore.d.ts +0 -5
  120. package/esm/vendors/mimeScore.js +0 -46
  121. package/esm/vendors/onFinished.d.ts +0 -14
  122. package/esm/vendors/onFinished.js +0 -241
  123. package/esm/vendors/rangeParser.d.ts +0 -10
  124. package/esm/vendors/rangeParser.js +0 -122
  125. /package/{cjs → lib}/helpers.d.ts +0 -0
  126. /package/{esm/helpers.d.ts → lib/helpers.js} +0 -0
  127. /package/{cjs → lib}/static.d.ts +0 -0
  128. /package/{cjs → lib}/vendors/contentType.d.ts +0 -0
@@ -0,0 +1,64 @@
1
+ // https://github.com/jshttp/content-type/blob/d02574e9640bd4370f148c767b1b877b5a300070/index.js#L106
2
+ /**
3
+ * RegExp to match type in RFC 7231 sec 3.1.1.1
4
+ *
5
+ * media-type = type "/" subtype
6
+ * type = token
7
+ * subtype = token
8
+ */
9
+ const typeRegexp = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+\/[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
10
+ /**
11
+ * RegExp to match *( ";" parameter ) in RFC 7231 sec 3.1.1.1
12
+ *
13
+ * parameter = token "=" ( token / quoted-string )
14
+ * token = 1*tchar
15
+ * tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*"
16
+ * / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~"
17
+ * / DIGIT / ALPHA
18
+ * ; any VCHAR, except delimiters
19
+ * quoted-string = DQUOTE *( qdtext / quoted-pair ) DQUOTE
20
+ * qdtext = HTAB / SP / %x21 / %x23-5B / %x5D-7E / obs-text
21
+ * obs-text = %x80-FF
22
+ * quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text )
23
+ */
24
+ const paramRegexp = /; *([!#$%&'*+.^_`|~0-9A-Za-z-]+) *= *("(?:[\u000b\u0020\u0021\u0023-\u005b\u005d-\u007e\u0080-\u00ff]|\\[\u000b\u0020-\u00ff])*"|[!#$%&'*+.^_`|~0-9A-Za-z-]+) */g; // eslint-disable-line no-control-regex
25
+ /**
26
+ * RegExp to match quoted-pair in RFC 7230 sec 3.2.6
27
+ *
28
+ * quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text )
29
+ * obs-text = %x80-FF
30
+ */
31
+ const qescRegexp = /\\([\u000b\u0020-\u00ff])/g; // eslint-disable-line no-control-regex
32
+ export function parseContentType(header) {
33
+ let index = header.indexOf(';');
34
+ const mediaType = index !== -1 ? header.slice(0, index).trim() : header.trim();
35
+ if (!typeRegexp.test(mediaType))
36
+ throw new TypeError(`invalid media type: ${mediaType}`);
37
+ const parameters = Object.create(null);
38
+ // parse parameters
39
+ if (index !== -1) {
40
+ let key;
41
+ let match;
42
+ let value;
43
+ const regexp = new RegExp(paramRegexp);
44
+ regexp.lastIndex = index;
45
+ while ((match = regexp.exec(header))) {
46
+ if (match.index !== index)
47
+ throw new TypeError('invalid parameter format');
48
+ index += match[0].length;
49
+ key = match[1].toLowerCase();
50
+ value = match[2];
51
+ if (value.charCodeAt(0) === 0x22 /* " */) {
52
+ // remove quotes
53
+ value = value.slice(1, -1);
54
+ // remove escapes
55
+ if (value.indexOf('\\') !== -1)
56
+ value = value.replace(qescRegexp, '$1');
57
+ }
58
+ parameters[key] = value;
59
+ }
60
+ if (index !== header.length)
61
+ throw new TypeError('invalid parameter format');
62
+ }
63
+ return { mediaType, parameters };
64
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contentType.js","sourceRoot":"","sources":["../../src/vendors/contentType.ts"],"names":[],"mappings":"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","sourcesContent":["// https://github.com/jshttp/content-type/blob/d02574e9640bd4370f148c767b1b877b5a300070/index.js#L106\n/**\n * RegExp to match type in RFC 7231 sec 3.1.1.1\n *\n * media-type = type \"/\" subtype\n * type = token\n * subtype = token\n */\nconst typeRegexp = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+\\/[!#$%&'*+.^_`|~0-9A-Za-z-]+$/\n/**\n * RegExp to match *( \";\" parameter ) in RFC 7231 sec 3.1.1.1\n *\n * parameter = token \"=\" ( token / quoted-string )\n * token = 1*tchar\n * tchar = \"!\" / \"#\" / \"$\" / \"%\" / \"&\" / \"'\" / \"*\"\n * / \"+\" / \"-\" / \".\" / \"^\" / \"_\" / \"`\" / \"|\" / \"~\"\n * / DIGIT / ALPHA\n * ; any VCHAR, except delimiters\n * quoted-string = DQUOTE *( qdtext / quoted-pair ) DQUOTE\n * qdtext = HTAB / SP / %x21 / %x23-5B / %x5D-7E / obs-text\n * obs-text = %x80-FF\n * quoted-pair = \"\\\" ( HTAB / SP / VCHAR / obs-text )\n */\nconst paramRegexp =\n\t/; *([!#$%&'*+.^_`|~0-9A-Za-z-]+) *= *(\"(?:[\\u000b\\u0020\\u0021\\u0023-\\u005b\\u005d-\\u007e\\u0080-\\u00ff]|\\\\[\\u000b\\u0020-\\u00ff])*\"|[!#$%&'*+.^_`|~0-9A-Za-z-]+) */g // eslint-disable-line no-control-regex\nconst textRegexp = /^[\\u000b\\u0020-\\u007e\\u0080-\\u00ff]+$/ // eslint-disable-line no-control-regex\nconst tokenRegexp = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/\n/**\n * RegExp to match quoted-pair in RFC 7230 sec 3.2.6\n *\n * quoted-pair = \"\\\" ( HTAB / SP / VCHAR / obs-text )\n * obs-text = %x80-FF\n */\nconst qescRegexp = /\\\\([\\u000b\\u0020-\\u00ff])/g // eslint-disable-line no-control-regex\n\nexport function parseContentType(header: string) {\n\tlet index = header.indexOf(';')\n\tconst mediaType = index !== -1 ? header.slice(0, index).trim() : header.trim()\n\n\tif (!typeRegexp.test(mediaType)) throw new TypeError(`invalid media type: ${mediaType}`)\n\tconst parameters: Record<string, string> = Object.create(null)\n\n\t// parse parameters\n\tif (index !== -1) {\n\t\tlet key\n\t\tlet match\n\t\tlet value\n\n\t\tconst regexp = new RegExp(paramRegexp)\n\t\tregexp.lastIndex = index\n\n\t\twhile ((match = regexp.exec(header))) {\n\t\t\tif (match.index !== index) throw new TypeError('invalid parameter format')\n\n\t\t\tindex += match[0].length\n\t\t\tkey = match[1].toLowerCase()\n\t\t\tvalue = match[2]\n\n\t\t\tif (value.charCodeAt(0) === 0x22 /* \" */) {\n\t\t\t\t// remove quotes\n\t\t\t\tvalue = value.slice(1, -1)\n\t\t\t\t// remove escapes\n\t\t\t\tif (value.indexOf('\\\\') !== -1) value = value.replace(qescRegexp, '$1')\n\t\t\t}\n\n\t\t\tparameters[key] = value\n\t\t}\n\n\t\tif (index !== header.length) throw new TypeError('invalid parameter format')\n\t}\n\n\treturn {mediaType, parameters}\n}\n\n/**\n * RegExp to match chars that must be quoted-pair in RFC 7230 sec 3.2.6\n */\nconst quoteRegexp = /([\\\\\"])/g\nfunction qstring(str: string) {\n\t// no need to quote tokens\n\tif (tokenRegexp.test(str)) return str\n\n\tif (str.length > 0 && !textRegexp.test(str)) throw new TypeError(`invalid parameter value: ${str}`)\n\treturn `\"${str.replace(quoteRegexp, '\\\\$1')}\"`\n}\n\nfunction formatContentType({mediaType, parameters}: {mediaType: string; parameters?: Record<string, string>}) {\n\tif (!mediaType || !typeRegexp.test(mediaType)) throw new TypeError(`invalid type: ${mediaType}`)\n\treturn `${mediaType}${\n\t\tparameters\n\t\t\t? Object.keys(parameters)\n\t\t\t\t\t.sort()\n\t\t\t\t\t.map(key => `; ${key}=${qstring(parameters[key])}`)\n\t\t\t\t\t.join('')\n\t\t\t: ''\n\t}`\n}\n"]}
@@ -1,10 +1,7 @@
1
- /// <reference types="node" resolution-mode="require"/>
2
- /// <reference types="node" resolution-mode="require"/>
3
- /// <reference types="node" resolution-mode="require"/>
4
1
  import type { IncomingMessage } from 'node:http';
5
2
  import { type Stats } from 'node:fs';
6
- export declare function entityTag(buf: Buffer, weak?: boolean): string;
7
- export declare function entityTagPath(fileStat: Stats, filePath: string, weak?: boolean): Promise<string>;
3
+ export declare function entityTag(buf: Buffer): string;
4
+ export declare function entityTagPath(fileStat: Stats, filePath: string): Promise<string>;
8
5
  export declare function statTag(stat: Stats): string;
9
6
  export declare function isFreshETag(req: IncomingMessage, etag: string): true | undefined;
10
7
  export declare function isFreshModifiedSince(req: IncomingMessage, lastModified: string): boolean | undefined;
@@ -0,0 +1,94 @@
1
+ // etag: https://github.com/jshttp/etag/blob/b9f0642256e63654287299d205bc6ced71b1a228/index.js#L39
2
+ import { createHash } from 'node:crypto';
3
+ import { createReadStream } from 'node:fs';
4
+ import { pipeline } from 'node:stream/promises';
5
+ export function entityTag(buf) {
6
+ // content hash is a strong validator: same bytes -> same tag. never weak (W/).
7
+ return buf.length
8
+ ? `"${buf.length.toString(16)}-${createHash('sha1').update(buf).digest('base64').substring(0, 27)}"`
9
+ : '"0-2jmj7l5rSw0yVb/vlWAYkK/YBwk"';
10
+ // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag#directives
11
+ // weak W/ vs strong eTag
12
+ // same weak eTag: 2 resources might be semantically equivalent, but not byte-for-byte identical
13
+ }
14
+ export async function entityTagPath(fileStat, filePath) {
15
+ const hash = createHash('sha1');
16
+ await pipeline(createReadStream(filePath), hash);
17
+ return `"${fileStat.size.toString(16)}-${hash.digest('base64').substring(0, 27)}"`;
18
+ // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag#directives
19
+ // weak W/ vs strong eTag
20
+ // same weak eTag: 2 resources might be semantically equivalent, but not byte-for-byte identical
21
+ }
22
+ const cacheControlNoCacheRegexp = /(?:^|,)\s*?no-cache\s*?(?:,|$)/;
23
+ export function statTag(stat) {
24
+ const mtime = stat.mtime.getTime().toString(16);
25
+ const size = stat.size.toString(16);
26
+ return `"${size}-${mtime}"`;
27
+ }
28
+ // https://github.com/jshttp/fresh/blob/05254186fd7428915224db46144fc94293a7df7d/index.js#L33
29
+ export function isFreshETag(req, etag) {
30
+ const noneMatch = req.headers['if-none-match'];
31
+ if (!noneMatch)
32
+ return;
33
+ // Always return stale when Cache-Control: no-cache
34
+ // to support end-to-end reload requests
35
+ // https://tools.ietf.org/html/rfc2616#section-14.9.4
36
+ const cacheControl = req.headers['cache-control'];
37
+ if (cacheControl && cacheControlNoCacheRegexp.test(cacheControl))
38
+ return;
39
+ if (noneMatch && noneMatch !== '*') {
40
+ if (!etag)
41
+ return;
42
+ let etagStale = true;
43
+ for (const match of parseTokenList(noneMatch)) {
44
+ if (match === etag || match === `W/${etag}` || `W/${match}` === etag) {
45
+ etagStale = false;
46
+ break;
47
+ }
48
+ }
49
+ if (etagStale)
50
+ return;
51
+ }
52
+ return true;
53
+ }
54
+ export function isFreshModifiedSince(req, lastModified) {
55
+ const modifiedSince = req.headers['if-modified-since'];
56
+ if (!modifiedSince)
57
+ return;
58
+ // Always return stale when Cache-Control: no-cache
59
+ // to support end-to-end reload requests
60
+ // https://tools.ietf.org/html/rfc2616#section-14.9.4
61
+ const cacheControl = req.headers['cache-control'];
62
+ if (cacheControl && cacheControlNoCacheRegexp.test(cacheControl))
63
+ return;
64
+ if (modifiedSince && lastModified) {
65
+ const lastModifiedDate = Date.parse(lastModified);
66
+ const modifiedSinceDate = Date.parse(modifiedSince);
67
+ return !isNaN(lastModifiedDate) && !isNaN(modifiedSinceDate) && lastModifiedDate <= modifiedSinceDate;
68
+ }
69
+ return true;
70
+ }
71
+ function parseTokenList(str) {
72
+ let end = 0;
73
+ const list = [];
74
+ let start = 0;
75
+ // gather tokens
76
+ for (let i = 0, len = str.length; i < len; i++) {
77
+ switch (str.charCodeAt(i)) {
78
+ case 0x20 /* */:
79
+ if (start === end)
80
+ start = end = i + 1;
81
+ break;
82
+ case 0x2c /* , */:
83
+ list.push(str.substring(start, end));
84
+ start = end = i + 1;
85
+ break;
86
+ default:
87
+ end = i + 1;
88
+ break;
89
+ }
90
+ }
91
+ // final token
92
+ list.push(str.substring(start, end));
93
+ return list;
94
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"file":"etag.js","sourceRoot":"","sources":["../../src/vendors/etag.ts"],"names":[],"mappings":"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","sourcesContent":["// etag: https://github.com/jshttp/etag/blob/b9f0642256e63654287299d205bc6ced71b1a228/index.js#L39\nimport {createHash} from 'node:crypto'\nimport type {IncomingMessage} from 'node:http'\nimport {createReadStream, type Stats} from 'node:fs'\nimport {pipeline} from 'node:stream/promises'\n\nexport function entityTag(buf: Buffer) {\n\t// content hash is a strong validator: same bytes -> same tag. never weak (W/).\n\treturn buf.length\n\t\t? `\"${buf.length.toString(16)}-${createHash('sha1').update(buf).digest('base64').substring(0, 27)}\"`\n\t\t: '\"0-2jmj7l5rSw0yVb/vlWAYkK/YBwk\"'\n\t// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag#directives\n\t// weak W/ vs strong eTag\n\t// same weak eTag: 2 resources might be semantically equivalent, but not byte-for-byte identical\n}\n\nexport async function entityTagPath(fileStat: Stats, filePath: string) {\n\tconst hash = createHash('sha1')\n\tawait pipeline(createReadStream(filePath), hash)\n\treturn `\"${fileStat.size.toString(16)}-${hash.digest('base64').substring(0, 27)}\"`\n\t// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag#directives\n\t// weak W/ vs strong eTag\n\t// same weak eTag: 2 resources might be semantically equivalent, but not byte-for-byte identical\n}\n\nconst cacheControlNoCacheRegexp = /(?:^|,)\\s*?no-cache\\s*?(?:,|$)/\nexport function statTag(stat: Stats) {\n\tconst mtime = stat.mtime.getTime().toString(16)\n\tconst size = stat.size.toString(16)\n\n\treturn `\"${size}-${mtime}\"`\n}\n// https://github.com/jshttp/fresh/blob/05254186fd7428915224db46144fc94293a7df7d/index.js#L33\nexport function isFreshETag(req: IncomingMessage, etag: string) {\n\tconst noneMatch = req.headers['if-none-match']\n\tif (!noneMatch) return\n\n\t// Always return stale when Cache-Control: no-cache\n\t// to support end-to-end reload requests\n\t// https://tools.ietf.org/html/rfc2616#section-14.9.4\n\tconst cacheControl = req.headers['cache-control']\n\tif (cacheControl && cacheControlNoCacheRegexp.test(cacheControl)) return\n\n\tif (noneMatch && noneMatch !== '*') {\n\t\tif (!etag) return\n\n\t\tlet etagStale = true\n\t\tfor (const match of parseTokenList(noneMatch)) {\n\t\t\tif (match === etag || match === `W/${etag}` || `W/${match}` === etag) {\n\t\t\t\tetagStale = false\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tif (etagStale) return\n\t}\n\n\treturn true\n}\n\nexport function isFreshModifiedSince(req: IncomingMessage, lastModified: string) {\n\tconst modifiedSince = req.headers['if-modified-since']\n\tif (!modifiedSince) return\n\n\t// Always return stale when Cache-Control: no-cache\n\t// to support end-to-end reload requests\n\t// https://tools.ietf.org/html/rfc2616#section-14.9.4\n\tconst cacheControl = req.headers['cache-control']\n\tif (cacheControl && cacheControlNoCacheRegexp.test(cacheControl)) return\n\n\tif (modifiedSince && lastModified) {\n\t\tconst lastModifiedDate = Date.parse(lastModified)\n\t\tconst modifiedSinceDate = Date.parse(modifiedSince)\n\t\treturn !isNaN(lastModifiedDate) && !isNaN(modifiedSinceDate) && lastModifiedDate <= modifiedSinceDate\n\t}\n\treturn true\n}\n\nfunction parseTokenList(str: string) {\n\tlet end = 0\n\tconst list = []\n\tlet start = 0\n\n\t// gather tokens\n\tfor (let i = 0, len = str.length; i < len; i++) {\n\t\tswitch (str.charCodeAt(i)) {\n\t\t\tcase 0x20 /* */:\n\t\t\t\tif (start === end) start = end = i + 1\n\t\t\t\tbreak\n\t\t\tcase 0x2c /* , */:\n\t\t\t\tlist.push(str.substring(start, end))\n\t\t\t\tstart = end = i + 1\n\t\t\t\tbreak\n\t\t\tdefault:\n\t\t\t\tend = i + 1\n\t\t\t\tbreak\n\t\t}\n\t}\n\t// final token\n\tlist.push(str.substring(start, end))\n\treturn list\n}\n"]}
@@ -6,7 +6,7 @@
6
6
  * @return {Boolean}
7
7
  * @public
8
8
  */
9
- export declare function fresh(reqHeaders: any, resHeaders: any): boolean;
9
+ export declare function fresh(reqHeaders: Record<string, any>, resHeaders: Record<string, any>): boolean;
10
10
  /**
11
11
  * Parse an HTTP Date into a number.
12
12
  *
@@ -20,4 +20,4 @@ export declare function parseHttpDate(date: any): number;
20
20
  * @param {string} str
21
21
  * @private
22
22
  */
23
- export declare function parseTokenList(str: any): any[];
23
+ export declare function parseTokenList(str: string): string[];
@@ -0,0 +1,88 @@
1
+ const cacheControlNoCacheRegexp = /(?:^|,)\s*?no-cache\s*?(?:,|$)/;
2
+ /**
3
+ * Check freshness of the response using request and response headers.
4
+ *
5
+ * @param {Object} reqHeaders
6
+ * @param {Object} resHeaders
7
+ * @return {Boolean}
8
+ * @public
9
+ */
10
+ export function fresh(reqHeaders, resHeaders) {
11
+ // fields
12
+ const modifiedSince = reqHeaders['if-modified-since'];
13
+ const noneMatch = reqHeaders['if-none-match'];
14
+ // unconditional request
15
+ if (!modifiedSince && !noneMatch)
16
+ return false;
17
+ // Always return stale when Cache-Control: no-cache
18
+ // to support end-to-end reload requests
19
+ // https://tools.ietf.org/html/rfc2616#section-14.9.4
20
+ const cacheControl = reqHeaders['cache-control'];
21
+ if (cacheControl && cacheControlNoCacheRegexp.test(cacheControl))
22
+ return false;
23
+ // if-none-match takes precedent over if-modified-since
24
+ if (noneMatch) {
25
+ if (noneMatch === '*')
26
+ return true;
27
+ const etag = resHeaders.etag;
28
+ if (!etag)
29
+ return false;
30
+ const matches = parseTokenList(noneMatch);
31
+ for (let i = 0; i < matches.length; i++) {
32
+ const match = matches[i];
33
+ if (match === etag || match === 'W/' + etag || 'W/' + match === etag)
34
+ return true;
35
+ }
36
+ return false;
37
+ }
38
+ // if-modified-since
39
+ if (modifiedSince) {
40
+ const lastModified = resHeaders['last-modified'];
41
+ const modifiedStale = !lastModified || !(parseHttpDate(lastModified) <= parseHttpDate(modifiedSince));
42
+ if (modifiedStale)
43
+ return false;
44
+ }
45
+ return true;
46
+ }
47
+ /**
48
+ * Parse an HTTP Date into a number.
49
+ *
50
+ * @param {string} date
51
+ * @private
52
+ */
53
+ export function parseHttpDate(date) {
54
+ const timestamp = date && Date.parse(date);
55
+ // istanbul ignore next: guard against date.js Date.parse patching
56
+ return typeof timestamp === 'number' ? timestamp : NaN;
57
+ }
58
+ /**
59
+ * Parse a HTTP token list.
60
+ *
61
+ * @param {string} str
62
+ * @private
63
+ */
64
+ export function parseTokenList(str) {
65
+ let end = 0;
66
+ const list = [];
67
+ let start = 0;
68
+ // gather tokens
69
+ let i = 0, len = str.length;
70
+ for (; i < len; i++) {
71
+ switch (str.charCodeAt(i)) {
72
+ case 0x20 /* */:
73
+ if (start === end)
74
+ start = end = i + 1;
75
+ break;
76
+ case 0x2c /* , */:
77
+ list.push(str.substring(start, end));
78
+ start = end = i + 1;
79
+ break;
80
+ default:
81
+ end = i + 1;
82
+ break;
83
+ }
84
+ }
85
+ // final token
86
+ list.push(str.substring(start, end));
87
+ return list;
88
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fresh.js","sourceRoot":"","sources":["../../src/vendors/fresh.ts"],"names":[],"mappings":"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","sourcesContent":["const cacheControlNoCacheRegexp = /(?:^|,)\\s*?no-cache\\s*?(?:,|$)/\n\n/**\n * Check freshness of the response using request and response headers.\n *\n * @param {Object} reqHeaders\n * @param {Object} resHeaders\n * @return {Boolean}\n * @public\n */\n\nexport function fresh(reqHeaders: Record<string, any>, resHeaders: Record<string, any>) {\n\t// fields\n\tconst modifiedSince = reqHeaders['if-modified-since']\n\tconst noneMatch = reqHeaders['if-none-match']\n\n\t// unconditional request\n\tif (!modifiedSince && !noneMatch) return false\n\n\t// Always return stale when Cache-Control: no-cache\n\t// to support end-to-end reload requests\n\t// https://tools.ietf.org/html/rfc2616#section-14.9.4\n\tconst cacheControl = reqHeaders['cache-control']\n\tif (cacheControl && cacheControlNoCacheRegexp.test(cacheControl)) return false\n\n\t// if-none-match takes precedent over if-modified-since\n\tif (noneMatch) {\n\t\tif (noneMatch === '*') return true\n\t\tconst etag = resHeaders.etag\n\n\t\tif (!etag) return false\n\n\t\tconst matches = parseTokenList(noneMatch)\n\t\tfor (let i = 0; i < matches.length; i++) {\n\t\t\tconst match = matches[i]\n\t\t\tif (match === etag || match === 'W/' + etag || 'W/' + match === etag) return true\n\t\t}\n\n\t\treturn false\n\t}\n\n\t// if-modified-since\n\tif (modifiedSince) {\n\t\tconst lastModified = resHeaders['last-modified']\n\t\tconst modifiedStale = !lastModified || !(parseHttpDate(lastModified) <= parseHttpDate(modifiedSince))\n\n\t\tif (modifiedStale) return false\n\t}\n\n\treturn true\n}\n\n/**\n * Parse an HTTP Date into a number.\n *\n * @param {string} date\n * @private\n */\n\nexport function parseHttpDate(date: any) {\n\tconst timestamp = date && Date.parse(date)\n\n\t// istanbul ignore next: guard against date.js Date.parse patching\n\treturn typeof timestamp === 'number' ? timestamp : NaN\n}\n\n/**\n * Parse a HTTP token list.\n *\n * @param {string} str\n * @private\n */\n\nexport function parseTokenList(str: string) {\n\tlet end = 0\n\tconst list: string[] = []\n\tlet start = 0\n\n\t// gather tokens\n\tlet i = 0,\n\t\tlen = str.length\n\tfor (; i < len; i++) {\n\t\tswitch (str.charCodeAt(i)) {\n\t\t\tcase 0x20 /* */:\n\t\t\t\tif (start === end) start = end = i + 1\n\t\t\t\tbreak\n\t\t\tcase 0x2c /* , */:\n\t\t\t\tlist.push(str.substring(start, end))\n\t\t\t\tstart = end = i + 1\n\t\t\t\tbreak\n\t\t\tdefault:\n\t\t\t\tend = i + 1\n\t\t\t\tbreak\n\t\t}\n\t}\n\n\t// final token\n\tlist.push(str.substring(start, end))\n\n\treturn list\n}\n"]}
@@ -0,0 +1 @@
1
+ export declare function contentTypeForExtension(extension: string): string | undefined;
@@ -0,0 +1,35 @@
1
+ import mimeDbRaw from './mimeDb.js';
2
+ import { mimeScore } from './mimeScore.js';
3
+ const mimeDb = mimeDbRaw;
4
+ const extensionToMime = Object.create(null);
5
+ for (const [type, { extensions = [] }] of Object.entries(mimeDb))
6
+ for (const extension of extensions)
7
+ extensionToMime[extension] = preferredType(extension, type, extensionToMime[extension]);
8
+ function preferredType(ext, type0, type1) {
9
+ const score0 = type0 ? mimeScore(type0, mimeDb[type0].source) : 0;
10
+ const score1 = type1 ? mimeScore(type1, mimeDb[type1].source) : 0;
11
+ return score0 > score1 ? type0 : type1;
12
+ }
13
+ export function contentTypeForExtension(extension) {
14
+ const mimeType = extensionToMime[extension.toLowerCase()];
15
+ if (!mimeType)
16
+ return;
17
+ if (!mimeType.includes('charset')) {
18
+ const charset = determineCharset(mimeType);
19
+ if (charset)
20
+ return mimeType + '; charset=' + charset.toLowerCase();
21
+ }
22
+ return mimeType;
23
+ }
24
+ const extractTypeRegexp = /^\s*([^;\s]*)(?:;|\s|$)/;
25
+ const textTypeRegexp = /^text\//i;
26
+ function determineCharset(type) {
27
+ // _TODO: use media-typer
28
+ const match = extractTypeRegexp.exec(type);
29
+ const mime = match && mimeDb[match[1].toLowerCase()];
30
+ if (mime?.charset)
31
+ return mime.charset;
32
+ // default text/* to utf-8
33
+ if (match && textTypeRegexp.test(match[1]))
34
+ return 'UTF-8';
35
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mime.js","sourceRoot":"","sources":["../../src/vendors/mime.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,aAAa,CAAA;AACnC,OAAO,EAAC,SAAS,EAAC,MAAM,gBAAgB,CAAA;AAExC,MAAM,MAAM,GAQR,SAAS,CAAA;AAEb,MAAM,eAAe,GAAuC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;AAE/E,KAAK,MAAM,CAAC,IAAI,EAAE,EAAC,UAAU,GAAG,EAAE,EAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC;IAC7D,KAAK,MAAM,SAAS,IAAI,UAAU;QACjC,eAAe,CAAC,SAAS,CAAC,GAAG,aAAa,CAAC,SAAS,EAAE,IAAI,EAAE,eAAe,CAAC,SAAS,CAAC,CAAC,CAAA;AAEzF,SAAS,aAAa,CAAC,GAAW,EAAE,KAAc,EAAE,KAAc;IACjE,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;IACjE,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;IAEjE,OAAO,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAA;AACvC,CAAC;AAED,MAAM,UAAU,uBAAuB,CAAC,SAAiB;IACxD,MAAM,QAAQ,GAAG,eAAe,CAAC,SAAS,CAAC,WAAW,EAAE,CAAC,CAAA;IACzD,IAAI,CAAC,QAAQ;QAAE,OAAM;IACrB,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;QACnC,MAAM,OAAO,GAAG,gBAAgB,CAAC,QAAQ,CAAC,CAAA;QAC1C,IAAI,OAAO;YAAE,OAAO,QAAQ,GAAG,YAAY,GAAG,OAAO,CAAC,WAAW,EAAE,CAAA;IACpE,CAAC;IACD,OAAO,QAAQ,CAAA;AAChB,CAAC;AAED,MAAM,iBAAiB,GAAG,yBAAyB,CAAA;AACnD,MAAM,cAAc,GAAG,UAAU,CAAA;AAEjC,SAAS,gBAAgB,CAAC,IAAY;IACrC,yBAAyB;IACzB,MAAM,KAAK,GAAG,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAC1C,MAAM,IAAI,GAAG,KAAK,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAA;IAEpD,IAAI,IAAI,EAAE,OAAO;QAAE,OAAO,IAAI,CAAC,OAAO,CAAA;IAEtC,0BAA0B;IAC1B,IAAI,KAAK,IAAI,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAAE,OAAO,OAAO,CAAA;AAC3D,CAAC","sourcesContent":["import mimeDbRaw from './mimeDb.js'\nimport {mimeScore} from './mimeScore.js'\n\nconst mimeDb: Record<\n\tstring,\n\t{\n\t\tsource?: string\n\t\tcharset?: string\n\t\tcompressible?: boolean\n\t\textensions?: string[]\n\t}\n> = mimeDbRaw\n\nconst extensionToMime: Record<string, string | undefined> = Object.create(null)\n\nfor (const [type, {extensions = []}] of Object.entries(mimeDb))\n\tfor (const extension of extensions)\n\t\textensionToMime[extension] = preferredType(extension, type, extensionToMime[extension])\n\nfunction preferredType(ext: string, type0?: string, type1?: string) {\n\tconst score0 = type0 ? mimeScore(type0, mimeDb[type0].source) : 0\n\tconst score1 = type1 ? mimeScore(type1, mimeDb[type1].source) : 0\n\n\treturn score0 > score1 ? type0 : type1\n}\n\nexport function contentTypeForExtension(extension: string) {\n\tconst mimeType = extensionToMime[extension.toLowerCase()]\n\tif (!mimeType) return\n\tif (!mimeType.includes('charset')) {\n\t\tconst charset = determineCharset(mimeType)\n\t\tif (charset) return mimeType + '; charset=' + charset.toLowerCase()\n\t}\n\treturn mimeType\n}\n\nconst extractTypeRegexp = /^\\s*([^;\\s]*)(?:;|\\s|$)/\nconst textTypeRegexp = /^text\\//i\n\nfunction determineCharset(type: string) {\n\t// _TODO: use media-typer\n\tconst match = extractTypeRegexp.exec(type)\n\tconst mime = match && mimeDb[match[1].toLowerCase()]\n\n\tif (mime?.charset) return mime.charset\n\n\t// default text/* to utf-8\n\tif (match && textTypeRegexp.test(match[1])) return 'UTF-8'\n}\n"]}