dx-server 0.0.1 → 0.0.3
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 +183 -0
- package/cjs/body.d.ts +30 -0
- package/cjs/body.js +91 -0
- package/cjs/contentType.d.ts +4 -0
- package/cjs/contentType.js +92 -0
- package/cjs/context.d.ts +15 -0
- package/cjs/context.js +27 -0
- package/cjs/error.js +42 -0
- package/cjs/etag.d.ts +7 -0
- package/cjs/etag.js +100 -0
- package/cjs/express.d.ts +49 -0
- package/cjs/express.js +168 -0
- package/cjs/expressApp.d.ts +4 -0
- package/cjs/expressApp.js +44 -0
- package/cjs/file.d.ts +3 -0
- package/cjs/file.js +12 -0
- package/cjs/index.d.ts +6 -0
- package/cjs/index.js +33 -0
- package/cjs/package.json +3 -0
- package/cjs/route.d.ts +38 -0
- package/cjs/route.js +86 -0
- package/cjs/static.d.ts +4 -0
- package/cjs/static.js +60 -0
- package/cjs/stream.d.ts +11 -0
- package/cjs/stream.js +100 -0
- package/esm/body.d.ts +30 -0
- package/esm/body.js +88 -0
- package/esm/contentType.d.ts +4 -0
- package/esm/contentType.js +88 -0
- package/esm/context.d.ts +15 -0
- package/esm/context.js +23 -0
- package/esm/error.js +37 -0
- package/esm/etag.d.ts +7 -0
- package/esm/etag.js +90 -0
- package/esm/express.d.ts +49 -0
- package/esm/express.js +156 -0
- package/esm/expressApp.d.ts +4 -0
- package/esm/expressApp.js +35 -0
- package/esm/file.d.ts +3 -0
- package/esm/file.js +8 -0
- package/esm/index.d.ts +6 -0
- package/esm/index.js +7 -0
- package/esm/route.d.ts +38 -0
- package/esm/route.js +82 -0
- package/esm/static.d.ts +4 -0
- package/esm/static.js +56 -0
- package/esm/stream.d.ts +11 -0
- package/esm/stream.js +92 -0
- package/package.json +32 -5
- package/index.js +0 -1
package/esm/body.js
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { getContentStream, readStream } from './stream.js';
|
|
2
|
+
import { parse } from 'qs';
|
|
3
|
+
import { parseContentType } from './contentType.js';
|
|
4
|
+
import { makeContext, requestContext } from './context.js';
|
|
5
|
+
export const bufferBodyContext = makeContext(async ({ limit = 100 << 10, // 100kb
|
|
6
|
+
} = {}) => {
|
|
7
|
+
const req = requestContext.value;
|
|
8
|
+
/**
|
|
9
|
+
* Check if a request has a request body.
|
|
10
|
+
* A request with a body __must__ either have `transfer-encoding`
|
|
11
|
+
* or `content-length` headers set.
|
|
12
|
+
* http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.3
|
|
13
|
+
*/
|
|
14
|
+
// https://github.com/jshttp/type-is/blob/cdcfe23e9833872e425b0aaf71ca0311373b6116/index.js#L92
|
|
15
|
+
const contentLengthParsed = parseInt(req.headers['content-length'] ?? '', 10);
|
|
16
|
+
if (req.headers['transfer-encoding'] === undefined
|
|
17
|
+
&& isNaN(contentLengthParsed))
|
|
18
|
+
return;
|
|
19
|
+
const contentLength = isNaN(contentLengthParsed) ? undefined : contentLengthParsed;
|
|
20
|
+
// read
|
|
21
|
+
const encoding = (req.headers['content-encoding'] ?? 'identity').toLowerCase();
|
|
22
|
+
const stream = getContentStream(req, encoding);
|
|
23
|
+
return await readStream(stream, {
|
|
24
|
+
length: encoding === 'identity' ? contentLength : undefined,
|
|
25
|
+
limit,
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
const forceGetContentTypeParams = (expected) => {
|
|
29
|
+
const req = requestContext.value;
|
|
30
|
+
const contentTypeRaw = req.headers['content-type'];
|
|
31
|
+
if (!contentTypeRaw)
|
|
32
|
+
return;
|
|
33
|
+
const { mediaType, parameters } = parseContentType(contentTypeRaw);
|
|
34
|
+
if (mediaType !== expected)
|
|
35
|
+
return;
|
|
36
|
+
return parameters;
|
|
37
|
+
};
|
|
38
|
+
const forceGetCharset = (expected) => {
|
|
39
|
+
const parameters = forceGetContentTypeParams(expected);
|
|
40
|
+
if (!parameters)
|
|
41
|
+
return;
|
|
42
|
+
// assert charset per RFC 7159 sec 8.1
|
|
43
|
+
const charset = parameters.charset?.toLowerCase() || 'utf-8';
|
|
44
|
+
if (!charset.startsWith('utf-'))
|
|
45
|
+
throw new Error(`unsupported charset "${charset.toUpperCase()}"`);
|
|
46
|
+
return charset;
|
|
47
|
+
};
|
|
48
|
+
export const jsonBodyContext = makeContext(async () => {
|
|
49
|
+
const charset = forceGetCharset('application/json');
|
|
50
|
+
if (!charset)
|
|
51
|
+
return;
|
|
52
|
+
const buffer = bufferBodyContext.value;
|
|
53
|
+
if (buffer) {
|
|
54
|
+
const str = buffer.toString(charset);
|
|
55
|
+
return str ? JSON.parse(str) : undefined;
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
export const rawBodyContext = makeContext(async () => {
|
|
59
|
+
if (!forceGetContentTypeParams('application/octet-stream'))
|
|
60
|
+
return;
|
|
61
|
+
return bufferBodyContext.value;
|
|
62
|
+
});
|
|
63
|
+
export const textBodyContext = makeContext(async () => {
|
|
64
|
+
const charset = forceGetCharset('text/plain');
|
|
65
|
+
if (!charset)
|
|
66
|
+
return;
|
|
67
|
+
const buffer = bufferBodyContext.value;
|
|
68
|
+
if (buffer)
|
|
69
|
+
return buffer.toString(charset);
|
|
70
|
+
});
|
|
71
|
+
export const urlencodedBodyContext = makeContext(async ({ simplify } = {}) => {
|
|
72
|
+
const charset = forceGetCharset('application/x-www-form-urlencoded');
|
|
73
|
+
if (!charset)
|
|
74
|
+
return;
|
|
75
|
+
const buffer = bufferBodyContext.value;
|
|
76
|
+
if (buffer) {
|
|
77
|
+
const str = buffer.toString(charset);
|
|
78
|
+
return simplify
|
|
79
|
+
? Object.fromEntries(new URLSearchParams(str))
|
|
80
|
+
: parse(str);
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
export const queryContext = makeContext(() => {
|
|
84
|
+
const req = requestContext.value;
|
|
85
|
+
const query = req.url?.split('?', 2)?.[1];
|
|
86
|
+
return query ? parse(query) : {};
|
|
87
|
+
});
|
|
88
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYm9keS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9ib2R5LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBQyxnQkFBZ0IsRUFBRSxVQUFVLEVBQUMsTUFBTSxhQUFhLENBQUE7QUFDeEQsT0FBTyxFQUFDLEtBQUssRUFBQyxNQUFNLElBQUksQ0FBQTtBQUN4QixPQUFPLEVBQUMsZ0JBQWdCLEVBQUMsTUFBTSxrQkFBa0IsQ0FBQTtBQUNqRCxPQUFPLEVBQUMsV0FBVyxFQUFFLGNBQWMsRUFBQyxNQUFNLGNBQWMsQ0FBQTtBQUV4RCxNQUFNLENBQUMsTUFBTSxpQkFBaUIsR0FBRyxXQUFXLENBQUMsS0FBSyxFQUNqRCxFQUNDLEtBQUssR0FBRyxHQUFHLElBQUksRUFBRSxFQUFFLFFBQVE7S0FHeEIsRUFBRSxFQUNMLEVBQUU7SUFDSCxNQUFNLEdBQUcsR0FBRyxjQUFjLENBQUMsS0FBSyxDQUFBO0lBRWhDOzs7OztPQUtHO0lBQ0YsK0ZBQStGO0lBQ2hHLE1BQU0sbUJBQW1CLEdBQUcsUUFBUSxDQUFDLEdBQUcsQ0FBQyxPQUFPLENBQUMsZ0JBQWdCLENBQUMsSUFBSSxFQUFFLEVBQUUsRUFBRSxDQUFDLENBQUE7SUFDN0UsSUFDQyxHQUFHLENBQUMsT0FBTyxDQUFDLG1CQUFtQixDQUFDLEtBQUssU0FBUztXQUMzQyxLQUFLLENBQUMsbUJBQW1CLENBQUM7UUFDNUIsT0FBTTtJQUNSLE1BQU0sYUFBYSxHQUFHLEtBQUssQ0FBQyxtQkFBbUIsQ0FBQyxDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDLG1CQUFtQixDQUFBO0lBRWxGLE9BQU87SUFDUCxNQUFNLFFBQVEsR0FBRyxDQUFDLEdBQUcsQ0FBQyxPQUFPLENBQUMsa0JBQWtCLENBQUMsSUFBSSxVQUFVLENBQUMsQ0FBQyxXQUFXLEVBQUUsQ0FBQTtJQUM5RSxNQUFNLE1BQU0sR0FBRyxnQkFBZ0IsQ0FBQyxHQUFHLEVBQUUsUUFBUSxDQUFD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Q0FBQTtJQUN0QyxJQUFJLE1BQU0sRUFBRTtRQUNYLE1BQU0sR0FBRyxHQUFHLE1BQU0sQ0FBQyxRQUFRLENBQUMsT0FBTyxDQUFDLENBQUE7UUFDcEMsT0FBTyxRQUFRO1lBQ2QsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxXQUFXLENBQUMsSUFBSSxlQUFlLENBQUMsR0FBRyxDQUFDLENBQUM7WUFDOUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQTtLQUNiO0FBQ0YsQ0FBQyxDQUFDLENBQUE7QUFFRixNQUFNLENBQUMsTUFBTSxZQUFZLEdBQUcsV0FBVyxDQUFDLEdBQUcsRUFBRTtJQUM1QyxNQUFNLEdBQUcsR0FBRyxjQUFjLENBQUMsS0FBSyxDQUFBO0lBQ2hDLE1BQU0sS0FBSyxHQUFHLEdBQUcsQ0FBQyxHQUFHLEVBQUUsS0FBSyxDQUFDLEdBQUcsRUFBRSxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFBO0lBQ3pDLE9BQU8sS0FBSyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQTtBQUNqQyxDQUFDLENBQUMsQ0FBQSJ9
|
|
@@ -0,0 +1,88 @@
|
|
|
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 TYPE_REGEXP = /^[!#$%&'*+.^_`|~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 PARAM_REGEXP = /; *([!#$%&'*+.^_`|~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
|
+
const TEXT_REGEXP = /^[\u000b\u0020-\u007e\u0080-\u00ff]+$/; // eslint-disable-line no-control-regex
|
|
26
|
+
const TOKEN_REGEXP = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
|
|
27
|
+
/**
|
|
28
|
+
* RegExp to match quoted-pair in RFC 7230 sec 3.2.6
|
|
29
|
+
*
|
|
30
|
+
* quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text )
|
|
31
|
+
* obs-text = %x80-FF
|
|
32
|
+
*/
|
|
33
|
+
const QESC_REGEXP = /\\([\u000b\u0020-\u00ff])/g; // eslint-disable-line no-control-regex
|
|
34
|
+
export function parseContentType(header) {
|
|
35
|
+
let index = header.indexOf(';');
|
|
36
|
+
const mediaType = index !== -1
|
|
37
|
+
? header.slice(0, index).trim()
|
|
38
|
+
: header.trim();
|
|
39
|
+
if (!TYPE_REGEXP.test(mediaType))
|
|
40
|
+
throw new TypeError(`invalid media type: ${mediaType}`);
|
|
41
|
+
const parameters = {};
|
|
42
|
+
// parse parameters
|
|
43
|
+
if (index !== -1) {
|
|
44
|
+
let key;
|
|
45
|
+
let match;
|
|
46
|
+
let value;
|
|
47
|
+
const regexp = new RegExp(PARAM_REGEXP);
|
|
48
|
+
regexp.lastIndex = index;
|
|
49
|
+
while ((match = regexp.exec(header))) {
|
|
50
|
+
if (match.index !== index)
|
|
51
|
+
throw new TypeError('invalid parameter format');
|
|
52
|
+
index += match[0].length;
|
|
53
|
+
key = match[1].toLowerCase();
|
|
54
|
+
value = match[2];
|
|
55
|
+
if (value.charCodeAt(0) === 0x22 /* " */) {
|
|
56
|
+
// remove quotes
|
|
57
|
+
value = value.slice(1, -1);
|
|
58
|
+
// remove escapes
|
|
59
|
+
if (value.indexOf('\\') !== -1)
|
|
60
|
+
value = value.replace(QESC_REGEXP, '$1');
|
|
61
|
+
}
|
|
62
|
+
parameters[key] = value;
|
|
63
|
+
}
|
|
64
|
+
if (index !== header.length)
|
|
65
|
+
throw new TypeError('invalid parameter format');
|
|
66
|
+
}
|
|
67
|
+
return { mediaType, parameters };
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* RegExp to match chars that must be quoted-pair in RFC 7230 sec 3.2.6
|
|
71
|
+
*/
|
|
72
|
+
const QUOTE_REGEXP = /([\\"])/g;
|
|
73
|
+
function qstring(str) {
|
|
74
|
+
// no need to quote tokens
|
|
75
|
+
if (TOKEN_REGEXP.test(str))
|
|
76
|
+
return str;
|
|
77
|
+
if (str.length > 0 && !TEXT_REGEXP.test(str))
|
|
78
|
+
throw new TypeError(`invalid parameter value: ${str}`);
|
|
79
|
+
return `"${str.replace(QUOTE_REGEXP, '\\$1')}"`;
|
|
80
|
+
}
|
|
81
|
+
function formatContentType({ mediaType, parameters }) {
|
|
82
|
+
if (!mediaType || !TYPE_REGEXP.test(mediaType))
|
|
83
|
+
throw new TypeError(`invalid type: ${mediaType}`);
|
|
84
|
+
return `${mediaType}${parameters
|
|
85
|
+
? Object.keys(parameters).sort().map(key => `; ${key}=${qstring(parameters[key])}`).join('')
|
|
86
|
+
: ''}`;
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29udGVudFR5cGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvY29udGVudFR5cGUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQ0EscUdBQXFHO0FBQ3JHOzs7Ozs7R0FNRztBQUNILE1BQU0sV0FBVyxHQUFHLDREQUE0RCxDQUFBO0FBQ2hGOzs7Ozs7Ozs7Ozs7O0dBYUc7QUFDSCxNQUFNLFlBQVksR0FBRyxrS0FBa0ssQ0FBQSxDQUFDLHVDQUF1QztBQUMvTixNQUFNLFdBQVcsR0FBRyx1Q0FBdUMsQ0FBQSxDQUFDLHVDQUF1QztBQUNuRyxNQUFNLFlBQVksR0FBRywrQkFBK0IsQ0FBQTtBQUNwRDs7Ozs7R0FLRztBQUNILE1BQU0sV0FBVyxHQUFHLDRCQUE0QixDQUFBLENBQUMsdUNBQXVDO0FBRXhGLE1BQU0sVUFBVSxnQkFBZ0IsQ0FBRSxNQUFjO0lBQy9DLElBQUksS0FBSyxHQUFHLE1BQU0sQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLENBQUE7SUFDL0IsTUFBTSxTQUFTLEdBQUcsS0FBSyxLQUFLLENBQUMsQ0FBQztRQUM3QixDQUFDLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDLEVBQUUsS0FBSyxDQUFDLENBQUMsSUFBSSxFQUFFO1FBQy9CLENBQUMsQ0FBQyxNQUFNLENBQUMsSUFBSSxFQUFFLENBQUE7SUFFaEIsSUFBSSxDQUFDLFdBQVcsQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDO1FBQUUsTUFBTSxJQUFJLFNBQVMsQ0FBQyx1QkFBdUIsU0FBUyxFQUFFLENBQUMsQ0FBQTtJQUN6RixNQUFNLFVBQVUsR0FBMkIsRUFBRSxDQUFBO0lBRTdDLG1CQUFtQjtJQUNuQixJQUFJLEtBQUssS0FBSyxDQUFDLENBQUMsRUFBRTtRQUNqQixJQUFJLEdBQUcsQ0FBQTtRQUNQLElBQUksS0FBSyxDQUFBO1FBQ1QsSUFBSSxLQUFLLENBQUE7UUFFVCxNQUFNLE1BQU0sR0FBRyxJQUFJLE1BQU0sQ0FBQyxZQUFZLENB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
|
package/esm/context.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/// <reference types="node" resolution-mode="require"/>
|
|
2
|
+
import { Promisable } from 'type-fest';
|
|
3
|
+
import { IncomingMessage, ServerResponse } from 'node:http';
|
|
4
|
+
export declare const makeContext: <T, Params extends any[] = unknown[]>(maker: (...params: Params) => Promisable<T>, end?: (result: any, value: T) => any) => {
|
|
5
|
+
readonly value: T;
|
|
6
|
+
chain(...params: Params): <V>(next: () => V) => Promise<any>;
|
|
7
|
+
};
|
|
8
|
+
export declare const requestContext: {
|
|
9
|
+
readonly value: IncomingMessage;
|
|
10
|
+
chain(params_0: IncomingMessage): <V>(next: () => V) => Promise<any>;
|
|
11
|
+
};
|
|
12
|
+
export declare const responseContext: {
|
|
13
|
+
readonly value: ServerResponse<IncomingMessage>;
|
|
14
|
+
chain(params_0: ServerResponse<IncomingMessage>): <V>(next: () => V) => Promise<any>;
|
|
15
|
+
};
|
package/esm/context.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
2
|
+
import { identity } from 'jmisc';
|
|
3
|
+
export const makeContext = (maker, end = identity) => {
|
|
4
|
+
const asyncLocalStorage = new AsyncLocalStorage();
|
|
5
|
+
return {
|
|
6
|
+
get value() {
|
|
7
|
+
return asyncLocalStorage.getStore();
|
|
8
|
+
},
|
|
9
|
+
chain(...params) {
|
|
10
|
+
return async (next) => {
|
|
11
|
+
const value = await maker(...params);
|
|
12
|
+
return end(await asyncLocalStorage.run(value, next), value);
|
|
13
|
+
};
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
// method: verb
|
|
18
|
+
// url: full url without server, protocol, port.
|
|
19
|
+
// headers: if headers are repeated, they are joined by comma. Header names are lowercased.
|
|
20
|
+
// rawHeaders: list of header name and value in a flat array. Case is preserved.
|
|
21
|
+
export const requestContext = makeContext(identity);
|
|
22
|
+
export const responseContext = makeContext(identity);
|
|
23
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29udGV4dC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9jb250ZXh0LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBQyxpQkFBaUIsRUFBQyxNQUFNLGtCQUFrQixDQUFBO0FBR2xELE9BQU8sRUFBQyxRQUFRLEVBQUMsTUFBTSxPQUFPLENBQUE7QUFFOUIsTUFBTSxDQUFDLE1BQU0sV0FBVyxHQUFHLENBQzFCLEtBQTJDLEVBQzNDLE1BQXNDLFFBQVEsRUFDN0MsRUFBRTtJQUNILE1BQU0saUJBQWlCLEdBQUcsSUFBSSxpQkFBaUIsRUFBSyxDQUFBO0lBQ3BELE9BQU87UUFDTixJQUFJLEtBQUs7WUFDUixPQUFPLGlCQUFpQixDQUFDLFFBQVEsRUFBRyxDQUFBO1FBQ3JDLENBQUM7UUFDRCxLQUFLLENBQUMsR0FBRyxNQUFjO1lBQ3RCLE9BQU8sS0FBSyxFQUFLLElBQWEsRUFBRSxFQUFFO2dCQUNqQyxNQUFNLEtBQUssR0FBRyxNQUFNLEtBQUssQ0FBQyxHQUFHLE1BQU0sQ0FBQyxDQUFBO2dCQUNwQyxPQUFPLEdBQUcsQ0FBQyxNQUFNLGlCQUFpQixDQUFDLEdBQUcsQ0FBQyxLQUFLLEVBQUUsSUFBSSxDQUFDLEVBQUUsS0FBSyxDQUFDLENBQUE7WUFDNUQsQ0FBQyxDQUFBO1FBQ0YsQ0FBQztLQUNELENBQUE7QUFDRixDQUFDLENBQUE7QUFFRCxlQUFlO0FBQ2YsZ0RBQWdEO0FBQ2hELDJGQUEyRjtBQUMzRixnRkFBZ0Y7QUFDaEYsTUFBTSxDQUFDLE1BQU0sY0FBYyxHQUFHLFdBQVcsQ0FBcUMsUUFBUSxDQUFDLENBQUE7QUFDdkYsTUFBTSxDQUFDLE1BQU0sZUFBZSxHQUFHLFdBQVcsQ0FBbUMsUUFBUSxDQUFDLENBQUEifQ==
|
package/esm/error.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { setHtml, setJson } from './express.js';
|
|
2
|
+
import { router } from './route.js';
|
|
3
|
+
export const catchError = async (next) => {
|
|
4
|
+
try {
|
|
5
|
+
await next();
|
|
6
|
+
}
|
|
7
|
+
catch (e) {
|
|
8
|
+
console.error(e);
|
|
9
|
+
setHtml('internal server error', { status: 500 });
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
export const catchApiError = router.post({
|
|
13
|
+
async '/api'({ next }) {
|
|
14
|
+
try {
|
|
15
|
+
await next();
|
|
16
|
+
}
|
|
17
|
+
catch (e) {
|
|
18
|
+
console.error(e);
|
|
19
|
+
setJson({
|
|
20
|
+
message: 'internal server error',
|
|
21
|
+
code: 'internal_server_error'
|
|
22
|
+
}, { status: 500 });
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}, { end: false });
|
|
26
|
+
export const notFound = () => {
|
|
27
|
+
setHtml('not found', { status: 404 });
|
|
28
|
+
};
|
|
29
|
+
export const notFoundApi = router.post({
|
|
30
|
+
'/api'() {
|
|
31
|
+
setJson({
|
|
32
|
+
message: 'not found',
|
|
33
|
+
code: 'not_found'
|
|
34
|
+
}, { status: 404 });
|
|
35
|
+
}
|
|
36
|
+
}, { end: false });
|
|
37
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXJyb3IuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvZXJyb3IudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQ0EsT0FBTyxFQUFDLE9BQU8sRUFBRSxPQUFPLEVBQUMsTUFBTSxjQUFjLENBQUE7QUFDN0MsT0FBTyxFQUFDLE1BQU0sRUFBQyxNQUFNLFlBQVksQ0FBQTtBQUVqQyxNQUFNLENBQUMsTUFBTSxVQUFVLEdBQWUsS0FBSyxFQUFDLElBQUksRUFBQyxFQUFFO0lBQ2xELElBQUk7UUFDSCxNQUFNLElBQUksRUFBRSxDQUFBO0tBQ1o7SUFBQyxPQUFPLENBQUMsRUFBRTtRQUNYLE9BQU8sQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUE7UUFDaEIsT0FBTyxDQUFDLHVCQUF1QixFQUFFLEVBQUMsTUFBTSxFQUFFLEdBQUcsRUFBQyxDQUFDLENBQUE7S0FDL0M7QUFDRixDQUFDLENBQUE7QUFFRCxNQUFNLENBQUMsTUFBTSxhQUFhLEdBQUcsTUFBTSxDQUFDLElBQUksQ0FBQztJQUN4QyxLQUFLLENBQUMsTUFBTSxDQUFDLEVBQUMsSUFBSSxFQUFDO1FBQ2xCLElBQUk7WUFDSCxNQUFNLElBQUksRUFBRSxDQUFBO1NBQ1o7UUFBQyxPQUFPLENBQUMsRUFBRTtZQUNYLE9BQU8sQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUE7WUFDaEIsT0FBTyxDQUFDO2dCQUNQLE9BQU8sRUFBRSx1QkFBdUI7Z0JBQ2hDLElBQUksRUFBRSx1QkFBdUI7YUFDN0IsRUFBRSxFQUFDLE1BQU0sRUFBRSxHQUFHLEVBQUMsQ0FBQyxDQUFBO1NBQ2pCO0lBQ0YsQ0FBQztDQUNELEVBQUUsRUFBQyxHQUFHLEVBQUUsS0FBSyxFQUFDLENBQUMsQ0FBQTtBQUVoQixNQUFNLENBQUMsTUFBTSxRQUFRLEdBQWUsR0FBRyxFQUFFO0lBQ3hDLE9BQU8sQ0FBQyxXQUFXLEVBQUUsRUFBQyxNQUFNLEVBQUUsR0FBRyxFQUFDLENBQUMsQ0FBQTtBQUNwQyxDQUFDLENBQUE7QUFDRCxNQUFNLENBQUMsTUFBTSxXQUFXLEdBQUcsTUFBTSxDQUFDLElBQUksQ0FBQztJQUN0QyxNQUFNO1FBQ0wsT0FBTyxDQUFDO1lBQ1AsT0FBTyxFQUFFLFdBQVc7WUFDcEIsSUFBSSxFQUFFLFdBQVc7U0FDakIsRUFBRSxFQUFDLE1BQU0sRUFBRSxHQUFHLEVBQUMsQ0FBQyxDQUFBO0lBQ2xCLENBQUM7Q0FDRCxFQUFFLEVBQUMsR0FBRyxFQUFFLEtBQUssRUFBQyxDQUFDLENBQUEifQ==
|
package/esm/etag.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/// <reference types="node" resolution-mode="require"/>
|
|
2
|
+
/// <reference types="node" resolution-mode="require"/>
|
|
3
|
+
import type { IncomingMessage } from 'node:http';
|
|
4
|
+
export declare function entityTag(buf: Buffer, weak?: boolean): string;
|
|
5
|
+
export declare function statTag(stat: any): string;
|
|
6
|
+
export declare function isFreshETag(req: IncomingMessage, etag: string): true | undefined;
|
|
7
|
+
export declare function isFreshModifiedSince(req: IncomingMessage, lastModified: string): boolean | undefined;
|
package/esm/etag.js
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
// etag: https://github.com/jshttp/etag/blob/b9f0642256e63654287299d205bc6ced71b1a228/index.js#L39
|
|
2
|
+
import crypto from 'node:crypto';
|
|
3
|
+
export function entityTag(buf, weak) {
|
|
4
|
+
// pre-computed empty
|
|
5
|
+
return buf.length
|
|
6
|
+
? `${buf.length.toString(16)}-${crypto
|
|
7
|
+
.createHash('sha1')
|
|
8
|
+
.update(buf)
|
|
9
|
+
.digest('base64')
|
|
10
|
+
.substring(0, 27)}"`
|
|
11
|
+
: `${weak ? 'W/' : ''}"0-2jmj7l5rSw0yVb/vlWAYkK/YBwk"`;
|
|
12
|
+
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag#directives
|
|
13
|
+
// weak W/ vs strong eTag
|
|
14
|
+
// same weak eTag: 2 resources might be semantically equivalent, but not byte-for-byte identical
|
|
15
|
+
}
|
|
16
|
+
const CACHE_CONTROL_NO_CACHE_REGEXP = /(?:^|,)\s*?no-cache\s*?(?:,|$)/;
|
|
17
|
+
export function statTag(stat) {
|
|
18
|
+
const mtime = stat.mtime.getTime().toString(16);
|
|
19
|
+
const size = stat.size.toString(16);
|
|
20
|
+
return `"${size}-${mtime}"`;
|
|
21
|
+
}
|
|
22
|
+
// https://github.com/jshttp/fresh/blob/05254186fd7428915224db46144fc94293a7df7d/index.js#L33
|
|
23
|
+
export function isFreshETag(req, etag) {
|
|
24
|
+
const noneMatch = req.headers['if-none-match'];
|
|
25
|
+
if (!noneMatch)
|
|
26
|
+
return;
|
|
27
|
+
// Always return stale when Cache-Control: no-cache
|
|
28
|
+
// to support end-to-end reload requests
|
|
29
|
+
// https://tools.ietf.org/html/rfc2616#section-14.9.4
|
|
30
|
+
const cacheControl = req.headers['cache-control'];
|
|
31
|
+
if (cacheControl && CACHE_CONTROL_NO_CACHE_REGEXP.test(cacheControl))
|
|
32
|
+
return;
|
|
33
|
+
if (noneMatch && noneMatch !== '*') {
|
|
34
|
+
if (!etag)
|
|
35
|
+
return;
|
|
36
|
+
let etagStale = true;
|
|
37
|
+
for (const match of parseTokenList(noneMatch)) {
|
|
38
|
+
if (match === etag || match === `W/${etag}` || `W/${match}` === etag) {
|
|
39
|
+
etagStale = false;
|
|
40
|
+
break;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
if (etagStale)
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
export function isFreshModifiedSince(req, lastModified) {
|
|
49
|
+
const modifiedSince = req.headers['if-modified-since'];
|
|
50
|
+
if (!modifiedSince)
|
|
51
|
+
return;
|
|
52
|
+
// Always return stale when Cache-Control: no-cache
|
|
53
|
+
// to support end-to-end reload requests
|
|
54
|
+
// https://tools.ietf.org/html/rfc2616#section-14.9.4
|
|
55
|
+
const cacheControl = req.headers['cache-control'];
|
|
56
|
+
if (cacheControl && CACHE_CONTROL_NO_CACHE_REGEXP.test(cacheControl))
|
|
57
|
+
return;
|
|
58
|
+
if (modifiedSince && lastModified) {
|
|
59
|
+
const lastModifiedDate = Date.parse(lastModified);
|
|
60
|
+
const modifiedSinceDate = Date.parse(modifiedSince);
|
|
61
|
+
return !isNaN(lastModifiedDate) && !isNaN(modifiedSinceDate) && lastModifiedDate <= modifiedSinceDate;
|
|
62
|
+
}
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
function parseTokenList(str) {
|
|
66
|
+
let end = 0;
|
|
67
|
+
const list = [];
|
|
68
|
+
let start = 0;
|
|
69
|
+
// gather tokens
|
|
70
|
+
for (let i = 0, len = str.length; i < len; i++) {
|
|
71
|
+
switch (str.charCodeAt(i)) {
|
|
72
|
+
case 0x20: /* */
|
|
73
|
+
if (start === end) {
|
|
74
|
+
start = end = i + 1;
|
|
75
|
+
}
|
|
76
|
+
break;
|
|
77
|
+
case 0x2c: /* , */
|
|
78
|
+
list.push(str.substring(start, end));
|
|
79
|
+
start = end = i + 1;
|
|
80
|
+
break;
|
|
81
|
+
default:
|
|
82
|
+
end = i + 1;
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
// final token
|
|
87
|
+
list.push(str.substring(start, end));
|
|
88
|
+
return list;
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXRhZy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9ldGFnLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLGtHQUFrRztBQUNsRyxPQUFPLE1BQU0sTUFBTSxhQUFhLENBQUE7QUFJaEMsTUFBTSxVQUFVLFNBQVMsQ0FBQyxHQUFXLEVBQUUsSUFBYztJQUNwRCxxQkFBcUI7SUFDckIsT0FBTyxHQUFHLENBQUMsTUFBTTtRQUNoQixDQUFDLENBQUMsR0FBRyxHQUFHLENBQUMsTUFBTSxDQUFDLFFBQVEsQ0FBQyxFQUFFLENBQUMsSUFBSSxNQUFNO2FBQ3BDLFVBQVUsQ0FBQyxNQUFNLENBQUM7YUFDbEIsTUFBTSxDQUFDLEdBQUcsQ0FBQzthQUNYLE1BQU0sQ0FBQyxRQUFRLENBQUM7YUFDaEIsU0FBUyxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUMsR0FBRztRQUNyQixDQUFDLENBQUMsR0FBRyxJQUFJLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsRUFBRSxpQ0FBaUMsQ0FBQTtJQUN2RCw0RUFBNEU7SUFDNUUseUJBQXlCO0lBQ3pCLGdHQUFnRztBQUNqRyxDQUFDO0FBRUQsTUFBTSw2QkFBNkIsR0FBRyxnQ0FBZ0MsQ0FBQTtBQUN0RSxNQUFNLFVBQVUsT0FBTyxDQUFDLElBQUk7SUFDM0IsTUFBTSxLQUFLLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxPQUFPLEVBQUUsQ0FBQyxRQUFRLENBQUMsRUFBRSxDQUFDLENBQUE7SUFDL0MsTUFBTSxJQUFJLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsRUFBRSxDQUFDLENBQUE7SUFFbkMsT0FBTyxJQUFJLElBQUksSUFBSSxLQUFLLEdBQUcsQ0FBQTtBQUM1QixDQUFDO0FBQ0QsNkZBQTZGO0FBQzdGLE1BQU0sVUFBVSxXQUFXLENBQUMsR0FBb0IsRUFBRSxJQUFZO0lBQzdELE1BQU0sU0FBUyxHQUFHLEdBQUcsQ0FBQyxPQUFPLENBQUMsZUFBZSxDQUFDLENBQUE7SUFDOUMsSUFBSSxDQUFDLFNBQVM7UUFBRSxPQUFNO0lBRXRCLG1EQUFtRDtJQUNuRCx3Q0FBd0M7SUFDeEMscURBQXFEO0lBQ3JELE1BQU0sWUFBWSxHQUFHLEdBQUcsQ0FBQyxPQUFPLENBQUMsZUFBZSxDQUFDLENBQUE7SUFDakQsSUFBSSxZQUFZLElBQUksNkJBQTZCLENBQUMsSUFBSSxDQUFDLFlBQVksQ0FBQztRQUFFLE9BQU07SUFFNUUsSUFBSSxTQUFTLElBQUksU0FBUyxLQUFLLEdBQUcsRUFBRTtRQUNuQyxJQUFJLENBQUMsSUFBSTtZQUFFLE9BQU07UUFFakIsSUFBSSxTQUFTLEdBQUcsSUFBSSxDQUFBO1FBQ3BCLEtBQUssTUFBTSxLQUFLLElBQUksY0FBYyxDQUFDLFNBQVMsQ0FBQyxFQUFFO1lBQzlDLElBQUksS0FBSyxLQUFLLElBQUksSUFBSSxLQUFLLEtBQUssS0FBSyxJQUFJLEVBQUUsSUFBSSxLQUFLLEtBQUssRUFBRSxLQUFLLElBQUksRUFBRTtnQkFDckUsU0FBUyxHQUFHLEtBQUssQ0FBQTtnQkFDakIsTUFBSzthQUNMO1NBQ0Q7UUFDRCxJQUFJLFNBQVM7WUFBRSxPQUFNO0tBQ3JCO0lBRUQsT0FBTyxJQUFJLENBQUE7QUFDWixDQUFDO0FBRUQsTUFBTSxVQUFVLG9CQUFvQixDQUFDLEdBQW9CLEVBQUUsWUFBb0I7SUFDOUUsTUFBTSxhQUFhLEdBQUcsR0FBRyxDQUFDLE9BQU8sQ0FBQyxtQkFBbUIsQ0FBQyxDQUFBO0lBQ3RELElBQUksQ0FBQyxhQUFhO1FBQUUsT0FBTTtJQUUxQixtREFBbUQ7SUFDbkQsd0NBQXdDO0lBQ3hDLHFEQUFxRDtJQUNyRCxNQUFNLFlBQVksR0FBRyxHQUFHLENBQUMsT0FBTyxDQUFDLGVBQWUsQ0FBQyxDQUFBO0lBQ2pELElBQUksWUFBWSxJQUFJLDZCQUE2QixDQUFDLElBQUksQ0FBQyxZQUFZLENBQUM7UUFBRSxPQUFNO0lBRTVFLElBQUksYUFBYSxJQUFJLFlBQVksRUFBRTtRQUNsQyxNQUFNLGdCQUFnQixHQUFHLElBQUksQ0FBQyxLQUFLLENBQUMsWUFBWSxDQUFDLENBQUE7UUFDakQsTUFBTSxpQkFBaUIsR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLGFBQWEsQ0FBQyxDQUFBO1FBQ25ELE9BQU8sQ0FBQyxLQUFLLENBQUMsZ0JBQWdCLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxpQkFBaUIsQ0FBQyxJQUFJLGdCQUFnQixJQUFJLGlCQUFpQixDQUFBO0tBQ3JHO0lBQ0QsT0FBTyxJQUFJLENBQUE7QUFDWixDQUFDO0FBRUQsU0FBUyxjQUFjLENBQUUsR0FBVztJQUNuQyxJQUFJLEdBQUcsR0FBRyxDQUFDLENBQUE7SUFDWCxNQUFNLElBQUksR0FBRyxFQUFFLENBQUE7SUFDZixJQUFJLEtBQUssR0FBRyxDQUFDLENBQUE7SUFFYixnQkFBZ0I7SUFDaEIsS0FBSyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsR0FBRyxHQUFHLEdBQUcsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxHQUFHLEdBQUcsRUFBRSxDQUFDLEVBQUUsRUFBRTtRQUMvQyxRQUFRLEdBQUcsQ0FBQyxVQUFVLENBQUMsQ0FBQyxDQUFDLEVBQUU7WUFDMUIsS0FBSyxJQUFJLEVBQUUsT0FBTztnQkFDakIsSUFBSSxLQUFLLEtBQUssR0FBRyxFQUFFO29CQUNsQixLQUFLLEdBQUcsR0FBRyxHQUFHLENBQUMsR0FBRyxDQUFDLENBQUE7aUJBQ25CO2dCQUNELE1BQUs7WUFDTixLQUFLLElBQUksRUFBRSxPQUFPO2dCQUNqQixJQUFJLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxTQUFTLENBQUMsS0FBSyxFQUFFLEdBQUcsQ0FBQyxDQUFDLENBQUE7Z0JBQ3BDLEtBQUssR0FBRyxHQUFHLEdBQUcsQ0FBQyxHQUFHLENBQUMsQ0FBQTtnQkFDbkIsTUFBSztZQUNOO2dCQUNDLEdBQUcsR0FBRyxDQUFDLEdBQUcsQ0FBQyxDQUFBO2dCQUNYLE1BQUs7U0FDTjtLQUNEO0lBQ0QsY0FBYztJQUNkLElBQUksQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLFNBQVMsQ0FBQyxLQUFLLEVBQUUsR0FBRyxDQUFDLENBQUMsQ0FBQTtJQUNwQyxPQUFPLElBQUksQ0FBQTtBQUNaLENBQUMifQ==
|
package/esm/express.d.ts
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/// <reference types="node" resolution-mode="require"/>
|
|
2
|
+
/// <reference types="node" resolution-mode="require"/>
|
|
3
|
+
import { Writable } from 'node:stream';
|
|
4
|
+
export declare const expressContext: {
|
|
5
|
+
readonly value: {
|
|
6
|
+
charset?: BufferEncoding | undefined;
|
|
7
|
+
jsonBeautify?: boolean | undefined;
|
|
8
|
+
disableEtag?: boolean | undefined;
|
|
9
|
+
} & ({
|
|
10
|
+
type: 'text';
|
|
11
|
+
data: string;
|
|
12
|
+
} | {
|
|
13
|
+
type: 'html';
|
|
14
|
+
data: string;
|
|
15
|
+
} | {
|
|
16
|
+
type: 'buffer';
|
|
17
|
+
data: Buffer;
|
|
18
|
+
} | {
|
|
19
|
+
type: 'json';
|
|
20
|
+
data: any;
|
|
21
|
+
} | {
|
|
22
|
+
type: 'redirect';
|
|
23
|
+
data: string;
|
|
24
|
+
} | {
|
|
25
|
+
type: 'stream';
|
|
26
|
+
data: Writable;
|
|
27
|
+
});
|
|
28
|
+
chain(params_0?: {
|
|
29
|
+
jsonBeautify?: boolean | undefined;
|
|
30
|
+
disableEtag?: boolean | undefined;
|
|
31
|
+
} | undefined): <V>(next: () => V) => Promise<any>;
|
|
32
|
+
};
|
|
33
|
+
export declare function setText(text: string, { status }?: {
|
|
34
|
+
status?: number;
|
|
35
|
+
}): void;
|
|
36
|
+
export declare function setHtml(html: string, opts?: {
|
|
37
|
+
status?: number;
|
|
38
|
+
}): void;
|
|
39
|
+
export declare function setBuffer(buffer: Buffer, { status }?: {
|
|
40
|
+
status?: number;
|
|
41
|
+
}): void;
|
|
42
|
+
export declare function setStream(stream: Writable, { status }?: {
|
|
43
|
+
status?: number;
|
|
44
|
+
}): void;
|
|
45
|
+
export declare function setJson(json: any, { status, beautify }?: {
|
|
46
|
+
status?: number;
|
|
47
|
+
beautify?: boolean;
|
|
48
|
+
}): void;
|
|
49
|
+
export declare function setRedirect(url: string, status: 301 | 302): void;
|
package/esm/express.js
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import makeDefer from 'jdefer';
|
|
2
|
+
import { promisify } from 'node:util';
|
|
3
|
+
import { entityTag, isFreshETag } from './etag.js';
|
|
4
|
+
import { makeContext, requestContext, responseContext } from './context.js';
|
|
5
|
+
export const expressContext = makeContext(async ({ jsonBeautify, disableEtag } = {}) => {
|
|
6
|
+
return {
|
|
7
|
+
jsonBeautify,
|
|
8
|
+
disableEtag,
|
|
9
|
+
};
|
|
10
|
+
}, async (ret, { type, data, charset, jsonBeautify, disableEtag }) => {
|
|
11
|
+
const res = responseContext.value;
|
|
12
|
+
const setContentType = (contentType) => {
|
|
13
|
+
if (res.headersSent || res.getHeader('content-type'))
|
|
14
|
+
return;
|
|
15
|
+
res.setHeader('content-type', `${contentType}${charset ? `; charset=${charset}` : ''}`);
|
|
16
|
+
};
|
|
17
|
+
let buffer;
|
|
18
|
+
switch (type) {
|
|
19
|
+
case 'text':
|
|
20
|
+
setContentType('text/plain');
|
|
21
|
+
case 'html':
|
|
22
|
+
setContentType('text/html');
|
|
23
|
+
// shared with text
|
|
24
|
+
buffer = Buffer.from(data, charset);
|
|
25
|
+
break;
|
|
26
|
+
case 'buffer':
|
|
27
|
+
setContentType('application/octet-stream');
|
|
28
|
+
buffer = data;
|
|
29
|
+
break;
|
|
30
|
+
case 'stream':
|
|
31
|
+
setContentType('application/octet-stream');
|
|
32
|
+
buffer = data;
|
|
33
|
+
break;
|
|
34
|
+
case 'json':
|
|
35
|
+
setContentType('application/json');
|
|
36
|
+
buffer = Buffer.from(jsonBeautify ? JSON.stringify(data, null, 2) : JSON.stringify(data), charset);
|
|
37
|
+
break;
|
|
38
|
+
case 'redirect':
|
|
39
|
+
buffer = Buffer.from(data, charset);
|
|
40
|
+
break;
|
|
41
|
+
case undefined:
|
|
42
|
+
// skip response. Some middleware may handle it outside the chain. For example, express middleware
|
|
43
|
+
return ret;
|
|
44
|
+
default:
|
|
45
|
+
if (!res.getHeader('content-type'))
|
|
46
|
+
res.setHeader('content-type', 'text/plain');
|
|
47
|
+
throw new Error(`unsupported response type ${type}`);
|
|
48
|
+
}
|
|
49
|
+
const req = requestContext.value;
|
|
50
|
+
if (res.headersSent) {
|
|
51
|
+
if (res.writableFinished) {
|
|
52
|
+
// skipped: response is already finished
|
|
53
|
+
}
|
|
54
|
+
else if (res.writableEnded) {
|
|
55
|
+
const defer = makeDefer();
|
|
56
|
+
res.addListener('finish', defer.resolve);
|
|
57
|
+
await defer.promise;
|
|
58
|
+
// skipped: response is already ended
|
|
59
|
+
// chunk is not fully flushed yet
|
|
60
|
+
}
|
|
61
|
+
else
|
|
62
|
+
await promisify(res.end.bind(res))(undefined); // to be consistent, we end the response immediately
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
// https://github.com/expressjs/express/blob/980d881e3b023db079de60477a2588a91f046ca5/lib/response.js#L210
|
|
66
|
+
if (res.statusCode === 204) { // No Content
|
|
67
|
+
res.removeHeader('content-type');
|
|
68
|
+
res.removeHeader('content-length');
|
|
69
|
+
res.removeHeader('transfer-encoding');
|
|
70
|
+
// write nothing
|
|
71
|
+
}
|
|
72
|
+
if (res.statusCode === 205) { // reset content. Tell client to clear the form, etc.
|
|
73
|
+
res.setHeader('content-length', 0);
|
|
74
|
+
res.removeHeader('transfer-encoding');
|
|
75
|
+
}
|
|
76
|
+
else if (req.method === 'HEAD') {
|
|
77
|
+
// write nothing
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
if (Buffer.isBuffer(buffer)) {
|
|
81
|
+
// support: 304 (etag), zipping, file etag and last modified
|
|
82
|
+
res.setHeader('content-length', buffer.length);
|
|
83
|
+
if (!disableEtag) {
|
|
84
|
+
const etag = entityTag(buffer);
|
|
85
|
+
const lastModified = res.getHeader('last-modified');
|
|
86
|
+
res.setHeader('ETag', etag);
|
|
87
|
+
if (isFreshETag(req, etag)) {
|
|
88
|
+
res.removeHeader('content-type');
|
|
89
|
+
res.removeHeader('content-length');
|
|
90
|
+
res.removeHeader('transfer-encoding');
|
|
91
|
+
res.statusCode = 304;
|
|
92
|
+
// write nothing
|
|
93
|
+
}
|
|
94
|
+
else
|
|
95
|
+
res.write(buffer);
|
|
96
|
+
}
|
|
97
|
+
else
|
|
98
|
+
res.write(buffer);
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
buffer.pipe(res);
|
|
102
|
+
}
|
|
103
|
+
// fixme: not support content-encoding (gzip, deflate, br) for now
|
|
104
|
+
}
|
|
105
|
+
await promisify(res.end.bind(res))(undefined); // some express middleware, such as express-session, requires explicitly passing chunk
|
|
106
|
+
}
|
|
107
|
+
return ret;
|
|
108
|
+
});
|
|
109
|
+
// todo: support setFile (with stream or with buffer)
|
|
110
|
+
export function setText(text, { status } = {}) {
|
|
111
|
+
const response = responseContext.value;
|
|
112
|
+
const express = expressContext.value;
|
|
113
|
+
if (status)
|
|
114
|
+
response.statusCode = status;
|
|
115
|
+
express.data = text;
|
|
116
|
+
express.type = 'text';
|
|
117
|
+
}
|
|
118
|
+
export function setHtml(html, opts = {}) {
|
|
119
|
+
setText(html, opts);
|
|
120
|
+
const express = expressContext.value;
|
|
121
|
+
express.type = 'html';
|
|
122
|
+
}
|
|
123
|
+
export function setBuffer(buffer, { status } = {}) {
|
|
124
|
+
const response = responseContext.value;
|
|
125
|
+
const express = expressContext.value;
|
|
126
|
+
if (status)
|
|
127
|
+
response.statusCode = status;
|
|
128
|
+
express.data = buffer;
|
|
129
|
+
express.type = 'buffer';
|
|
130
|
+
}
|
|
131
|
+
export function setStream(stream, { status } = {}) {
|
|
132
|
+
const response = responseContext.value;
|
|
133
|
+
const express = expressContext.value;
|
|
134
|
+
if (status)
|
|
135
|
+
response.statusCode = status;
|
|
136
|
+
express.data = stream;
|
|
137
|
+
express.type = 'stream';
|
|
138
|
+
}
|
|
139
|
+
export function setJson(json, { status, beautify } = {}) {
|
|
140
|
+
const response = responseContext.value;
|
|
141
|
+
if (status)
|
|
142
|
+
response.statusCode = status;
|
|
143
|
+
const express = expressContext.value;
|
|
144
|
+
express.data = json;
|
|
145
|
+
express.type = 'json';
|
|
146
|
+
if (beautify !== undefined)
|
|
147
|
+
express.jsonBeautify = beautify;
|
|
148
|
+
}
|
|
149
|
+
export function setRedirect(url, status) {
|
|
150
|
+
const response = responseContext.value;
|
|
151
|
+
const express = expressContext.value;
|
|
152
|
+
response.statusCode = status;
|
|
153
|
+
express.data = url;
|
|
154
|
+
express.type = 'redirect';
|
|
155
|
+
}
|
|
156
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXhwcmVzcy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9leHByZXNzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sU0FBUyxNQUFNLFFBQVEsQ0FBQTtBQUM5QixPQUFPLEVBQUMsU0FBUyxFQUFDLE1BQU0sV0FBVyxDQUFBO0FBQ25DLE9BQU8sRUFBQyxTQUFTLEVBQUUsV0FBVyxFQUFDLE1BQU0sV0FBVyxDQUFBO0FBQ2hELE9BQU8sRUFBQyxXQUFXLEVBQUUsY0FBYyxFQUFFLGVBQWUsRUFBQyxNQUFNLGNBQWMsQ0FBQTtBQUd6RSxNQUFNLENBQUMsTUFBTSxjQUFjLEdBQUcsV0FBVyxDQUFDLEtBQUssRUFDOUMsRUFBQyxZQUFZLEVBQUUsV0FBVyxLQUd0QixFQUFFLEVBQ0wsRUFBRTtJQUNILE9BQU87UUFDTixZQUFZO1FBQ1osV0FBVztLQStCVixDQUFBO0FBQ0gsQ0FBQyxFQUFFLEtBQUssRUFBRSxHQUFHLEVBQUUsRUFBQyxJQUFJLEVBQUUsSUFBSSxFQUFFLE9BQU8sRUFBRSxZQUFZLEVBQUUsV0FBVyxFQUFDLEVBQUUsRUFBRTtJQUNsRSxNQUFNLEdBQUcsR0FBRyxlQUFlLENBQUMsS0FBSyxDQUFBO0lBQ2pDLE1BQU0sY0FBYyxHQUFHLENBQUMsV0FBbUIsRUFBRSxFQUFFO1FBQzlDLElBQUksR0FBRyxDQUFDLFdBQVcsSUFBSSxHQUFHLENBQUMsU0FBUyxDQUFDLGNBQWMsQ0FBQztZQUFFLE9BQU07UUFDNUQsR0FBRyxDQUFDLFNBQVMsQ0FBQyxjQUFjLEVBQUUsR0FBRyxXQUFXLEdBQUcsT0FBTyxDQUFDLENBQUMsQ0FBQyxhQUFhLE9BQU8sRUFBRSxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFBO0lBQ3hGLENBQUMsQ0FBQTtJQUNELElBQUksTUFBTSxDQUFBO0lBRVYsUUFBUSxJQUFJLEVBQUU7UUFDYixLQUFLLE1BQU07WUFDVixjQUFjLENBQUMsWUFBWSxDQUFDLENBQUE7UUFD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
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { type Express, type Request, type Response, type Router } from 'express';
|
|
2
|
+
export declare const expressApp: (setup: (app: Express) => any) => Promise<(next: any) => Promise<void>>;
|
|
3
|
+
export declare const expressRouter: (setup: (router: Router) => any) => Promise<(next: any) => Promise<void>>;
|
|
4
|
+
export declare const chainExpressMiddlewares: (...middlewares: ((req: Request, res: Response, next: () => any) => any)[]) => Promise<(next: any) => Promise<void>>;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import express from 'express';
|
|
2
|
+
import { requestContext, responseContext } from './context.js';
|
|
3
|
+
import makeDefer from 'jdefer';
|
|
4
|
+
export const expressApp = async (setup) => {
|
|
5
|
+
const symbol = Symbol('expressApp');
|
|
6
|
+
const app = express();
|
|
7
|
+
await setup(app);
|
|
8
|
+
app.use((req, _res, _next) => req[symbol].resolve());
|
|
9
|
+
app.use((err, req, _res, _next) => req[symbol].reject(err));
|
|
10
|
+
return async (next) => {
|
|
11
|
+
const req = requestContext.value;
|
|
12
|
+
const defer = makeDefer();
|
|
13
|
+
req[symbol] = defer;
|
|
14
|
+
app(req, responseContext.value);
|
|
15
|
+
await defer.promise;
|
|
16
|
+
await next();
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
export const expressRouter = async (setup) => {
|
|
20
|
+
const symbol = Symbol('expressRouter');
|
|
21
|
+
const router = express.Router();
|
|
22
|
+
await setup(router);
|
|
23
|
+
router.use((req, _res, _next) => req[symbol].resolve());
|
|
24
|
+
router.use((err, req, _res, _next) => req[symbol].reject(err));
|
|
25
|
+
return async (next) => {
|
|
26
|
+
const req = requestContext.value;
|
|
27
|
+
const defer = makeDefer();
|
|
28
|
+
req[symbol] = defer;
|
|
29
|
+
router(req, responseContext.value);
|
|
30
|
+
await defer.promise;
|
|
31
|
+
await next();
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
export const chainExpressMiddlewares = (...middlewares) => expressRouter(router => router.use(...middlewares));
|
|
35
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXhwcmVzc0FwcC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9leHByZXNzQXBwLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sT0FBaUUsTUFBTSxTQUFTLENBQUE7QUFDdkYsT0FBTyxFQUFDLGNBQWMsRUFBRSxlQUFlLEVBQUMsTUFBTSxjQUFjLENBQUE7QUFDNUQsT0FBTyxTQUFTLE1BQU0sUUFBUSxDQUFBO0FBRTlCLE1BQU0sQ0FBQyxNQUFNLFVBQVUsR0FBRyxLQUFLLEVBQUUsS0FBNEIsRUFBRSxFQUFFO0lBQ2hFLE1BQU0sTUFBTSxHQUFHLE1BQU0sQ0FBQyxZQUFZLENBQUMsQ0FBQTtJQUVuQyxNQUFNLEdBQUcsR0FBRyxPQUFPLEVBQUUsQ0FBQTtJQUNyQixNQUFNLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQTtJQUNoQixHQUFHLENBQUMsR0FBRyxDQUFDLENBQUMsR0FBRyxFQUFFLElBQUksRUFBRSxLQUFLLEVBQUUsRUFBRSxDQUFDLEdBQUcsQ0FBQyxNQUFNLENBQUMsQ0FBQyxPQUFPLEVBQUUsQ0FBQyxDQUFBO0lBQ3BELEdBQUcsQ0FBQyxHQUFHLENBQUMsQ0FBQyxHQUFHLEVBQUUsR0FBRyxFQUFFLElBQUksRUFBRSxLQUFLLEVBQUUsRUFBRSxDQUFDLEdBQUcsQ0FBQyxNQUFNLENBQUMsQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQTtJQUMzRCxPQUFPLEtBQUssRUFBQyxJQUFJLEVBQUMsRUFBRTtRQUNuQixNQUFNLEdBQUcsR0FBRyxjQUFjLENBQUMsS0FBSyxDQUFBO1FBQ2hDLE1BQU0sS0FBSyxHQUFHLFNBQVMsRUFBRSxDQUFBO1FBQ3pCLEdBQUcsQ0FBQyxNQUFNLENBQUMsR0FBRyxLQUFLLENBQUE7UUFDbkIsR0FBRyxDQUFDLEdBQUcsRUFBRSxlQUFlLENBQUMsS0FBSyxDQUFDLENBQUE7UUFDL0IsTUFBTSxLQUFLLENBQUMsT0FBTyxDQUFBO1FBQ25CLE1BQU0sSUFBSSxFQUFFLENBQUE7SUFDYixDQUFDLENBQUE7QUFDRixDQUFDLENBQUE7QUFFRCxNQUFNLENBQUMsTUFBTSxhQUFhLEdBQUcsS0FBSyxFQUFFLEtBQThCLEVBQUUsRUFBRTtJQUNyRSxNQUFNLE1BQU0sR0FBRyxNQUFNLENBQUMsZUFBZSxDQUFDLENBQUE7SUFFdEMsTUFBTSxNQUFNLEdBQUcsT0FBTyxDQUFDLE1BQU0sRUFBRSxDQUFBO0lBQy9CLE1BQU0sS0FBSyxDQUFDLE1BQU0sQ0FBQyxDQUFBO0lBQ25CLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQyxHQUFHLEVBQUUsSUFBSSxFQUFFLEtBQUssRUFBRSxFQUFFLENBQUMsR0FBRyxDQUFDLE1BQU0sQ0FBQyxDQUFDLE9BQU8sRUFBRSxDQUFDLENBQUE7SUFDdkQsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsRUFBRSxHQUFHLEVBQUUsSUFBSSxFQUFFLEtBQUssRUFBRSxFQUFFLENBQUMsR0FBRyxDQUFDLE1BQU0sQ0FBQyxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFBO0lBQzlELE9BQU8sS0FBSyxFQUFDLElBQUksRUFBQyxFQUFFO1FBQ25CLE1BQU0sR0FBRyxHQUFHLGNBQWMsQ0FBQyxLQUFLLENBQUE7UUFDaEMsTUFBTSxLQUFLLEdBQUcsU0FBUyxFQUFFLENBQUE7UUFDekIsR0FBRyxDQUFDLE1BQU0sQ0FBQyxHQUFHLEtBQUssQ0FBQTtRQUNuQixNQUFNLENBQUMsR0FBRyxFQUFFLGVBQWUsQ0FBQyxLQUFLLENBQUMsQ0FBQTtRQUNsQyxNQUFNLEtBQUssQ0FBQyxPQUFPLENBQUE7UUFDbkIsTUFBTSxJQUFJLEVBQUUsQ0FBQTtJQUNiLENBQUMsQ0FBQTtBQUNGLENBQUMsQ0FBQTtBQUVELE1BQU0sQ0FBQyxNQUFNLHVCQUF1QixHQUFHLENBQ3RDLEdBQUcsV0FBeUUsRUFDM0UsRUFBRSxDQUFDLGFBQWEsQ0FBQyxNQUFNLENBQUMsRUFBRSxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsR0FBRyxXQUFXLENBQUMsQ0FBQyxDQUFBIn0=
|
package/esm/file.d.ts
ADDED