moost 0.2.26 → 0.2.28

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/dist/index.mjs CHANGED
@@ -18,6 +18,8 @@ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
18
18
  OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
19
19
  PERFORMANCE OF THIS SOFTWARE.
20
20
  ***************************************************************************** */
21
+ /* global Reflect, Promise */
22
+
21
23
 
22
24
  function __awaiter(thisArg, _arguments, P, generator) {
23
25
  function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
@@ -39,7 +41,7 @@ const moostMate = new Mate(METADATA_WORKSPACE, {
39
41
  return !!(classMeta === null || classMeta === void 0 ? void 0 : classMeta.inherit);
40
42
  }
41
43
  if (level === 'PROP') {
42
- return !!(targetMeta === null || targetMeta === void 0 ? void 0 : targetMeta.inherit) || !!((classMeta === null || classMeta === void 0 ? void 0 : classMeta.inherit) && !targetMeta);
44
+ return (!!(targetMeta === null || targetMeta === void 0 ? void 0 : targetMeta.inherit) || !!((classMeta === null || classMeta === void 0 ? void 0 : classMeta.inherit) && !targetMeta));
43
45
  }
44
46
  return !!(targetMeta === null || targetMeta === void 0 ? void 0 : targetMeta.inherit);
45
47
  },
@@ -73,22 +75,32 @@ function getNewMoostInfact() {
73
75
  constructorParams: (meta === null || meta === void 0 ? void 0 : meta.params) || [],
74
76
  provide: meta === null || meta === void 0 ? void 0 : meta.provide,
75
77
  properties: (meta === null || meta === void 0 ? void 0 : meta.properties) || [],
76
- scopeId: (meta === null || meta === void 0 ? void 0 : meta.injectable) === 'FOR_EVENT' ? useEventId().getId() : undefined,
78
+ scopeId: (meta === null || meta === void 0 ? void 0 : meta.injectable) === 'FOR_EVENT'
79
+ ? useEventId().getId()
80
+ : undefined,
77
81
  };
78
82
  return infactMeta;
79
83
  },
80
84
  resolveParam({ paramMeta, classMeta, customData }) {
81
85
  if (paramMeta && customData && customData.pipes) {
82
- return runPipes(customData.pipes, undefined, { paramMeta, classMeta: classMeta }, 'PARAM');
86
+ return runPipes(customData.pipes, undefined, {
87
+ paramMeta,
88
+ classMeta: classMeta,
89
+ }, 'PARAM');
83
90
  }
84
91
  },
85
92
  describeProp(classConstructor, key) {
86
93
  const meta = getMoostMate().read(classConstructor, key);
87
94
  return meta;
88
95
  },
89
- resolveProp({ instance, key, initialValue, propMeta, classMeta, customData }) {
96
+ resolveProp({ instance, key, initialValue, propMeta, classMeta, customData, }) {
90
97
  if (propMeta && customData && customData.pipes) {
91
- return runPipes(customData.pipes, initialValue, { instance, key, propMeta, classMeta: classMeta }, 'PROP');
98
+ return runPipes(customData.pipes, initialValue, {
99
+ instance,
100
+ key,
101
+ propMeta,
102
+ classMeta: classMeta,
103
+ }, 'PROP');
92
104
  }
93
105
  },
94
106
  storeProvideRegByInstance: true,
@@ -102,13 +114,19 @@ function getInstanceOwnMethods(instance) {
102
114
  ...getParentProps(getConstructor(instance)),
103
115
  ...Object.getOwnPropertyNames(proto),
104
116
  ...Object.getOwnPropertyNames(instance),
105
- ].filter(m => typeof instance[m] === 'function');
117
+ ].filter((m) => typeof instance[m] === 'function');
106
118
  }
107
119
  const fnProto = Object.getPrototypeOf(Function);
