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.
- package/README.md +417 -293
- package/{cjs → lib}/body.d.ts +2 -3
- package/lib/body.js +10 -0
- package/lib/body.js.map +1 -0
- package/{esm → lib}/bodyHelpers.d.ts +2 -4
- package/lib/bodyHelpers.js +102 -0
- package/lib/bodyHelpers.js.map +1 -0
- package/{esm → lib}/dx.d.ts +6 -9
- package/lib/dx.js +133 -0
- package/lib/dx.js.map +1 -0
- package/{cjs → lib}/dxHelpers.d.ts +2 -5
- package/lib/dxHelpers.js +135 -0
- package/lib/dxHelpers.js.map +1 -0
- package/lib/helpers.js.map +1 -0
- package/{cjs → lib}/index.d.ts +1 -2
- package/{esm/index.d.ts → lib/index.js} +1 -2
- package/lib/index.js.map +1 -0
- package/lib/logger.d.ts +4 -0
- package/lib/logger.js +64 -0
- package/lib/logger.js.map +1 -0
- package/lib/router.d.ts +42 -0
- package/lib/router.js +43 -0
- package/lib/router.js.map +1 -0
- package/lib/static.js +23 -0
- package/lib/static.js.map +1 -0
- package/{cjs → lib}/staticHelpers.d.ts +5 -3
- package/lib/staticHelpers.js +202 -0
- package/lib/staticHelpers.js.map +1 -0
- package/{cjs → lib}/stream.d.ts +3 -8
- package/lib/stream.js +96 -0
- package/lib/stream.js.map +1 -0
- package/lib/vendors/contentType.js +64 -0
- package/lib/vendors/contentType.js.map +1 -0
- package/{cjs → lib}/vendors/etag.d.ts +2 -5
- package/lib/vendors/etag.js +94 -0
- package/lib/vendors/etag.js.map +1 -0
- package/{cjs → lib}/vendors/fresh.d.ts +2 -2
- package/lib/vendors/fresh.js +88 -0
- package/lib/vendors/fresh.js.map +1 -0
- package/lib/vendors/mime.d.ts +1 -0
- package/lib/vendors/mime.js +35 -0
- package/lib/vendors/mime.js.map +1 -0
- package/{cjs → lib}/vendors/mimeDb.d.ts +2544 -2544
- package/lib/vendors/mimeDb.js +9435 -0
- package/lib/vendors/mimeDb.js.map +1 -0
- package/{cjs → lib}/vendors/mimeScore.d.ts +1 -1
- package/lib/vendors/mimeScore.js +44 -0
- package/lib/vendors/mimeScore.js.map +1 -0
- package/{cjs → lib}/vendors/onFinished.d.ts +1 -1
- package/lib/vendors/onFinished.js +231 -0
- package/lib/vendors/rangeParser.d.ts +12 -0
- package/lib/vendors/rangeParser.js +108 -0
- package/lib/vendors/rangeParser.js.map +1 -0
- package/package.json +32 -36
- package/cjs/body.js +0 -14
- package/cjs/bodyHelpers.d.ts +0 -16
- package/cjs/bodyHelpers.js +0 -101
- package/cjs/connect.d.ts +0 -5
- package/cjs/connect.js +0 -44
- package/cjs/dx.d.ts +0 -46
- package/cjs/dx.js +0 -144
- package/cjs/dxHelpers.js +0 -123
- package/cjs/express.d.ts +0 -4
- package/cjs/express.js +0 -43
- package/cjs/helpers.js +0 -14
- package/cjs/index.js +0 -38
- package/cjs/logger.d.ts +0 -3
- package/cjs/logger.js +0 -61
- package/cjs/package.json +0 -3
- package/cjs/polyfillWithResolvers.d.ts +0 -1
- package/cjs/polyfillWithResolvers.js +0 -17
- package/cjs/router.js +0 -47
- package/cjs/static.js +0 -27
- package/cjs/staticHelpers.js +0 -195
- package/cjs/stream.js +0 -97
- package/cjs/vendors/contentType.js +0 -92
- package/cjs/vendors/etag.js +0 -136
- package/cjs/vendors/fresh.js +0 -102
- package/cjs/vendors/mime.d.ts +0 -1
- package/cjs/vendors/mime.js +0 -42
- package/cjs/vendors/mimeDb.js +0 -9417
- package/cjs/vendors/mimeScore.js +0 -50
- package/cjs/vendors/onFinished.js +0 -245
- package/cjs/vendors/rangeParser.d.ts +0 -10
- package/cjs/vendors/rangeParser.js +0 -126
- package/esm/body.d.ts +0 -8
- package/esm/body.js +0 -11
- package/esm/bodyHelpers.js +0 -90
- package/esm/connect.d.ts +0 -5
- package/esm/connect.js +0 -40
- package/esm/dx.js +0 -128
- package/esm/dxHelpers.d.ts +0 -49
- package/esm/dxHelpers.js +0 -119
- package/esm/express.d.ts +0 -4
- package/esm/express.js +0 -35
- package/esm/helpers.js +0 -3
- package/esm/index.js +0 -9
- package/esm/logger.d.ts +0 -3
- package/esm/logger.js +0 -57
- package/esm/polyfillWithResolvers.d.ts +0 -1
- package/esm/polyfillWithResolvers.js +0 -16
- package/esm/router.js +0 -44
- package/esm/static.d.ts +0 -5
- package/esm/static.js +0 -23
- package/esm/staticHelpers.d.ts +0 -18
- package/esm/staticHelpers.js +0 -188
- package/esm/stream.d.ts +0 -12
- package/esm/stream.js +0 -92
- package/esm/vendors/contentType.d.ts +0 -4
- package/esm/vendors/contentType.js +0 -88
- package/esm/vendors/etag.d.ts +0 -10
- package/esm/vendors/etag.js +0 -105
- package/esm/vendors/fresh.d.ts +0 -23
- package/esm/vendors/fresh.js +0 -96
- package/esm/vendors/mime.d.ts +0 -1
- package/esm/vendors/mime.js +0 -35
- package/esm/vendors/mimeDb.d.ts +0 -9413
- package/esm/vendors/mimeDb.js +0 -9415
- package/esm/vendors/mimeScore.d.ts +0 -5
- package/esm/vendors/mimeScore.js +0 -46
- package/esm/vendors/onFinished.d.ts +0 -14
- package/esm/vendors/onFinished.js +0 -241
- package/esm/vendors/rangeParser.d.ts +0 -10
- package/esm/vendors/rangeParser.js +0 -122
- /package/{cjs → lib}/helpers.d.ts +0 -0
- /package/{esm/helpers.d.ts → lib/helpers.js} +0 -0
- /package/{cjs → lib}/static.d.ts +0 -0
- /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
|
|
7
|
-
export declare function entityTagPath(fileStat: Stats, filePath: 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:
|
|
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:
|
|
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"]}
|