moost 0.6.18 → 0.6.21
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.cjs +800 -598
- package/dist/index.d.ts +416 -271
- package/dist/index.mjs +795 -599
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -215,12 +215,27 @@ const scopeVarsMap = /* @__PURE__ */ new Map();
|
|
|
215
215
|
});
|
|
216
216
|
}
|
|
217
217
|
|
|
218
|
+
//#endregion
|
|
219
|
+
//#region packages/moost/src/composables/global-key.ts
|
|
220
|
+
var _g;
|
|
221
|
+
const registry = (_g = globalThis).__moost_keys ?? (_g.__moost_keys = /* @__PURE__ */ new Map());
|
|
222
|
+
/**
|
|
223
|
+
* Creates a typed event-context key that stays a single instance across duplicate
|
|
224
|
+
* moost loads (dual ESM/CJS, or duplicate installs). Use this for every
|
|
225
|
+
* moost-owned context key instead of `key()` directly.
|
|
226
|
+
*
|
|
227
|
+
* @param name - Debug label AND the registry lookup key; must be unique per slot.
|
|
228
|
+
*/ function globalKey(name) {
|
|
229
|
+
if (!registry.has(name)) registry.set(name, (0, _wooksjs_event_core.key)(name));
|
|
230
|
+
return registry.get(name);
|
|
231
|
+
}
|
|
232
|
+
|
|
218
233
|
//#endregion
|
|
219
234
|
//#region packages/moost/src/composables/controller.composable.ts
|
|
220
|
-
const controllerInstanceKey = (
|
|
221
|
-
const controllerMethodKey = (
|
|
222
|
-
const controllerRouteKey = (
|
|
223
|
-
const controllerPrefixKey = (
|
|
235
|
+
const controllerInstanceKey = globalKey("controller.instance");
|
|
236
|
+
const controllerMethodKey = globalKey("controller.method");
|
|
237
|
+
const controllerRouteKey = globalKey("controller.route");
|
|
238
|
+
const controllerPrefixKey = globalKey("controller.prefix");
|
|
224
239
|
/**
|
|
225
240
|
* Sets the controller context for the current event scope.
|
|
226
241
|
* Called internally by adapters when dispatching events to handlers.
|
|
@@ -262,10 +277,8 @@ const controllerPrefixKey = (0, _wooksjs_event_core.key)("controller.prefix");
|
|
|
262
277
|
|
|
263
278
|
//#endregion
|
|
264
279
|
//#region packages/moost/src/composables/interceptor.composable.ts
|
|
265
|
-
|
|
266
|
-
const
|
|
267
|
-
const overtakeKey = (_g = g).__moost_overtakeKey ?? (_g.__moost_overtakeKey = (0, _wooksjs_event_core.key)("interceptor.overtake"));
|
|
268
|
-
const interceptResultKey = (_g1 = g).__moost_interceptResultKey ?? (_g1.__moost_interceptResultKey = (0, _wooksjs_event_core.key)("interceptor.result"));
|
|
280
|
+
const overtakeKey = globalKey("interceptor.overtake");
|
|
281
|
+
const interceptResultKey = globalKey("interceptor.result");
|
|
269
282
|
/** Stores the overtake (reply) function in the current event context. */ function setOvertake(fn, ctx) {
|
|
270
283
|
(ctx || (0, _wooksjs_event_core.current)()).set(overtakeKey, fn);
|
|
271
284
|
}
|
|
@@ -467,432 +480,68 @@ function getParentProps(constructor) {
|
|
|
467
480
|
}
|
|
468
481
|
|
|
469
482
|
//#endregion
|
|
470
|
-
//#region packages/moost/src/
|
|
483
|
+
//#region packages/moost/src/decorators/circular.decorator.ts
|
|
471
484
|
/**
|
|
472
|
-
*
|
|
485
|
+
* Marks a constructor parameter dependency as circular.
|
|
486
|
+
* The resolver function is called lazily to break the circular reference.
|
|
473
487
|
*
|
|
474
|
-
*
|
|
475
|
-
*
|
|
476
|
-
*
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
const result = runPipes(pipes, void 0, {
|
|
485
|
-
classMeta: context.classMeta,
|
|
486
|
-
methodMeta: context.methodMeta,
|
|
487
|
-
paramMeta,
|
|
488
|
-
type: context.type,
|
|
489
|
-
key: context.key,
|
|
490
|
-
index: i,
|
|
491
|
-
targetMeta: paramMeta,
|
|
492
|
-
instantiate: (t) => useControllerContext().instantiate(t)
|
|
493
|
-
}, "PARAM");
|
|
494
|
-
if (!hasAsync && isThenable(result)) hasAsync = true;
|
|
495
|
-
args[i] = result;
|
|
496
|
-
}
|
|
497
|
-
return hasAsync ? Promise.all(args) : args;
|
|
498
|
-
};
|
|
488
|
+
* @param resolver - Factory that returns the class constructor (e.g. `() => MyService`).
|
|
489
|
+
*
|
|
490
|
+
* @example
|
|
491
|
+
* ```ts
|
|
492
|
+
* class ServiceA {
|
|
493
|
+
* constructor(@Circular(() => ServiceB) private b: ServiceB) {}
|
|
494
|
+
* }
|
|
495
|
+
* ```
|
|
496
|
+
*/ function Circular(resolver) {
|
|
497
|
+
return getMoostMate().decorate("circular", resolver);
|
|
499
498
|
}
|
|
500
499
|
|
|
501
500
|
//#endregion
|
|
502
|
-
//#region packages/moost/src/
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
else obj[key] = value;
|
|
511
|
-
return obj;
|
|
501
|
+
//#region packages/moost/src/decorators/common.decorator.ts
|
|
502
|
+
/**
|
|
503
|
+
* Apply Multiple Decorators
|
|
504
|
+
*
|
|
505
|
+
* @param decorators - array of decorators
|
|
506
|
+
* @returns
|
|
507
|
+
*/ function ApplyDecorators(...decorators) {
|
|
508
|
+
return getMoostMate().apply(...decorators);
|
|
512
509
|
}
|
|
513
510
|
/**
|
|
514
|
-
*
|
|
515
|
-
*
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
});
|
|
522
|
-
}
|
|
523
|
-
get count() {
|
|
524
|
-
return this.handlers.length;
|
|
525
|
-
}
|
|
526
|
-
get countAfter() {
|
|
527
|
-
return this.after?.length ?? 0;
|
|
528
|
-
}
|
|
529
|
-
get countOnError() {
|
|
530
|
-
return this.onError?.length ?? 0;
|
|
531
|
-
}
|
|
532
|
-
/**
|
|
533
|
-
* Register hooks from a TInterceptorDef.
|
|
534
|
-
* Returns a pending PromiseLike if `before` went async, or undefined.
|
|
535
|
-
*/ registerDef(def, entry, ci) {
|
|
536
|
-
if (def.after) (this.after ?? (this.after = [])).unshift({
|
|
537
|
-
name: entry.name,
|
|
538
|
-
fn: def.after
|
|
539
|
-
});
|
|
540
|
-
if (def.error) (this.onError ?? (this.onError = [])).unshift({
|
|
541
|
-
name: entry.name,
|
|
542
|
-
fn: def.error
|
|
543
|
-
});
|
|
544
|
-
if (def.before) {
|
|
545
|
-
const spanName = entry.spanName;
|
|
546
|
-
const result = ci ? ci.with(spanName, { "moost.interceptor.stage": "before" }, () => def.before?.(this.getReplyFn())) : def.before(this.getReplyFn());
|
|
547
|
-
if (isThenable(result)) return result;
|
|
548
|
-
}
|
|
549
|
-
}
|
|
550
|
-
before() {
|
|
551
|
-
const ci = (0, _wooksjs_event_core.getContextInjector)();
|
|
552
|
-
for (let i = 0; i < this.handlers.length; i++) {
|
|
553
|
-
const entry = this.handlers[i];
|
|
554
|
-
const { handler } = entry;
|
|
555
|
-
if (typeof handler === "function") {
|
|
556
|
-
const factoryResult = handler();
|
|
557
|
-
if (isThenable(factoryResult)) return this._beforeAsyncFactory(ci, factoryResult, i);
|
|
558
|
-
const pending = this.registerDef(factoryResult, entry, ci);
|
|
559
|
-
if (pending) return this._beforeAsyncPending(ci, pending, i);
|
|
560
|
-
if (this.responseOverwritten) return this.response;
|
|
561
|
-
} else {
|
|
562
|
-
const pending = this.registerDef(handler, entry, ci);
|
|
563
|
-
if (pending) return this._beforeAsyncPending(ci, pending, i);
|
|
564
|
-
if (this.responseOverwritten) return this.response;
|
|
565
|
-
}
|
|
566
|
-
}
|
|
567
|
-
}
|
|
568
|
-
async _beforeAsyncFactory(ci, factoryPromise, startIndex) {
|
|
569
|
-
const def = await factoryPromise;
|
|
570
|
-
const entry = this.handlers[startIndex];
|
|
571
|
-
const pending = this.registerDef(def, entry, ci);
|
|
572
|
-
if (pending) await pending;
|
|
573
|
-
if (this.responseOverwritten) return this.response;
|
|
574
|
-
return this._beforeFrom(ci, startIndex + 1);
|
|
575
|
-
}
|
|
576
|
-
async _beforeAsyncPending(ci, pending, startIndex) {
|
|
577
|
-
await pending;
|
|
578
|
-
if (this.responseOverwritten) return this.response;
|
|
579
|
-
return this._beforeFrom(ci, startIndex + 1);
|
|
580
|
-
}
|
|
581
|
-
async _beforeFrom(ci, startIndex) {
|
|
582
|
-
for (let i = startIndex; i < this.handlers.length; i++) {
|
|
583
|
-
const entry = this.handlers[i];
|
|
584
|
-
const { handler } = entry;
|
|
585
|
-
let def;
|
|
586
|
-
if (typeof handler === "function") def = await handler();
|
|
587
|
-
else def = handler;
|
|
588
|
-
const pending = this.registerDef(def, entry, ci);
|
|
589
|
-
if (pending) await pending;
|
|
590
|
-
if (this.responseOverwritten) return this.response;
|
|
591
|
-
}
|
|
592
|
-
}
|
|
593
|
-
fireAfter(response) {
|
|
594
|
-
this.response = response;
|
|
595
|
-
const isError = response instanceof Error;
|
|
596
|
-
const handlers = isError ? this.onError : this.after;
|
|
597
|
-
if (!handlers) return this.response;
|
|
598
|
-
const ci = (0, _wooksjs_event_core.getContextInjector)();
|
|
599
|
-
const stage = isError ? "onError" : "after";
|
|
600
|
-
for (let i = 0; i < handlers.length; i++) {
|
|
601
|
-
const { name, fn } = handlers[i];
|
|
602
|
-
const result = ci ? ci.with(`Interceptor:${name}`, { "moost.interceptor.stage": stage }, () => fn(response, this.getReplyFn())) : fn(response, this.getReplyFn());
|
|
603
|
-
if (isThenable(result)) return this._fireAfterAsync({
|
|
604
|
-
ci,
|
|
605
|
-
handlers,
|
|
606
|
-
stage,
|
|
607
|
-
response
|
|
608
|
-
}, result, i);
|
|
609
|
-
}
|
|
610
|
-
return this.response;
|
|
611
|
-
}
|
|
612
|
-
async _fireAfterAsync(ctx, pending, startIndex) {
|
|
613
|
-
await pending;
|
|
614
|
-
for (let i = startIndex + 1; i < ctx.handlers.length; i++) {
|
|
615
|
-
const { name, fn } = ctx.handlers[i];
|
|
616
|
-
if (ctx.ci) await ctx.ci.with(`Interceptor:${name}`, { "moost.interceptor.stage": ctx.stage }, () => fn(ctx.response, this.getReplyFn()));
|
|
617
|
-
else await fn(ctx.response, this.getReplyFn());
|
|
618
|
-
}
|
|
619
|
-
return this.response;
|
|
620
|
-
}
|
|
621
|
-
constructor(handlers) {
|
|
622
|
-
_define_property$1(this, "handlers", void 0);
|
|
623
|
-
_define_property$1(this, "after", void 0);
|
|
624
|
-
_define_property$1(this, "onError", void 0);
|
|
625
|
-
_define_property$1(this, "response", void 0);
|
|
626
|
-
_define_property$1(this, "responseOverwritten", void 0);
|
|
627
|
-
_define_property$1(this, "_boundReplyFn", void 0);
|
|
628
|
-
this.handlers = handlers;
|
|
629
|
-
this.responseOverwritten = false;
|
|
630
|
-
}
|
|
631
|
-
};
|
|
632
|
-
|
|
633
|
-
//#endregion
|
|
634
|
-
//#region packages/moost/src/utils.ts
|
|
635
|
-
const mate = getMoostMate();
|
|
636
|
-
const noInterceptors = () => void 0;
|
|
637
|
-
function findInterceptorMethods(handler) {
|
|
638
|
-
const fakeInstance = Object.create(handler.prototype);
|
|
639
|
-
const methods = getInstanceOwnMethods(fakeInstance);
|
|
640
|
-
const result = {};
|
|
641
|
-
for (const method of methods) {
|
|
642
|
-
const hook = mate.read(fakeInstance, method)?.interceptorHook;
|
|
643
|
-
if (hook === "before" || hook === "after" || hook === "error") result[hook] = method;
|
|
644
|
-
}
|
|
645
|
-
return result;
|
|
511
|
+
* ## Label
|
|
512
|
+
* ### @Decorator
|
|
513
|
+
* _Common purpose decorator that may be used by various adapters for various purposes_
|
|
514
|
+
*
|
|
515
|
+
* Stores Label metadata
|
|
516
|
+
*/ function Label(value) {
|
|
517
|
+
return getMoostMate().decorate("label", value);
|
|
646
518
|
}
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
if (argsPipes.length === 0) return;
|
|
656
|
-
return resolveArguments(argsPipes, {
|
|
657
|
-
classMeta: opts.classMeta,
|
|
658
|
-
methodMeta,
|
|
659
|
-
type: opts.handler,
|
|
660
|
-
key: opts.methodName
|
|
661
|
-
});
|
|
519
|
+
/**
|
|
520
|
+
* ## Description
|
|
521
|
+
* ### @Decorator
|
|
522
|
+
* _Common purpose decorator that may be used by various adapters for various purposes_
|
|
523
|
+
*
|
|
524
|
+
* Stores Description metadata
|
|
525
|
+
*/ function Description(value) {
|
|
526
|
+
return getMoostMate().decorate("description", value);
|
|
662
527
|
}
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
528
|
+
/**
|
|
529
|
+
* ## Value
|
|
530
|
+
* ### @Decorator
|
|
531
|
+
* _Common purpose decorator that may be used by various adapters for various purposes_
|
|
532
|
+
*
|
|
533
|
+
* Stores Value metadata
|
|
534
|
+
*/ function Value(value) {
|
|
535
|
+
return getMoostMate().decorate("value", value);
|
|
668
536
|
}
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
if (opts.methods.before) {
|
|
678
|
-
const methodName = opts.methods.before;
|
|
679
|
-
const resolveArgs = opts.resolvers[methodName];
|
|
680
|
-
if (resolveArgs) def.before = (reply) => {
|
|
681
|
-
setOvertake(reply);
|
|
682
|
-
return callWithArgs(instance, methodName, resolveArgs);
|
|
683
|
-
};
|
|
684
|
-
else def.before = () => instance[methodName]();
|
|
685
|
-
}
|
|
686
|
-
if (opts.methods.after) {
|
|
687
|
-
const methodName = opts.methods.after;
|
|
688
|
-
const resolveArgs = opts.resolvers[methodName];
|
|
689
|
-
def.after = (response, reply) => {
|
|
690
|
-
setOvertake(reply);
|
|
691
|
-
setInterceptResult(response);
|
|
692
|
-
return callMethod(instance, methodName, resolveArgs);
|
|
693
|
-
};
|
|
694
|
-
}
|
|
695
|
-
if (opts.methods.error) {
|
|
696
|
-
const methodName = opts.methods.error;
|
|
697
|
-
const resolveArgs = opts.resolvers[methodName];
|
|
698
|
-
def.error = (error, reply) => {
|
|
699
|
-
setOvertake(reply);
|
|
700
|
-
setInterceptResult(error);
|
|
701
|
-
return callMethod(instance, methodName, resolveArgs);
|
|
702
|
-
};
|
|
703
|
-
}
|
|
704
|
-
return def;
|
|
705
|
-
}
|
|
706
|
-
function fromTarget(targetInstance) {
|
|
707
|
-
const result = infact.getForInstance(targetInstance, opts.handler, { customData: { pipes: opts.pipes } });
|
|
708
|
-
if (isThenable(result)) return result.then(buildDef);
|
|
709
|
-
return buildDef(result);
|
|
710
|
-
}
|
|
711
|
-
return () => {
|
|
712
|
-
const result = opts.getTargetInstance();
|
|
713
|
-
if (isThenable(result)) return result.then(fromTarget);
|
|
714
|
-
return fromTarget(result);
|
|
715
|
-
};
|
|
716
|
-
}
|
|
717
|
-
function getIterceptorHandlerFactory(interceptors, getTargetInstance, pipes) {
|
|
718
|
-
if (interceptors.length === 0) return noInterceptors;
|
|
719
|
-
const precomputedHandlers = interceptors.map(({ handler, name }) => {
|
|
720
|
-
const spanName = `Interceptor:${name}`;
|
|
721
|
-
if (typeof handler !== "function") return {
|
|
722
|
-
handler,
|
|
723
|
-
name,
|
|
724
|
-
spanName
|
|
725
|
-
};
|
|
726
|
-
const interceptorMeta = mate.read(handler);
|
|
727
|
-
if (!interceptorMeta?.interceptor) throw new Error(`Invalid interceptor "${name}": must be TInterceptorDef or @Interceptor class`);
|
|
728
|
-
const classMeta = interceptorMeta;
|
|
729
|
-
const methods = findInterceptorMethods(handler);
|
|
730
|
-
const mergedPipes = mergeSorted(pipes, classMeta.pipes);
|
|
731
|
-
const resolvers = {};
|
|
732
|
-
for (const hook of [
|
|
733
|
-
"before",
|
|
734
|
-
"after",
|
|
735
|
-
"error"
|
|
736
|
-
]) {
|
|
737
|
-
const methodName = methods[hook];
|
|
738
|
-
if (methodName) resolvers[methodName] = buildMethodResolver({
|
|
739
|
-
handler,
|
|
740
|
-
methodName,
|
|
741
|
-
pipes: mergedPipes,
|
|
742
|
-
classMeta
|
|
743
|
-
});
|
|
744
|
-
}
|
|
745
|
-
return {
|
|
746
|
-
handler: createClassInterceptorFactory({
|
|
747
|
-
handler,
|
|
748
|
-
methods,
|
|
749
|
-
resolvers,
|
|
750
|
-
getTargetInstance,
|
|
751
|
-
pipes: mergedPipes
|
|
752
|
-
}),
|
|
753
|
-
name,
|
|
754
|
-
spanName
|
|
755
|
-
};
|
|
756
|
-
});
|
|
757
|
-
return () => new InterceptorHandler(precomputedHandlers);
|
|
758
|
-
}
|
|
759
|
-
|
|
760
|
-
//#endregion
|
|
761
|
-
//#region packages/moost/src/binding/bind-controller.ts
|
|
762
|
-
async function bindControllerMethods(options) {
|
|
763
|
-
const opts = options || {};
|
|
764
|
-
const { getInstance } = opts;
|
|
765
|
-
const { classConstructor } = opts;
|
|
766
|
-
const { adapters } = opts;
|
|
767
|
-
opts.globalPrefix = opts.globalPrefix || "";
|
|
768
|
-
opts.provide = opts.provide || {};
|
|
769
|
-
const fakeInstance = Object.create(classConstructor.prototype);
|
|
770
|
-
const methods = getInstanceOwnMethods(fakeInstance);
|
|
771
|
-
const meta = getMoostMate().read(classConstructor) || {};
|
|
772
|
-
const ownPrefix = typeof opts.replaceOwnPrefix === "string" ? opts.replaceOwnPrefix : meta.controller?.prefix || "";
|
|
773
|
-
const prefix = `${opts.globalPrefix}/${ownPrefix}`;
|
|
774
|
-
const controllerOverview = {
|
|
775
|
-
meta,
|
|
776
|
-
computedPrefix: prefix,
|
|
777
|
-
type: classConstructor,
|
|
778
|
-
handlers: []
|
|
779
|
-
};
|
|
780
|
-
for (const method of methods) {
|
|
781
|
-
const methodMeta = getMoostMate().read(fakeInstance, method) || {};
|
|
782
|
-
if (!methodMeta.handlers?.length) continue;
|
|
783
|
-
const pipes = mergeSorted(opts.pipes, methodMeta.pipes);
|
|
784
|
-
const getIterceptorHandler = getIterceptorHandlerFactory(mergeSorted(opts.interceptors, meta.interceptors, methodMeta.interceptors), getInstance, pipes);
|
|
785
|
-
const argsPipes = [];
|
|
786
|
-
for (const p of methodMeta.params || []) argsPipes.push({
|
|
787
|
-
meta: p,
|
|
788
|
-
pipes: mergeSorted(pipes, p.pipes)
|
|
789
|
-
});
|
|
790
|
-
const resolveArgs = resolveArguments(argsPipes, {
|
|
791
|
-
classMeta: meta,
|
|
792
|
-
methodMeta,
|
|
793
|
-
type: classConstructor,
|
|
794
|
-
key: method
|
|
795
|
-
});
|
|
796
|
-
const wm = /* @__PURE__ */ new WeakMap();
|
|
797
|
-
controllerOverview.handlers.push(...methodMeta.handlers.map((h) => {
|
|
798
|
-
const data = {
|
|
799
|
-
meta: methodMeta,
|
|
800
|
-
path: h.path,
|
|
801
|
-
type: h.type,
|
|
802
|
-
method,
|
|
803
|
-
handler: h,
|
|
804
|
-
registeredAs: []
|
|
805
|
-
};
|
|
806
|
-
wm.set(h, data);
|
|
807
|
-
return data;
|
|
808
|
-
}));
|
|
809
|
-
for (const adapter of adapters) await adapter.bindHandler({
|
|
810
|
-
prefix,
|
|
811
|
-
fakeInstance,
|
|
812
|
-
getInstance,
|
|
813
|
-
method,
|
|
814
|
-
handlers: methodMeta.handlers,
|
|
815
|
-
getIterceptorHandler,
|
|
816
|
-
resolveArgs,
|
|
817
|
-
controllerName: classConstructor.name,
|
|
818
|
-
logHandler: (eventName) => {
|
|
819
|
-
options.moostInstance.logMappedHandler(eventName, classConstructor, method);
|
|
820
|
-
},
|
|
821
|
-
register(h, path, args) {
|
|
822
|
-
const data = wm.get(h);
|
|
823
|
-
if (data) data.registeredAs.push({
|
|
824
|
-
path,
|
|
825
|
-
args
|
|
826
|
-
});
|
|
827
|
-
}
|
|
828
|
-
});
|
|
829
|
-
}
|
|
830
|
-
return controllerOverview;
|
|
831
|
-
}
|
|
832
|
-
|
|
833
|
-
//#endregion
|
|
834
|
-
//#region packages/moost/src/decorators/circular.decorator.ts
|
|
835
|
-
/**
|
|
836
|
-
* Marks a constructor parameter dependency as circular.
|
|
837
|
-
* The resolver function is called lazily to break the circular reference.
|
|
838
|
-
*
|
|
839
|
-
* @param resolver - Factory that returns the class constructor (e.g. `() => MyService`).
|
|
840
|
-
*
|
|
841
|
-
* @example
|
|
842
|
-
* ```ts
|
|
843
|
-
* class ServiceA {
|
|
844
|
-
* constructor(@Circular(() => ServiceB) private b: ServiceB) {}
|
|
845
|
-
* }
|
|
846
|
-
* ```
|
|
847
|
-
*/ function Circular(resolver) {
|
|
848
|
-
return getMoostMate().decorate("circular", resolver);
|
|
849
|
-
}
|
|
850
|
-
|
|
851
|
-
//#endregion
|
|
852
|
-
//#region packages/moost/src/decorators/common.decorator.ts
|
|
853
|
-
/**
|
|
854
|
-
* Apply Multiple Decorators
|
|
855
|
-
*
|
|
856
|
-
* @param decorators - array of decorators
|
|
857
|
-
* @returns
|
|
858
|
-
*/ function ApplyDecorators(...decorators) {
|
|
859
|
-
return getMoostMate().apply(...decorators);
|
|
860
|
-
}
|
|
861
|
-
/**
|
|
862
|
-
* ## Label
|
|
863
|
-
* ### @Decorator
|
|
864
|
-
* _Common purpose decorator that may be used by various adapters for various purposes_
|
|
865
|
-
*
|
|
866
|
-
* Stores Label metadata
|
|
867
|
-
*/ function Label(value) {
|
|
868
|
-
return getMoostMate().decorate("label", value);
|
|
869
|
-
}
|
|
870
|
-
/**
|
|
871
|
-
* ## Description
|
|
872
|
-
* ### @Decorator
|
|
873
|
-
* _Common purpose decorator that may be used by various adapters for various purposes_
|
|
874
|
-
*
|
|
875
|
-
* Stores Description metadata
|
|
876
|
-
*/ function Description(value) {
|
|
877
|
-
return getMoostMate().decorate("description", value);
|
|
878
|
-
}
|
|
879
|
-
/**
|
|
880
|
-
* ## Value
|
|
881
|
-
* ### @Decorator
|
|
882
|
-
* _Common purpose decorator that may be used by various adapters for various purposes_
|
|
883
|
-
*
|
|
884
|
-
* Stores Value metadata
|
|
885
|
-
*/ function Value(value) {
|
|
886
|
-
return getMoostMate().decorate("value", value);
|
|
887
|
-
}
|
|
888
|
-
/**
|
|
889
|
-
* ## Id
|
|
890
|
-
* ### @Decorator
|
|
891
|
-
* _Common purpose decorator that may be used by various adapters for various purposes_
|
|
892
|
-
*
|
|
893
|
-
* Stores Id metadata
|
|
894
|
-
*/ function Id(value) {
|
|
895
|
-
return getMoostMate().decorate("id", value);
|
|
537
|
+
/**
|
|
538
|
+
* ## Id
|
|
539
|
+
* ### @Decorator
|
|
540
|
+
* _Common purpose decorator that may be used by various adapters for various purposes_
|
|
541
|
+
*
|
|
542
|
+
* Stores Id metadata
|
|
543
|
+
*/ function Id(value) {
|
|
544
|
+
return getMoostMate().decorate("id", value);
|
|
896
545
|
}
|
|
897
546
|
/**
|
|
898
547
|
* ## Optional
|
|
@@ -958,69 +607,24 @@ function ImportController(prefix, controller, provide) {
|
|
|
958
607
|
}
|
|
959
608
|
|
|
960
609
|
//#endregion
|
|
961
|
-
//#region packages/moost/src/decorators/
|
|
610
|
+
//#region packages/moost/src/decorators/resolve.decorator.ts
|
|
962
611
|
/**
|
|
963
|
-
*
|
|
964
|
-
*
|
|
965
|
-
*
|
|
966
|
-
* @
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
return TInterceptorPriority;
|
|
980
|
-
}({});
|
|
981
|
-
/**
|
|
982
|
-
* ## Intercept
|
|
983
|
-
* ### @Decorator
|
|
984
|
-
* Attach an interceptor to a class or method.
|
|
985
|
-
* @param handler — @Interceptor class constructor or TInterceptorDef object
|
|
986
|
-
* @param priority — interceptor priority (overrides handler's own priority)
|
|
987
|
-
* @param name — interceptor name for tracing
|
|
988
|
-
*/ function Intercept(handler, priority, name) {
|
|
989
|
-
const mate = getMoostMate();
|
|
990
|
-
if (typeof handler === "function") {
|
|
991
|
-
const interceptorMeta = mate.read(handler);
|
|
992
|
-
return mate.decorate("interceptors", {
|
|
993
|
-
handler,
|
|
994
|
-
priority: priority ?? interceptorMeta?.interceptor?.priority ?? 4,
|
|
995
|
-
name: name || handler.name || "<anonymous>"
|
|
996
|
-
}, true);
|
|
997
|
-
}
|
|
998
|
-
return mate.decorate("interceptors", {
|
|
999
|
-
handler,
|
|
1000
|
-
priority: priority ?? handler.priority ?? 4,
|
|
1001
|
-
name: name || handler._name || "<anonymous>"
|
|
1002
|
-
}, true);
|
|
1003
|
-
}
|
|
1004
|
-
|
|
1005
|
-
//#endregion
|
|
1006
|
-
//#region packages/moost/src/decorators/resolve.decorator.ts
|
|
1007
|
-
/**
|
|
1008
|
-
* Hook to the Response Status
|
|
1009
|
-
* @decorator
|
|
1010
|
-
* @param resolver - resolver function
|
|
1011
|
-
* @param label - field label
|
|
1012
|
-
* @paramType unknown
|
|
1013
|
-
*/ function Resolve(resolver, label) {
|
|
1014
|
-
return (target, key, index) => {
|
|
1015
|
-
const i = typeof index === "number" ? index : void 0;
|
|
1016
|
-
getMoostMate().decorate("resolver", (metas, level) => {
|
|
1017
|
-
let newLabel = label;
|
|
1018
|
-
if (!newLabel && level === "PROP" && typeof metas.key === "string") newLabel = metas.key;
|
|
1019
|
-
fillLabel(target, key || "", i, newLabel);
|
|
1020
|
-
return resolver(metas, level);
|
|
1021
|
-
})(target, key, i);
|
|
1022
|
-
};
|
|
1023
|
-
}
|
|
612
|
+
* Hook to the Response Status
|
|
613
|
+
* @decorator
|
|
614
|
+
* @param resolver - resolver function
|
|
615
|
+
* @param label - field label
|
|
616
|
+
* @paramType unknown
|
|
617
|
+
*/ function Resolve(resolver, label) {
|
|
618
|
+
return (target, key, index) => {
|
|
619
|
+
const i = typeof index === "number" ? index : void 0;
|
|
620
|
+
getMoostMate().decorate("resolver", (metas, level) => {
|
|
621
|
+
let newLabel = label;
|
|
622
|
+
if (!newLabel && level === "PROP" && typeof metas.key === "string") newLabel = metas.key;
|
|
623
|
+
fillLabel(target, key || "", i, newLabel);
|
|
624
|
+
return resolver(metas, level);
|
|
625
|
+
})(target, key, i);
|
|
626
|
+
};
|
|
627
|
+
}
|
|
1024
628
|
/**
|
|
1025
629
|
* Get Param Value from url parh
|
|
1026
630
|
* @decorator
|
|
@@ -1063,6 +667,122 @@ function fillLabel(target, key, index, name) {
|
|
|
1063
667
|
}
|
|
1064
668
|
}
|
|
1065
669
|
|
|
670
|
+
//#endregion
|
|
671
|
+
//#region packages/moost/src/decorators/handler-paths.decorator.ts
|
|
672
|
+
/**
|
|
673
|
+
* ## HandlerPaths
|
|
674
|
+
* ### @Decorator
|
|
675
|
+
* Injects the actual mounted path(s) (`string[]`) of a handler method on the
|
|
676
|
+
* current controller, read from the post-bind overview. Built for `@MoostInit`
|
|
677
|
+
* method parameters:
|
|
678
|
+
*
|
|
679
|
+
* ```ts
|
|
680
|
+
* @MoostInit()
|
|
681
|
+
* init(@HandlerPaths('refresh') paths: string[]) {
|
|
682
|
+
* const path = paths[0] // e.g. '/api/auth/refresh'
|
|
683
|
+
* }
|
|
684
|
+
* ```
|
|
685
|
+
*
|
|
686
|
+
* `method` defaults to the current context method (useful inside event handlers);
|
|
687
|
+
* pass it explicitly in `@MoostInit`, where the current method is the init method,
|
|
688
|
+
* not the handler. Returns all distinct paths — see {@link useHandlerPaths}.
|
|
689
|
+
*/ function HandlerPaths(method, opts) {
|
|
690
|
+
return Resolve(() => useHandlerPaths(method, opts));
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
//#endregion
|
|
694
|
+
//#region packages/moost/src/decorators/inherit.decorator.ts
|
|
695
|
+
/**
|
|
696
|
+
* ## Inherit
|
|
697
|
+
* ### @Decorator
|
|
698
|
+
* Inherit metadata from super class
|
|
699
|
+
* @returns
|
|
700
|
+
*/ const Inherit = () => getMoostMate().decorate("inherit", true);
|
|
701
|
+
|
|
702
|
+
//#endregion
|
|
703
|
+
//#region packages/moost/src/decorators/init.decorator.ts
|
|
704
|
+
/**
|
|
705
|
+
* ## MoostInit
|
|
706
|
+
* ### @Decorator
|
|
707
|
+
* Marks a controller method to run **once**, after Moost has bound every
|
|
708
|
+
* controller (so the full `getControllersOverview()` is available) and
|
|
709
|
+
* **before** adapters begin serving.
|
|
710
|
+
*
|
|
711
|
+
* The method runs on the controller's SINGLETON instance inside a synthetic
|
|
712
|
+
* init context. Argument injection goes through the RESOLVE pipe only —
|
|
713
|
+
* constructor injection, `@InjectMoost`, `@Inject`, and other `@Resolve`-based
|
|
714
|
+
* params work; transform/validate pipes and interceptors are **not** applied
|
|
715
|
+
* (init is application setup, not an event). Request-scoped composables
|
|
716
|
+
* (`useRequest`, `useHeaders`, `useRouteParams`, …) are unavailable. Async
|
|
717
|
+
* methods are awaited; a throwing hook rejects `Moost.init()` (fail-fast).
|
|
718
|
+
*
|
|
719
|
+
* Applying `@MoostInit` to a `FOR_EVENT` controller is a configuration error and
|
|
720
|
+
* throws at bind time (there is no singleton instance / event at init).
|
|
721
|
+
*
|
|
722
|
+
* @param opts.priority lower runs first across all `@MoostInit` methods (default 0)
|
|
723
|
+
*
|
|
724
|
+
* @example
|
|
725
|
+
* ```ts
|
|
726
|
+
* │ @Controller('auth')
|
|
727
|
+
* │ export class AuthController {
|
|
728
|
+
* │ constructor(private readonly holder: RefreshPathHolder) {}
|
|
729
|
+
* │
|
|
730
|
+
* │ @MoostInit()
|
|
731
|
+
* │ initRefreshCookiePath(@InjectMoost() moost: Moost) {
|
|
732
|
+
* │ // overview is COMPLETE here — this controller's route is mounted
|
|
733
|
+
* │ this.holder.value = resolveRefreshCookiePath(moost)
|
|
734
|
+
* │ }
|
|
735
|
+
* │ }
|
|
736
|
+
* ```
|
|
737
|
+
*/ function MoostInit(opts) {
|
|
738
|
+
return getMoostMate().decorate("moostInit", { priority: opts?.priority ?? 0 }, false);
|
|
739
|
+
}
|
|
740
|
+
/**
|
|
741
|
+
* ## InjectMoost
|
|
742
|
+
* ### @Decorator
|
|
743
|
+
* Injects the running {@link Moost} application instance into a method (or
|
|
744
|
+
* constructor) parameter. Intended for `@MoostInit` methods that need the wired
|
|
745
|
+
* app (e.g. `getControllersOverview()`), but works in any DI-resolved position.
|
|
746
|
+
*/ function InjectMoost() {
|
|
747
|
+
return Resolve(() => resolveMoost());
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
//#endregion
|
|
751
|
+
//#region packages/moost/src/decorators/intercept.decorator.ts
|
|
752
|
+
var TInterceptorPriority = /* @__PURE__ */ function(TInterceptorPriority) {
|
|
753
|
+
TInterceptorPriority[TInterceptorPriority["BEFORE_ALL"] = 0] = "BEFORE_ALL";
|
|
754
|
+
TInterceptorPriority[TInterceptorPriority["BEFORE_GUARD"] = 1] = "BEFORE_GUARD";
|
|
755
|
+
TInterceptorPriority[TInterceptorPriority["GUARD"] = 2] = "GUARD";
|
|
756
|
+
TInterceptorPriority[TInterceptorPriority["AFTER_GUARD"] = 3] = "AFTER_GUARD";
|
|
757
|
+
TInterceptorPriority[TInterceptorPriority["INTERCEPTOR"] = 4] = "INTERCEPTOR";
|
|
758
|
+
TInterceptorPriority[TInterceptorPriority["CATCH_ERROR"] = 5] = "CATCH_ERROR";
|
|
759
|
+
TInterceptorPriority[TInterceptorPriority["AFTER_ALL"] = 6] = "AFTER_ALL";
|
|
760
|
+
return TInterceptorPriority;
|
|
761
|
+
}({});
|
|
762
|
+
/**
|
|
763
|
+
* ## Intercept
|
|
764
|
+
* ### @Decorator
|
|
765
|
+
* Attach an interceptor to a class or method.
|
|
766
|
+
* @param handler — @Interceptor class constructor or TInterceptorDef object
|
|
767
|
+
* @param priority — interceptor priority (overrides handler's own priority)
|
|
768
|
+
* @param name — interceptor name for tracing
|
|
769
|
+
*/ function Intercept(handler, priority, name) {
|
|
770
|
+
const mate = getMoostMate();
|
|
771
|
+
if (typeof handler === "function") {
|
|
772
|
+
const interceptorMeta = mate.read(handler);
|
|
773
|
+
return mate.decorate("interceptors", {
|
|
774
|
+
handler,
|
|
775
|
+
priority: priority ?? interceptorMeta?.interceptor?.priority ?? 4,
|
|
776
|
+
name: name || handler.name || "<anonymous>"
|
|
777
|
+
}, true);
|
|
778
|
+
}
|
|
779
|
+
return mate.decorate("interceptors", {
|
|
780
|
+
handler,
|
|
781
|
+
priority: priority ?? handler.priority ?? 4,
|
|
782
|
+
name: name || handler._name || "<anonymous>"
|
|
783
|
+
}, true);
|
|
784
|
+
}
|
|
785
|
+
|
|
1066
786
|
//#endregion
|
|
1067
787
|
//#region packages/moost/src/decorators/interceptor.decorator.ts
|
|
1068
788
|
/**
|
|
@@ -1145,9 +865,7 @@ function fillLabel(target, key, index, name) {
|
|
|
1145
865
|
* @returns
|
|
1146
866
|
*/ function InjectMoostLogger(topic) {
|
|
1147
867
|
return Resolve(async (metas) => {
|
|
1148
|
-
const
|
|
1149
|
-
const controller = getController();
|
|
1150
|
-
const moostApp = controller instanceof Moost ? controller : await instantiate(Moost);
|
|
868
|
+
const moostApp = await resolveMoost();
|
|
1151
869
|
const meta = metas.classMeta;
|
|
1152
870
|
return moostApp.getLogger(meta?.loggerTopic || topic || meta?.id);
|
|
1153
871
|
});
|
|
@@ -1237,119 +955,502 @@ function fillLabel(target, key, index, name) {
|
|
|
1237
955
|
*/ function InjectFromScope(name) {
|
|
1238
956
|
return getMoostMate().decorate("fromScope", name);
|
|
1239
957
|
}
|
|
1240
|
-
/**
|
|
1241
|
-
* Inject vars from scope for instances
|
|
1242
|
-
* instantiated with `@InjectFromScope` decorator
|
|
1243
|
-
*/ function InjectScopeVars(name) {
|
|
1244
|
-
return Resolve(({ scopeId }) => {
|
|
1245
|
-
if (scopeId) return name ? getInfactScopeVars(scopeId)?.[name] : getInfactScopeVars(scopeId);
|
|
958
|
+
/**
|
|
959
|
+
* Inject vars from scope for instances
|
|
960
|
+
* instantiated with `@InjectFromScope` decorator
|
|
961
|
+
*/ function InjectScopeVars(name) {
|
|
962
|
+
return Resolve(({ scopeId }) => {
|
|
963
|
+
if (scopeId) return name ? getInfactScopeVars(scopeId)?.[name] : getInfactScopeVars(scopeId);
|
|
964
|
+
});
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
//#endregion
|
|
968
|
+
//#region packages/moost/src/define.ts
|
|
969
|
+
/**
|
|
970
|
+
* Define a before-phase interceptor.
|
|
971
|
+
*
|
|
972
|
+
* Runs before argument resolution and handler execution.
|
|
973
|
+
* Call `reply(value)` to short-circuit the handler and respond early.
|
|
974
|
+
*
|
|
975
|
+
* @example
|
|
976
|
+
* ```ts
|
|
977
|
+
* const authGuard = defineBeforeInterceptor((reply) => {
|
|
978
|
+
* if (!isAuthenticated()) reply(new HttpError(401))
|
|
979
|
+
* }, TInterceptorPriority.GUARD)
|
|
980
|
+
* ```
|
|
981
|
+
*/ function defineBeforeInterceptor(fn, priority = TInterceptorPriority.INTERCEPTOR) {
|
|
982
|
+
return {
|
|
983
|
+
before: fn,
|
|
984
|
+
priority
|
|
985
|
+
};
|
|
986
|
+
}
|
|
987
|
+
/**
|
|
988
|
+
* Define an after-phase interceptor.
|
|
989
|
+
*
|
|
990
|
+
* Runs after successful handler execution.
|
|
991
|
+
* Call `reply(value)` to transform/replace the response.
|
|
992
|
+
*
|
|
993
|
+
* @example
|
|
994
|
+
* ```ts
|
|
995
|
+
* const setHeader = defineAfterInterceptor(() => {
|
|
996
|
+
* useResponse().setHeader('x-server', 'my-server')
|
|
997
|
+
* }, TInterceptorPriority.AFTER_ALL)
|
|
998
|
+
* ```
|
|
999
|
+
*/ function defineAfterInterceptor(fn, priority = TInterceptorPriority.AFTER_ALL) {
|
|
1000
|
+
return {
|
|
1001
|
+
after: fn,
|
|
1002
|
+
priority
|
|
1003
|
+
};
|
|
1004
|
+
}
|
|
1005
|
+
/**
|
|
1006
|
+
* Define an error-phase interceptor.
|
|
1007
|
+
*
|
|
1008
|
+
* Runs when the handler throws or returns an Error.
|
|
1009
|
+
* Call `reply(value)` to recover from the error with a replacement response.
|
|
1010
|
+
*
|
|
1011
|
+
* @example
|
|
1012
|
+
* ```ts
|
|
1013
|
+
* const errorFormatter = defineErrorInterceptor((error, reply) => {
|
|
1014
|
+
* reply({ message: error.message, status: 500 })
|
|
1015
|
+
* }, TInterceptorPriority.CATCH_ERROR)
|
|
1016
|
+
* ```
|
|
1017
|
+
*/ function defineErrorInterceptor(fn, priority = TInterceptorPriority.CATCH_ERROR) {
|
|
1018
|
+
return {
|
|
1019
|
+
error: fn,
|
|
1020
|
+
priority
|
|
1021
|
+
};
|
|
1022
|
+
}
|
|
1023
|
+
/**
|
|
1024
|
+
* Define a full interceptor with multiple lifecycle hooks.
|
|
1025
|
+
*
|
|
1026
|
+
* @example
|
|
1027
|
+
* ```ts
|
|
1028
|
+
* const myInterceptor = defineInterceptor({
|
|
1029
|
+
* before(reply) { ... },
|
|
1030
|
+
* after(response, reply) { ... },
|
|
1031
|
+
* error(error, reply) { ... },
|
|
1032
|
+
* }, TInterceptorPriority.INTERCEPTOR)
|
|
1033
|
+
* ```
|
|
1034
|
+
*/ function defineInterceptor(def, priority = TInterceptorPriority.INTERCEPTOR) {
|
|
1035
|
+
def.priority = priority;
|
|
1036
|
+
return def;
|
|
1037
|
+
}
|
|
1038
|
+
/**
|
|
1039
|
+
* ### Define Pipe Function
|
|
1040
|
+
*
|
|
1041
|
+
* ```ts
|
|
1042
|
+
* // example of a transform pipe
|
|
1043
|
+
* const uppercaseTransformPipe = definePipeFn((value, metas, level) => {
|
|
1044
|
+
* return typeof value === 'string' ? value.toUpperCase() : value
|
|
1045
|
+
* },
|
|
1046
|
+
* TPipePriority.TRANSFORM,
|
|
1047
|
+
* )
|
|
1048
|
+
* ```
|
|
1049
|
+
*
|
|
1050
|
+
* @param fn pipe function
|
|
1051
|
+
* @param priority priority of the pipe
|
|
1052
|
+
* @returns
|
|
1053
|
+
*/ function definePipeFn(fn, priority = TPipePriority.TRANSFORM) {
|
|
1054
|
+
fn.priority = priority;
|
|
1055
|
+
return fn;
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
//#endregion
|
|
1059
|
+
//#region packages/moost/src/pipes/resolve.pipe.ts
|
|
1060
|
+
const resolvePipe = definePipeFn((_value, metas, level) => {
|
|
1061
|
+
const resolver = metas.targetMeta?.resolver;
|
|
1062
|
+
if (resolver) return resolver(metas, level);
|
|
1063
|
+
}, TPipePriority.RESOLVE);
|
|
1064
|
+
|
|
1065
|
+
//#endregion
|
|
1066
|
+
//#region packages/moost/src/pipes/shared-pipes.ts
|
|
1067
|
+
const sharedPipes = [{
|
|
1068
|
+
handler: resolvePipe,
|
|
1069
|
+
priority: TPipePriority.RESOLVE
|
|
1070
|
+
}];
|
|
1071
|
+
|
|
1072
|
+
//#endregion
|
|
1073
|
+
//#region packages/moost/src/resolve-arguments.ts
|
|
1074
|
+
/**
|
|
1075
|
+
* Builds an argument-resolver function from pre-computed per-parameter pipe lists.
|
|
1076
|
+
*
|
|
1077
|
+
* Returns `undefined` when there are no parameters to resolve.
|
|
1078
|
+
* The returned function runs pipes for each parameter and returns
|
|
1079
|
+
* `unknown[]` synchronously when possible, or `Promise<unknown[]>` otherwise.
|
|
1080
|
+
*/ function resolveArguments(argsPipes, context) {
|
|
1081
|
+
if (argsPipes.length === 0) return;
|
|
1082
|
+
return () => {
|
|
1083
|
+
const args = [];
|
|
1084
|
+
let hasAsync = false;
|
|
1085
|
+
for (let i = 0; i < argsPipes.length; i++) {
|
|
1086
|
+
const { pipes, meta: paramMeta } = argsPipes[i];
|
|
1087
|
+
const result = runPipes(pipes, void 0, {
|
|
1088
|
+
classMeta: context.classMeta,
|
|
1089
|
+
methodMeta: context.methodMeta,
|
|
1090
|
+
paramMeta,
|
|
1091
|
+
type: context.type,
|
|
1092
|
+
key: context.key,
|
|
1093
|
+
index: i,
|
|
1094
|
+
targetMeta: paramMeta,
|
|
1095
|
+
instantiate: (t) => useControllerContext().instantiate(t)
|
|
1096
|
+
}, "PARAM");
|
|
1097
|
+
if (!hasAsync && isThenable(result)) hasAsync = true;
|
|
1098
|
+
args[i] = result;
|
|
1099
|
+
}
|
|
1100
|
+
return hasAsync ? Promise.all(args) : args;
|
|
1101
|
+
};
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1104
|
+
//#endregion
|
|
1105
|
+
//#region packages/moost/src/interceptor-handler.ts
|
|
1106
|
+
function _define_property$1(obj, key, value) {
|
|
1107
|
+
if (key in obj) Object.defineProperty(obj, key, {
|
|
1108
|
+
value,
|
|
1109
|
+
enumerable: true,
|
|
1110
|
+
configurable: true,
|
|
1111
|
+
writable: true
|
|
1112
|
+
});
|
|
1113
|
+
else obj[key] = value;
|
|
1114
|
+
return obj;
|
|
1115
|
+
}
|
|
1116
|
+
/**
|
|
1117
|
+
* Manages the before/after/error interceptor lifecycle for a single event.
|
|
1118
|
+
* Optimised for the common sync path — only allocates promises when an interceptor goes async.
|
|
1119
|
+
*/ var InterceptorHandler = class {
|
|
1120
|
+
getReplyFn() {
|
|
1121
|
+
return this._boundReplyFn ?? (this._boundReplyFn = (reply) => {
|
|
1122
|
+
this.response = reply;
|
|
1123
|
+
this.responseOverwritten = true;
|
|
1124
|
+
});
|
|
1125
|
+
}
|
|
1126
|
+
get count() {
|
|
1127
|
+
return this.handlers.length;
|
|
1128
|
+
}
|
|
1129
|
+
get countAfter() {
|
|
1130
|
+
return this.after?.length ?? 0;
|
|
1131
|
+
}
|
|
1132
|
+
get countOnError() {
|
|
1133
|
+
return this.onError?.length ?? 0;
|
|
1134
|
+
}
|
|
1135
|
+
/**
|
|
1136
|
+
* Register hooks from a TInterceptorDef.
|
|
1137
|
+
* Returns a pending PromiseLike if `before` went async, or undefined.
|
|
1138
|
+
*/ registerDef(def, entry, ci) {
|
|
1139
|
+
if (def.after) (this.after ?? (this.after = [])).unshift({
|
|
1140
|
+
name: entry.name,
|
|
1141
|
+
fn: def.after
|
|
1142
|
+
});
|
|
1143
|
+
if (def.error) (this.onError ?? (this.onError = [])).unshift({
|
|
1144
|
+
name: entry.name,
|
|
1145
|
+
fn: def.error
|
|
1146
|
+
});
|
|
1147
|
+
if (def.before) {
|
|
1148
|
+
const spanName = entry.spanName;
|
|
1149
|
+
const result = ci ? ci.with(spanName, { "moost.interceptor.stage": "before" }, () => def.before?.(this.getReplyFn())) : def.before(this.getReplyFn());
|
|
1150
|
+
if (isThenable(result)) return result;
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
before() {
|
|
1154
|
+
const ci = (0, _wooksjs_event_core.getContextInjector)();
|
|
1155
|
+
for (let i = 0; i < this.handlers.length; i++) {
|
|
1156
|
+
const entry = this.handlers[i];
|
|
1157
|
+
const { handler } = entry;
|
|
1158
|
+
if (typeof handler === "function") {
|
|
1159
|
+
const factoryResult = handler();
|
|
1160
|
+
if (isThenable(factoryResult)) return this._beforeAsyncFactory(ci, factoryResult, i);
|
|
1161
|
+
const pending = this.registerDef(factoryResult, entry, ci);
|
|
1162
|
+
if (pending) return this._beforeAsyncPending(ci, pending, i);
|
|
1163
|
+
if (this.responseOverwritten) return this.response;
|
|
1164
|
+
} else {
|
|
1165
|
+
const pending = this.registerDef(handler, entry, ci);
|
|
1166
|
+
if (pending) return this._beforeAsyncPending(ci, pending, i);
|
|
1167
|
+
if (this.responseOverwritten) return this.response;
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
}
|
|
1171
|
+
async _beforeAsyncFactory(ci, factoryPromise, startIndex) {
|
|
1172
|
+
const def = await factoryPromise;
|
|
1173
|
+
const entry = this.handlers[startIndex];
|
|
1174
|
+
const pending = this.registerDef(def, entry, ci);
|
|
1175
|
+
if (pending) await pending;
|
|
1176
|
+
if (this.responseOverwritten) return this.response;
|
|
1177
|
+
return this._beforeFrom(ci, startIndex + 1);
|
|
1178
|
+
}
|
|
1179
|
+
async _beforeAsyncPending(ci, pending, startIndex) {
|
|
1180
|
+
await pending;
|
|
1181
|
+
if (this.responseOverwritten) return this.response;
|
|
1182
|
+
return this._beforeFrom(ci, startIndex + 1);
|
|
1183
|
+
}
|
|
1184
|
+
async _beforeFrom(ci, startIndex) {
|
|
1185
|
+
for (let i = startIndex; i < this.handlers.length; i++) {
|
|
1186
|
+
const entry = this.handlers[i];
|
|
1187
|
+
const { handler } = entry;
|
|
1188
|
+
let def;
|
|
1189
|
+
if (typeof handler === "function") def = await handler();
|
|
1190
|
+
else def = handler;
|
|
1191
|
+
const pending = this.registerDef(def, entry, ci);
|
|
1192
|
+
if (pending) await pending;
|
|
1193
|
+
if (this.responseOverwritten) return this.response;
|
|
1194
|
+
}
|
|
1195
|
+
}
|
|
1196
|
+
fireAfter(response) {
|
|
1197
|
+
this.response = response;
|
|
1198
|
+
const isError = response instanceof Error;
|
|
1199
|
+
const handlers = isError ? this.onError : this.after;
|
|
1200
|
+
if (!handlers) return this.response;
|
|
1201
|
+
const ci = (0, _wooksjs_event_core.getContextInjector)();
|
|
1202
|
+
const stage = isError ? "onError" : "after";
|
|
1203
|
+
for (let i = 0; i < handlers.length; i++) {
|
|
1204
|
+
const { name, fn } = handlers[i];
|
|
1205
|
+
const result = ci ? ci.with(`Interceptor:${name}`, { "moost.interceptor.stage": stage }, () => fn(response, this.getReplyFn())) : fn(response, this.getReplyFn());
|
|
1206
|
+
if (isThenable(result)) return this._fireAfterAsync({
|
|
1207
|
+
ci,
|
|
1208
|
+
handlers,
|
|
1209
|
+
stage,
|
|
1210
|
+
response
|
|
1211
|
+
}, result, i);
|
|
1212
|
+
}
|
|
1213
|
+
return this.response;
|
|
1214
|
+
}
|
|
1215
|
+
async _fireAfterAsync(ctx, pending, startIndex) {
|
|
1216
|
+
await pending;
|
|
1217
|
+
for (let i = startIndex + 1; i < ctx.handlers.length; i++) {
|
|
1218
|
+
const { name, fn } = ctx.handlers[i];
|
|
1219
|
+
if (ctx.ci) await ctx.ci.with(`Interceptor:${name}`, { "moost.interceptor.stage": ctx.stage }, () => fn(ctx.response, this.getReplyFn()));
|
|
1220
|
+
else await fn(ctx.response, this.getReplyFn());
|
|
1221
|
+
}
|
|
1222
|
+
return this.response;
|
|
1223
|
+
}
|
|
1224
|
+
constructor(handlers) {
|
|
1225
|
+
_define_property$1(this, "handlers", void 0);
|
|
1226
|
+
_define_property$1(this, "after", void 0);
|
|
1227
|
+
_define_property$1(this, "onError", void 0);
|
|
1228
|
+
_define_property$1(this, "response", void 0);
|
|
1229
|
+
_define_property$1(this, "responseOverwritten", void 0);
|
|
1230
|
+
_define_property$1(this, "_boundReplyFn", void 0);
|
|
1231
|
+
this.handlers = handlers;
|
|
1232
|
+
this.responseOverwritten = false;
|
|
1233
|
+
}
|
|
1234
|
+
};
|
|
1235
|
+
|
|
1236
|
+
//#endregion
|
|
1237
|
+
//#region packages/moost/src/utils.ts
|
|
1238
|
+
const mate = getMoostMate();
|
|
1239
|
+
const noInterceptors = () => void 0;
|
|
1240
|
+
function findInterceptorMethods(handler) {
|
|
1241
|
+
const fakeInstance = Object.create(handler.prototype);
|
|
1242
|
+
const methods = getInstanceOwnMethods(fakeInstance);
|
|
1243
|
+
const result = {};
|
|
1244
|
+
for (const method of methods) {
|
|
1245
|
+
const hook = mate.read(fakeInstance, method)?.interceptorHook;
|
|
1246
|
+
if (hook === "before" || hook === "after" || hook === "error") result[hook] = method;
|
|
1247
|
+
}
|
|
1248
|
+
return result;
|
|
1249
|
+
}
|
|
1250
|
+
function buildMethodResolver(opts) {
|
|
1251
|
+
const fakeInstance = Object.create(opts.handler.prototype);
|
|
1252
|
+
const methodMeta = mate.read(fakeInstance, opts.methodName) || {};
|
|
1253
|
+
const argsPipes = [];
|
|
1254
|
+
for (const p of methodMeta.params || []) argsPipes.push({
|
|
1255
|
+
meta: p,
|
|
1256
|
+
pipes: mergeSorted(opts.pipes, p.pipes)
|
|
1257
|
+
});
|
|
1258
|
+
if (argsPipes.length === 0) return;
|
|
1259
|
+
return resolveArguments(argsPipes, {
|
|
1260
|
+
classMeta: opts.classMeta,
|
|
1261
|
+
methodMeta,
|
|
1262
|
+
type: opts.handler,
|
|
1263
|
+
key: opts.methodName
|
|
1264
|
+
});
|
|
1265
|
+
}
|
|
1266
|
+
function callWithArgs(instance, methodName, resolveArgs) {
|
|
1267
|
+
const ci = (0, _wooksjs_event_core.getContextInjector)();
|
|
1268
|
+
const args = ci ? ci.with("Arguments:resolve", resolveArgs) : resolveArgs();
|
|
1269
|
+
if (isThenable(args)) return args.then((a) => instance[methodName](...a));
|
|
1270
|
+
return instance[methodName](...args);
|
|
1271
|
+
}
|
|
1272
|
+
function callMethod(instance, methodName, resolveArgs) {
|
|
1273
|
+
if (resolveArgs) return callWithArgs(instance, methodName, resolveArgs);
|
|
1274
|
+
return instance[methodName]();
|
|
1275
|
+
}
|
|
1276
|
+
function createClassInterceptorFactory(opts) {
|
|
1277
|
+
const infact = getMoostInfact();
|
|
1278
|
+
function buildDef(instance) {
|
|
1279
|
+
const def = {};
|
|
1280
|
+
if (opts.methods.before) {
|
|
1281
|
+
const methodName = opts.methods.before;
|
|
1282
|
+
const resolveArgs = opts.resolvers[methodName];
|
|
1283
|
+
if (resolveArgs) def.before = (reply) => {
|
|
1284
|
+
setOvertake(reply);
|
|
1285
|
+
return callWithArgs(instance, methodName, resolveArgs);
|
|
1286
|
+
};
|
|
1287
|
+
else def.before = () => instance[methodName]();
|
|
1288
|
+
}
|
|
1289
|
+
if (opts.methods.after) {
|
|
1290
|
+
const methodName = opts.methods.after;
|
|
1291
|
+
const resolveArgs = opts.resolvers[methodName];
|
|
1292
|
+
def.after = (response, reply) => {
|
|
1293
|
+
setOvertake(reply);
|
|
1294
|
+
setInterceptResult(response);
|
|
1295
|
+
return callMethod(instance, methodName, resolveArgs);
|
|
1296
|
+
};
|
|
1297
|
+
}
|
|
1298
|
+
if (opts.methods.error) {
|
|
1299
|
+
const methodName = opts.methods.error;
|
|
1300
|
+
const resolveArgs = opts.resolvers[methodName];
|
|
1301
|
+
def.error = (error, reply) => {
|
|
1302
|
+
setOvertake(reply);
|
|
1303
|
+
setInterceptResult(error);
|
|
1304
|
+
return callMethod(instance, methodName, resolveArgs);
|
|
1305
|
+
};
|
|
1306
|
+
}
|
|
1307
|
+
return def;
|
|
1308
|
+
}
|
|
1309
|
+
function fromTarget(targetInstance) {
|
|
1310
|
+
const result = infact.getForInstance(targetInstance, opts.handler, { customData: { pipes: opts.pipes } });
|
|
1311
|
+
if (isThenable(result)) return result.then(buildDef);
|
|
1312
|
+
return buildDef(result);
|
|
1313
|
+
}
|
|
1314
|
+
return () => {
|
|
1315
|
+
const result = opts.getTargetInstance();
|
|
1316
|
+
if (isThenable(result)) return result.then(fromTarget);
|
|
1317
|
+
return fromTarget(result);
|
|
1318
|
+
};
|
|
1319
|
+
}
|
|
1320
|
+
function getIterceptorHandlerFactory(interceptors, getTargetInstance, pipes) {
|
|
1321
|
+
if (interceptors.length === 0) return noInterceptors;
|
|
1322
|
+
const precomputedHandlers = interceptors.map(({ handler, name }) => {
|
|
1323
|
+
const spanName = `Interceptor:${name}`;
|
|
1324
|
+
if (typeof handler !== "function") return {
|
|
1325
|
+
handler,
|
|
1326
|
+
name,
|
|
1327
|
+
spanName
|
|
1328
|
+
};
|
|
1329
|
+
const interceptorMeta = mate.read(handler);
|
|
1330
|
+
if (!interceptorMeta?.interceptor) throw new Error(`Invalid interceptor "${name}": must be TInterceptorDef or @Interceptor class`);
|
|
1331
|
+
const classMeta = interceptorMeta;
|
|
1332
|
+
const methods = findInterceptorMethods(handler);
|
|
1333
|
+
const mergedPipes = mergeSorted(pipes, classMeta.pipes);
|
|
1334
|
+
const resolvers = {};
|
|
1335
|
+
for (const hook of [
|
|
1336
|
+
"before",
|
|
1337
|
+
"after",
|
|
1338
|
+
"error"
|
|
1339
|
+
]) {
|
|
1340
|
+
const methodName = methods[hook];
|
|
1341
|
+
if (methodName) resolvers[methodName] = buildMethodResolver({
|
|
1342
|
+
handler,
|
|
1343
|
+
methodName,
|
|
1344
|
+
pipes: mergedPipes,
|
|
1345
|
+
classMeta
|
|
1346
|
+
});
|
|
1347
|
+
}
|
|
1348
|
+
return {
|
|
1349
|
+
handler: createClassInterceptorFactory({
|
|
1350
|
+
handler,
|
|
1351
|
+
methods,
|
|
1352
|
+
resolvers,
|
|
1353
|
+
getTargetInstance,
|
|
1354
|
+
pipes: mergedPipes
|
|
1355
|
+
}),
|
|
1356
|
+
name,
|
|
1357
|
+
spanName
|
|
1358
|
+
};
|
|
1246
1359
|
});
|
|
1360
|
+
return () => new InterceptorHandler(precomputedHandlers);
|
|
1247
1361
|
}
|
|
1248
1362
|
|
|
1249
1363
|
//#endregion
|
|
1250
|
-
//#region packages/moost/src/
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
}
|
|
1269
|
-
/**
|
|
1270
|
-
* Define an after-phase interceptor.
|
|
1271
|
-
*
|
|
1272
|
-
* Runs after successful handler execution.
|
|
1273
|
-
* Call `reply(value)` to transform/replace the response.
|
|
1274
|
-
*
|
|
1275
|
-
* @example
|
|
1276
|
-
* ```ts
|
|
1277
|
-
* const setHeader = defineAfterInterceptor(() => {
|
|
1278
|
-
* useResponse().setHeader('x-server', 'my-server')
|
|
1279
|
-
* }, TInterceptorPriority.AFTER_ALL)
|
|
1280
|
-
* ```
|
|
1281
|
-
*/ function defineAfterInterceptor(fn, priority = TInterceptorPriority.AFTER_ALL) {
|
|
1282
|
-
return {
|
|
1283
|
-
after: fn,
|
|
1284
|
-
priority
|
|
1285
|
-
};
|
|
1286
|
-
}
|
|
1287
|
-
/**
|
|
1288
|
-
* Define an error-phase interceptor.
|
|
1289
|
-
*
|
|
1290
|
-
* Runs when the handler throws or returns an Error.
|
|
1291
|
-
* Call `reply(value)` to recover from the error with a replacement response.
|
|
1292
|
-
*
|
|
1293
|
-
* @example
|
|
1294
|
-
* ```ts
|
|
1295
|
-
* const errorFormatter = defineErrorInterceptor((error, reply) => {
|
|
1296
|
-
* reply({ message: error.message, status: 500 })
|
|
1297
|
-
* }, TInterceptorPriority.CATCH_ERROR)
|
|
1298
|
-
* ```
|
|
1299
|
-
*/ function defineErrorInterceptor(fn, priority = TInterceptorPriority.CATCH_ERROR) {
|
|
1300
|
-
return {
|
|
1301
|
-
error: fn,
|
|
1302
|
-
priority
|
|
1364
|
+
//#region packages/moost/src/binding/bind-controller.ts
|
|
1365
|
+
async function bindControllerMethods(options) {
|
|
1366
|
+
const opts = options || {};
|
|
1367
|
+
const { getInstance } = opts;
|
|
1368
|
+
const { classConstructor } = opts;
|
|
1369
|
+
const { adapters } = opts;
|
|
1370
|
+
opts.globalPrefix = opts.globalPrefix || "";
|
|
1371
|
+
opts.provide = opts.provide || {};
|
|
1372
|
+
const fakeInstance = Object.create(classConstructor.prototype);
|
|
1373
|
+
const methods = getInstanceOwnMethods(fakeInstance);
|
|
1374
|
+
const meta = getMoostMate().read(classConstructor) || {};
|
|
1375
|
+
const ownPrefix = typeof opts.replaceOwnPrefix === "string" ? opts.replaceOwnPrefix : meta.controller?.prefix || "";
|
|
1376
|
+
const prefix = `${opts.globalPrefix}/${ownPrefix}`;
|
|
1377
|
+
const controllerOverview = {
|
|
1378
|
+
meta,
|
|
1379
|
+
computedPrefix: prefix,
|
|
1380
|
+
type: classConstructor,
|
|
1381
|
+
handlers: []
|
|
1303
1382
|
};
|
|
1383
|
+
for (const method of methods) {
|
|
1384
|
+
const methodMeta = getMoostMate().read(fakeInstance, method) || {};
|
|
1385
|
+
if (methodMeta.moostInit) {
|
|
1386
|
+
if (meta.injectable === "FOR_EVENT") throw new Error(`@MoostInit is not allowed on a FOR_EVENT controller (${classConstructor.name}.${String(method)}). Init hooks run once at boot on the SINGLETON instance; FOR_EVENT controllers have no init-time instance.`);
|
|
1387
|
+
const initArgsPipes = (methodMeta.params || []).map((p) => ({
|
|
1388
|
+
meta: p,
|
|
1389
|
+
pipes: sharedPipes
|
|
1390
|
+
}));
|
|
1391
|
+
options.registerInitHook?.({
|
|
1392
|
+
priority: methodMeta.moostInit.priority,
|
|
1393
|
+
method,
|
|
1394
|
+
computedPrefix: prefix,
|
|
1395
|
+
getInstance,
|
|
1396
|
+
resolveArgs: resolveArguments(initArgsPipes, {
|
|
1397
|
+
classMeta: meta,
|
|
1398
|
+
methodMeta,
|
|
1399
|
+
type: classConstructor,
|
|
1400
|
+
key: method
|
|
1401
|
+
})
|
|
1402
|
+
});
|
|
1403
|
+
}
|
|
1404
|
+
if (!methodMeta.handlers?.length) continue;
|
|
1405
|
+
const pipes = mergeSorted(opts.pipes, methodMeta.pipes);
|
|
1406
|
+
const getIterceptorHandler = getIterceptorHandlerFactory(mergeSorted(opts.interceptors, meta.interceptors, methodMeta.interceptors), getInstance, pipes);
|
|
1407
|
+
const argsPipes = [];
|
|
1408
|
+
for (const p of methodMeta.params || []) argsPipes.push({
|
|
1409
|
+
meta: p,
|
|
1410
|
+
pipes: mergeSorted(pipes, p.pipes)
|
|
1411
|
+
});
|
|
1412
|
+
const resolveArgs = resolveArguments(argsPipes, {
|
|
1413
|
+
classMeta: meta,
|
|
1414
|
+
methodMeta,
|
|
1415
|
+
type: classConstructor,
|
|
1416
|
+
key: method
|
|
1417
|
+
});
|
|
1418
|
+
const wm = /* @__PURE__ */ new WeakMap();
|
|
1419
|
+
controllerOverview.handlers.push(...methodMeta.handlers.map((h) => {
|
|
1420
|
+
const data = {
|
|
1421
|
+
meta: methodMeta,
|
|
1422
|
+
path: h.path,
|
|
1423
|
+
type: h.type,
|
|
1424
|
+
method,
|
|
1425
|
+
handler: h,
|
|
1426
|
+
registeredAs: []
|
|
1427
|
+
};
|
|
1428
|
+
wm.set(h, data);
|
|
1429
|
+
return data;
|
|
1430
|
+
}));
|
|
1431
|
+
for (const adapter of adapters) await adapter.bindHandler({
|
|
1432
|
+
prefix,
|
|
1433
|
+
fakeInstance,
|
|
1434
|
+
getInstance,
|
|
1435
|
+
method,
|
|
1436
|
+
handlers: methodMeta.handlers,
|
|
1437
|
+
getIterceptorHandler,
|
|
1438
|
+
resolveArgs,
|
|
1439
|
+
controllerName: classConstructor.name,
|
|
1440
|
+
logHandler: (eventName) => {
|
|
1441
|
+
options.moostInstance.logMappedHandler(eventName, classConstructor, method);
|
|
1442
|
+
},
|
|
1443
|
+
register(h, path, args) {
|
|
1444
|
+
const data = wm.get(h);
|
|
1445
|
+
if (data) data.registeredAs.push({
|
|
1446
|
+
path,
|
|
1447
|
+
args
|
|
1448
|
+
});
|
|
1449
|
+
}
|
|
1450
|
+
});
|
|
1451
|
+
}
|
|
1452
|
+
return controllerOverview;
|
|
1304
1453
|
}
|
|
1305
|
-
/**
|
|
1306
|
-
* Define a full interceptor with multiple lifecycle hooks.
|
|
1307
|
-
*
|
|
1308
|
-
* @example
|
|
1309
|
-
* ```ts
|
|
1310
|
-
* const myInterceptor = defineInterceptor({
|
|
1311
|
-
* before(reply) { ... },
|
|
1312
|
-
* after(response, reply) { ... },
|
|
1313
|
-
* error(error, reply) { ... },
|
|
1314
|
-
* }, TInterceptorPriority.INTERCEPTOR)
|
|
1315
|
-
* ```
|
|
1316
|
-
*/ function defineInterceptor(def, priority = TInterceptorPriority.INTERCEPTOR) {
|
|
1317
|
-
def.priority = priority;
|
|
1318
|
-
return def;
|
|
1319
|
-
}
|
|
1320
|
-
/**
|
|
1321
|
-
* ### Define Pipe Function
|
|
1322
|
-
*
|
|
1323
|
-
* ```ts
|
|
1324
|
-
* // example of a transform pipe
|
|
1325
|
-
* const uppercaseTransformPipe = definePipeFn((value, metas, level) => {
|
|
1326
|
-
* return typeof value === 'string' ? value.toUpperCase() : value
|
|
1327
|
-
* },
|
|
1328
|
-
* TPipePriority.TRANSFORM,
|
|
1329
|
-
* )
|
|
1330
|
-
* ```
|
|
1331
|
-
*
|
|
1332
|
-
* @param fn pipe function
|
|
1333
|
-
* @param priority priority of the pipe
|
|
1334
|
-
* @returns
|
|
1335
|
-
*/ function definePipeFn(fn, priority = TPipePriority.TRANSFORM) {
|
|
1336
|
-
fn.priority = priority;
|
|
1337
|
-
return fn;
|
|
1338
|
-
}
|
|
1339
|
-
|
|
1340
|
-
//#endregion
|
|
1341
|
-
//#region packages/moost/src/pipes/resolve.pipe.ts
|
|
1342
|
-
const resolvePipe = definePipeFn((_value, metas, level) => {
|
|
1343
|
-
const resolver = metas.targetMeta?.resolver;
|
|
1344
|
-
if (resolver) return resolver(metas, level);
|
|
1345
|
-
}, TPipePriority.RESOLVE);
|
|
1346
|
-
|
|
1347
|
-
//#endregion
|
|
1348
|
-
//#region packages/moost/src/pipes/shared-pipes.ts
|
|
1349
|
-
const sharedPipes = [{
|
|
1350
|
-
handler: resolvePipe,
|
|
1351
|
-
priority: TPipePriority.RESOLVE
|
|
1352
|
-
}];
|
|
1353
1454
|
|
|
1354
1455
|
//#endregion
|
|
1355
1456
|
//#region packages/moost/src/moost.ts
|
|
@@ -1441,6 +1542,29 @@ function _define_property(obj, key, value) {
|
|
|
1441
1542
|
return this.controllersOverview;
|
|
1442
1543
|
}
|
|
1443
1544
|
/**
|
|
1545
|
+
* @internal Memoized index of the controllers overview (controller class →
|
|
1546
|
+
* method name → handler records), built once and reused for fast handler
|
|
1547
|
+
* lookups (see `getHandlerPaths`). Rebuilt whenever controllers are (re)bound.
|
|
1548
|
+
*/ getHandlerOverviewIndex() {
|
|
1549
|
+
if (!this.handlerOverviewIndex) {
|
|
1550
|
+
const index = /* @__PURE__ */ new Map();
|
|
1551
|
+
for (const c of this.controllersOverview) {
|
|
1552
|
+
let byMethod = index.get(c.type);
|
|
1553
|
+
if (!byMethod) {
|
|
1554
|
+
byMethod = /* @__PURE__ */ new Map();
|
|
1555
|
+
index.set(c.type, byMethod);
|
|
1556
|
+
}
|
|
1557
|
+
for (const h of c.handlers) {
|
|
1558
|
+
const list = byMethod.get(h.method);
|
|
1559
|
+
if (list) list.push(h);
|
|
1560
|
+
else byMethod.set(h.method, [h]);
|
|
1561
|
+
}
|
|
1562
|
+
}
|
|
1563
|
+
this.handlerOverviewIndex = index;
|
|
1564
|
+
}
|
|
1565
|
+
return this.handlerOverviewIndex;
|
|
1566
|
+
}
|
|
1567
|
+
/**
|
|
1444
1568
|
* ### init
|
|
1445
1569
|
* Ititializes adapter. Must be called after adapters are attached.
|
|
1446
1570
|
*/ async init() {
|
|
@@ -1452,8 +1576,26 @@ function _define_property(obj, key, value) {
|
|
|
1452
1576
|
}
|
|
1453
1577
|
this.unregisteredControllers.unshift(this);
|
|
1454
1578
|
await this.bindControllers();
|
|
1579
|
+
await this.runInitHooks();
|
|
1455
1580
|
for (const a of this.adapters) await (a.onInit && a.onInit(this));
|
|
1456
1581
|
}
|
|
1582
|
+
/**
|
|
1583
|
+
* Runs every `@MoostInit`-decorated controller method exactly once, after all
|
|
1584
|
+
* controllers are bound (complete `getControllersOverview()`) and before the
|
|
1585
|
+
* `adapter.onInit` loop. Hooks run in ascending `priority`, then registration
|
|
1586
|
+
* order. Each runs on its controller's SINGLETON instance inside a synthetic
|
|
1587
|
+
* init context (no interceptors; params resolve via the RESOLVE pipe only).
|
|
1588
|
+
* A throwing hook rejects `init()` (fail-fast).
|
|
1589
|
+
*/ async runInitHooks() {
|
|
1590
|
+
if (this.initHooks.length === 0) return;
|
|
1591
|
+
const hooks = this.initHooks.toSorted((a, b) => a.priority - b.priority);
|
|
1592
|
+
for (const hook of hooks) await (0, _wooksjs_event_core.createEventContext)({ logger: this.logger }, async () => {
|
|
1593
|
+
const instance = await hook.getInstance();
|
|
1594
|
+
setControllerContext(instance, hook.method, "", { prefix: hook.computedPrefix });
|
|
1595
|
+
const args = hook.resolveArgs ? await hook.resolveArgs() : [];
|
|
1596
|
+
await instance[hook.method](...args);
|
|
1597
|
+
});
|
|
1598
|
+
}
|
|
1457
1599
|
async bindControllers() {
|
|
1458
1600
|
const thisMeta = getMoostMate().read(this);
|
|
1459
1601
|
const provide = {
|
|
@@ -1508,8 +1650,10 @@ function _define_property(obj, key, value) {
|
|
|
1508
1650
|
provide: classMeta?.provide,
|
|
1509
1651
|
replace: classMeta?.replace,
|
|
1510
1652
|
logger: this.logger,
|
|
1511
|
-
moostInstance: this
|
|
1653
|
+
moostInstance: this,
|
|
1654
|
+
registerInitHook: (hook) => this.initHooks.push(hook)
|
|
1512
1655
|
}));
|
|
1656
|
+
this.handlerOverviewIndex = void 0;
|
|
1513
1657
|
if (classMeta?.importController) {
|
|
1514
1658
|
const prefix = typeof replaceOwnPrefix === "string" ? replaceOwnPrefix : classMeta.controller?.prefix;
|
|
1515
1659
|
const mergedProvide = {
|
|
@@ -1609,7 +1753,7 @@ function _define_property(obj, key, value) {
|
|
|
1609
1753
|
this.logger.info(`${prefix || ""}${c}${eventName} ${"\x1B[0m\x1B[2m\x1B[32m" + c}→ ${classConstructor.name}.${"\x1B[36m" + c}${method}[32m()${coff}`);
|
|
1610
1754
|
}
|
|
1611
1755
|
constructor(options) {
|
|
1612
|
-
super(), _define_property(this, "options", void 0), _define_property(this, "logger", void 0), _define_property(this, "pipes", void 0), _define_property(this, "interceptors", void 0), _define_property(this, "adapters", void 0), _define_property(this, "controllersOverview", void 0), _define_property(this, "provide", void 0), _define_property(this, "replace", void 0), _define_property(this, "unregisteredControllers", void 0), _define_property(this, "globalInterceptorHandler", void 0), this.options = options, this.pipes = Array.from(sharedPipes), this.interceptors = [], this.adapters = [], this.controllersOverview = [], this.provide = (0, _prostojs_infact.createProvideRegistry)([_prostojs_infact.Infact, getMoostInfact], [_prostojs_mate.Mate, getMoostMate]), this.replace = {}, this.unregisteredControllers = [];
|
|
1756
|
+
super(), _define_property(this, "options", void 0), _define_property(this, "logger", void 0), _define_property(this, "pipes", void 0), _define_property(this, "interceptors", void 0), _define_property(this, "adapters", void 0), _define_property(this, "controllersOverview", void 0), _define_property(this, "handlerOverviewIndex", void 0), _define_property(this, "initHooks", void 0), _define_property(this, "provide", void 0), _define_property(this, "replace", void 0), _define_property(this, "unregisteredControllers", void 0), _define_property(this, "globalInterceptorHandler", void 0), this.options = options, this.pipes = Array.from(sharedPipes), this.interceptors = [], this.adapters = [], this.controllersOverview = [], this.initHooks = [], this.provide = (0, _prostojs_infact.createProvideRegistry)([_prostojs_infact.Infact, getMoostInfact], [_prostojs_mate.Mate, getMoostMate]), this.replace = {}, this.unregisteredControllers = [];
|
|
1613
1757
|
this.logger = options?.logger || getDefaultLogger(`[2m[35mmoost`);
|
|
1614
1758
|
setDefaultLogger(this.logger);
|
|
1615
1759
|
const mate = getMoostMate();
|
|
@@ -1617,6 +1761,58 @@ function _define_property(obj, key, value) {
|
|
|
1617
1761
|
}
|
|
1618
1762
|
};
|
|
1619
1763
|
|
|
1764
|
+
//#endregion
|
|
1765
|
+
//#region packages/moost/src/decorators/resolve-moost.ts
|
|
1766
|
+
/**
|
|
1767
|
+
* Resolves the running {@link Moost} application instance from the current
|
|
1768
|
+
* controller context: returns the controller directly when it already IS the
|
|
1769
|
+
* Moost app (Moost registers itself as a controller), otherwise resolves it
|
|
1770
|
+
* through DI. Shared by `@InjectMoost` and `@InjectMoostLogger`.
|
|
1771
|
+
*/ async function resolveMoost() {
|
|
1772
|
+
const { instantiate, getController } = useControllerContext();
|
|
1773
|
+
const controller = getController();
|
|
1774
|
+
return controller instanceof Moost ? controller : await instantiate(Moost);
|
|
1775
|
+
}
|
|
1776
|
+
|
|
1777
|
+
//#endregion
|
|
1778
|
+
//#region packages/moost/src/handler-paths.ts
|
|
1779
|
+
/**
|
|
1780
|
+
* Returns every actual mounted path under which `controller.method` is registered,
|
|
1781
|
+
* read from the post-bind controllers overview. Accounts for multi-prefix mounts
|
|
1782
|
+
* (`@ImportController` at several places), multiple verbs on one method, and
|
|
1783
|
+
* multiple `registeredAs` entries. Returns **distinct** paths; empty array if none
|
|
1784
|
+
* match.
|
|
1785
|
+
*
|
|
1786
|
+
* Only meaningful after `Moost.init()` has bound controllers — typically called
|
|
1787
|
+
* from a `@MoostInit` method (see [MoostInit](./decorators/init.decorator.ts)).
|
|
1788
|
+
*
|
|
1789
|
+
* @param controller class constructor or instance whose method to look up
|
|
1790
|
+
* @param method controller method name (e.g. the one carrying `@Get('refresh')`)
|
|
1791
|
+
*/ function getHandlerPaths(moost, controller, method, opts) {
|
|
1792
|
+
const ctor = (0, _prostojs_mate.isConstructor)(controller) ? controller : (0, _prostojs_mate.getConstructor)(controller);
|
|
1793
|
+
const handlers = moost.getHandlerOverviewIndex().get(ctor)?.get(method);
|
|
1794
|
+
if (!handlers) return [];
|
|
1795
|
+
const { type, predicate } = opts ?? {};
|
|
1796
|
+
const paths = /* @__PURE__ */ new Set();
|
|
1797
|
+
for (const h of handlers) {
|
|
1798
|
+
if (type && h.type !== type) continue;
|
|
1799
|
+
if (predicate && !predicate(h)) continue;
|
|
1800
|
+
for (const r of h.registeredAs) paths.add(r.path);
|
|
1801
|
+
}
|
|
1802
|
+
return [...paths];
|
|
1803
|
+
}
|
|
1804
|
+
/**
|
|
1805
|
+
* Composable form of {@link getHandlerPaths}. Resolves the running Moost app and
|
|
1806
|
+
* defaults the controller to the current one from context — designed for use
|
|
1807
|
+
* inside a `@MoostInit` method or an event handler. `method` defaults to the
|
|
1808
|
+
* current context method; pass it explicitly from `@MoostInit` to target a
|
|
1809
|
+
* handler method (the init method itself is not a handler).
|
|
1810
|
+
*/ async function useHandlerPaths(method, opts) {
|
|
1811
|
+
const moost = await resolveMoost();
|
|
1812
|
+
const { getController, getMethod } = useControllerContext();
|
|
1813
|
+
return getHandlerPaths(moost, getController(), method ?? getMethod() ?? "", opts);
|
|
1814
|
+
}
|
|
1815
|
+
|
|
1620
1816
|
//#endregion
|
|
1621
1817
|
exports.After = After;
|
|
1622
1818
|
exports.ApplyDecorators = ApplyDecorators;
|
|
@@ -1632,12 +1828,14 @@ Object.defineProperty(exports, 'ContextInjector', {
|
|
|
1632
1828
|
});
|
|
1633
1829
|
exports.Controller = Controller;
|
|
1634
1830
|
exports.Description = Description;
|
|
1831
|
+
exports.HandlerPaths = HandlerPaths;
|
|
1635
1832
|
exports.Id = Id;
|
|
1636
1833
|
exports.ImportController = ImportController;
|
|
1637
1834
|
exports.Inherit = Inherit;
|
|
1638
1835
|
exports.Inject = Inject;
|
|
1639
1836
|
exports.InjectEventLogger = InjectEventLogger;
|
|
1640
1837
|
exports.InjectFromScope = InjectFromScope;
|
|
1838
|
+
exports.InjectMoost = InjectMoost;
|
|
1641
1839
|
exports.InjectMoostLogger = InjectMoostLogger;
|
|
1642
1840
|
exports.InjectScopeVars = InjectScopeVars;
|
|
1643
1841
|
exports.Injectable = Injectable;
|
|
@@ -1647,6 +1845,7 @@ exports.InterceptorHandler = InterceptorHandler;
|
|
|
1647
1845
|
exports.Label = Label;
|
|
1648
1846
|
exports.LoggerTopic = LoggerTopic;
|
|
1649
1847
|
exports.Moost = Moost;
|
|
1848
|
+
exports.MoostInit = MoostInit;
|
|
1650
1849
|
exports.OnError = OnError;
|
|
1651
1850
|
exports.Optional = Optional;
|
|
1652
1851
|
exports.Overtake = Overtake;
|
|
@@ -1735,12 +1934,14 @@ Object.defineProperty(exports, 'getGlobalWooks', {
|
|
|
1735
1934
|
return wooks.getGlobalWooks;
|
|
1736
1935
|
}
|
|
1737
1936
|
});
|
|
1937
|
+
exports.getHandlerPaths = getHandlerPaths;
|
|
1738
1938
|
exports.getInfactScopeVars = getInfactScopeVars;
|
|
1739
1939
|
exports.getInstanceOwnMethods = getInstanceOwnMethods;
|
|
1740
1940
|
exports.getInstanceOwnProps = getInstanceOwnProps;
|
|
1741
1941
|
exports.getMoostInfact = getMoostInfact;
|
|
1742
1942
|
exports.getMoostMate = getMoostMate;
|
|
1743
1943
|
exports.getNewMoostInfact = getNewMoostInfact;
|
|
1944
|
+
exports.globalKey = globalKey;
|
|
1744
1945
|
Object.defineProperty(exports, 'isConstructor', {
|
|
1745
1946
|
enumerable: true,
|
|
1746
1947
|
get: function () {
|
|
@@ -1781,6 +1982,7 @@ exports.setInfactLoggingOptions = setInfactLoggingOptions;
|
|
|
1781
1982
|
exports.setInterceptResult = setInterceptResult;
|
|
1782
1983
|
exports.setOvertake = setOvertake;
|
|
1783
1984
|
exports.useControllerContext = useControllerContext;
|
|
1985
|
+
exports.useHandlerPaths = useHandlerPaths;
|
|
1784
1986
|
exports.useInterceptResult = useInterceptResult;
|
|
1785
1987
|
Object.defineProperty(exports, 'useLogger', {
|
|
1786
1988
|
enumerable: true,
|