108
120
  function getParentProps(constructor) {
109
121
  const parent = Object.getPrototypeOf(constructor);
110
- if (typeof parent === 'function' && parent !== fnProto && parent !== constructor && parent.prototype) {
111
- return [...getParentProps(parent), ...Object.getOwnPropertyNames(parent.prototype)];
122
+ if (typeof parent === 'function' &&
123
+ parent !== fnProto &&
124
+ parent !== constructor &&
125
+ parent.prototype) {
126
+ return [
127
+ ...getParentProps(parent),
128
+ ...Object.getOwnPropertyNames(parent.prototype),
129
+ ];
112
130
  }
113
131
  return [];
114
132
  }
@@ -120,10 +138,14 @@ function getCallableFn(targetInstance, fn, restoreCtx, pipes, logger) {
120
138
  if (meta === null || meta === void 0 ? void 0 : meta.injectable) {
121
139
  const infact = getMoostInfact();
122
140
  infact.silent(true);
123
- const instance = yield infact.getForInstance(targetInstance, fn, {
124
- syncContextFn: () => { restoreCtx && restoreCtx(); },
125
- customData: { pipes: [...(pipes || []), ...(meta.pipes || [])].sort((a, b) => a.priority - b.priority) },
126
- });
141
+ const instance = (yield infact.getForInstance(targetInstance, fn, {
142
+ syncContextFn: () => {
143
+ restoreCtx && restoreCtx();
144
+ },
145
+ customData: {
146
+ pipes: [...(pipes || []), ...(meta.pipes || [])].sort((a, b) => a.priority - b.priority),
147
+ },
148
+ }));
127
149
  infact.silent(false);
128
150
  return ((...args) => {
129
151
  return instance.handler(...args);
@@ -155,7 +177,15 @@ class InterceptorHandler {
155
177
  const { restoreCtx } = useEventContext();
156
178
  for (const handler of this.handlers) {
157
179
  restoreCtx();
158
- yield handler((fn) => { this.before.push(fn); }, (fn) => { this.after.unshift(fn); }, (fn) => { this.onError.unshift(fn); });
180
+ const response = yield handler((fn) => {
181
+ this.before.push(fn);
182
+ }, (fn) => {
183
+ this.after.unshift(fn);
184
+ }, (fn) => {
185
+ this.onError.unshift(fn);
186
+ });
187
+ if (typeof response !== 'undefined')
188
+ return response;
159
189
  }
160
190
  });
161
191
  }
@@ -193,6 +223,28 @@ class InterceptorHandler {
193
223
  }
194
224
  }
195
225
 
226
+ const mate = getMoostMate();
227
+ function getIterceptorHandlerFactory(interceptors, getTargetInstance, pipes, logger) {
228
+ return () => {
229
+ const interceptorHandlers = [];
230
+ for (const { handler } of interceptors) {
231
+ const interceptorMeta = mate.read(handler);
232
+ if (interceptorMeta === null || interceptorMeta === void 0 ? void 0 : interceptorMeta.injectable) {
233
+ interceptorHandlers.push((...args) => __awaiter(this, void 0, void 0, function* () {
234
+ const { restoreCtx } = useEventContext();
235
+ const targetInstance = yield getTargetInstance();
236
+ restoreCtx();
237
+ return (yield getCallableFn(targetInstance, handler, restoreCtx, pipes, logger))(...args);
238
+ }));
239
+ }
240
+ else {
241
+ interceptorHandlers.push(handler);
242
+ }
243
+ }
244
+ return Promise.resolve(new InterceptorHandler(interceptorHandlers));
245
+ };
246
+ }
247
+
196
248
  function bindControllerMethods(options) {
197
249
  var _a;
198
250
  return __awaiter(this, void 0, void 0, function* () {
@@ -207,7 +259,9 @@ function bindControllerMethods(options) {
207
259
  const methods = getInstanceOwnMethods(fakeInstance);
208
260
  const mate = getMoostMate();
209
261
  const meta = mate.read(classConstructor) || {};
210
- const ownPrefix = typeof opts.replaceOwnPrefix === 'string' ? opts.replaceOwnPrefix : (((_a = meta.controller) === null || _a === void 0 ? void 0 : _a.prefix) || '');
262
+ const ownPrefix = typeof opts.replaceOwnPrefix === 'string'
263
+ ? opts.replaceOwnPrefix
264
+ : ((_a = meta.controller) === null || _a === void 0 ? void 0 : _a.prefix) || '';
211
265
  const prefix = `${opts.globalPrefix}/${ownPrefix}`;
212
266
  for (const method of methods) {
213
267
  const methodMeta = getMoostMate().read(fakeInstance, method) || {};
@@ -215,25 +269,12 @@ function bindControllerMethods(options) {
215
269
  continue;
216
270
  const pipes = [...(opts.pipes || []), ...(methodMeta.pipes || [])].sort((a, b) => a.priority - b.priority);
217
271
  // preparing interceptors
218
- const interceptors = [...(opts.interceptors || []), ...(meta.interceptors || []), ...(methodMeta.interceptors || [])].sort((a, b) => a.priority - b.priority);
219
- const getIterceptorHandler = () => {
220
- const interceptorHandlers = [];
221
- for (const { handler } of interceptors) {
222
- const interceptorMeta = mate.read(handler);
223
- if (interceptorMeta === null || interceptorMeta === void 0 ? void 0 : interceptorMeta.injectable) {
224
- interceptorHandlers.push((...args) => __awaiter(this, void 0, void 0, function* () {
225
- const { restoreCtx } = useEventContext();
226
- const targetInstance = yield getInstance();
227
- restoreCtx();
228
- return (yield getCallableFn(targetInstance, handler, restoreCtx, pipes, options.logger))(...args);
229
- }));
230
- }
231
- else {
232
- interceptorHandlers.push(handler);
233
- }
234
- }
235
- return Promise.resolve(new InterceptorHandler(interceptorHandlers));
236
- };
272
+ const interceptors = [
273
+ ...(opts.interceptors || []),
274
+ ...(meta.interceptors || []),
275
+ ...(methodMeta.interceptors || []),
276
+ ].sort((a, b) => a.priority - b.priority);
277
+ const getIterceptorHandler = getIterceptorHandlerFactory(interceptors, getInstance, pipes, options.logger);
237
278
  // preparing pipes
238
279
  const argsPipes = [];
239
280
  for (const p of methodMeta.params || []) {
@@ -262,11 +303,6 @@ function bindControllerMethods(options) {
262
303
  prefix,
263
304
  fakeInstance,
264
305
  getInstance,
265
- registerEventScope: (scopeId) => {
266
- const infact = getMoostInfact();
267
- infact.registerScope(scopeId);
268
- return () => infact.unregisterScope(scopeId);
269
- },
270
306
  method,
271
307
  handlers: methodMeta.handlers,
272
308
  getIterceptorHandler,
@@ -278,21 +314,71 @@ function bindControllerMethods(options) {
278
314
  });
279
315
  }
280
316
 
317
+ /**
318
+ * ## Label
319
+ * ### @Decorator
320
+ * _Common purpose decorator that may be used by various adapters for various purposes_
321
+ *
322
+ * Stores Label metadata
323
+ */
281
324
  function Label(value) {
282
325
  return getMoostMate().decorate('label', value);
283
326
  }
327
+ /**
328
+ * ## Description
329
+ * ### @Decorator
330
+ * _Common purpose decorator that may be used by various adapters for various purposes_
331
+ *
332
+ * Stores Description metadata
333
+ */
334
+ function Description(value) {
335
+ return getMoostMate().decorate('description', value);
336
+ }
337
+ /**
338
+ * ## Value
339
+ * ### @Decorator
340
+ * _Common purpose decorator that may be used by various adapters for various purposes_
341
+ *
342
+ * Stores Value metadata
343
+ */
344
+ function Value(value) {
345
+ return getMoostMate().decorate('value', value);
346
+ }
347
+ /**
348
+ * ## Id
349
+ * ### @Decorator
350
+ * _Common purpose decorator that may be used by various adapters for various purposes_
351
+ *
352
+ * Stores Id metadata
353
+ */
284
354
  function Id(value) {
285
355
  return getMoostMate().decorate('id', value);
286
356
  }
357
+ /**
358
+ * ## Optional
359
+ * ### @Decorator
360
+ * _Common purpose decorator that may be used by various adapters for various purposes_
361
+ *
362
+ * Stores Optional metadata
363
+ */
287
364
  function Optional() {
288
365
  return getMoostMate().decorate('optional', true);
289
366
  }
367
+ /**
368
+ * ## Required
369
+ * ### @Decorator
370
+ * _Common purpose decorator that may be used by various adapters for various purposes_
371
+ *
372
+ * Stores Required metadata
373
+ */
290
374
  function Required() {
291
375
  const mate = getMoostMate();
292
376
  return mate.apply(mate.decorate('required', true),
293
377
  // eslint-disable-next-line @typescript-eslint/no-unsafe-call
294
378
  mate.decorateClass((meta, level, key, index) => {
295
- if (typeof index !== 'number' && meta && ['string', 'symbol'].includes(typeof key)) {
379
+ if (typeof index !== 'number' &&
380
+ meta &&
381
+ ['string', 'symbol'].includes(typeof key)) {
296
382
  // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access
297
383
  meta.requiredProps = meta.requiredProps || [];
298
384
  // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call
@@ -312,7 +398,7 @@ function Required() {
312
398
  function Resolve(resolver, label) {
313
399
  return (target, key, index) => {
314
400
  const i = typeof index === 'number' ? index : undefined;
315
- fillLabel(target, key, i, label);
401
+ fillLabel(target, key || '', i, label);
316
402
  getMoostMate().decorate('resolver', resolver)(target, key, i);
317
403
  };
318
404
  }
@@ -323,7 +409,7 @@ function Resolve(resolver, label) {
323
409
  * @paramType string
324
410
  */
325
411
  function Param(name) {
326
- return Resolve(() => useRouteParams().get(name), name);
412
+ return getMoostMate().apply(getMoostMate().decorate('isRouteParam', name), Resolve(() => useRouteParams().get(name), name));
327
413
  }
328
414
  /**
329
415
  * Get Parsed Params from url parh
@@ -365,7 +451,9 @@ function fillLabel(target, key, index, name) {
365
451
  if (name) {
366
452
  const meta = getMoostMate().read(target, key);
367
453
  if (typeof index === 'number') {
368
- if (!(meta === null || meta === void 0 ? void 0 : meta.params) || !(meta === null || meta === void 0 ? void 0 : meta.params[index]) || !(meta === null || meta === void 0 ? void 0 : meta.params[index].label)) {
454
+ if (!(meta === null || meta === void 0 ? void 0 : meta.params) ||
455
+ !(meta === null || meta === void 0 ? void 0 : meta.params[index]) ||
456
+ !(meta === null || meta === void 0 ? void 0 : meta.params[index].label)) {
369
457
  Label(name)(target, key, index);
370
458
  }
371
459
  }
@@ -378,8 +466,9 @@ function fillLabel(target, key, index, name) {
378
466
  }
379
467
 
380
468
  /**
469
+ * ## Injectable
470
+ * ### @Decorator
381
471
  * Mark the Class as Injectable to enable it to be used in dependency injection
382
- * @decorator
383
472
  * @param scope - Scope for injection ("FOR_EVENT" | "SINGLETON" | true)
384
473
  * FOR_EVENT - will create a new instance for each incoming request
385
474
  * SINGLETON | true - will create a new instance only once
@@ -395,8 +484,9 @@ const insureInjectable = getMoostMate().decorate((meta) => {
395
484
  });
396
485
 
397
486
  /**
487
+ * ## Controller
488
+ * ### @Decorator
398
489
  * Set Class as a Controller
399
- * @decorator
400
490
  * @param prefix - define the prefix for all the paths of this controller
401
491
  */
402
492
  function Controller(prefix) {
@@ -406,8 +496,12 @@ function Controller(prefix) {
406
496
  function ImportController(prefix, controller, provide) {
407
497
  return getMoostMate().decorate('importController', {
408
498
  prefix: typeof prefix === 'string' ? prefix : undefined,
409
- typeResolver: typeof prefix === 'string' ? controller : prefix,
410
- provide: typeof prefix === 'string' ? provide || undefined : controller || undefined,
499
+ typeResolver: typeof prefix === 'string'
500
+ ? controller
501
+ : prefix,
502
+ provide: typeof prefix === 'string'
503
+ ? provide || undefined
504
+ : controller || undefined,
411
505
  }, true);
412
506
  }
413
507
 
@@ -425,26 +519,40 @@ var TInterceptorPriority;
425
519
  TInterceptorPriority[TInterceptorPriority["CATCH_ERROR"] = 5] = "CATCH_ERROR";
426
520
  TInterceptorPriority[TInterceptorPriority["AFTER_ALL"] = 6] = "AFTER_ALL";
427
521
  })(TInterceptorPriority || (TInterceptorPriority = {}));
522
+ /**
523
+ * ## Intercept
524
+ * ### @Decorator
525
+ * Set interceptor
526
+ * @param handler interceptor fn (use defineInterceptorFn)
527
+ * @param priority interceptor priority
528
+ * @returns
529
+ */
428
530
  function Intercept(handler, priority) {
429
531
  return getMoostMate().decorate('interceptors', {
430
532
  handler,
431
- priority: priority || handler.priority || TInterceptorPriority.INTERCEPTOR,
533
+ priority: priority ||
534
+ handler.priority ||
535
+ TInterceptorPriority.INTERCEPTOR,
432
536
  }, true);
433
537
  }
434
538
 
435
539
  /**
540
+ * ## Provide
541
+ * ### @Decorator
436
542
  * Defines provide registry for class (and all the children)
437
543
  * @param type - string or class constructor
438
544
  * @param fn - factory function for provided value
439
545
  */
440
546
  function Provide(type, fn) {
441
- return getMoostMate().decorate(meta => {
547
+ return getMoostMate().decorate((meta) => {
442
548
  meta.provide = meta.provide || {};
443
549
  Object.assign(meta.provide, createProvideRegistry([type, fn]));
444
550
  return meta;
445
551
  });
446
552
  }
447
553
  /**
554
+ * ## Inject
555
+ * ### @Decorator
448
556
  * Defines a key from provide registry to inject value
449
557
  * (For optional values use with @Nullable())
450
558
  * @param type - string or class constructor
@@ -453,6 +561,8 @@ function Inject(type) {
453
561
  return getMoostMate().decorate('inject', type);
454
562
  }
455
563
  /**
564
+ * ## Nullable
565
+ * ### @Decorator
456
566
  * Makes injectable value optional
457
567
  * @param value default true
458
568
  */
@@ -494,6 +604,12 @@ function IsBoolean(...args) {
494
604
  return Validate(validoIsBoolean(...args));
495
605
  }
496
606
 
607
+ /**
608
+ * ## Inherit
609
+ * ### @Decorator
610
+ * Inherit metadata from super class
611
+ * @returns
612
+ */
497
613
  const Inherit = () => getMoostMate().decorate('inherit', true);
498
614
 
499
615
  var TPipePriority;
@@ -509,7 +625,202 @@ var TPipePriority;
509
625
  TPipePriority[TPipePriority["AFTER_VALIDATE"] = 8] = "AFTER_VALIDATE";
510
626
  })(TPipePriority || (TPipePriority = {}));
511
627
 
512
- const resolvePipe = (_value, metas, level) => {
628
+ const genericTypesCastPipe = (strict) => {
629
+ const handler = (value, { paramMeta: meta }) => {
630
+ if (meta === null || meta === void 0 ? void 0 : meta.type) {
631
+ if ((value === undefined ||
632
+ value === null ||
633
+ (meta.type !== String && value === '')) &&
634
+ meta.optional) {
635
+ return undefined;
636
+ }
637
+ switch (meta.type) {
638
+ case Date: {
639
+ let d;
640
+ if (typeof value === 'string') {
641
+ d = new Date(/^\d+$/.test(value) ? Number(value) : value);
642
+ }
643
+ else {
644
+ d = new Date(value);
645
+ }
646
+ if (strict && Number.isNaN(d.getTime())) {
647
+ typeError(value, 'Date', meta.label);
648
+ }
649
+ return Number.isNaN(d.getTime()) ? value : d;
650
+ }
651
+ case Boolean:
652
+ if ([
653
+ true,
654
+ 'true',
655
+ 'TRUE',
656
+ 'True',
657
+ 1,
658
+ '1',
659
+ 'X',
660
+ 'x',
661
+ ].includes(value)) {
662
+ return true;
663
+ }
664
+ if ([
665
+ false,
666
+ 'false',
667
+ 'FALSE',
668
+ 'False',
669
+ 0,
670
+ '0',
671
+ '',
672
+ ' ',
673
+ null,
674
+ undefined,
675
+ ].includes(value)) {
676
+ return false;
677
+ }
678
+ if (strict) {
679
+ typeError(value, 'boolean', meta.label);
680
+ }
681
+ return value;
682
+ case Number: {
683
+ if (strict && !value && value !== 0) {
684
+ typeError(value, 'numeric', meta.label);
685
+ }
686
+ const n = typeof value === 'string' && value.length > 0
687
+ ? Number(value)
688
+ : NaN;
689
+ if (strict && Number.isNaN(n)) {
690
+ typeError(value, 'numeric', meta.label);
691
+ }
692
+ return Number.isNaN(n) ? value : n;
693
+ }
694
+ case String:
695
+ if (strict &&
696
+ ['object', 'function'].includes(typeof value)) {
697
+ typeError(value, 'string', meta.label);
698
+ }
699
+ return (value && String(value)) || value;
700
+ default:
701
+ return value;
702
+ }
703
+ }
704
+ };
705
+ handler.priority = TPipePriority.AFTER_TRANSFORM;
706
+ return handler;
707
+ };
708
+ function typeError(value, targetType, label) {
709
+ const prefix = label ? `Argument "${label}" with value ` : '';
710
+ throw new Error(`${prefix}${JSON.stringify(value)} is not a ${targetType} type`);
711
+ }
712
+
713
+ const valido = new Valido({
714
+ getDtoMeta(value, _type) {
715
+ let type = _type;
716
+ if (!type) {
717
+ type = getConstructor(value);
718
+ }
719
+ const mate = getMoostMate();
720
+ return mate.read(type);
721
+ },
722
+ getDtoParamMeta(value, type, key) {
723
+ const mate = getMoostMate();
724
+ return mate.read(type, key);
725
+ },
726
+ });
727
+ function getMoostValido() {
728
+ return valido;
729
+ }
730
+
731
+ const DEFAULT_ERROR_LIMIT = 10;
732
+ function firstString(errors) {
733
+ const keys = Object.keys(errors);
734
+ for (const key of keys) {
735
+ if (typeof errors[key] === 'string')
736
+ return errors[key];
737
+ return firstString(errors[key]);
738
+ }
739
+ return '';
740
+ }
741
+ const validatePipe = (opts) => {
742
+ const pipe = (_value, metas, level) => __awaiter(void 0, void 0, void 0, function* () {
743
+ const { restoreCtx } = useEventContext();
744
+ const valido = getMoostValido();
745
+ let meta = {};
746
+ if (level === 'PARAM') {
747
+ meta = metas.paramMeta || {};
748
+ }
749
+ else if (level === 'PROP') {
750
+ meta = metas.propMeta || {};
751
+ }
752
+ else if (level === 'METHOD') {
753
+ meta = metas.methodMeta || {};
754
+ }
755
+ else if (level === 'CLASS') {
756
+ meta = metas.classMeta || {};
757
+ }
758
+ const result = yield valido.validateParam(_value, meta, metas.key, undefined, metas.instance, undefined, 0, 0, (opts === null || opts === void 0 ? void 0 : opts.errorLimit) || DEFAULT_ERROR_LIMIT, restoreCtx);
759
+ if (result !== true) {
760
+ const message = typeof result === 'string' ? result : firstString(result);
761
+ if (opts === null || opts === void 0 ? void 0 : opts.errorCb) {
762
+ opts.errorCb(message, result);
763
+ }
764
+ else {
765
+ throw new Error('Validation error: ' + message);
766
+ }
767
+ }
768
+ return _value;
769
+ });
770
+ pipe.priority = TPipePriority.VALIDATE;
771
+ return pipe;
772
+ };
773
+
774
+ /**
775
+ * ### Define Interceptor Function
776
+ *
777
+ * ```ts
778
+ * defineInterceptorFn((before, after, onError) => {
779
+ * //init
780
+ * before(() => {
781
+ * // before handler
782
+ * })
783
+ * after((response, reply) => {
784
+ * // after handler
785
+ * })
786
+ * onError((error, reply) => {
787
+ * // when error occured
788
+ * })
789
+ * },
790
+ * TInterceptorPriority.INTERCEPTOR,
791
+ * )
792
+ * ```
793
+ *
794
+ * @param fn interceptor function
795
+ * @param priority priority of the interceptor where BEFORE_ALL = 0, BEFORE_GUARD = 1, GUARD = 2, AFTER_GUARD = 3, INTERCEPTOR = 4, CATCH_ERROR = 5, AFTER_ALL = 6
796
+ * @returns
797
+ */
798
+ function defineInterceptorFn(fn, priority = TInterceptorPriority.INTERCEPTOR) {
799
+ fn.priority = priority;
800
+ return fn;
801
+ }
802
+ /**
803
+ * ### Define Pipe Function
804
+ *
805
+ * ```ts
806
+ * // example of a transform pipe
807
+ * const uppercaseTransformPipe = definePipeFn((value, metas, level) => {
808
+ * return typeof value === 'string' ? value.toUpperCase() : value
809
+ * },
810
+ * TPipePriority.TRANSFORM,
811
+ * )
812
+ * ```
813
+ *
814
+ * @param fn interceptor function
815
+ * @param priority priority of the pipe where BEFORE_RESOLVE = 0, RESOLVE = 1, AFTER_RESOLVE = 2, BEFORE_TRANSFORM = 3, TRANSFORM = 4, AFTER_TRANSFORM = 5, BEFORE_VALIDATE = 6, VALIDATE = 7, AFTER_VALIDATE = 8
816
+ * @returns
817
+ */
818
+ function definePipeFn(fn, priority = TPipePriority.TRANSFORM) {
819
+ fn.priority = priority;
820
+ return fn;
821
+ }
822
+
823
+ const resolvePipe = definePipeFn((_value, metas, level) => {
513
824
  var _a, _b, _c, _d;
514
825
  let resolver;
515
826
  if (level === 'PARAM') {
@@ -528,8 +839,7 @@ const resolvePipe = (_value, metas, level) => {
528
839
  return resolver(metas, level);
529
840
  }
530
841
  return undefined;
531
- };
532
- resolvePipe.priority = TPipePriority.RESOLVE;
842
+ }, TPipePriority.RESOLVE);
533
843
 
534
844
  const sharedPipes = [
535
845
  {
@@ -538,24 +848,6 @@ const sharedPipes = [
538
848
  },
539
849
  ];
540
850
 
541
- const valido = new Valido({
542
- getDtoMeta(value, _type) {
543
- let type = _type;
544
- if (!type) {
545
- type = getConstructor(value);
546
- }
547
- const mate = getMoostMate();
548
- return mate.read(type);
549
- },
550
- getDtoParamMeta(value, type, key) {
551
- const mate = getMoostMate();
552
- return mate.read(type, key);
553
- },
554
- });
555
- function getMoostValido() {
556
- return valido;
557
- }
558
-
559
851
  function getDefaultLogger(topic) {
560
852
  return new ProstoLogger({
561
853
  level: 4,
@@ -567,6 +859,56 @@ function getDefaultLogger(topic) {
567
859
  }, topic);
568
860
  }
569
861
 
862
+ /**
863
+ * ## Moost
864
+ * Main moostjs class that serves as a shell for Moost Adapters
865
+ *
866
+ * ### Usage with HTTP Adapter
867
+ * ```ts
868
+ * │ // HTTP server example
869
+ * │ import { MoostHttp, Get } from '@moostjs/event-http'
870
+ * │ import { Moost, Param } from 'moost'
871
+ * │
872
+ * │ class MyServer extends Moost {
873
+ * │ @Get('test/:name')
874
+ * │ test(@Param('name') name: string) {
875
+ * │ return { message: `Hello ${name}!` }
876
+ * │ }
877
+ * │ }
878
+ * │
879
+ * │ const app = new MyServer()
880
+ * │ const http = new MoostHttp()
881
+ * │ app.adapter(http).listen(3000, () => {
882
+ * │ app.getLogger('MyApp').log('Up on port 3000')
883
+ * │ })
884
+ * │ app.init()
885
+ * ```
886
+ * ### Usage with CLI Adapter
887
+ * ```ts
888
+ * │ // CLI example
889
+ * │ import { MoostCli, Cli, CliOption, cliHelpInterceptor } from '@moostjs/event-cli'
890
+ * │ import { Moost, Param } from 'moost'
891
+ * │
892
+ * │ class MyApp extends Moost {
893
+ * │ @Cli('command/:arg')
894
+ * │ command(
895
+ * │ @Param('arg')
896
+ * │ arg: string,
897
+ * │ @CliOption('test', 't')
898
+ * │ test: boolean,
899
+ * │ ) {
900
+ * │ return `command run with flag arg=${ arg }, test=${ test }`
901
+ * │ }
902
+ * │ }
903
+ * │
904
+ * │ const app = new MyApp()
905
+ * │ app.applyGlobalInterceptors(cliHelpInterceptor())
906
+ * │
907
+ * │ const cli = new MoostCli()
908
+ * │ app.adapter(cli)
909
+ * │ app.init()
910
+ * ```
911
+ */
570
912
  class Moost {
571
913
  constructor(options) {
572
914
  this.options = options;
@@ -580,6 +922,17 @@ class Moost {
580
922
  const mate = getMoostMate();
581
923
  Object.assign(mate, { logger: this.getLogger('mate') });
582
924
  }
925
+ /**
926
+ * ### getLogger
927
+ * Provides application logger
928
+ * ```js
929
+ * // get logger with topic = "App"
930
+ * const logger = app.getLogger('App')
931
+ * logger.log('...')
932
+ * ```
933
+ * @param topic
934
+ * @returns
935
+ */
583
936
  getLogger(topic) {
584
937
  if (this.logger instanceof ProstoLogger) {
585
938
  return this.logger.createTopic(topic);
@@ -590,13 +943,20 @@ class Moost {
590
943
  this.adapters.push(a);
591
944
  return a;
592
945
  }
946
+ /**
947
+ * ### init
948
+ * Ititializes adapter. Must be called after adapters are attached.
949
+ */
593
950
  init() {
594
951
  return __awaiter(this, void 0, void 0, function* () {
595
952
  this.setProvideRegistry(createProvideRegistry([Moost, () => this]));
596
953
  for (const a of this.adapters) {
597
954
  const constructor = getConstructor(a);
598
955
  if (constructor) {
599
- this.setProvideRegistry(createProvideRegistry([constructor, () => a]));
956
+ this.setProvideRegistry(createProvideRegistry([
957
+ constructor,
958
+ () => a,
959
+ ]));
600
960
  }
601
961
  if (typeof a.getProvideRegistry === 'function') {
602
962
  this.setProvideRegistry(a.getProvideRegistry());
@@ -605,7 +965,7 @@ class Moost {
605
965
  this.unregisteredControllers.unshift(this);
606
966
  yield this.bindControllers();
607
967
  for (const a of this.adapters) {
608
- yield (a.onInit && a.onInit());
968
+ yield (a.onInit && a.onInit(this));
609
969
  }
610
970
  });
611
971
  }
@@ -633,7 +993,9 @@ class Moost {
633
993
  const pipes = [...this.pipes, ...((classMeta === null || classMeta === void 0 ? void 0 : classMeta.pipes) || [])].sort((a, b) => a.priority - b.priority);
634
994
  let instance;
635
995
  const infactOpts = { provide, customData: { pipes } };
636
- if (isControllerConsructor && ((classMeta === null || classMeta === void 0 ? void 0 : classMeta.injectable) === 'SINGLETON' || (classMeta === null || classMeta === void 0 ? void 0 : classMeta.injectable) === true)) {
996
+ if (isControllerConsructor &&
997
+ ((classMeta === null || classMeta === void 0 ? void 0 : classMeta.injectable) === 'SINGLETON' ||
998
+ (classMeta === null || classMeta === void 0 ? void 0 : classMeta.injectable) === true)) {
637
999
  instance = (yield infact.get(controller, infactOpts));
638
1000
  }
639
1001
  else if (!isControllerConsructor) {
@@ -641,16 +1003,20 @@ class Moost {
641
1003
  infact.setProvideRegByInstance(instance, provide);
642
1004
  }
643
1005
  // getInstance - instance factory for resolving SINGLETON and FOR_EVENT instance
644
- const getInstance = instance ? () => Promise.resolve(instance) : () => __awaiter(this, void 0, void 0, function* () {
645
- // if (!instance) {
646
- infact.silent(true);
647
- const { restoreCtx } = useEventContext();
648
- const instance = yield infact.get(controller, Object.assign(Object.assign({}, infactOpts), { syncContextFn: restoreCtx }));
649
- infact.silent(false);
650
- // }
651
- return instance;
652
- });
653
- const classConstructor = isConstructor(controller) ? controller : getConstructor(controller);
1006
+ const getInstance = instance
1007
+ ? () => Promise.resolve(instance)
1008
+ : () => __awaiter(this, void 0, void 0, function* () {
1009
+ // if (!instance) {
1010
+ infact.silent(true);
1011
+ const { restoreCtx } = useEventContext();
1012
+ const instance = (yield infact.get(controller, Object.assign(Object.assign({}, infactOpts), { syncContextFn: restoreCtx })));
1013
+ infact.silent(false);
1014
+ // }
1015
+ return instance;
1016
+ });
1017
+ const classConstructor = isConstructor(controller)
1018
+ ? controller
1019
+ : getConstructor(controller);
654
1020
  yield bindControllerMethods({
655
1021
  getInstance,
656
1022
  classConstructor,
@@ -663,7 +1029,9 @@ class Moost {
663
1029
  logger: this.logger,
664
1030
  });
665
1031
  if (classMeta && classMeta.importController) {
666
- const prefix = typeof replaceOwnPrefix === 'string' ? replaceOwnPrefix : (_a = classMeta === null || classMeta === void 0 ? void 0 : classMeta.controller) === null || _a === void 0 ? void 0 : _a.prefix;
1032
+ const prefix = typeof replaceOwnPrefix === 'string'
1033
+ ? replaceOwnPrefix
1034
+ : (_a = classMeta === null || classMeta === void 0 ? void 0 : classMeta.controller) === null || _a === void 0 ? void 0 : _a.prefix;
667
1035
  const mergedProvide = Object.assign(Object.assign({}, provide), ((classMeta === null || classMeta === void 0 ? void 0 : classMeta.provide) || {}));
668
1036
  for (const ic of classMeta.importController) {
669
1037
  if (ic.typeResolver) {
@@ -671,7 +1039,12 @@ class Moost {
671
1039
  const isFunc = typeof ic.typeResolver === 'function';
672
1040
  yield this.bindController(
673
1041
  // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
674
- isConstr ? ic.typeResolver : isFunc ? yield ic.typeResolver() : ic.typeResolver, ic.provide ? Object.assign(Object.assign({}, mergedProvide), ic.provide) : mergedProvide, `${globalPrefix}/${(prefix || '')}`, ic.prefix);
1042
+ isConstr
1043
+ ? ic.typeResolver
1044
+ : isFunc
1045
+ ? yield ic.typeResolver()
1046
+ : ic.typeResolver, ic.provide
1047
+ ? Object.assign(Object.assign({}, mergedProvide), ic.provide) : mergedProvide, `${globalPrefix}/${prefix || ''}`, ic.prefix);
675
1048
  }
676
1049
  }
677
1050
  }
@@ -682,7 +1055,9 @@ class Moost {
682
1055
  if (typeof item === 'function') {
683
1056
  this.pipes.push({
684
1057
  handler: item,
685
- priority: typeof item.priority === 'number' ? item.priority : TPipePriority.TRANSFORM,
1058
+ priority: typeof item.priority === 'number'
1059
+ ? item.priority
1060
+ : TPipePriority.TRANSFORM,
686
1061
  });
687
1062
  }
688
1063
  else {
@@ -692,14 +1067,35 @@ class Moost {
692
1067
  });
693
1068
  }
694
1069
  }
1070
+ this.globalInterceptorHandler = undefined;
695
1071
  return this;
696
1072
  }
1073
+ /**
1074
+ * Provides InterceptorHandler with global interceptors and pipes.
1075
+ * Used to process interceptors when event handler was not found.
1076
+ *
1077
+ * @returns array of interceptors
1078
+ */
1079
+ getGlobalInterceptorHandler() {
1080
+ if (!this.globalInterceptorHandler) {
1081
+ const mate = getMoostMate();
1082
+ const thisMeta = mate.read(this);
1083
+ const pipes = [...(this.pipes || []), ...((thisMeta === null || thisMeta === void 0 ? void 0 : thisMeta.pipes) || [])].sort((a, b) => a.priority - b.priority);
1084
+ const interceptors = [...this.interceptors, ...((thisMeta === null || thisMeta === void 0 ? void 0 : thisMeta.interceptors) || [])]
1085
+ .sort((a, b) => a.priority - b.priority);
1086
+ this.globalInterceptorHandler = getIterceptorHandlerFactory(interceptors, () => Promise.resolve(this), pipes, this.logger)();
1087
+ }
1088
+ return this.globalInterceptorHandler;
1089
+ }
697
1090
  applyGlobalInterceptors(...items) {
698
1091
  for (const item of items) {
699
1092
  if (typeof item === 'function') {
700
1093
  this.interceptors.push({
701
1094
  handler: item,
702
- priority: typeof item.priority === 'number' ? item.priority : TInterceptorPriority.INTERCEPTOR,
1095
+ priority: typeof item.priority === 'number'
1096
+ ? item
1097
+ .priority
1098
+ : TInterceptorPriority.INTERCEPTOR,
703
1099
  });
704
1100
  }
705
1101
  else {
@@ -709,6 +1105,7 @@ class Moost {
709
1105
  });
710
1106
  }
711
1107
  }
1108
+ this.globalInterceptorHandler = undefined;
712
1109
  return this;
713
1110
  }
714
1111
  /**
@@ -731,106 +1128,141 @@ class Moost {
731
1128
  }
732
1129
  }
733
1130
 
734
- const genericTypesCastPipe = (strict) => {
735
- const handler = (value, { paramMeta: meta }) => {
736
- if (meta === null || meta === void 0 ? void 0 : meta.type) {
737
- if ((value === undefined || value === null || (meta.type !== String && value === '')) && meta.optional) {
738
- return undefined;
739
- }
740
- switch (meta.type) {
741
- case Date: {
742
- let d;
743
- if (typeof value === 'string') {
744
- d = new Date(/^\d+$/.test(value) ? Number(value) : value);
745
- }
746
- else {
747
- d = new Date(value);
748
- }
749
- if (strict && Number.isNaN(d.getTime())) {
750
- typeError(value, 'Date', meta.label);
751
- }
752
- return Number.isNaN(d.getTime()) ? value : d;
753
- }
754
- case Boolean:
755
- if ([true, 'true', 'TRUE', 'True', 1, '1', 'X', 'x'].includes(value)) {
756
- return true;
757
- }
758
- if ([false, 'false', 'FALSE', 'False', 0, '0', '', ' ', null, undefined].includes(value)) {
759
- return false;
760
- }
761
- if (strict) {
762
- typeError(value, 'boolean', meta.label);
763
- }
764
- return value;
765
- case Number: {
766
- if (strict && !value && value !== 0) {
767
- typeError(value, 'numeric', meta.label);
768
- }
769
- const n = typeof value === 'string' && value.length > 0 ? Number(value) : NaN;
770
- if (strict && Number.isNaN(n)) {
771
- typeError(value, 'numeric', meta.label);
772
- }
773
- return Number.isNaN(n) ? value : n;
774
- }
775
- case String:
776
- if (strict && ['object', 'function'].includes(typeof value)) {
777
- typeError(value, 'string', meta.label);
778
- }
779
- return value && String(value) || value;
780
- default:
781
- return value;
782
- }
783
- }
1131
+ function setControllerContext(controller, method) {
1132
+ const { store } = useEventContext();
1133
+ const { set } = store('controller');
1134
+ set('instance', controller);
1135
+ set('method', method);
1136
+ }
1137
+ function useControllerContext() {
1138
+ const { store } = useEventContext();
1139
+ const { get } = store('controller');
1140
+ const getController = () => get('instance');
1141
+ const getMethod = () => get('method');
1142
+ // todo: add generic types to getControllerMeta
1143
+ const getControllerMeta = () => getMoostMate().read(getController());
1144
+ // todo: add generic types to getMethodMeta
1145
+ const getMethodMeta = () => getMoostMate().read(getController(), getMethod());
1146
+ return {
1147
+ getController,
1148
+ getMethod,
1149
+ getControllerMeta,
1150
+ getMethodMeta,
784
1151
  };
785
- handler.priority = TPipePriority.AFTER_TRANSFORM;
786
- return handler;
787
- };
788
- function typeError(value, targetType, label) {
789
- const prefix = label ? `Argument "${label}" with value ` : '';
790
- throw new Error(`${prefix}${JSON.stringify(value)} is not a ${targetType} type`);
791
1152
  }
792
1153
 
793
- const DEFAULT_ERROR_LIMIT = 10;
794
- function firstString(errors) {
795
- const keys = Object.keys(errors);
796
- for (const key of keys) {
797
- if (typeof errors[key] === 'string')
798
- return errors[key];
799
- return firstString(errors[key]);
800
- }
801
- return '';
1154
+ const infact = getMoostInfact();
1155
+ function registerEventScope(scopeId) {
1156
+ infact.registerScope(scopeId);
1157
+ return () => infact.unregisterScope(scopeId);
802
1158
  }
803
- const validatePipe = (opts) => {
804
- const pipe = (_value, metas, level) => __awaiter(void 0, void 0, void 0, function* () {
805
- const { restoreCtx } = useEventContext();
806
- const valido = getMoostValido();
807
- let meta = {};
808
- if (level === 'PARAM') {
809
- meta = metas.paramMeta || {};
1159
+ function defineMoostEventHandler(options) {
1160
+ return () => __awaiter(this, void 0, void 0, function* () {
1161
+ var _a;
1162
+ const { restoreCtx } = useEventContext(options.contextType);
1163
+ const scopeId = useEventId().getId();
1164
+ const logger = useEventLogger(options.loggerTitle);
1165
+ const unscope = registerEventScope(scopeId);
1166
+ let response;
1167
+ const hookOptions = {
1168
+ restoreCtx,
1169
+ scopeId,
1170
+ logger,
1171
+ unscope,
1172
+ method: options.controllerMethod,
1173
+ getResponse: () => response,
1174
+ reply: (r) => response = r,
1175
+ };
1176
+ if ((_a = options.hooks) === null || _a === void 0 ? void 0 : _a.init) {
1177
+ yield options.hooks.init(hookOptions);
1178
+ restoreCtx();
810
1179
  }
811
- else if (level === 'PROP') {
812
- meta = metas.propMeta || {};
1180
+ const instance = yield options.getControllerInstance();
1181
+ restoreCtx();
1182
+ if (instance) {
1183
+ setControllerContext(instance, options.controllerMethod || '');
813
1184
  }
814
- else if (level === 'METHOD') {
815
- meta = metas.methodMeta || {};
1185
+ const interceptorHandler = yield options.getIterceptorHandler();
1186
+ if (interceptorHandler) {
1187
+ restoreCtx();
1188
+ try {
1189
+ response = yield interceptorHandler.init();
1190
+ if (typeof response !== 'undefined')
1191
+ return endWithResponse();
1192
+ }
1193
+ catch (e) {
1194
+ options.logErrors && logger.error(e);
1195
+ response = e;
1196
+ return endWithResponse();
1197
+ }
816
1198
  }
817
- else if (level === 'CLASS') {
818
- meta = metas.classMeta || {};
1199
+ let args = [];
1200
+ if (options.resolveArgs) {
1201
+ // params
1202
+ restoreCtx();
1203
+ try {
1204
+ // logger.trace(`resolving method args for "${ opts.method as string }"`)
1205
+ args = yield options.resolveArgs();
1206
+ // logger.trace(`args for method "${ opts.method as string }" resolved (count ${String(args.length)})`)
1207
+ }
1208
+ catch (e) {
1209
+ options.logErrors && logger.error(e);
1210
+ response = e;
1211
+ return endWithResponse();
1212
+ }
819
1213
  }
820
- const result = yield valido.validateParam(_value, meta, metas.key, undefined, metas.instance, undefined, 0, 0, (opts === null || opts === void 0 ? void 0 : opts.errorLimit) || DEFAULT_ERROR_LIMIT, restoreCtx);
821
- if (result !== true) {
822
- const message = typeof result === 'string' ? result : firstString(result);
823
- if (opts === null || opts === void 0 ? void 0 : opts.errorCb) {
824
- opts.errorCb(message, result);
1214
+ if (interceptorHandler) {
1215
+ restoreCtx();
1216
+ response = yield interceptorHandler.fireBefore(response);
1217
+ if (typeof response !== 'undefined')
1218
+ return endWithResponse();
1219
+ }
1220
+ // fire request handler
1221
+ const callControllerMethod = () => {
1222
+ restoreCtx();
1223
+ if (options.callControllerMethod) {
1224
+ return options.callControllerMethod(args);
825
1225
  }
826
- else {
827
- throw new Error('Validation error: ' + message);
1226
+ else if (instance && options.controllerMethod && typeof instance[options.controllerMethod] === 'function') {
1227
+ return instance[options.controllerMethod](...args);
828
1228
  }
1229
+ };
1230
+ try {
1231
+ response = callControllerMethod();
829
1232
  }
830
- return _value;
1233
+ catch (e) {
1234
+ options.logErrors && logger.error(e);
1235
+ response = e;
1236
+ }
1237
+ function endWithResponse() {
1238
+ var _a;
1239
+ return __awaiter(this, void 0, void 0, function* () {
1240
+ // fire after interceptors
1241
+ if (interceptorHandler) {
1242
+ restoreCtx();
1243
+ try {
1244
+ // logger.trace('firing after interceptors')
1245
+ response = yield interceptorHandler.fireAfter(response);
1246
+ }
1247
+ catch (e) {
1248
+ options.logErrors && logger.error(e);
1249
+ if (!options.manualUnscope) {
1250
+ unscope();
1251
+ }
1252
+ throw e;
1253
+ }
1254
+ }
1255
+ if (!options.manualUnscope) {
1256
+ unscope();
1257
+ }
1258
+ if ((_a = options.hooks) === null || _a === void 0 ? void 0 : _a.end) {
1259
+ yield options.hooks.end(hookOptions);
1260
+ }
1261
+ return response;
1262
+ });
1263
+ }
1264
+ return yield endWithResponse();
831
1265
  });
832
- pipe.priority = TPipePriority.VALIDATE;
833
- return pipe;
834
- };
1266
+ }
835
1267
 
836
- export { Circular, Const, ConstFactory, Controller, Dto, Id, ImportController, Inherit, Inject, InjectEventLogger, Injectable, Intercept, InterceptorHandler, IsArray, IsBoolean, IsNumber, IsString, IsTypeOf, Label, Moost, Nullable, Optional, Param, Params, Provide, Required, Resolve, TInterceptorPriority, TPipePriority, Validate, genericTypesCastPipe, getMoostInfact, getMoostMate, getNewMoostInfact, resolvePipe, validatePipe };
1268
+ export { Circular, Const, ConstFactory, Controller, Description, Dto, Id, ImportController, Inherit, Inject, InjectEventLogger, Injectable, Intercept, InterceptorHandler, IsArray, IsBoolean, IsNumber, IsString, IsTypeOf, Label, Moost, Nullable, Optional, Param, Params, Provide, Required, Resolve, TInterceptorPriority, TPipePriority, Validate, Value, defineInterceptorFn, defineMoostEventHandler, definePipeFn, genericTypesCastPipe, getMoostInfact, getMoostMate, getNewMoostInfact, registerEventScope, resolvePipe, setControllerContext, useControllerContext, validatePipe };