dx-server 0.4.0 → 0.5.1
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 +33 -39
- package/cjs/body.d.ts +2 -9
- package/cjs/body.js +11 -100
- package/cjs/bodyHelpers.d.ts +16 -0
- package/cjs/bodyHelpers.js +102 -0
- package/cjs/dx.d.ts +13 -60
- package/cjs/dx.js +41 -134
- package/cjs/dxHelpers.d.ts +32 -0
- package/cjs/dxHelpers.js +113 -0
- package/cjs/etag.js +1 -1
- package/cjs/express.js +6 -8
- package/cjs/file.d.ts +1 -2
- package/cjs/file.js +4 -4
- package/cjs/helpers.d.ts +2 -0
- package/cjs/helpers.js +14 -0
- package/cjs/index.d.ts +4 -3
- package/cjs/index.js +6 -9
- package/cjs/route.js +4 -4
- package/cjs/static.js +4 -4
- package/esm/body.d.ts +2 -9
- package/esm/body.js +11 -98
- package/esm/bodyHelpers.d.ts +16 -0
- package/esm/bodyHelpers.js +89 -0
- package/esm/dx.d.ts +13 -60
- package/esm/dx.js +37 -130
- package/esm/dxHelpers.d.ts +32 -0
- package/esm/dxHelpers.js +106 -0
- package/esm/etag.js +1 -1
- package/esm/express.js +6 -8
- package/esm/file.d.ts +1 -2
- package/esm/file.js +4 -4
- package/esm/helpers.d.ts +2 -0
- package/esm/helpers.js +3 -0
- package/esm/index.d.ts +4 -3
- package/esm/index.js +5 -4
- package/esm/route.js +4 -4
- package/esm/static.js +3 -3
- package/package.json +6 -4
- package/cjs/context.d.ts +0 -19
- package/cjs/context.js +0 -32
- package/esm/context.d.ts +0 -19
- package/esm/context.js +0 -28
package/README.md
CHANGED
|
@@ -14,17 +14,14 @@ Simple server
|
|
|
14
14
|
```javascript
|
|
15
15
|
import {Server} from 'node:http'
|
|
16
16
|
import chain from 'jchain'
|
|
17
|
-
import {
|
|
17
|
+
import dxServer, {getReq, getRes, router, setHtml, setText,} from 'dx-server'
|
|
18
18
|
|
|
19
|
-
new Server().on('request',
|
|
20
|
-
|
|
21
|
-
reqContext.chain(req),
|
|
22
|
-
resContext.chain(res),
|
|
23
|
-
dxContext.chain(),
|
|
19
|
+
new Server().on('request', (req, res) => chain(
|
|
20
|
+
dxServer(req, res),
|
|
24
21
|
next => {
|
|
25
|
-
|
|
26
|
-
console.log(
|
|
27
|
-
next()
|
|
22
|
+
getRes().setHeader('Cache-Control', 'no-cache')
|
|
23
|
+
console.log(getReq().method, getReq().url)
|
|
24
|
+
return next()
|
|
28
25
|
},
|
|
29
26
|
async next => {
|
|
30
27
|
try {await next()} catch (e) {
|
|
@@ -38,7 +35,7 @@ new Server().on('request', async (req, res) => {
|
|
|
38
35
|
}),
|
|
39
36
|
() => {setHtml('not found', {status: 404})},
|
|
40
37
|
)()
|
|
41
|
-
|
|
38
|
+
).listen(3000, () => console.log('server is listening at 3000'))
|
|
42
39
|
```
|
|
43
40
|
|
|
44
41
|
More complex server with express.
|
|
@@ -49,18 +46,16 @@ This sample additionally requires: `yarn install express morgan`
|
|
|
49
46
|
import {Server} from 'node:http'
|
|
50
47
|
import {promisify} from 'node:util'
|
|
51
48
|
import chain from 'jchain'
|
|
52
|
-
import {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
dxContext,
|
|
49
|
+
import dxServer, {
|
|
50
|
+
getReq, getRes,
|
|
56
51
|
getBuffer, getJson, getRaw, getText, getUrlEncoded, getQuery,
|
|
57
52
|
setHtml, setJson, setText, setBuffer, setRedirect, setNodeStream, setWebStream,
|
|
58
|
-
|
|
59
|
-
router
|
|
53
|
+
router,
|
|
60
54
|
} from 'dx-server'
|
|
61
55
|
import {expressApp} from 'dx-server/express'
|
|
62
56
|
import express from 'express'
|
|
63
57
|
import morgan from 'morgan'
|
|
58
|
+
import {AsyncLocalStorage} from 'node:async_hooks'
|
|
64
59
|
|
|
65
60
|
// it is best practice to create custom error class for non-system error
|
|
66
61
|
class ServerError extends Error {
|
|
@@ -73,25 +68,21 @@ class ServerError extends Error {
|
|
|
73
68
|
}
|
|
74
69
|
}
|
|
75
70
|
|
|
76
|
-
|
|
77
|
-
const
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
if (req.headers.authorization) {
|
|
82
|
-
return {id: 1, name: 'joe'}
|
|
83
|
-
}
|
|
84
|
-
})
|
|
71
|
+
const authStorage = new AsyncLocalStorage()
|
|
72
|
+
const authChain = async next => {
|
|
73
|
+
const auth = getReq().headers.authorization ? {id: 1, name: 'joe'} : undefined
|
|
74
|
+
return authStorage.run(auth, next)
|
|
75
|
+
}
|
|
85
76
|
|
|
86
77
|
const requireAuth = () => {
|
|
87
|
-
if (!
|
|
78
|
+
if (!authStorage.getStore()) throw new ServerError('unauthorized', 401, 'unauthorized')
|
|
88
79
|
}
|
|
89
80
|
|
|
90
81
|
const serverChain = chain(
|
|
91
82
|
next => {
|
|
92
83
|
// this is the difference between express and dx-server
|
|
93
84
|
// req, res can be accessed from anywhere via context which uses NodeJS's AsyncLocalStorage under the hood
|
|
94
|
-
|
|
85
|
+
getRes().setHeader('Cache-Control', 'no-cache')
|
|
95
86
|
return next() // must return or await
|
|
96
87
|
},
|
|
97
88
|
async next => {// global error catching for all following middlewares
|
|
@@ -111,7 +102,7 @@ const serverChain = chain(
|
|
|
111
102
|
app.use(morgan('common')) // in future, we will provide native implementation of express middlewares
|
|
112
103
|
app.use('/public', express.static('public'))
|
|
113
104
|
}),
|
|
114
|
-
|
|
105
|
+
authChain,
|
|
115
106
|
router.post({// example of catching error for all /api/* routes
|
|
116
107
|
async '/api'({next}) {
|
|
117
108
|
try {
|
|
@@ -137,7 +128,7 @@ const serverChain = chain(
|
|
|
137
128
|
},
|
|
138
129
|
'/api/me'() { // sample private router
|
|
139
130
|
requireAuth()
|
|
140
|
-
setJson({name:
|
|
131
|
+
setJson({name: authStorage.getStore().name})
|
|
141
132
|
},
|
|
142
133
|
}),
|
|
143
134
|
router.get({ // sample GET router
|
|
@@ -157,9 +148,7 @@ const tcpServer = new Server()
|
|
|
157
148
|
.on('request', async (req, res) => {
|
|
158
149
|
try {
|
|
159
150
|
await chain(
|
|
160
|
-
|
|
161
|
-
resContext.chain(res), // required for most middlewares
|
|
162
|
-
dxContext.chain({jsonBeautify: process.env.NODE_ENV !== 'production'}), // basic dx-server context
|
|
151
|
+
dxServer(req, res, {jsonBeautify: process.env.NODE_ENV !== 'production'}), // basic dx-server context
|
|
163
152
|
serverChain,
|
|
164
153
|
)()
|
|
165
154
|
} catch (e) {
|
|
@@ -178,16 +167,21 @@ Until these middlewares are available as native dx-server middlewares, express m
|
|
|
178
167
|
- [ ] cors
|
|
179
168
|
|
|
180
169
|
## Note:
|
|
181
|
-
|
|
170
|
+
|
|
171
|
+
`getBuffer, getJson, getRaw, getText, getUrlEncoded, getQuery` are all synchronous functions.
|
|
182
172
|
The associated results are calculated in the first time they are called and cached for subsequent calls.
|
|
183
173
|
|
|
184
174
|
If you want to get these values synchronously, you can do as follows:
|
|
185
175
|
```javascript
|
|
186
|
-
import {
|
|
187
|
-
|
|
176
|
+
import {AsyncLocalStorage} from 'node:async_hooks'
|
|
177
|
+
import {getJson} from 'dx-server'
|
|
178
|
+
const jsonStorage = new AsyncLocalStorage()
|
|
188
179
|
|
|
189
|
-
chain(
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
180
|
+
chain(
|
|
181
|
+
async next => jsonStorage.run(await getJson(), next),
|
|
182
|
+
next => {
|
|
183
|
+
console.log(jsonContext.value) // json body can be accessed synchronously
|
|
184
|
+
return next()
|
|
185
|
+
}
|
|
186
|
+
)
|
|
193
187
|
```
|
package/cjs/body.d.ts
CHANGED
|
@@ -1,15 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
limit: number;
|
|
3
|
-
}
|
|
4
|
-
export declare function setBufferBodyDefaultOptions(options: Partial<BufferBodyOptions>): void;
|
|
5
|
-
export declare function getBuffer(options?: Partial<BufferBodyOptions>): Promise<any>;
|
|
1
|
+
import { BufferBodyOptions } from './bodyHelpers.js';
|
|
6
2
|
export declare function getJson(options?: Partial<BufferBodyOptions>): Promise<any>;
|
|
7
3
|
export declare function getRaw(options?: Partial<BufferBodyOptions>): Promise<any>;
|
|
8
4
|
export declare function getText(options?: Partial<BufferBodyOptions>): Promise<any>;
|
|
9
|
-
export declare function getUrlEncoded(
|
|
10
|
-
simplify?: boolean;
|
|
11
|
-
}): Promise<any>;
|
|
5
|
+
export declare function getUrlEncoded(options: Partial<BufferBodyOptions>): Promise<any>;
|
|
12
6
|
export declare function getQuery({ simplify, ...options }?: Partial<BufferBodyOptions> & {
|
|
13
7
|
simplify?: boolean;
|
|
14
8
|
}): Promise<any>;
|
|
15
|
-
export {};
|
package/cjs/body.js
CHANGED
|
@@ -1,124 +1,35 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getQuery = exports.getUrlEncoded = exports.getText = exports.getRaw = exports.getJson =
|
|
4
|
-
const
|
|
5
|
-
const
|
|
6
|
-
const contentType_js_1 = require("./contentType.js");
|
|
7
|
-
const context_js_1 = require("./context.js");
|
|
8
|
-
let bufferBodyDefaultOptions = { limit: 100 << 10 }; // 100kb
|
|
9
|
-
function setBufferBodyDefaultOptions(options) {
|
|
10
|
-
bufferBodyDefaultOptions = { ...bufferBodyDefaultOptions, ...options };
|
|
11
|
-
}
|
|
12
|
-
exports.setBufferBodyDefaultOptions = setBufferBodyDefaultOptions;
|
|
3
|
+
exports.getQuery = exports.getUrlEncoded = exports.getText = exports.getRaw = exports.getJson = void 0;
|
|
4
|
+
const dx_js_1 = require("./dx.js");
|
|
5
|
+
const bodyHelpers_js_1 = require("./bodyHelpers.js");
|
|
13
6
|
const bufferBodySymbol = Symbol('bufferBody');
|
|
14
7
|
async function getBuffer(options) {
|
|
15
|
-
|
|
16
|
-
const req = context_js_1.reqContext.value;
|
|
17
|
-
return req[bufferBodySymbol] ??= (async () => {
|
|
18
|
-
/**
|
|
19
|
-
* Check if a request has a request body.
|
|
20
|
-
* A request with a body __must__ either have `transfer-encoding`
|
|
21
|
-
* or `content-length` headers set.
|
|
22
|
-
* http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.3
|
|
23
|
-
*/
|
|
24
|
-
// https://github.com/jshttp/type-is/blob/cdcfe23e9833872e425b0aaf71ca0311373b6116/index.js#L92
|
|
25
|
-
const contentLengthParsed = parseInt(req.headers['content-length'] ?? '', 10);
|
|
26
|
-
if (req.headers['transfer-encoding'] === undefined
|
|
27
|
-
&& isNaN(contentLengthParsed))
|
|
28
|
-
return;
|
|
29
|
-
const contentLength = isNaN(contentLengthParsed) ? undefined : contentLengthParsed;
|
|
30
|
-
// read
|
|
31
|
-
const encoding = (req.headers['content-encoding'] ?? 'identity').toLowerCase();
|
|
32
|
-
const stream = (0, stream_js_1.getContentStream)(req, encoding);
|
|
33
|
-
return await (0, stream_js_1.readStream)(stream, {
|
|
34
|
-
length: encoding === 'identity' ? contentLength : undefined,
|
|
35
|
-
limit,
|
|
36
|
-
});
|
|
37
|
-
})();
|
|
38
|
-
}
|
|
39
|
-
exports.getBuffer = getBuffer;
|
|
40
|
-
// if content-type is not as expected, return undefined
|
|
41
|
-
function forceGetContentTypeParams(expected) {
|
|
42
|
-
const req = context_js_1.reqContext.value;
|
|
43
|
-
const contentTypeRaw = req.headers['content-type'];
|
|
44
|
-
if (!contentTypeRaw)
|
|
45
|
-
return;
|
|
46
|
-
const { mediaType, parameters } = (0, contentType_js_1.parseContentType)(contentTypeRaw);
|
|
47
|
-
if (mediaType !== expected)
|
|
48
|
-
return;
|
|
49
|
-
return parameters;
|
|
50
|
-
}
|
|
51
|
-
function forceGetCharset(expected) {
|
|
52
|
-
const parameters = forceGetContentTypeParams(expected);
|
|
53
|
-
if (!parameters)
|
|
54
|
-
return;
|
|
55
|
-
// assert charset per RFC 7159 sec 8.1
|
|
56
|
-
const charset = parameters.charset?.toLowerCase() || 'utf-8';
|
|
57
|
-
if (!charset.startsWith('utf-'))
|
|
58
|
-
throw new Error(`unsupported charset "${charset.toUpperCase()}"`);
|
|
59
|
-
return charset;
|
|
8
|
+
return (0, dx_js_1.getReq)()[bufferBodySymbol] ??= (0, bodyHelpers_js_1.bufferFromReq)((0, dx_js_1.getReq)(), options);
|
|
60
9
|
}
|
|
61
10
|
const jsonBodySymbol = Symbol('jsonBody');
|
|
62
11
|
async function getJson(options) {
|
|
63
|
-
return
|
|
64
|
-
const charset = forceGetCharset('application/json');
|
|
65
|
-
if (!charset)
|
|
66
|
-
return;
|
|
67
|
-
const buffer = await getBuffer(options);
|
|
68
|
-
if (buffer) {
|
|
69
|
-
const str = buffer.toString(charset);
|
|
70
|
-
return str ? JSON.parse(str) : undefined;
|
|
71
|
-
}
|
|
72
|
-
})();
|
|
12
|
+
return (0, dx_js_1.getReq)()[jsonBodySymbol] ??= (0, bodyHelpers_js_1.jsonFromReq)((0, dx_js_1.getReq)(), options);
|
|
73
13
|
}
|
|
74
14
|
exports.getJson = getJson;
|
|
75
15
|
const rawBodySymbol = Symbol('rawBody');
|
|
76
16
|
async function getRaw(options) {
|
|
77
|
-
return
|
|
78
|
-
if (!forceGetContentTypeParams('application/octet-stream'))
|
|
79
|
-
return;
|
|
80
|
-
return await getBuffer(options);
|
|
81
|
-
})();
|
|
17
|
+
return (0, dx_js_1.getReq)()[rawBodySymbol] ??= (0, bodyHelpers_js_1.rawFromReq)((0, dx_js_1.getReq)(), options);
|
|
82
18
|
}
|
|
83
19
|
exports.getRaw = getRaw;
|
|
84
20
|
const textBodySymbol = Symbol('textBody');
|
|
85
21
|
async function getText(options) {
|
|
86
|
-
return
|
|
87
|
-
const charset = forceGetCharset('text/plain');
|
|
88
|
-
if (!charset)
|
|
89
|
-
return;
|
|
90
|
-
const buffer = await getBuffer(options);
|
|
91
|
-
if (buffer)
|
|
92
|
-
return buffer.toString(charset);
|
|
93
|
-
})();
|
|
22
|
+
return (0, dx_js_1.getReq)()[textBodySymbol] ??= (0, bodyHelpers_js_1.textFromReq)((0, dx_js_1.getReq)(), options);
|
|
94
23
|
}
|
|
95
24
|
exports.getText = getText;
|
|
96
25
|
const urlEncodedBodySymbol = Symbol('urlencodedBody');
|
|
97
|
-
async function getUrlEncoded(
|
|
98
|
-
return
|
|
99
|
-
const charset = forceGetCharset('application/x-www-form-urlencoded');
|
|
100
|
-
if (!charset)
|
|
101
|
-
return;
|
|
102
|
-
const buffer = await getBuffer(options);
|
|
103
|
-
if (buffer) {
|
|
104
|
-
const str = buffer.toString(charset);
|
|
105
|
-
return simplify
|
|
106
|
-
? Object.fromEntries(new URLSearchParams(str))
|
|
107
|
-
: (0, qs_1.parse)(str);
|
|
108
|
-
}
|
|
109
|
-
})();
|
|
26
|
+
async function getUrlEncoded(options) {
|
|
27
|
+
return (0, dx_js_1.getReq)()[urlEncodedBodySymbol] ??= (0, bodyHelpers_js_1.urlEncodedFromReq)((0, dx_js_1.getReq)(), options);
|
|
110
28
|
}
|
|
111
29
|
exports.getUrlEncoded = getUrlEncoded;
|
|
112
30
|
const querySymbol = Symbol('query');
|
|
113
31
|
async function getQuery({ simplify, ...options } = {}) {
|
|
114
|
-
return
|
|
115
|
-
const query = context_js_1.reqContext.value.url?.split('?', 2)?.[1];
|
|
116
|
-
return query
|
|
117
|
-
? simplify
|
|
118
|
-
? Object.fromEntries(new URLSearchParams(query))
|
|
119
|
-
: (0, qs_1.parse)(query)
|
|
120
|
-
: {};
|
|
121
|
-
})();
|
|
32
|
+
return (0, dx_js_1.getReq)()[querySymbol] ??= (0, bodyHelpers_js_1.queryFromReq)((0, dx_js_1.getReq)(), options);
|
|
122
33
|
}
|
|
123
34
|
exports.getQuery = getQuery;
|
|
124
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
35
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYm9keS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9ib2R5LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUFBLG1DQUE4QjtBQUM5QixxREFReUI7QUFFekIsTUFBTSxnQkFBZ0IsR0FBRyxNQUFNLENBQUMsWUFBWSxDQUFDLENBQUE7QUFDN0MsS0FBSyxVQUFVLFNBQVMsQ0FBQyxPQUFvQztJQUM1RCxPQUFPLElBQUEsY0FBTSxHQUFFLENBQUMsZ0JBQWdCLENBQUMsS0FBSyxJQUFBLDhCQUFhLEVBQUMsSUFBQSxjQUFNLEdBQUUsRUFBRSxPQUFPLENBQUMsQ0FBQTtBQUN2RSxDQUFDO0FBRUQsTUFBTSxjQUFjLEdBQUcsTUFBTSxDQUFDLFVBQVUsQ0FBQyxDQUFBO0FBQ2xDLEtBQUssVUFBVSxPQUFPLENBQUMsT0FBb0M7SUFDakUsT0FBTyxJQUFBLGNBQU0sR0FBRSxDQUFDLGNBQWMsQ0FBQyxLQUFLLElBQUEsNEJBQVcsRUFBQyxJQUFBLGNBQU0sR0FBRSxFQUFFLE9BQU8sQ0FBQyxDQUFBO0FBQ25FLENBQUM7QUFGRCwwQkFFQztBQUVELE1BQU0sYUFBYSxHQUFHLE1BQU0sQ0FBQyxTQUFTLENBQUMsQ0FBQTtBQUNoQyxLQUFLLFVBQVUsTUFBTSxDQUFDLE9BQW9DO0lBQ2hFLE9BQU8sSUFBQSxjQUFNLEdBQUUsQ0FBQyxhQUFhLENBQUMsS0FBSyxJQUFBLDJCQUFVLEVBQUMsSUFBQSxjQUFNLEdBQUUsRUFBRSxPQUFPLENBQUMsQ0FBQTtBQUNqRSxDQUFDO0FBRkQsd0JBRUM7QUFFRCxNQUFNLGNBQWMsR0FBRyxNQUFNLENBQUMsVUFBVSxDQUFDLENBQUE7QUFDbEMsS0FBSyxVQUFVLE9BQU8sQ0FBQyxPQUFvQztJQUNqRSxPQUFPLElBQUEsY0FBTSxHQUFFLENBQUMsY0FBYyxDQUFDLEtBQUssSUFBQSw0QkFBVyxFQUFDLElBQUEsY0FBTSxHQUFFLEVBQUUsT0FBTyxDQUFDLENBQUE7QUFDbkUsQ0FBQztBQUZELDBCQUVDO0FBRUQsTUFBTSxvQkFBb0IsR0FBRyxNQUFNLENBQUMsZ0JBQWdCLENBQUMsQ0FBQTtBQUM5QyxLQUFLLFVBQVUsYUFBYSxDQUFDLE9BQW1DO0lBQ3RFLE9BQU8sSUFBQSxjQUFNLEdBQUUsQ0FBQyxvQkFBb0IsQ0FBQyxLQUFLLElBQUEsa0NBQWlCLEVBQUMsSUFBQSxjQUFNLEdBQUUsRUFBRSxPQUFPLENBQUMsQ0FBQTtBQUMvRSxDQUFDO0FBRkQsc0NBRUM7QUFFRCxNQUFNLFdBQVcsR0FBRyxNQUFNLENBQUMsT0FBTyxDQUFDLENBQUE7QUFDNUIsS0FBSyxVQUFVLFFBQVEsQ0FBQyxFQUFDLFFBQVEsRUFBRSxHQUFHLE9BQU8sS0FBdUQsRUFBRTtJQUM1RyxPQUFPLElBQUEsY0FBTSxHQUFFLENBQUMsV0FBVyxDQUFDLEtBQUssSUFBQSw2QkFBWSxFQUFDLElBQUEsY0FBTSxHQUFFLEVBQUUsT0FBTyxDQUFDLENBQUE7QUFDakUsQ0FBQztBQUZELDRCQUVDIn0=
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/// <reference types="node" resolution-mode="require"/>
|
|
2
|
+
/// <reference types="node" resolution-mode="require"/>
|
|
3
|
+
import { IncomingMessage } from 'node:http';
|
|
4
|
+
import qs from 'qs';
|
|
5
|
+
export interface BufferBodyOptions {
|
|
6
|
+
bodyLimit: number;
|
|
7
|
+
simplifyUrlEncodedBody?: boolean;
|
|
8
|
+
simplifyQuery?: boolean;
|
|
9
|
+
}
|
|
10
|
+
export declare function setBufferBodyDefaultOptions(options: Partial<BufferBodyOptions>): void;
|
|
11
|
+
export declare function bufferFromReq(req: IncomingMessage, options?: Partial<BufferBodyOptions>): Promise<Buffer | undefined>;
|
|
12
|
+
export declare function jsonFromReq(req: IncomingMessage, options?: Partial<BufferBodyOptions>): Promise<any>;
|
|
13
|
+
export declare function rawFromReq(req: IncomingMessage, options?: Partial<BufferBodyOptions>): Promise<Buffer | undefined>;
|
|
14
|
+
export declare function textFromReq(req: IncomingMessage, options?: Partial<BufferBodyOptions>): Promise<string | undefined>;
|
|
15
|
+
export declare function urlEncodedFromReq(req: IncomingMessage, options?: Partial<BufferBodyOptions>): Promise<qs.ParsedQs | undefined>;
|
|
16
|
+
export declare function queryFromReq(req: IncomingMessage, options?: Partial<BufferBodyOptions>): Promise<qs.ParsedQs>;
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.queryFromReq = exports.urlEncodedFromReq = exports.textFromReq = exports.rawFromReq = exports.jsonFromReq = exports.bufferFromReq = exports.setBufferBodyDefaultOptions = void 0;
|
|
7
|
+
const stream_js_1 = require("./stream.js");
|
|
8
|
+
const contentType_js_1 = require("./contentType.js");
|
|
9
|
+
const qs_1 = __importDefault(require("qs"));
|
|
10
|
+
let bodyDefaultOptions = { bodyLimit: 100 << 10 }; // 100kb
|
|
11
|
+
function setBufferBodyDefaultOptions(options) {
|
|
12
|
+
bodyDefaultOptions = { ...bodyDefaultOptions, ...options };
|
|
13
|
+
}
|
|
14
|
+
exports.setBufferBodyDefaultOptions = setBufferBodyDefaultOptions;
|
|
15
|
+
async function bufferFromReq(req, options) {
|
|
16
|
+
const { bodyLimit } = { ...bodyDefaultOptions, ...options };
|
|
17
|
+
/**
|
|
18
|
+
* Check if a request has a request body.
|
|
19
|
+
* A request with a body __must__ either have `transfer-encoding`
|
|
20
|
+
* or `content-length` headers set.
|
|
21
|
+
* http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.3
|
|
22
|
+
*/
|
|
23
|
+
// https://github.com/jshttp/type-is/blob/cdcfe23e9833872e425b0aaf71ca0311373b6116/index.js#L92
|
|
24
|
+
const contentLengthParsed = parseInt(req.headers['content-length'] ?? '', 10);
|
|
25
|
+
if (req.headers['transfer-encoding'] === undefined
|
|
26
|
+
&& isNaN(contentLengthParsed))
|
|
27
|
+
return;
|
|
28
|
+
const contentLength = isNaN(contentLengthParsed) ? undefined : contentLengthParsed;
|
|
29
|
+
// read
|
|
30
|
+
const encoding = (req.headers['content-encoding'] ?? 'identity').toLowerCase();
|
|
31
|
+
const stream = (0, stream_js_1.getContentStream)(req, encoding);
|
|
32
|
+
return await (0, stream_js_1.readStream)(stream, {
|
|
33
|
+
length: encoding === 'identity' ? contentLength : undefined,
|
|
34
|
+
limit: bodyLimit,
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
exports.bufferFromReq = bufferFromReq;
|
|
38
|
+
// if content-type is not as expected, return undefined
|
|
39
|
+
function forceGetContentTypeParams(req, expected) {
|
|
40
|
+
const contentTypeRaw = req.headers['content-type'];
|
|
41
|
+
if (!contentTypeRaw)
|
|
42
|
+
return;
|
|
43
|
+
const { mediaType, parameters } = (0, contentType_js_1.parseContentType)(contentTypeRaw);
|
|
44
|
+
if (mediaType !== expected)
|
|
45
|
+
return;
|
|
46
|
+
return parameters;
|
|
47
|
+
}
|
|
48
|
+
function forceGetCharset(req, expected) {
|
|
49
|
+
const parameters = forceGetContentTypeParams(req, expected);
|
|
50
|
+
if (!parameters)
|
|
51
|
+
return;
|
|
52
|
+
// assert charset per RFC 7159 sec 8.1
|
|
53
|
+
const charset = parameters.charset?.toLowerCase() || 'utf-8';
|
|
54
|
+
if (!charset.startsWith('utf-'))
|
|
55
|
+
throw new Error(`unsupported charset "${charset.toUpperCase()}"`);
|
|
56
|
+
return charset;
|
|
57
|
+
}
|
|
58
|
+
async function jsonFromReq(req, options) {
|
|
59
|
+
const charset = forceGetCharset(req, 'application/json');
|
|
60
|
+
if (!charset)
|
|
61
|
+
return;
|
|
62
|
+
const buffer = await bufferFromReq(req, options);
|
|
63
|
+
if (buffer) {
|
|
64
|
+
const str = buffer.toString(charset);
|
|
65
|
+
return str ? JSON.parse(str) : undefined;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
exports.jsonFromReq = jsonFromReq;
|
|
69
|
+
async function rawFromReq(req, options) {
|
|
70
|
+
if (!forceGetContentTypeParams(req, 'application/octet-stream'))
|
|
71
|
+
return;
|
|
72
|
+
return await bufferFromReq(req, options);
|
|
73
|
+
}
|
|
74
|
+
exports.rawFromReq = rawFromReq;
|
|
75
|
+
async function textFromReq(req, options) {
|
|
76
|
+
const charset = forceGetCharset(req, 'text/plain');
|
|
77
|
+
if (!charset)
|
|
78
|
+
return;
|
|
79
|
+
const buffer = await bufferFromReq(req, options);
|
|
80
|
+
if (buffer)
|
|
81
|
+
return buffer.toString(charset);
|
|
82
|
+
}
|
|
83
|
+
exports.textFromReq = textFromReq;
|
|
84
|
+
async function urlEncodedFromReq(req, options) {
|
|
85
|
+
const charset = forceGetCharset(req, 'application/x-www-form-urlencoded');
|
|
86
|
+
if (!charset)
|
|
87
|
+
return;
|
|
88
|
+
const buffer = await bufferFromReq(req, options);
|
|
89
|
+
if (buffer) {
|
|
90
|
+
const str = buffer.toString(charset);
|
|
91
|
+
const { simplifyUrlEncodedBody } = { ...bodyDefaultOptions, ...options };
|
|
92
|
+
return simplifyUrlEncodedBody ? Object.fromEntries(new URLSearchParams(str)) : qs_1.default.parse(str);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
exports.urlEncodedFromReq = urlEncodedFromReq;
|
|
96
|
+
async function queryFromReq(req, options) {
|
|
97
|
+
const query = new URL(req.url ?? '', 'https://example.com').searchParams.toString();
|
|
98
|
+
const { simplifyQuery } = { ...bodyDefaultOptions, ...options };
|
|
99
|
+
return simplifyQuery ? Object.fromEntries(new URLSearchParams(query)) : qs_1.default.parse(query);
|
|
100
|
+
}
|
|
101
|
+
exports.queryFromReq = queryFromReq;
|
|
102
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYm9keUhlbHBlcnMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvYm9keUhlbHBlcnMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7O0FBQ0EsMkNBQXdEO0FBQ3hELHFEQUFpRDtBQUNqRCw0Q0FBbUI7QUFRbkIsSUFBSSxrQkFBa0IsR0FBc0IsRUFBQyxTQUFTLEVBQUUsR0FBRyxJQUFJLEVBQUUsRUFBQyxDQUFBLENBQUMsUUFBUTtBQUMzRSxTQUFnQiwyQkFBMkIsQ0FBQyxPQUFtQztJQUM5RSxrQkFBa0IsR0FBRyxFQUFDLEdBQUcsa0JBQWtCLEVBQUUsR0FBRyxPQUFPLEVBQUMsQ0FBQTtBQUN6RCxDQUFDO0FBRkQsa0VBRUM7QUFFTSxLQUFLLFVBQVUsYUFBYSxDQUFDLEdBQW9CLEVBQUUsT0FBb0M7SUFDN0YsTUFBTSxFQUFDLFNBQVMsRUFBQyxHQUFHLEVBQUMsR0FBRyxrQkFBa0IsRUFBRSxHQUFHLE9BQU8sRUFBQyxDQUFBO0lBQ3ZEOzs7OztPQUtHO0lBQ0YsK0ZBQStGO0lBQ2hHLE1BQU0sbUJBQW1CLEdBQUcsUUFBUSxDQUFDLEdBQUcsQ0FBQyxPQUFPLENBQUMsZ0JBQWdCLENBQUMsSUFBSSxFQUFFLEVBQUUsRUFBRSxDQUFDLENBQUE7SUFDN0UsSUFDQyxHQUFHLENBQUMsT0FBTyxDQUFDLG1CQUFtQixDQUFDLEtBQUssU0FBUztXQUMzQyxLQUFLLENBQUMsbUJBQW1CLENBQUM7UUFDNUIsT0FBTTtJQUNSLE1BQU0sYUFBYSxHQUFHLEtBQUssQ0FBQyxtQkFBbUIsQ0FBQyxDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDLG1CQUFtQixDQUFBO0lBRWxGLE9BQU87SUFDUCxNQUFNLFFBQVEsR0FBRyxDQUFDLEdBQUcsQ0FBQyxPQUFPLENBQUMsa0JBQWtCLENBQUMsSUFBSSxVQUFVLENBQUMsQ0FBQyxXQUFXLEVBQUUsQ0FBQTtJQUM5RSxNQUFNLE1BQU0sR0FBRyxJQUFBLDRCQUFnQixFQUFD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
|
package/cjs/dx.d.ts
CHANGED
|
@@ -1,64 +1,17 @@
|
|
|
1
1
|
/// <reference types="node" resolution-mode="require"/>
|
|
2
2
|
/// <reference types="node" resolution-mode="require"/>
|
|
3
|
+
/// <reference types="node" resolution-mode="require"/>
|
|
3
4
|
import { Readable } from 'node:stream';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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: 'nodeStream';
|
|
26
|
-
data: Readable;
|
|
27
|
-
} | {
|
|
28
|
-
type: 'webStream';
|
|
29
|
-
data: ReadableStream;
|
|
30
|
-
});
|
|
31
|
-
chain(params_0?: {
|
|
32
|
-
jsonBeautify?: boolean | undefined;
|
|
33
|
-
disableEtag?: boolean | undefined;
|
|
34
|
-
} | undefined): <V>(next: () => V) => Promise<any>;
|
|
35
|
-
with(value: {
|
|
36
|
-
charset?: BufferEncoding | undefined;
|
|
37
|
-
jsonBeautify?: boolean | undefined;
|
|
38
|
-
disableEtag?: boolean | undefined;
|
|
39
|
-
} & ({
|
|
40
|
-
type: 'text';
|
|
41
|
-
data: string;
|
|
42
|
-
} | {
|
|
43
|
-
type: 'html';
|
|
44
|
-
data: string;
|
|
45
|
-
} | {
|
|
46
|
-
type: 'buffer';
|
|
47
|
-
data: Buffer;
|
|
48
|
-
} | {
|
|
49
|
-
type: 'json';
|
|
50
|
-
data: any;
|
|
51
|
-
} | {
|
|
52
|
-
type: 'redirect';
|
|
53
|
-
data: string;
|
|
54
|
-
} | {
|
|
55
|
-
type: 'nodeStream';
|
|
56
|
-
data: Readable;
|
|
57
|
-
} | {
|
|
58
|
-
type: 'webStream';
|
|
59
|
-
data: ReadableStream;
|
|
60
|
-
})): <V_1>(next: () => V_1) => Promise<any>;
|
|
61
|
-
};
|
|
5
|
+
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
6
|
+
interface Chainable<P extends any[] = any[], R = any, Next = (...np: any[]) => any> {
|
|
7
|
+
(next: Next, ...p: P): R;
|
|
8
|
+
}
|
|
9
|
+
export declare function dxServer(req: IncomingMessage, res: ServerResponse, options?: {
|
|
10
|
+
jsonBeautify?: boolean;
|
|
11
|
+
disableEtag?: boolean;
|
|
12
|
+
}): Chainable;
|
|
13
|
+
export declare function getReq(): IncomingMessage;
|
|
14
|
+
export declare function getRes(): ServerResponse;
|
|
62
15
|
export declare function setText(text: string, { status }?: {
|
|
63
16
|
status?: number;
|
|
64
17
|
}): void;
|
|
@@ -74,8 +27,8 @@ export declare function setNodeStream(stream: Readable, { status }?: {
|
|
|
74
27
|
export declare function setWebStream(stream: ReadableStream, { status }?: {
|
|
75
28
|
status?: number;
|
|
76
29
|
}): void;
|
|
77
|
-
export declare function setJson(json: any, { status
|
|
30
|
+
export declare function setJson(json: any, { status }?: {
|
|
78
31
|
status?: number;
|
|
79
|
-
beautify?: boolean;
|
|
80
32
|
}): void;
|
|
81
33
|
export declare function setRedirect(url: string, status: 301 | 302): void;
|
|
34
|
+
export {};
|