dx-server 0.0.1-pre

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 (51) hide show
  1. package/README.md +113 -0
  2. package/cjs/body.d.ts +29 -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.d.ts +5 -0
  9. package/cjs/error.js +42 -0
  10. package/cjs/etag.d.ts +7 -0
  11. package/cjs/etag.js +97 -0
  12. package/cjs/express.d.ts +39 -0
  13. package/cjs/express.js +143 -0
  14. package/cjs/expressApp.d.ts +2 -0
  15. package/cjs/expressApp.js +14 -0
  16. package/cjs/file.d.ts +4 -0
  17. package/cjs/file.js +12 -0
  18. package/cjs/index.d.ts +6 -0
  19. package/cjs/index.js +27 -0
  20. package/cjs/route.d.ts +33 -0
  21. package/cjs/route.js +87 -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 +97 -0
  26. package/esm/body.d.ts +28 -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.d.ts +5 -0
  33. package/esm/error.js +37 -0
  34. package/esm/etag.d.ts +7 -0
  35. package/esm/etag.js +90 -0
  36. package/esm/express.d.ts +21 -0
  37. package/esm/express.js +135 -0
  38. package/esm/expressApp.d.ts +2 -0
  39. package/esm/expressApp.js +10 -0
  40. package/esm/file.d.ts +3 -0
  41. package/esm/file.js +8 -0
  42. package/esm/index.d.ts +6 -0
  43. package/esm/index.js +7 -0
  44. package/esm/package.json +3 -0
  45. package/esm/route.d.ts +33 -0
  46. package/esm/route.js +83 -0
  47. package/esm/static.d.ts +4 -0
  48. package/esm/static.js +56 -0
  49. package/esm/stream.d.ts +10 -0
  50. package/esm/stream.js +92 -0
  51. package/package.json +35 -0
