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.
Files changed (50) hide show
  1. package/README.md +183 -0
  2. package/cjs/body.d.ts +30 -0
  3. package/cjs/body.js +91 -0
  4. package/cjs/contentType.d.ts +4 -0
  5. package/cjs/contentType.js +92 -0
  6. package/cjs/context.d.ts +15 -0
  7. package/cjs/context.js +27 -0
  8. package/cjs/error.js +42 -0
  9. package/cjs/etag.d.ts +7 -0
  10. package/cjs/etag.js +100 -0
  11. package/cjs/express.d.ts +49 -0
  12. package/cjs/express.js +168 -0
  13. package/cjs/expressApp.d.ts +4 -0
  14. package/cjs/expressApp.js +44 -0
  15. package/cjs/file.d.ts +3 -0
  16. package/cjs/file.js +12 -0
  17. package/cjs/index.d.ts +6 -0
  18. package/cjs/index.js +33 -0
  19. package/cjs/package.json +3 -0
  20. package/cjs/route.d.ts +38 -0
  21. package/cjs/route.js +86 -0
  22. package/cjs/static.d.ts +4 -0
  23. package/cjs/static.js +60 -0
  24. package/cjs/stream.d.ts +11 -0
  25. package/cjs/stream.js +100 -0
  26. package/esm/body.d.ts +30 -0
  27. package/esm/body.js +88 -0
  28. package/esm/contentType.d.ts +4 -0
  29. package/esm/contentType.js +88 -0
  30. package/esm/context.d.ts +15 -0
  31. package/esm/context.js +23 -0
  32. package/esm/error.js +37 -0
  33. package/esm/etag.d.ts +7 -0
  34. package/esm/etag.js +90 -0
  35. package/esm/express.d.ts +49 -0
  36. package/esm/express.js +156 -0
  37. package/esm/expressApp.d.ts +4 -0
  38. package/esm/expressApp.js +35 -0
  39. package/esm/file.d.ts +3 -0
  40. package/esm/file.js +8 -0
  41. package/esm/index.d.ts +6 -0
  42. package/esm/index.js +7 -0
  43. package/esm/route.d.ts +38 -0
  44. package/esm/route.js +82 -0
  45. package/esm/static.d.ts +4 -0
  46. package/esm/static.js +56 -0
  47. package/esm/stream.d.ts +11 -0
  48. package/esm/stream.js +92 -0
  49. package/package.json +32 -5
  50. 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,4 @@
1
+ export declare function parseContentType(header: string): {
2
+ mediaType: string;
3
+ parameters: Record<string, string>;
4
+ };
@@ -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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
@@ -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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXRhZy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9ldGFnLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLGtHQUFrRztBQUNsRyxPQUFPLE1BQU0sTUFBTSxhQUFhLENBQUE7QUFJaEMsTUFBTSxVQUFVLFNBQVMsQ0FBQyxHQUFXLEVBQUUsSUFBYztJQUNwRCxxQkFBcUI7SUFDckIsT0FBTyxHQUFHLENBQUMsTUFBTTtRQUNoQixDQUFDLENBQUMsR0FBRyxHQUFHLENBQUMsTUFBTSxDQUFDLFFBQVEsQ0FBQyxFQUFFLENBQUMsSUFBSSxNQUFNO2FBQ3BDLFVBQVUsQ0FBQyxNQUFNLENBQUM7YUFDbEIsTUFBTSxDQUFDLEdBQUcsQ0FBQzthQUNYLE1BQU0sQ0FBQyxRQUFRLENBQUM7YUFDaEIsU0FBUyxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUMsR0FBRztRQUNyQixDQUFDLENBQUMsR0FBRyxJQUFJLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsRUFBRSxpQ0FBaUMsQ0FBQTtJQUN2RCw0RUFBNEU7SUFDNUUseUJBQXlCO0lBQ3pCLGdHQUFnRztBQUNqRyxDQUFDO0FBRUQsTUFBTSw2QkFBNkIsR0FBRyxnQ0FBZ0MsQ0FBQTtBQUN0RSxNQUFNLFVBQVUsT0FBTyxDQUFDLElBQUk7SUFDM0IsTUFBTSxLQUFLLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxPQUFPLEVBQUUsQ0FBQyxRQUFRLENBQUMsRUFBRSxDQUFDLENBQUE7SUFDL0MsTUFBTSxJQUFJLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsRUFBRSxDQUFDLENBQUE7SUFFbkMsT0FBTyxJQUFJLElBQUksSUFBSSxLQUFLLEdBQUcsQ0FBQTtBQUM1QixDQUFDO0FBQ0QsNkZBQTZGO0FBQzdGLE1BQU0sVUFBVSxXQUFXLENBQUMsR0FBb0IsRUFBRSxJQUFZO0lBQzdELE1BQU0sU0FBUyxHQUFHLEdBQUcsQ0FBQyxPQUFPLENBQUMsZUFB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
@@ -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
@@ -0,0 +1,3 @@
1
+ export declare const serveFile: ({ prefix }: {
2
+ prefix?: string | undefined;
3
+ }) => IChainable;