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 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 = (0, _wooksjs_event_core.key)("controller.instance");
221
- const controllerMethodKey = (0, _wooksjs_event_core.key)("controller.method");
222
- const controllerRouteKey = (0, _wooksjs_event_core.key)("controller.route");
223
- const controllerPrefixKey = (0, _wooksjs_event_core.key)("controller.prefix");
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
- var _g, _g1;
266
- const g = globalThis;
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/resolve-arguments.ts
483
+ //#region packages/moost/src/decorators/circular.decorator.ts
471
484
  /**
472
- * Builds an argument-resolver function from pre-computed per-parameter pipe lists.
485
+ * Marks a constructor parameter dependency as circular.
486
+ * The resolver function is called lazily to break the circular reference.
473
487
  *
474
- * Returns `undefined` when there are no parameters to resolve.
475
- * The returned function runs pipes for each parameter and returns
476
- * `unknown[]` synchronously when possible, or `Promise<unknown[]>` otherwise.
477
- */ function resolveArguments(argsPipes, context) {
478
- if (argsPipes.length === 0) return;
479
- return () => {
480
- const args = [];
481
- let hasAsync = false;
482
- for (let i = 0; i < argsPipes.length; i++) {
483
- const { pipes, meta: paramMeta } = argsPipes[i];
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/interceptor-handler.ts
503
- function _define_property$1(obj, key, value) {
504
- if (key in obj) Object.defineProperty(obj, key, {
505
- value,
506
- enumerable: true,
507
- configurable: true,
508
- writable: true
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
- * Manages the before/after/error interceptor lifecycle for a single event.
515
- * Optimised for the common sync path — only allocates promises when an interceptor goes async.
516
- */ var InterceptorHandler = class {
517
- getReplyFn() {
518
- return this._boundReplyFn ?? (this._boundReplyFn = (reply) => {
519
- this.response = reply;
520
- this.responseOverwritten = true;
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
- function buildMethodResolver(opts) {
648
- const fakeInstance = Object.create(opts.handler.prototype);
649
- const methodMeta = mate.read(fakeInstance, opts.methodName) || {};
650
- const argsPipes = [];
651
- for (const p of methodMeta.params || []) argsPipes.push({
652
- meta: p,
653
- pipes: mergeSorted(opts.pipes, p.pipes)
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
- function callWithArgs(instance, methodName, resolveArgs) {
664
- const ci = (0, _wooksjs_event_core.getContextInjector)();
665
- const args = ci ? ci.with("Arguments:resolve", resolveArgs) : resolveArgs();
666
- if (isThenable(args)) return args.then((a) => instance[methodName](...a));
667
- return instance[methodName](...args);
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
- function callMethod(instance, methodName, resolveArgs) {
670
- if (resolveArgs) return callWithArgs(instance, methodName, resolveArgs);
671
- return instance[methodName]();
672
- }
673
- function createClassInterceptorFactory(opts) {
674
- const infact = getMoostInfact();
675
- function buildDef(instance) {
676
- const def = {};
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/inherit.decorator.ts
610
+ //#region packages/moost/src/decorators/resolve.decorator.ts
962
611
  /**
963
- * ## Inherit
964
- * ### @Decorator
965
- * Inherit metadata from super class
966
- * @returns
967
- */ const Inherit = () => getMoostMate().decorate("inherit", true);
968
-
969
- //#endregion
970
- //#region packages/moost/src/decorators/intercept.decorator.ts
971
- var TInterceptorPriority = /* @__PURE__ */ function(TInterceptorPriority) {
972
- TInterceptorPriority[TInterceptorPriority["BEFORE_ALL"] = 0] = "BEFORE_ALL";
973
- TInterceptorPriority[TInterceptorPriority["BEFORE_GUARD"] = 1] = "BEFORE_GUARD";
974
- TInterceptorPriority[TInterceptorPriority["GUARD"] = 2] = "GUARD";
975
- TInterceptorPriority[TInterceptorPriority["AFTER_GUARD"] = 3] = "AFTER_GUARD";
976
- TInterceptorPriority[TInterceptorPriority["INTERCEPTOR"] = 4] = "INTERCEPTOR";
977
- TInterceptorPriority[TInterceptorPriority["CATCH_ERROR"] = 5] = "CATCH_ERROR";
978
- TInterceptorPriority[TInterceptorPriority["AFTER_ALL"] = 6] = "AFTER_ALL";
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 { instantiate, getController } = useControllerContext();
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/define.ts
1251
- /**
1252
- * Define a before-phase interceptor.
1253
- *
1254
- * Runs before argument resolution and handler execution.
1255
- * Call `reply(value)` to short-circuit the handler and respond early.
1256
- *
1257
- * @example
1258
- * ```ts
1259
- * const authGuard = defineBeforeInterceptor((reply) => {
1260
- * if (!isAuthenticated()) reply(new HttpError(401))
1261
- * }, TInterceptorPriority.GUARD)
1262
- * ```
1263
- */ function defineBeforeInterceptor(fn, priority = TInterceptorPriority.INTERCEPTOR) {
1264
- return {
1265
- before: fn,
1266
- priority
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}()${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(`moost`);
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,