package/esm/body.js ADDED
@@ -0,0 +1,88 @@
1
+ import { getContentStream, readStream } from './stream.js';
2
+ import qs 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
+ : qs.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 ? qs.parse(query) : {};
87
+ });
88
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYm9keS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9ib2R5LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBQyxnQkFBZ0IsRUFBRSxVQUFVLEVBQUMsTUFBTSxhQUFhLENBQUE7QUFDeEQsT0FBTyxFQUFFLE1BQU0sSUFBSSxDQUFBO0FBQ25CLE9BQU8sRUFBQyxnQkFBZ0IsRUFBQyxNQUFNLGtCQUFrQixDQUFBO0FBQ2pELE9BQU8sRUFBQyxXQUFXLEVBQUUsY0FBYyxFQUFDLE1BQU0sY0FBYyxDQUFBO0FBRXhELE1BQU0sQ0FBQyxNQUFNLGlCQUFpQixHQUFHLFdBQVcsQ0FBQyxLQUFLLEVBQ2pELEVBQ0MsS0FBSyxHQUFHLEdBQUcsSUFBSSxFQUFFLEVBQUUsUUFBUTtLQUd4QixFQUFFLEVBQ0wsRUFBRTtJQUNILE1BQU0sR0FBRyxHQUFHLGNBQWMsQ0FBQyxLQUFLLENBQUE7SUFFaEM7Ozs7O09BS0c7SUFDRiwrRkFBK0Y7SUFDaEcsTUFBTSxtQkFBbUIsR0FBRyxRQUFRLENBQUMsR0FBRyxDQUFDLE9BQU8sQ0FBQyxnQkFBZ0IsQ0FBQyxJQUFJLEVBQUUsRUFBRSxFQUFFLENBQUMsQ0FBQTtJQUM3RSxJQUNDLEdBQUcsQ0FBQyxPQUFPLENBQUMsbUJBQW1CLENBQUMsS0FBSyxTQUFTO1dBQzNDLEtBQUssQ0FBQyxtQkFBbUIsQ0FBQztRQUM1QixPQUFNO0lBQ1IsTUFBTSxhQUFhLEdBQUcsS0FBSyxDQUFDLG1CQUFtQixDQUFDLENBQUMsQ0FBQyxDQUFDLFNBQVMsQ0FBQyxDQUFDLENBQUMsbUJBQW1CLENBQUE7SUFFbEYsT0FBTztJQUNQLE1BQU0sUUFBUSxHQUFHLENBQUMsR0FBRyxDQUFDLE9BQU8sQ0FBQyxrQkFBa0IsQ0FBQyxJQUFJLFVBQVUsQ0FBQyxDQUFDLFdBQVcsRUFBRSxDQUFBO0lBQzlFLE1BQU0sTUFBTSxHQUFHLGdCQUFnQixDQUFDLEdBQUcsRUFBRSxRQUFRLENBQUMsQ0FBQTtJQUM5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
@@ -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" />
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.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ import { IChainable } from 'jchain';
2
+ export declare const catchError: IChainable;
3
+ export declare const catchApiError: IChainable;
4
+ export declare const notFound: IChainable;
5
+ export declare const notFoundApi: IChainable;
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" />
2
+ /// <reference types="node" />
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==
@@ -0,0 +1,21 @@
1
+ /// <reference types="node" />
2
+ export declare const expressContext: {
3
+ readonly value: unknown;
4
+ chain(params_0?: {
5
+ jsonBeautify?: boolean | undefined;
6
+ } | undefined): <V>(next: () => V) => Promise<any>;
7
+ };
8
+ export declare function setText(text: string, { status }?: {
9
+ status?: number;
10
+ }): void;
11
+ export declare function setHtml(html: string, opts?: {
12
+ status?: number;
13
+ }): void;
14
+ export declare function setBuffer(buffer: Buffer, { status }?: {
15
+ status?: number;
16
+ }): void;
17
+ export declare function setJson(json: any, { status, beautify }?: {
18
+ status?: number;
19
+ beautify?: boolean;
20
+ }): void;
21
+ export declare function setRedirect(url: string, status: 301 | 302): void;
package/esm/express.js ADDED
@@ -0,0 +1,135 @@
1
+ import makeDefer from 'jdefer';
2
+ import { promisify } from 'util';
3
+ import { entityTag, isFreshETag } from './etag.js';
4
+ import { makeContext, requestContext, responseContext } from './context.js';
5
+ export const expressContext = makeContext(async ({ jsonBeautify } = {}) => {
6
+ return {
7
+ beautify: jsonBeautify,
8
+ };
9
+ }, async (ret, { type, data, charset, beautify }) => {
10
+ const res = responseContext.value;
11
+ const setContentType = (contentType) => {
12
+ if (res.headersSent || res.getHeader('content-type'))
13
+ return;
14
+ res.setHeader('content-type', `${contentType}${charset ? `; charset=${charset}` : ''}`);
15
+ };
16
+ let buffer;
17
+ switch (type) {
18
+ case 'text':
19
+ setContentType('text/plain');
20
+ case 'html':
21
+ setContentType('text/html');
22
+ // shared with text
23
+ buffer = Buffer.from(data, charset);
24
+ break;
25
+ case 'buffer':
26
+ setContentType('application/octet-stream');
27
+ buffer = data;
28
+ break;
29
+ case 'json':
30
+ setContentType('application/json');
31
+ buffer = Buffer.from(beautify ? JSON.stringify(data, null, 2) : JSON.stringify(data), charset);
32
+ break;
33
+ case 'redirect':
34
+ buffer = Buffer.from(data, charset);
35
+ break;
36
+ case undefined:
37
+ // not found
38
+ buffer = Buffer.from('not found', charset);
39
+ res.statusCode = 404;
40
+ break;
41
+ default:
42
+ if (!res.getHeader('content-type'))
43
+ res.setHeader('content-type', 'text/plain');
44
+ throw new Error(`unsupported response type ${type}`);
45
+ }
46
+ const req = requestContext.value;
47
+ if (res.headersSent) {
48
+ if (res.writableFinished) {
49
+ // skipped: response is already finished
50
+ }
51
+ else if (res.writableEnded) {
52
+ const defer = makeDefer();
53
+ res.addListener('finish', defer.resolve);
54
+ await defer.promise;
55
+ // skipped: response is already ended
56
+ // chunk is not fully flushed yet
57
+ }
58
+ else
59
+ await promisify(res.end.bind(res))(); // to be consistent, we end the response immediately
60
+ }
61
+ else {
62
+ // https://github.com/expressjs/express/blob/980d881e3b023db079de60477a2588a91f046ca5/lib/response.js#L210
63
+ if (res.statusCode === 204) { // No Content
64
+ res.removeHeader('content-type');
65
+ res.removeHeader('content-length');
66
+ res.removeHeader('transfer-encoding');
67
+ // write nothing
68
+ }
69
+ if (res.statusCode === 205) { // reset content. Tell client to clear the form, etc.
70
+ res.setHeader('content-length', 0);
71
+ res.removeHeader('transfer-encoding');
72
+ }
73
+ else if (req.method === 'HEAD') {
74
+ // write nothing
75
+ }
76
+ else {
77
+ // support: 304 (etag), zipping, file etag and last modified
78
+ res.setHeader('content-length', buffer.length);
79
+ const etag = entityTag(buffer);
80
+ const lastModified = res.getHeader('last-modified');
81
+ res.setHeader('ETag', etag);
82
+ if (isFreshETag(req, etag)) {
83
+ res.removeHeader('content-type');
84
+ res.removeHeader('content-length');
85
+ res.removeHeader('transfer-encoding');
86
+ res.statusCode = 304;
87
+ // write nothing
88
+ }
89
+ else
90
+ res.write(buffer);
91
+ // fixme: not support content-encoding (gzip, deflate, br) for now
92
+ }
93
+ await promisify(res.end.bind(res))();
94
+ }
95
+ return ret;
96
+ });
97
+ export function setText(text, { status } = {}) {
98
+ const response = responseContext.value;
99
+ const express = expressContext.value;
100
+ if (status)
101
+ response.statusCode = status;
102
+ express.data = text;
103
+ express.type = 'text';
104
+ }
105
+ export function setHtml(html, opts = {}) {
106
+ setText(html, opts);
107
+ const express = expressContext.value;
108
+ express.type = 'html';
109
+ }
110
+ export function setBuffer(buffer, { status } = {}) {
111
+ const response = responseContext.value;
112
+ const express = expressContext.value;
113
+ if (status)
114
+ response.statusCode = status;
115
+ express.data = buffer;
116
+ express.type = 'buffer';
117
+ }
118
+ export function setJson(json, { status, beautify } = {}) {
119
+ const response = responseContext.value;
120
+ if (status)
121
+ response.statusCode = status;
122
+ const express = expressContext.value;
123
+ express.data = json;
124
+ express.type = 'json';
125
+ if (beautify !== undefined)
126
+ express.beautify = beautify;
127
+ }
128
+ export function setRedirect(url, status) {
129
+ const response = responseContext.value;
130
+ const express = expressContext.value;
131
+ response.statusCode = status;
132
+ express.data = url;
133
+ express.type = 'redirect';
134
+ }
135
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXhwcmVzcy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9leHByZXNzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sU0FBUyxNQUFNLFFBQVEsQ0FBQTtBQUM5QixPQUFPLEVBQUMsU0FBUyxFQUFDLE1BQU0sTUFBTSxDQUFBO0FBQzlCLE9BQU8sRUFBQyxTQUFTLEVBQUUsV0FBVyxFQUFDLE1BQU0sV0FBVyxDQUFBO0FBQ2hELE9BQU8sRUFBQyxXQUFXLEVBQUUsY0FBYyxFQUFFLGVBQWUsRUFBQyxNQUFNLGNBQWMsQ0FBQTtBQUV6RSxNQUFNLENBQUMsTUFBTSxjQUFjLEdBQUcsV0FBVyxDQUFDLEtBQUssRUFDOUMsRUFBQyxZQUFZLEtBRVQsRUFBRSxFQUNMLEVBQUU7SUFDSCxPQUFPO1FBQ04sUUFBUSxFQUFFLFlBQVk7S0EwQnJCLENBQUE7QUFDSCxDQUFDLEVBQUUsS0FBSyxFQUFFLEdBQUcsRUFBRSxFQUFDLElBQUksRUFBRSxJQUFJLEVBQUUsT0FBTyxFQUFFLFFBQVEsRUFBQyxFQUFFLEVBQUU7SUFDakQsTUFBTSxHQUFHLEdBQUcsZUFBZSxDQUFDLEtBQUssQ0FBQTtJQUNqQyxNQUFNLGNBQWMsR0FBRyxDQUFDLFdBQW1CLEVBQUUsRUFBRTtRQUM5QyxJQUFJLEdBQUcsQ0FBQyxXQUFXLElBQUksR0FBRyxDQUFDLFNBQVMsQ0FBQyxjQUFjLENBQUM7WUFBRSxPQUFNO1FBQzVELEdBQUcsQ0FBQyxTQUFTLENBQUMsY0FBYyxFQUFFLEdBQUcsV0FBVyxHQUFHLE9BQU8sQ0FBQyxDQUFDLENBQUMsYUFBYSxPQUFPLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQTtJQUN4RixDQUFDLENBQUE7SUFDRCxJQUFJLE1BQU0sQ0FBQTtJQUVWLFFBQVEsSUFBSSxFQUFFO1FBQ2IsS0FBSyxNQUFNO1lBQ1YsY0FBYyxDQUFDLFlBQVksQ0FBQyxDQUFBO1FBQzdCLEtBQUssTUFBTTtZQUNWLGNB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
@@ -0,0 +1,2 @@
1
+ import { Express } from 'express';
2
+ export declare const expressApp: (setup: (app: Express) => any) => <V>(next: () => V) => Promise<any>;
@@ -0,0 +1,10 @@
1
+ import express from 'express';
2
+ import { requestContext, responseContext } from './context.js';
3
+ export const expressApp = (setup) => {
4
+ const app = express();
5
+ setup(app);
6
+ return async (next) => {
7
+ return app(requestContext.value, responseContext.value, next);
8
+ };
9
+ };
10
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXhwcmVzc0FwcC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9leHByZXNzQXBwLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sT0FBa0IsTUFBTSxTQUFTLENBQUE7QUFDeEMsT0FBTyxFQUFDLGNBQWMsRUFBRSxlQUFlLEVBQUMsTUFBTSxjQUFjLENBQUE7QUFFNUQsTUFBTSxDQUFDLE1BQU0sVUFBVSxHQUFHLENBQUMsS0FBNEIsRUFBRSxFQUFFO0lBQzFELE1BQU0sR0FBRyxHQUFHLE9BQU8sRUFBRSxDQUFBO0lBQ3JCLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQTtJQUNWLE9BQU8sS0FBSyxFQUFLLElBQWEsRUFBRSxFQUFFO1FBQ2pDLE9BQU8sR0FBRyxDQUFDLGNBQWMsQ0FBQyxLQUFLLEVBQUUsZUFBZSxDQUFDLEtBQUssRUFBRSxJQUFJLENBQUMsQ0FBQTtJQUM5RCxDQUFDLENBQUE7QUFDRixDQUFDLENBQUEifQ==
package/esm/file.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ export declare const serveFile: ({ prefix }: {
2
+ prefix?: string | undefined;
3
+ }) => IChainable;
package/esm/file.js ADDED
@@ -0,0 +1,8 @@
1
+ import { requestContext, responseContext } from './context.js';
2
+ export const serveFile = ({ prefix = '' }) => next => {
3
+ const req = requestContext.value;
4
+ const res = responseContext.value;
5
+ if (req.method !== 'GET' && req.method !== 'HEAD')
6
+ return next();
7
+ };
8
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmlsZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9maWxlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBQyxjQUFjLEVBQUUsZUFBZSxFQUFDLE1BQU0sY0FBYyxDQUFBO0FBRzVELE1BQU0sQ0FBQyxNQUFNLFNBQVMsR0FBRyxDQUN4QixFQUFDLE1BQU0sR0FBRyxFQUFFLEVBRVgsRUFDWSxFQUFFLENBQUMsSUFBSSxDQUFDLEVBQUU7SUFDdkIsTUFBTSxHQUFHLEdBQUcsY0FBYyxDQUFDLEtBQUssQ0FBQTtJQUNoQyxNQUFNLEdBQUcsR0FBRyxlQUFlLENBQUMsS0FBSyxDQUFBO0lBQ2pDLElBQUksR0FBRyxDQUFDLE1BQU0sS0FBSyxLQUFLLElBQUksR0FBRyxDQUFDLE1BQU0sS0FBSyxNQUFNO1FBQUUsT0FBTyxJQUFJLEVBQUUsQ0FBQTtBQUNqRSxDQUFDLENBQUEifQ==
package/esm/index.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ export { requestContext, responseContext } from './context.js';
2
+ export { expressContext, setHtml, setJson } from './express.js';
3
+ export { bufferBodyContext, jsonBodyContext, queryContext, rawBodyContext, textBodyContext, urlencodedBodyContext } from './body.js';
4
+ export { router } from './route.js';
5
+ export { catchApiError, catchError, notFound, notFoundApi } from './error.js';
6
+ export { expressApp } from './expressApp.js';
package/esm/index.js ADDED
@@ -0,0 +1,7 @@
1
+ export { requestContext, responseContext } from './context.js';
2
+ export { expressContext, setHtml, setJson } from './express.js';
3
+ export { bufferBodyContext, jsonBodyContext, queryContext, rawBodyContext, textBodyContext, urlencodedBodyContext } from './body.js';
4
+ export { router } from './route.js';
5
+ export { catchApiError, catchError, notFound, notFoundApi } from './error.js';
6
+ export { expressApp } from './expressApp.js';
7
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFDLGNBQWMsRUFBRSxlQUFlLEVBQUMsTUFBTSxjQUFjLENBQUE7QUFDNUQsT0FBTyxFQUFDLGNBQWMsRUFBRSxPQUFPLEVBQUUsT0FBTyxFQUFDLE1BQU0sY0FBYyxDQUFBO0FBQzdELE9BQU8sRUFDTixpQkFBaUIsRUFDakIsZUFBZSxFQUNmLFlBQVksRUFDWixjQUFjLEVBQ2QsZUFBZSxFQUNmLHFCQUFxQixFQUNyQixNQUFNLFdBQVcsQ0FBQTtBQUNsQixPQUFPLEVBQUMsTUFBTSxFQUFDLE1BQU0sWUFBWSxDQUFBO0FBQ2pDLE9BQU8sRUFBQyxhQUFhLEVBQUUsVUFBVSxFQUFFLFFBQVEsRUFBRSxXQUFXLEVBQUMsTUFBTSxZQUFZLENBQUE7QUFDM0UsT0FBTyxFQUFDLFVBQVUsRUFBQyxNQUFNLGlCQUFpQixDQUFBIn0=
@@ -0,0 +1,3 @@
1
+ {
2
+ "type": "module"
3
+ }
package/esm/route.d.ts ADDED
@@ -0,0 +1,33 @@
1
+ import type { Key } from 'path-to-regexp';
2
+ import { IChainable } from 'jchain';
3
+ interface RegexpToPathOptions {
4
+ end?: boolean;
5
+ strict?: boolean;
6
+ sensitive?: boolean;
7
+ start?: boolean;
8
+ delimiter?: string;
9
+ endsWith?: string;
10
+ encode?(value: string): string;
11
+ prefixes?: string;
12
+ }
13
+ export declare function matchPattern<Params extends Record<Key['name'], string>>(pathname: string, pattern: string, options?: RegexpToPathOptions): {
14
+ matched: any;
15
+ params: any;
16
+ } | undefined;
17
+ interface RouteContext {
18
+ matched: string;
19
+ next(): any;
20
+ params: Record<Key['name'], string>;
21
+ }
22
+ interface RouteDefinition {
23
+ [k: string]: (p: RouteContext) => any;
24
+ }
25
+ declare const allMethods: readonly ["get", "head", "post", "put", "delete", "connect", "options", "trace", "patch"];
26
+ type IRouter = {
27
+ [K in typeof allMethods[number]]: (definition: RouteDefinition, options?: RegexpToPathOptions) => IChainable;
28
+ } & {
29
+ all(definition: RouteDefinition, options?: RegexpToPathOptions): IChainable;
30
+ method(method: string, definition: RouteDefinition, options?: RegexpToPathOptions): IChainable;
31
+ };
32
+ export declare const router: IRouter;
33
+ export {};