@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 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(`${''}(${handler.method})${''}${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('' + '' + banner() + error + '');
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;
@@ -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(`${''}(${handler.method})${''}${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('' + '' + banner() + error + '');
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
+ }