@moostjs/event-http 0.2.0
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 +37 -0
- package/dist/index.cjs +497 -0
- package/dist/index.d.ts +172 -0
- package/dist/index.mjs +472 -0
- package/package.json +37 -0
package/README.md
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# @moostjs/event-http
|
|
2
|
+
|
|
3
|
+
**!!! This is work-in-progress library, breaking changes are expected !!!**
|
|
4
|
+
|
|
5
|
+
<p align="center">
|
|
6
|
+
<img src="../../moost-logo.png" width="450px"><br>
|
|
7
|
+
<a href="https://github.com/moostjs/moostjs/blob/main/LICENSE">
|
|
8
|
+
<img src="https://img.shields.io/badge/License-MIT-green?style=for-the-badge" />
|
|
9
|
+
</a>
|
|
10
|
+
</p>
|
|
11
|
+
|
|
12
|
+
`event-http` is a moostjs wrapper of [@wooksjs/event-http](https://github.com/wooksjs/wooksjs/tree/main/packages/event-http) with corresponding decorators for composable functions.
|
|
13
|
+
|
|
14
|
+
## Quick Start
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
import { MoostHttp, Get } from '@moostjs/event-http'
|
|
18
|
+
import { Moost, Param } from 'moost'
|
|
19
|
+
|
|
20
|
+
class MyServer extends Moost {
|
|
21
|
+
@Get('test/:name')
|
|
22
|
+
test(@Param('name') name: string) {
|
|
23
|
+
return { message: `Hello ${ name }!` }
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const app = new MyServer()
|
|
28
|
+
const http = new MoostHttp()
|
|
29
|
+
app.adapter(http).listen(3000, () => { console.log('Up on port 3000') })
|
|
30
|
+
app.init()
|
|
31
|
+
// curl http://localhost:3000/test/World
|
|
32
|
+
// {"message":"Hello World!"}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Install
|
|
36
|
+
|
|
37
|
+
`npm install moost @moostjs/event-http`
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,497 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var eventHttp = require('@wooksjs/event-http');
|
|
4
|
+
var moost = require('moost');
|
|
5
|
+
|
|
6
|
+
/******************************************************************************
|
|
7
|
+
Copyright (c) Microsoft Corporation.
|
|
8
|
+
|
|
9
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
10
|
+
purpose with or without fee is hereby granted.
|
|
11
|
+
|
|
12
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
13
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
14
|
+
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
15
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
16
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
17
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
18
|
+
PERFORMANCE OF THIS SOFTWARE.
|
|
19
|
+
***************************************************************************** */
|
|
20
|
+
|
|
21
|
+
function __awaiter$1(thisArg, _arguments, P, generator) {
|
|
22
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
23
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
24
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
25
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
26
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
27
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
class MoostHttp {
|
|
32
|
+
constructor(httpApp) {
|
|
33
|
+
this.pathBuilders = {};
|
|
34
|
+
if (httpApp && httpApp instanceof eventHttp.WooksHttp) {
|
|
35
|
+
this.httpApp = httpApp;
|
|
36
|
+
}
|
|
37
|
+
else if (httpApp) {
|
|
38
|
+
this.httpApp = eventHttp.createHttpApp(httpApp);
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
this.httpApp = eventHttp.createHttpApp();
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
getHttpApp() {
|
|
45
|
+
return this.httpApp;
|
|
46
|
+
}
|
|
47
|
+
getServerCb() {
|
|
48
|
+
return this.httpApp.getServerCb();
|
|
49
|
+
}
|
|
50
|
+
listen(...args) {
|
|
51
|
+
return this.httpApp.listen(...args);
|
|
52
|
+
}
|
|
53
|
+
bindHandler(opts) {
|
|
54
|
+
let fn;
|
|
55
|
+
for (const handler of opts.handlers) {
|
|
56
|
+
if (handler.type !== 'HTTP')
|
|
57
|
+
continue;
|
|
58
|
+
const httpPath = handler.path;
|
|
59
|
+
const path = typeof httpPath === 'string' ? httpPath : typeof opts.method === 'string' ? opts.method : '';
|
|
60
|
+
const targetPath = `${opts.prefix || ''}/${path}`.replace(/\/\/+/g, '/');
|
|
61
|
+
if (!fn) {
|
|
62
|
+
fn = () => __awaiter$1(this, void 0, void 0, function* () {
|
|
63
|
+
const { restoreCtx } = eventHttp.useHttpContext();
|
|
64
|
+
const { reqId, rawRequest } = eventHttp.useRequest();
|
|
65
|
+
const scopeId = reqId();
|
|
66
|
+
rawRequest.on('end', opts.registerEventScope(scopeId));
|
|
67
|
+
const instance = yield opts.getInstance();
|
|
68
|
+
restoreCtx();
|
|
69
|
+
// setComposableControllerContext({
|
|
70
|
+
// controller: instance,
|
|
71
|
+
// method: method as string,
|
|
72
|
+
// pathBuilder: pathBuilder as ReturnType<Wooks['on']>,
|
|
73
|
+
// })
|
|
74
|
+
let response;
|
|
75
|
+
const interceptorHandler = yield opts.getIterceptorHandler();
|
|
76
|
+
restoreCtx();
|
|
77
|
+
yield interceptorHandler.init();
|
|
78
|
+
// params
|
|
79
|
+
let args = [];
|
|
80
|
+
try {
|
|
81
|
+
restoreCtx();
|
|
82
|
+
args = yield opts.resolveArgs();
|
|
83
|
+
}
|
|
84
|
+
catch (e) {
|
|
85
|
+
response = e;
|
|
86
|
+
}
|
|
87
|
+
if (!response) {
|
|
88
|
+
restoreCtx();
|
|
89
|
+
// fire before interceptors
|
|
90
|
+
response = yield interceptorHandler.fireBefore(response);
|
|
91
|
+
// fire request handler
|
|
92
|
+
if (!interceptorHandler.responseOverwritten) {
|
|
93
|
+
try {
|
|
94
|
+
restoreCtx();
|
|
95
|
+
response = yield instance[opts.method](...args);
|
|
96
|
+
}
|
|
97
|
+
catch (e) {
|
|
98
|
+
response = e;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
// fire after interceptors
|
|
103
|
+
response = yield interceptorHandler.fireAfter(response);
|
|
104
|
+
return response;
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
const pathBuilder = this.httpApp.on(handler.method, targetPath, fn);
|
|
108
|
+
const methodMeta = moost.getMoostMate().read(opts.fakeInstance, opts.method) || {};
|
|
109
|
+
const id = (methodMeta.id || opts.method);
|
|
110
|
+
if (id) {
|
|
111
|
+
const methods = this.pathBuilders[id] = this.pathBuilders[id] || {};
|
|
112
|
+
if (handler.method === '*') {
|
|
113
|
+
methods.GET = pathBuilder;
|
|
114
|
+
methods.PUT = pathBuilder;
|
|
115
|
+
methods.PATCH = pathBuilder;
|
|
116
|
+
methods.POST = pathBuilder;
|
|
117
|
+
methods.DELETE = pathBuilder;
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
methods[handler.method] = pathBuilder;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
opts.logHandler(`${'[36m'}(${handler.method})${'[32m'}${targetPath}`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function HttpMethod(method, path) {
|
|
129
|
+
return moost.getMoostMate().decorate('handlers', { method, path, type: 'HTTP' }, true);
|
|
130
|
+
}
|
|
131
|
+
const All = (path) => HttpMethod('*', path);
|
|
132
|
+
const Get = (path) => HttpMethod('GET', path);
|
|
133
|
+
const Post = (path) => HttpMethod('POST', path);
|
|
134
|
+
const Put = (path) => HttpMethod('PUT', path);
|
|
135
|
+
const Delete = (path) => HttpMethod('DELETE', path);
|
|
136
|
+
const Patch = (path) => HttpMethod('PATCH', path);
|
|
137
|
+
|
|
138
|
+
/******************************************************************************
|
|
139
|
+
Copyright (c) Microsoft Corporation.
|
|
140
|
+
|
|
141
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
142
|
+
purpose with or without fee is hereby granted.
|
|
143
|
+
|
|
144
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
145
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
146
|
+
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
147
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
148
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
149
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
150
|
+
PERFORMANCE OF THIS SOFTWARE.
|
|
151
|
+
***************************************************************************** */
|
|
152
|
+
|
|
153
|
+
function __awaiter(thisArg, _arguments, P, generator) {
|
|
154
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
155
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
156
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
157
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
158
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
159
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const banner = () => `[${"@wooksjs/http-body"}][${new Date().toISOString().replace('T', ' ').replace(/\.\d{3}z$/i, '')}] `;
|
|
164
|
+
|
|
165
|
+
/* istanbul ignore file */
|
|
166
|
+
function logError(error) {
|
|
167
|
+
console.error('[91m' + '[1m' + banner() + error + '[0m');
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function panic(error) {
|
|
171
|
+
logError(error);
|
|
172
|
+
return new Error(error);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const compressors = {
|
|
176
|
+
identity: {
|
|
177
|
+
compress: v => v,
|
|
178
|
+
uncompress: v => v,
|
|
179
|
+
},
|
|
180
|
+
};
|
|
181
|
+
function uncompressBody(encodings, body) {
|
|
182
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
183
|
+
let newBody = body;
|
|
184
|
+
for (const e of encodings.reverse()) {
|
|
185
|
+
if (!compressors[e]) {
|
|
186
|
+
throw panic(`Usupported compression type "${e}".`);
|
|
187
|
+
}
|
|
188
|
+
newBody = yield compressors[e].uncompress(body);
|
|
189
|
+
}
|
|
190
|
+
return newBody;
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function useBody() {
|
|
195
|
+
const { store } = eventHttp.useHttpContext();
|
|
196
|
+
const { init } = store('request');
|
|
197
|
+
const { rawBody } = eventHttp.useRequest();
|
|
198
|
+
const { 'content-type': contentType, 'content-encoding': contentEncoding } = eventHttp.useHeaders();
|
|
199
|
+
function contentIs(type) {
|
|
200
|
+
return (contentType || '').indexOf(type) >= 0;
|
|
201
|
+
}
|
|
202
|
+
const isJson = () => init('isJson', () => contentIs('application/json'));
|
|
203
|
+
const isHtml = () => init('isHtml', () => contentIs('text/html'));
|
|
204
|
+
const isXml = () => init('isXml', () => contentIs('text/xml'));
|
|
205
|
+
const isText = () => init('isText', () => contentIs('text/plain'));
|
|
206
|
+
const isBinary = () => init('isBinary', () => contentIs('application/octet-stream'));
|
|
207
|
+
const isFormData = () => init('isFormData', () => contentIs('multipart/form-data'));
|
|
208
|
+
const isUrlencoded = () => init('isUrlencoded', () => contentIs('application/x-www-form-urlencoded'));
|
|
209
|
+
const isCompressed = () => init('isCompressed', () => {
|
|
210
|
+
const parts = contentEncodings();
|
|
211
|
+
for (const p of parts) {
|
|
212
|
+
if (['deflate', 'gzip', 'br'].includes(p))
|
|
213
|
+
return true;
|
|
214
|
+
}
|
|
215
|
+
return false;
|
|
216
|
+
});
|
|
217
|
+
const contentEncodings = () => init('contentEncodings', () => (contentEncoding || '').split(',').map(p => p.trim()).filter(p => !!p));
|
|
218
|
+
const parseBody = () => init('parsed', () => __awaiter(this, void 0, void 0, function* () {
|
|
219
|
+
const body = yield uncompressBody(contentEncodings(), (yield rawBody()).toString());
|
|
220
|
+
if (isJson())
|
|
221
|
+
return jsonParser(body);
|
|
222
|
+
else if (isFormData())
|
|
223
|
+
return formDataParser(body);
|
|
224
|
+
else if (isUrlencoded())
|
|
225
|
+
return urlEncodedParser(body);
|
|
226
|
+
else if (isBinary())
|
|
227
|
+
return textParser(body);
|
|
228
|
+
else
|
|
229
|
+
return textParser(body);
|
|
230
|
+
}));
|
|
231
|
+
function jsonParser(v) {
|
|
232
|
+
try {
|
|
233
|
+
return JSON.parse(v);
|
|
234
|
+
}
|
|
235
|
+
catch (e) {
|
|
236
|
+
throw new eventHttp.WooksError(400, e.message);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
function textParser(v) {
|
|
240
|
+
return v;
|
|
241
|
+
}
|
|
242
|
+
function formDataParser(v) {
|
|
243
|
+
const boundary = '--' + (/boundary=([^;]+)(?:;|$)/.exec(contentType || '') || [, ''])[1];
|
|
244
|
+
if (!boundary)
|
|
245
|
+
throw new eventHttp.WooksError(eventHttp.EHttpStatusCode.BadRequest, 'form-data boundary not recognized');
|
|
246
|
+
const parts = v.trim().split(boundary);
|
|
247
|
+
const result = {};
|
|
248
|
+
let key = '';
|
|
249
|
+
let partContentType = 'text/plain';
|
|
250
|
+
for (const part of parts) {
|
|
251
|
+
parsePart();
|
|
252
|
+
key = '';
|
|
253
|
+
partContentType = 'text/plain';
|
|
254
|
+
let valueMode = false;
|
|
255
|
+
const lines = part.trim().split(/\n/g).map(s => s.trim());
|
|
256
|
+
for (const line of lines) {
|
|
257
|
+
if (valueMode) {
|
|
258
|
+
if (!result[key]) {
|
|
259
|
+
result[key] = line;
|
|
260
|
+
}
|
|
261
|
+
else {
|
|
262
|
+
result[key] += '\n' + line;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
else {
|
|
266
|
+
if (!line || line === '--') {
|
|
267
|
+
valueMode = !!key;
|
|
268
|
+
if (valueMode) {
|
|
269
|
+
key = key.replace(/^["']/, '').replace(/["']$/, '');
|
|
270
|
+
}
|
|
271
|
+
continue;
|
|
272
|
+
}
|
|
273
|
+
if (line.toLowerCase().startsWith('content-disposition: form-data;')) {
|
|
274
|
+
key = (/name=([^;]+)/.exec(line) || [])[1];
|
|
275
|
+
if (!key)
|
|
276
|
+
throw new eventHttp.WooksError(eventHttp.EHttpStatusCode.BadRequest, 'Could not read multipart name: ' + line);
|
|
277
|
+
continue;
|
|
278
|
+
}
|
|
279
|
+
if (line.toLowerCase().startsWith('content-type:')) {
|
|
280
|
+
partContentType = (/content-type:\s?([^;]+)/i.exec(line) || [])[1];
|
|
281
|
+
if (!partContentType)
|
|
282
|
+
throw new eventHttp.WooksError(eventHttp.EHttpStatusCode.BadRequest, 'Could not read content-type: ' + line);
|
|
283
|
+
continue;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
parsePart();
|
|
289
|
+
function parsePart() {
|
|
290
|
+
if (key) {
|
|
291
|
+
if (partContentType.indexOf('application/json') >= 0) {
|
|
292
|
+
result[key] = JSON.parse(result[key]);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
return result;
|
|
297
|
+
}
|
|
298
|
+
function urlEncodedParser(v) {
|
|
299
|
+
return new eventHttp.WooksURLSearchParams(v.trim()).toJson();
|
|
300
|
+
}
|
|
301
|
+
return {
|
|
302
|
+
isJson,
|
|
303
|
+
isHtml,
|
|
304
|
+
isXml,
|
|
305
|
+
isText,
|
|
306
|
+
isBinary,
|
|
307
|
+
isFormData,
|
|
308
|
+
isUrlencoded,
|
|
309
|
+
isCompressed,
|
|
310
|
+
contentEncodings,
|
|
311
|
+
parseBody,
|
|
312
|
+
rawBody,
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Hook to the Response Status
|
|
318
|
+
* @decorator
|
|
319
|
+
* @paramType TStatusHook
|
|
320
|
+
*/
|
|
321
|
+
const StatusHook = moost.Resolve(() => eventHttp.useStatus(), 'status');
|
|
322
|
+
/**
|
|
323
|
+
* Hook to the Response Header
|
|
324
|
+
* @decorator
|
|
325
|
+
* @param name - header name
|
|
326
|
+
* @paramType THeaderHook
|
|
327
|
+
*/
|
|
328
|
+
const HeaderHook = (name) => moost.Resolve(() => eventHttp.useSetHeader(name), name);
|
|
329
|
+
/**
|
|
330
|
+
* Hook to the Response Cookie
|
|
331
|
+
* @decorator
|
|
332
|
+
* @param name - header name
|
|
333
|
+
* @paramType TCookieHook
|
|
334
|
+
*/
|
|
335
|
+
const CookieHook = (name) => moost.Resolve(() => eventHttp.useSetCookie(name), name);
|
|
336
|
+
/**
|
|
337
|
+
* Parse Authorisation Header
|
|
338
|
+
* @decorator
|
|
339
|
+
* @param name - define what to take from the Auth header
|
|
340
|
+
* @paramType string
|
|
341
|
+
*/
|
|
342
|
+
function Authorization(name) {
|
|
343
|
+
return moost.Resolve(() => {
|
|
344
|
+
var _a, _b;
|
|
345
|
+
const auth = eventHttp.useAuthorization();
|
|
346
|
+
switch (name) {
|
|
347
|
+
case 'username':
|
|
348
|
+
return auth.isBasic() ? (_a = auth.basicCredentials()) === null || _a === void 0 ? void 0 : _a.username : undefined;
|
|
349
|
+
case 'password':
|
|
350
|
+
return auth.isBasic() ? (_b = auth.basicCredentials()) === null || _b === void 0 ? void 0 : _b.password : undefined;
|
|
351
|
+
case 'bearer':
|
|
352
|
+
return auth.isBearer() ? auth.authorization : undefined;
|
|
353
|
+
case 'raw':
|
|
354
|
+
return auth.authRawCredentials();
|
|
355
|
+
case 'type':
|
|
356
|
+
return auth.authType();
|
|
357
|
+
}
|
|
358
|
+
}, 'authorization');
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* Get Request Header Value
|
|
362
|
+
* @decorator
|
|
363
|
+
* @param name - header name
|
|
364
|
+
* @paramType string
|
|
365
|
+
*/
|
|
366
|
+
function Header(name) {
|
|
367
|
+
return moost.Resolve(() => {
|
|
368
|
+
const headers = eventHttp.useHeaders();
|
|
369
|
+
return headers[name];
|
|
370
|
+
}, 'header: ' + name);
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* Get Request Cookie Value
|
|
374
|
+
* @decorator
|
|
375
|
+
* @param name - cookie name
|
|
376
|
+
* @paramType string
|
|
377
|
+
*/
|
|
378
|
+
function Cookie(name) {
|
|
379
|
+
return moost.Resolve(() => eventHttp.useCookies().getCookie(name), 'cookie: ' + name);
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* Get Query Item value or the whole parsed Query as an object
|
|
383
|
+
* @decorator
|
|
384
|
+
* @param name - query item name (optional)
|
|
385
|
+
* @paramType string | object
|
|
386
|
+
*/
|
|
387
|
+
function Query(name) {
|
|
388
|
+
return moost.Resolve(() => {
|
|
389
|
+
const { jsonSearchParams, urlSearchParams } = eventHttp.useSearchParams();
|
|
390
|
+
if (name) {
|
|
391
|
+
const p = urlSearchParams();
|
|
392
|
+
const value = p.get(name);
|
|
393
|
+
console.log(name + ' = ', value);
|
|
394
|
+
return value === '' && p.has(name) || value;
|
|
395
|
+
}
|
|
396
|
+
const json = jsonSearchParams();
|
|
397
|
+
return Object.keys(json).length ? json : undefined;
|
|
398
|
+
}, name || 'Query');
|
|
399
|
+
}
|
|
400
|
+
/**
|
|
401
|
+
* Get Requested URL
|
|
402
|
+
* @decorator
|
|
403
|
+
* @paramType string
|
|
404
|
+
*/
|
|
405
|
+
function Url() {
|
|
406
|
+
return moost.Resolve(() => eventHttp.useRequest().url, 'url');
|
|
407
|
+
}
|
|
408
|
+
/**
|
|
409
|
+
* Get Requested HTTP Method
|
|
410
|
+
* @decorator
|
|
411
|
+
* @paramType string
|
|
412
|
+
*/
|
|
413
|
+
function Method() {
|
|
414
|
+
return moost.Resolve(() => eventHttp.useRequest().method, 'http_method');
|
|
415
|
+
}
|
|
416
|
+
/**
|
|
417
|
+
* Get Raw Request Instance
|
|
418
|
+
* @decorator
|
|
419
|
+
* @paramType IncomingMessage
|
|
420
|
+
*/
|
|
421
|
+
function Req() {
|
|
422
|
+
return moost.Resolve(() => eventHttp.useRequest().rawRequest, 'request');
|
|
423
|
+
}
|
|
424
|
+
/**
|
|
425
|
+
* Get Request Unique Identificator (UUID)
|
|
426
|
+
* @decorator
|
|
427
|
+
* @paramType string
|
|
428
|
+
*/
|
|
429
|
+
function ReqId() {
|
|
430
|
+
return moost.Resolve(() => eventHttp.useRequest().reqId(), 'reqId');
|
|
431
|
+
}
|
|
432
|
+
/**
|
|
433
|
+
* Get Request IP Address
|
|
434
|
+
* @decorator
|
|
435
|
+
* @paramType string
|
|
436
|
+
*/
|
|
437
|
+
function Ip(opts) {
|
|
438
|
+
return moost.Resolve(() => eventHttp.useRequest().getIp(opts), 'ip');
|
|
439
|
+
}
|
|
440
|
+
/**
|
|
441
|
+
* Get Request IP Address list
|
|
442
|
+
* @decorator
|
|
443
|
+
* @paramType string[]
|
|
444
|
+
*/
|
|
445
|
+
function IpList() {
|
|
446
|
+
return moost.Resolve(() => eventHttp.useRequest().getIpList(), 'ipList');
|
|
447
|
+
}
|
|
448
|
+
/**
|
|
449
|
+
* Get Raw Response Object
|
|
450
|
+
* @decorator
|
|
451
|
+
* @param options - passthrough options
|
|
452
|
+
* @paramType string
|
|
453
|
+
*/
|
|
454
|
+
function Res(options) {
|
|
455
|
+
return moost.Resolve(() => eventHttp.useResponse().rawResponse(options), 'response');
|
|
456
|
+
}
|
|
457
|
+
/**
|
|
458
|
+
* Get Parsed Request Body
|
|
459
|
+
* @decorator
|
|
460
|
+
* @paramType object | string | unknown
|
|
461
|
+
*/
|
|
462
|
+
function Body() {
|
|
463
|
+
return moost.Resolve(() => useBody().parseBody(), 'body');
|
|
464
|
+
}
|
|
465
|
+
/**
|
|
466
|
+
* Get Raw Request Body Buffer
|
|
467
|
+
* @decorator
|
|
468
|
+
* @paramType Promise<Buffer>
|
|
469
|
+
*/
|
|
470
|
+
function RawBody() {
|
|
471
|
+
return moost.Resolve(() => useBody().rawBody(), 'body');
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
exports.All = All;
|
|
475
|
+
exports.Authorization = Authorization;
|
|
476
|
+
exports.Body = Body;
|
|
477
|
+
exports.Cookie = Cookie;
|
|
478
|
+
exports.CookieHook = CookieHook;
|
|
479
|
+
exports.Delete = Delete;
|
|
480
|
+
exports.Get = Get;
|
|
481
|
+
exports.Header = Header;
|
|
482
|
+
exports.HeaderHook = HeaderHook;
|
|
483
|
+
exports.HttpMethod = HttpMethod;
|
|
484
|
+
exports.Ip = Ip;
|
|
485
|
+
exports.IpList = IpList;
|
|
486
|
+
exports.Method = Method;
|
|
487
|
+
exports.MoostHttp = MoostHttp;
|
|
488
|
+
exports.Patch = Patch;
|
|
489
|
+
exports.Post = Post;
|
|
490
|
+
exports.Put = Put;
|
|
491
|
+
exports.Query = Query;
|
|
492
|
+
exports.RawBody = RawBody;
|
|
493
|
+
exports.Req = Req;
|
|
494
|
+
exports.ReqId = ReqId;
|
|
495
|
+
exports.Res = Res;
|
|
496
|
+
exports.StatusHook = StatusHook;
|
|
497
|
+
exports.Url = Url;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
|
|
3
|
+
import { IncomingMessage } from 'http';
|
|
4
|
+
import { ServerResponse } from 'http';
|
|
5
|
+
import { TMoostAdapter } from 'moost';
|
|
6
|
+
import { TMoostAdapterOptions } from 'moost';
|
|
7
|
+
import { TProstoRouterPathBuilder } from '@prostojs/router';
|
|
8
|
+
import { TWooksHttpOptions } from '@wooksjs/event-http';
|
|
9
|
+
import { WooksHttp } from '@wooksjs/event-http';
|
|
10
|
+
|
|
11
|
+
export declare const All: (path?: string) => MethodDecorator;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Parse Authorisation Header
|
|
15
|
+
* @decorator
|
|
16
|
+
* @param name - define what to take from the Auth header
|
|
17
|
+
* @paramType string
|
|
18
|
+
*/
|
|
19
|
+
export declare function Authorization(name: 'username' | 'password' | 'bearer' | 'raw' | 'type'): ParameterDecorator;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Get Parsed Request Body
|
|
23
|
+
* @decorator
|
|
24
|
+
* @paramType object | string | unknown
|
|
25
|
+
*/
|
|
26
|
+
declare function Body_2(): ParameterDecorator;
|
|
27
|
+
export { Body_2 as Body }
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Get Request Cookie Value
|
|
31
|
+
* @decorator
|
|
32
|
+
* @param name - cookie name
|
|
33
|
+
* @paramType string
|
|
34
|
+
*/
|
|
35
|
+
export declare function Cookie(name: string): ParameterDecorator;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Hook to the Response Cookie
|
|
39
|
+
* @decorator
|
|
40
|
+
* @param name - header name
|
|
41
|
+
* @paramType TCookieHook
|
|
42
|
+
*/
|
|
43
|
+
export declare const CookieHook: (name: string) => ParameterDecorator;
|
|
44
|
+
|
|
45
|
+
export declare const Delete: (path?: string) => MethodDecorator;
|
|
46
|
+
|
|
47
|
+
export declare const Get: (path?: string) => MethodDecorator;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Get Request Header Value
|
|
51
|
+
* @decorator
|
|
52
|
+
* @param name - header name
|
|
53
|
+
* @paramType string
|
|
54
|
+
*/
|
|
55
|
+
export declare function Header(name: string): ParameterDecorator;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Hook to the Response Header
|
|
59
|
+
* @decorator
|
|
60
|
+
* @param name - header name
|
|
61
|
+
* @paramType THeaderHook
|
|
62
|
+
*/
|
|
63
|
+
export declare const HeaderHook: (name: string) => ParameterDecorator;
|
|
64
|
+
|
|
65
|
+
export declare function HttpMethod(method: '*' | 'GET' | 'PUT' | 'POST' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIONS', path?: string): MethodDecorator;
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Get Request IP Address
|
|
69
|
+
* @decorator
|
|
70
|
+
* @paramType string
|
|
71
|
+
*/
|
|
72
|
+
export declare function Ip(opts?: {
|
|
73
|
+
trustProxy: boolean;
|
|
74
|
+
}): ParameterDecorator;
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Get Request IP Address list
|
|
78
|
+
* @decorator
|
|
79
|
+
* @paramType string[]
|
|
80
|
+
*/
|
|
81
|
+
export declare function IpList(): ParameterDecorator;
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Get Requested HTTP Method
|
|
85
|
+
* @decorator
|
|
86
|
+
* @paramType string
|
|
87
|
+
*/
|
|
88
|
+
export declare function Method(): ParameterDecorator;
|
|
89
|
+
|
|
90
|
+
export declare class MoostHttp implements TMoostAdapter<THttpHandlerMeta> {
|
|
91
|
+
protected httpApp: WooksHttp;
|
|
92
|
+
constructor(httpApp?: WooksHttp | TWooksHttpOptions);
|
|
93
|
+
getHttpApp(): WooksHttp;
|
|
94
|
+
getServerCb(): (req: IncomingMessage, res: ServerResponse<IncomingMessage>) => Promise<void>;
|
|
95
|
+
listen(...args: Parameters<WooksHttp['listen']>): Promise<unknown>;
|
|
96
|
+
readonly pathBuilders: {
|
|
97
|
+
[id: string]: {
|
|
98
|
+
GET?: TProstoRouterPathBuilder<Record<string, string | string[]>>;
|
|
99
|
+
PUT?: TProstoRouterPathBuilder<Record<string, string | string[]>>;
|
|
100
|
+
PATCH?: TProstoRouterPathBuilder<Record<string, string | string[]>>;
|
|
101
|
+
POST?: TProstoRouterPathBuilder<Record<string, string | string[]>>;
|
|
102
|
+
DELETE?: TProstoRouterPathBuilder<Record<string, string | string[]>>;
|
|
103
|
+
};
|
|
104
|
+
};
|
|
105
|
+
bindHandler<T extends object = object>(opts: TMoostAdapterOptions<THttpHandlerMeta, T>): void | Promise<void>;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export declare const Patch: (path?: string) => MethodDecorator;
|
|
109
|
+
|
|
110
|
+
export declare const Post: (path?: string) => MethodDecorator;
|
|
111
|
+
|
|
112
|
+
export declare const Put: (path?: string) => MethodDecorator;
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Get Query Item value or the whole parsed Query as an object
|
|
116
|
+
* @decorator
|
|
117
|
+
* @param name - query item name (optional)
|
|
118
|
+
* @paramType string | object
|
|
119
|
+
*/
|
|
120
|
+
export declare function Query(name?: string): ParameterDecorator;
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Get Raw Request Body Buffer
|
|
124
|
+
* @decorator
|
|
125
|
+
* @paramType Promise<Buffer>
|
|
126
|
+
*/
|
|
127
|
+
export declare function RawBody(): ParameterDecorator;
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Get Raw Request Instance
|
|
131
|
+
* @decorator
|
|
132
|
+
* @paramType IncomingMessage
|
|
133
|
+
*/
|
|
134
|
+
export declare function Req(): ParameterDecorator;
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Get Request Unique Identificator (UUID)
|
|
138
|
+
* @decorator
|
|
139
|
+
* @paramType string
|
|
140
|
+
*/
|
|
141
|
+
export declare function ReqId(): ParameterDecorator;
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Get Raw Response Object
|
|
145
|
+
* @decorator
|
|
146
|
+
* @param options - passthrough options
|
|
147
|
+
* @paramType string
|
|
148
|
+
*/
|
|
149
|
+
export declare function Res(options?: {
|
|
150
|
+
passthrough: boolean;
|
|
151
|
+
}): ParameterDecorator;
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Hook to the Response Status
|
|
155
|
+
* @decorator
|
|
156
|
+
* @paramType TStatusHook
|
|
157
|
+
*/
|
|
158
|
+
export declare const StatusHook: ParameterDecorator;
|
|
159
|
+
|
|
160
|
+
export declare interface THttpHandlerMeta {
|
|
161
|
+
method: string;
|
|
162
|
+
path: string;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Get Requested URL
|
|
167
|
+
* @decorator
|
|
168
|
+
* @paramType string
|
|
169
|
+
*/
|
|
170
|
+
export declare function Url(): ParameterDecorator;
|
|
171
|
+
|
|
172
|
+
export { }
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,472 @@
|
|
|
1
|
+
import { WooksHttp, createHttpApp, useHttpContext, useRequest, useHeaders, WooksError, EHttpStatusCode, WooksURLSearchParams, useStatus, useSetHeader, useSetCookie, useAuthorization, useCookies, useSearchParams, useResponse } from '@wooksjs/event-http';
|
|
2
|
+
import { getMoostMate, Resolve } from 'moost';
|
|
3
|
+
|
|
4
|
+
/******************************************************************************
|
|
5
|
+
Copyright (c) Microsoft Corporation.
|
|
6
|
+
|
|
7
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
8
|
+
purpose with or without fee is hereby granted.
|
|
9
|
+
|
|
10
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
11
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
12
|
+
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
13
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
14
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
15
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
16
|
+
PERFORMANCE OF THIS SOFTWARE.
|
|
17
|
+
***************************************************************************** */
|
|
18
|
+
|
|
19
|
+
function __awaiter$1(thisArg, _arguments, P, generator) {
|
|
20
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
21
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
22
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
23
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
24
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
25
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
class MoostHttp {
|
|
30
|
+
constructor(httpApp) {
|
|
31
|
+
this.pathBuilders = {};
|
|
32
|
+
if (httpApp && httpApp instanceof WooksHttp) {
|
|
33
|
+
this.httpApp = httpApp;
|
|
34
|
+
}
|
|
35
|
+
else if (httpApp) {
|
|
36
|
+
this.httpApp = createHttpApp(httpApp);
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
this.httpApp = createHttpApp();
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
getHttpApp() {
|
|
43
|
+
return this.httpApp;
|
|
44
|
+
}
|
|
45
|
+
getServerCb() {
|
|
46
|
+
return this.httpApp.getServerCb();
|
|
47
|
+
}
|
|
48
|
+
listen(...args) {
|
|
49
|
+
return this.httpApp.listen(...args);
|
|
50
|
+
}
|
|
51
|
+
bindHandler(opts) {
|
|
52
|
+
let fn;
|
|
53
|
+
for (const handler of opts.handlers) {
|
|
54
|
+
if (handler.type !== 'HTTP')
|
|
55
|
+
continue;
|
|
56
|
+
const httpPath = handler.path;
|
|
57
|
+
const path = typeof httpPath === 'string' ? httpPath : typeof opts.method === 'string' ? opts.method : '';
|
|
58
|
+
const targetPath = `${opts.prefix || ''}/${path}`.replace(/\/\/+/g, '/');
|
|
59
|
+
if (!fn) {
|
|
60
|
+
fn = () => __awaiter$1(this, void 0, void 0, function* () {
|
|
61
|
+
const { restoreCtx } = useHttpContext();
|
|
62
|
+
const { reqId, rawRequest } = useRequest();
|
|
63
|
+
const scopeId = reqId();
|
|
64
|
+
rawRequest.on('end', opts.registerEventScope(scopeId));
|
|
65
|
+
const instance = yield opts.getInstance();
|
|
66
|
+
restoreCtx();
|
|
67
|
+
// setComposableControllerContext({
|
|
68
|
+
// controller: instance,
|
|
69
|
+
// method: method as string,
|
|
70
|
+
// pathBuilder: pathBuilder as ReturnType<Wooks['on']>,
|
|
71
|
+
// })
|
|
72
|
+
let response;
|
|
73
|
+
const interceptorHandler = yield opts.getIterceptorHandler();
|
|
74
|
+
restoreCtx();
|
|
75
|
+
yield interceptorHandler.init();
|
|
76
|
+
// params
|
|
77
|
+
let args = [];
|
|
78
|
+
try {
|
|
79
|
+
restoreCtx();
|
|
80
|
+
args = yield opts.resolveArgs();
|
|
81
|
+
}
|
|
82
|
+
catch (e) {
|
|
83
|
+
response = e;
|
|
84
|
+
}
|
|
85
|
+
if (!response) {
|
|
86
|
+
restoreCtx();
|
|
87
|
+
// fire before interceptors
|
|
88
|
+
response = yield interceptorHandler.fireBefore(response);
|
|
89
|
+
// fire request handler
|
|
90
|
+
if (!interceptorHandler.responseOverwritten) {
|
|
91
|
+
try {
|
|
92
|
+
restoreCtx();
|
|
93
|
+
response = yield instance[opts.method](...args);
|
|
94
|
+
}
|
|
95
|
+
catch (e) {
|
|
96
|
+
response = e;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
// fire after interceptors
|
|
101
|
+
response = yield interceptorHandler.fireAfter(response);
|
|
102
|
+
return response;
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
const pathBuilder = this.httpApp.on(handler.method, targetPath, fn);
|
|
106
|
+
const methodMeta = getMoostMate().read(opts.fakeInstance, opts.method) || {};
|
|
107
|
+
const id = (methodMeta.id || opts.method);
|
|
108
|
+
if (id) {
|
|
109
|
+
const methods = this.pathBuilders[id] = this.pathBuilders[id] || {};
|
|
110
|
+
if (handler.method === '*') {
|
|
111
|
+
methods.GET = pathBuilder;
|
|
112
|
+
methods.PUT = pathBuilder;
|
|
113
|
+
methods.PATCH = pathBuilder;
|
|
114
|
+
methods.POST = pathBuilder;
|
|
115
|
+
methods.DELETE = pathBuilder;
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
methods[handler.method] = pathBuilder;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
opts.logHandler(`${'[36m'}(${handler.method})${'[32m'}${targetPath}`);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function HttpMethod(method, path) {
|
|
127
|
+
return getMoostMate().decorate('handlers', { method, path, type: 'HTTP' }, true);
|
|
128
|
+
}
|
|
129
|
+
const All = (path) => HttpMethod('*', path);
|
|
130
|
+
const Get = (path) => HttpMethod('GET', path);
|
|
131
|
+
const Post = (path) => HttpMethod('POST', path);
|
|
132
|
+
const Put = (path) => HttpMethod('PUT', path);
|
|
133
|
+
const Delete = (path) => HttpMethod('DELETE', path);
|
|
134
|
+
const Patch = (path) => HttpMethod('PATCH', path);
|
|
135
|
+
|
|
136
|
+
/******************************************************************************
|
|
137
|
+
Copyright (c) Microsoft Corporation.
|
|
138
|
+
|
|
139
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
140
|
+
purpose with or without fee is hereby granted.
|
|
141
|
+
|
|
142
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
143
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
144
|
+
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
145
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
146
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
147
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
148
|
+
PERFORMANCE OF THIS SOFTWARE.
|
|
149
|
+
***************************************************************************** */
|
|
150
|
+
|
|
151
|
+
function __awaiter(thisArg, _arguments, P, generator) {
|
|
152
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
153
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
154
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
155
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
156
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
157
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const banner = () => `[${"@wooksjs/http-body"}][${new Date().toISOString().replace('T', ' ').replace(/\.\d{3}z$/i, '')}] `;
|
|
162
|
+
|
|
163
|
+
/* istanbul ignore file */
|
|
164
|
+
function logError(error) {
|
|
165
|
+
console.error('[91m' + '[1m' + banner() + error + '[0m');
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function panic(error) {
|
|
169
|
+
logError(error);
|
|
170
|
+
return new Error(error);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const compressors = {
|
|
174
|
+
identity: {
|
|
175
|
+
compress: v => v,
|
|
176
|
+
uncompress: v => v,
|
|
177
|
+
},
|
|
178
|
+
};
|
|
179
|
+
function uncompressBody(encodings, body) {
|
|
180
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
181
|
+
let newBody = body;
|
|
182
|
+
for (const e of encodings.reverse()) {
|
|
183
|
+
if (!compressors[e]) {
|
|
184
|
+
throw panic(`Usupported compression type "${e}".`);
|
|
185
|
+
}
|
|
186
|
+
newBody = yield compressors[e].uncompress(body);
|
|
187
|
+
}
|
|
188
|
+
return newBody;
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function useBody() {
|
|
193
|
+
const { store } = useHttpContext();
|
|
194
|
+
const { init } = store('request');
|
|
195
|
+
const { rawBody } = useRequest();
|
|
196
|
+
const { 'content-type': contentType, 'content-encoding': contentEncoding } = useHeaders();
|
|
197
|
+
function contentIs(type) {
|
|
198
|
+
return (contentType || '').indexOf(type) >= 0;
|
|
199
|
+
}
|
|
200
|
+
const isJson = () => init('isJson', () => contentIs('application/json'));
|
|
201
|
+
const isHtml = () => init('isHtml', () => contentIs('text/html'));
|
|
202
|
+
const isXml = () => init('isXml', () => contentIs('text/xml'));
|
|
203
|
+
const isText = () => init('isText', () => contentIs('text/plain'));
|
|
204
|
+
const isBinary = () => init('isBinary', () => contentIs('application/octet-stream'));
|
|
205
|
+
const isFormData = () => init('isFormData', () => contentIs('multipart/form-data'));
|
|
206
|
+
const isUrlencoded = () => init('isUrlencoded', () => contentIs('application/x-www-form-urlencoded'));
|
|
207
|
+
const isCompressed = () => init('isCompressed', () => {
|
|
208
|
+
const parts = contentEncodings();
|
|
209
|
+
for (const p of parts) {
|
|
210
|
+
if (['deflate', 'gzip', 'br'].includes(p))
|
|
211
|
+
return true;
|
|
212
|
+
}
|
|
213
|
+
return false;
|
|
214
|
+
});
|
|
215
|
+
const contentEncodings = () => init('contentEncodings', () => (contentEncoding || '').split(',').map(p => p.trim()).filter(p => !!p));
|
|
216
|
+
const parseBody = () => init('parsed', () => __awaiter(this, void 0, void 0, function* () {
|
|
217
|
+
const body = yield uncompressBody(contentEncodings(), (yield rawBody()).toString());
|
|
218
|
+
if (isJson())
|
|
219
|
+
return jsonParser(body);
|
|
220
|
+
else if (isFormData())
|
|
221
|
+
return formDataParser(body);
|
|
222
|
+
else if (isUrlencoded())
|
|
223
|
+
return urlEncodedParser(body);
|
|
224
|
+
else if (isBinary())
|
|
225
|
+
return textParser(body);
|
|
226
|
+
else
|
|
227
|
+
return textParser(body);
|
|
228
|
+
}));
|
|
229
|
+
function jsonParser(v) {
|
|
230
|
+
try {
|
|
231
|
+
return JSON.parse(v);
|
|
232
|
+
}
|
|
233
|
+
catch (e) {
|
|
234
|
+
throw new WooksError(400, e.message);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
function textParser(v) {
|
|
238
|
+
return v;
|
|
239
|
+
}
|
|
240
|
+
function formDataParser(v) {
|
|
241
|
+
const boundary = '--' + (/boundary=([^;]+)(?:;|$)/.exec(contentType || '') || [, ''])[1];
|
|
242
|
+
if (!boundary)
|
|
243
|
+
throw new WooksError(EHttpStatusCode.BadRequest, 'form-data boundary not recognized');
|
|
244
|
+
const parts = v.trim().split(boundary);
|
|
245
|
+
const result = {};
|
|
246
|
+
let key = '';
|
|
247
|
+
let partContentType = 'text/plain';
|
|
248
|
+
for (const part of parts) {
|
|
249
|
+
parsePart();
|
|
250
|
+
key = '';
|
|
251
|
+
partContentType = 'text/plain';
|
|
252
|
+
let valueMode = false;
|
|
253
|
+
const lines = part.trim().split(/\n/g).map(s => s.trim());
|
|
254
|
+
for (const line of lines) {
|
|
255
|
+
if (valueMode) {
|
|
256
|
+
if (!result[key]) {
|
|
257
|
+
result[key] = line;
|
|
258
|
+
}
|
|
259
|
+
else {
|
|
260
|
+
result[key] += '\n' + line;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
else {
|
|
264
|
+
if (!line || line === '--') {
|
|
265
|
+
valueMode = !!key;
|
|
266
|
+
if (valueMode) {
|
|
267
|
+
key = key.replace(/^["']/, '').replace(/["']$/, '');
|
|
268
|
+
}
|
|
269
|
+
continue;
|
|
270
|
+
}
|
|
271
|
+
if (line.toLowerCase().startsWith('content-disposition: form-data;')) {
|
|
272
|
+
key = (/name=([^;]+)/.exec(line) || [])[1];
|
|
273
|
+
if (!key)
|
|
274
|
+
throw new WooksError(EHttpStatusCode.BadRequest, 'Could not read multipart name: ' + line);
|
|
275
|
+
continue;
|
|
276
|
+
}
|
|
277
|
+
if (line.toLowerCase().startsWith('content-type:')) {
|
|
278
|
+
partContentType = (/content-type:\s?([^;]+)/i.exec(line) || [])[1];
|
|
279
|
+
if (!partContentType)
|
|
280
|
+
throw new WooksError(EHttpStatusCode.BadRequest, 'Could not read content-type: ' + line);
|
|
281
|
+
continue;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
parsePart();
|
|
287
|
+
function parsePart() {
|
|
288
|
+
if (key) {
|
|
289
|
+
if (partContentType.indexOf('application/json') >= 0) {
|
|
290
|
+
result[key] = JSON.parse(result[key]);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
return result;
|
|
295
|
+
}
|
|
296
|
+
function urlEncodedParser(v) {
|
|
297
|
+
return new WooksURLSearchParams(v.trim()).toJson();
|
|
298
|
+
}
|
|
299
|
+
return {
|
|
300
|
+
isJson,
|
|
301
|
+
isHtml,
|
|
302
|
+
isXml,
|
|
303
|
+
isText,
|
|
304
|
+
isBinary,
|
|
305
|
+
isFormData,
|
|
306
|
+
isUrlencoded,
|
|
307
|
+
isCompressed,
|
|
308
|
+
contentEncodings,
|
|
309
|
+
parseBody,
|
|
310
|
+
rawBody,
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Hook to the Response Status
|
|
316
|
+
* @decorator
|
|
317
|
+
* @paramType TStatusHook
|
|
318
|
+
*/
|
|
319
|
+
const StatusHook = Resolve(() => useStatus(), 'status');
|
|
320
|
+
/**
|
|
321
|
+
* Hook to the Response Header
|
|
322
|
+
* @decorator
|
|
323
|
+
* @param name - header name
|
|
324
|
+
* @paramType THeaderHook
|
|
325
|
+
*/
|
|
326
|
+
const HeaderHook = (name) => Resolve(() => useSetHeader(name), name);
|
|
327
|
+
/**
|
|
328
|
+
* Hook to the Response Cookie
|
|
329
|
+
* @decorator
|
|
330
|
+
* @param name - header name
|
|
331
|
+
* @paramType TCookieHook
|
|
332
|
+
*/
|
|
333
|
+
const CookieHook = (name) => Resolve(() => useSetCookie(name), name);
|
|
334
|
+
/**
|
|
335
|
+
* Parse Authorisation Header
|
|
336
|
+
* @decorator
|
|
337
|
+
* @param name - define what to take from the Auth header
|
|
338
|
+
* @paramType string
|
|
339
|
+
*/
|
|
340
|
+
function Authorization(name) {
|
|
341
|
+
return Resolve(() => {
|
|
342
|
+
var _a, _b;
|
|
343
|
+
const auth = useAuthorization();
|
|
344
|
+
switch (name) {
|
|
345
|
+
case 'username':
|
|
346
|
+
return auth.isBasic() ? (_a = auth.basicCredentials()) === null || _a === void 0 ? void 0 : _a.username : undefined;
|
|
347
|
+
case 'password':
|
|
348
|
+
return auth.isBasic() ? (_b = auth.basicCredentials()) === null || _b === void 0 ? void 0 : _b.password : undefined;
|
|
349
|
+
case 'bearer':
|
|
350
|
+
return auth.isBearer() ? auth.authorization : undefined;
|
|
351
|
+
case 'raw':
|
|
352
|
+
return auth.authRawCredentials();
|
|
353
|
+
case 'type':
|
|
354
|
+
return auth.authType();
|
|
355
|
+
}
|
|
356
|
+
}, 'authorization');
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* Get Request Header Value
|
|
360
|
+
* @decorator
|
|
361
|
+
* @param name - header name
|
|
362
|
+
* @paramType string
|
|
363
|
+
*/
|
|
364
|
+
function Header(name) {
|
|
365
|
+
return Resolve(() => {
|
|
366
|
+
const headers = useHeaders();
|
|
367
|
+
return headers[name];
|
|
368
|
+
}, 'header: ' + name);
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* Get Request Cookie Value
|
|
372
|
+
* @decorator
|
|
373
|
+
* @param name - cookie name
|
|
374
|
+
* @paramType string
|
|
375
|
+
*/
|
|
376
|
+
function Cookie(name) {
|
|
377
|
+
return Resolve(() => useCookies().getCookie(name), 'cookie: ' + name);
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* Get Query Item value or the whole parsed Query as an object
|
|
381
|
+
* @decorator
|
|
382
|
+
* @param name - query item name (optional)
|
|
383
|
+
* @paramType string | object
|
|
384
|
+
*/
|
|
385
|
+
function Query(name) {
|
|
386
|
+
return Resolve(() => {
|
|
387
|
+
const { jsonSearchParams, urlSearchParams } = useSearchParams();
|
|
388
|
+
if (name) {
|
|
389
|
+
const p = urlSearchParams();
|
|
390
|
+
const value = p.get(name);
|
|
391
|
+
console.log(name + ' = ', value);
|
|
392
|
+
return value === '' && p.has(name) || value;
|
|
393
|
+
}
|
|
394
|
+
const json = jsonSearchParams();
|
|
395
|
+
return Object.keys(json).length ? json : undefined;
|
|
396
|
+
}, name || 'Query');
|
|
397
|
+
}
|
|
398
|
+
/**
|
|
399
|
+
* Get Requested URL
|
|
400
|
+
* @decorator
|
|
401
|
+
* @paramType string
|
|
402
|
+
*/
|
|
403
|
+
function Url() {
|
|
404
|
+
return Resolve(() => useRequest().url, 'url');
|
|
405
|
+
}
|
|
406
|
+
/**
|
|
407
|
+
* Get Requested HTTP Method
|
|
408
|
+
* @decorator
|
|
409
|
+
* @paramType string
|
|
410
|
+
*/
|
|
411
|
+
function Method() {
|
|
412
|
+
return Resolve(() => useRequest().method, 'http_method');
|
|
413
|
+
}
|
|
414
|
+
/**
|
|
415
|
+
* Get Raw Request Instance
|
|
416
|
+
* @decorator
|
|
417
|
+
* @paramType IncomingMessage
|
|
418
|
+
*/
|
|
419
|
+
function Req() {
|
|
420
|
+
return Resolve(() => useRequest().rawRequest, 'request');
|
|
421
|
+
}
|
|
422
|
+
/**
|
|
423
|
+
* Get Request Unique Identificator (UUID)
|
|
424
|
+
* @decorator
|
|
425
|
+
* @paramType string
|
|
426
|
+
*/
|
|
427
|
+
function ReqId() {
|
|
428
|
+
return Resolve(() => useRequest().reqId(), 'reqId');
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* Get Request IP Address
|
|
432
|
+
* @decorator
|
|
433
|
+
* @paramType string
|
|
434
|
+
*/
|
|
435
|
+
function Ip(opts) {
|
|
436
|
+
return Resolve(() => useRequest().getIp(opts), 'ip');
|
|
437
|
+
}
|
|
438
|
+
/**
|
|
439
|
+
* Get Request IP Address list
|
|
440
|
+
* @decorator
|
|
441
|
+
* @paramType string[]
|
|
442
|
+
*/
|
|
443
|
+
function IpList() {
|
|
444
|
+
return Resolve(() => useRequest().getIpList(), 'ipList');
|
|
445
|
+
}
|
|
446
|
+
/**
|
|
447
|
+
* Get Raw Response Object
|
|
448
|
+
* @decorator
|
|
449
|
+
* @param options - passthrough options
|
|
450
|
+
* @paramType string
|
|
451
|
+
*/
|
|
452
|
+
function Res(options) {
|
|
453
|
+
return Resolve(() => useResponse().rawResponse(options), 'response');
|
|
454
|
+
}
|
|
455
|
+
/**
|
|
456
|
+
* Get Parsed Request Body
|
|
457
|
+
* @decorator
|
|
458
|
+
* @paramType object | string | unknown
|
|
459
|
+
*/
|
|
460
|
+
function Body() {
|
|
461
|
+
return Resolve(() => useBody().parseBody(), 'body');
|
|
462
|
+
}
|
|
463
|
+
/**
|
|
464
|
+
* Get Raw Request Body Buffer
|
|
465
|
+
* @decorator
|
|
466
|
+
* @paramType Promise<Buffer>
|
|
467
|
+
*/
|
|
468
|
+
function RawBody() {
|
|
469
|
+
return Resolve(() => useBody().rawBody(), 'body');
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
export { All, Authorization, Body, Cookie, CookieHook, Delete, Get, Header, HeaderHook, HttpMethod, Ip, IpList, Method, MoostHttp, Patch, Post, Put, Query, RawBody, Req, ReqId, Res, StatusHook, Url };
|
package/package.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@moostjs/event-http",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "@moostjs/event-http",
|
|
5
|
+
"main": "dist/index.cjs",
|
|
6
|
+
"module": "dist/index.mjs",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist"
|
|
10
|
+
],
|
|
11
|
+
"repository": {
|
|
12
|
+
"type": "git",
|
|
13
|
+
"url": "git+https://github.com/moostjs/moostjs.git",
|
|
14
|
+
"directory": "packages/event-http"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"moost",
|
|
18
|
+
"moostjs",
|
|
19
|
+
"composables",
|
|
20
|
+
"framework",
|
|
21
|
+
"wooksjs",
|
|
22
|
+
"prostojs"
|
|
23
|
+
],
|
|
24
|
+
"author": "Artem Maltsev",
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"bugs": {
|
|
27
|
+
"url": "https://github.com/moostjs/moostjs/issues"
|
|
28
|
+
},
|
|
29
|
+
"homepage": "https://github.com/moostjs/moostjs/tree/main/packages/event-http#readme",
|
|
30
|
+
"peerDependencies": {
|
|
31
|
+
"moost": "0.2.0"
|
|
32
|
+
},
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"@wooksjs/event-http": "^0.1.0",
|
|
35
|
+
"wooks": "^0.1.0"
|
|
36
|
+
}
|
|
37
|
+
}
|