moost 0.5.33 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
- import { Mate, getConstructor, getConstructor as getConstructor$1, isConstructor, isConstructor as isConstructor$1 } from "@prostojs/mate";
2
- import { ContextInjector, EventLogger, createAsyncEventContext, getContextInjector, getContextInjector as getContextInjector$1, replaceContextInjector, useAsyncEventContext, useAsyncEventContext as useAsyncEventContext$1, useEventId, useEventLogger, useEventLogger as useEventLogger$1, useRouteParams } from "@wooksjs/event-core";
1
+ import { ContextInjector, cached, createEventContext, createEventContext as createEventContext$1, current, current as current$1, defineWook, eventTypeKey, getContextInjector, getContextInjector as getContextInjector$1, key, key as key$1, replaceContextInjector, resetContextInjector, run, useLogger, useLogger as useLogger$1, useRouteParams } from "@wooksjs/event-core";
3
2
  import { Infact, createProvideRegistry, createProvideRegistry as createProvideRegistry$1, createReplaceRegistry, createReplaceRegistry as createReplaceRegistry$1 } from "@prostojs/infact";
3
+ import { Mate, getConstructor, getConstructor as getConstructor$1, isConstructor, isConstructor as isConstructor$1 } from "@prostojs/mate";
4
4
  import { ProstoLogger, ProstoLogger as ProstoLogger$1, coloredConsole, createConsoleTransort } from "@prostojs/logger";
5
5
  import { Hookable } from "hookable";
6
6
  import { clearGlobalWooks, getGlobalWooks } from "wooks";
@@ -17,20 +17,48 @@ function getDefaultLogger(topic) {
17
17
  });
18
18
  return topic && defaultLogger instanceof ProstoLogger$1 ? defaultLogger.createTopic(topic) : defaultLogger;
19
19
  }
20
- function createLogger(opts) {
20
+ /** Creates a new `ProstoLogger` instance with sensible defaults (level 4, colored console). */ function createLogger(opts) {
21
21
  return new ProstoLogger$1({
22
22
  ...opts,
23
23
  level: opts?.level ?? 4,
24
24
  transports: opts?.transports ?? [loggerConsoleTransport]
25
25
  });
26
26
  }
27
- const loggerConsoleTransport = createConsoleTransort({ format: coloredConsole });
27
+ /** Default colored console transport used by Moost loggers. */ const loggerConsoleTransport = createConsoleTransort({ format: coloredConsole });
28
+
29
+ //#endregion
30
+ //#region packages/moost/src/shared-utils.ts
31
+ /**
32
+ * Checks whether a value is a PromiseLike (thenable).
33
+ * Single source of truth — import from here instead of duplicating.
34
+ */ function isThenable(value) {
35
+ return value !== null && value !== void 0 && typeof value.then === "function";
36
+ }
37
+ /**
38
+ * Merges multiple arrays of items with a `priority` field, returning a
39
+ * single sorted-by-priority array. Accepts `undefined` entries for convenience.
40
+ */ function mergeSorted(...lists) {
41
+ let total = 0;
42
+ for (const l of lists) if (l) total += l.length;
43
+ if (total === 0) return [];
44
+ const merged = [];
45
+ for (const l of lists) if (l) for (const item of l) merged.push(item);
46
+ return merged.toSorted((a, b) => a.priority - b.priority);
47
+ }
28
48
 
29
49
  //#endregion
30
50
  //#region packages/moost/src/pipes/run-pipes.ts
31
- async function runPipes(pipes, initialValue, metas, level) {
51
+ function runPipes(pipes, initialValue, metas, level) {
32
52
  let v = initialValue;
33
- for (const pipe of pipes) v = await pipe.handler(v, metas, level);
53
+ for (let i = 0; i < pipes.length; i++) {
54
+ const result = pipes[i].handler(v, metas, level);
55
+ if (isThenable(result)) return (async () => {
56
+ v = await result;
57
+ for (let j = i + 1; j < pipes.length; j++) v = await pipes[j].handler(v, metas, level);
58
+ return v;
59
+ })();
60
+ v = result;
61
+ }
34
62
  return v;
35
63
  }
36
64
 
@@ -47,7 +75,7 @@ const moostMate = new Mate(METADATA_WORKSPACE, {
47
75
  return !!targetMeta?.inherit;
48
76
  }
49
77
  });
50
- function getMoostMate() {
78
+ /** Returns the shared `Mate` instance operating in the `'moost'` metadata workspace. */ function getMoostMate() {
51
79
  return moostMate;
52
80
  }
53
81
 
@@ -60,13 +88,13 @@ let loggingOptions = {
60
88
  warn: true,
61
89
  error: true
62
90
  };
63
- function setInfactLoggingOptions(options) {
91
+ /** Configures which Infact DI events are logged (instance creation, warnings, errors). */ function setInfactLoggingOptions(options) {
64
92
  loggingOptions = {
65
93
  ...loggingOptions,
66
94
  ...options
67
95
  };
68
96
  }
69
- function getMoostInfact() {
97
+ /** Returns the shared Infact DI container used by Moost for dependency injection. */ function getMoostInfact() {
70
98
  return sharedMoostInfact;
71
99
  }
72
100
  const scopeVarsMap = /* @__PURE__ */ new Map();
@@ -99,7 +127,7 @@ const scopeVarsMap = /* @__PURE__ */ new Map();
99
127
  constructorParams: meta?.params || [],
100
128
  provide: meta?.provide,
101
129
  properties: meta?.properties || [],
102
- scopeId: meta?.injectable === "FOR_EVENT" ? useEventId().getId() : void 0
130
+ scopeId: meta?.injectable === "FOR_EVENT" ? useScopeId() : void 0
103
131
  };
104
132
  },
105
133
  resolveParam({ paramMeta, customData, classConstructor, index, scopeId, instantiate }) {
@@ -114,15 +142,15 @@ const scopeVarsMap = /* @__PURE__ */ new Map();
114
142
  instantiate
115
143
  }, "PARAM");
116
144
  },
117
- describeProp(classConstructor, key) {
118
- const meta = getMoostMate().read(classConstructor, key);
145
+ describeProp(classConstructor, key$2) {
146
+ const meta = getMoostMate().read(classConstructor, key$2);
119
147
  return meta;
120
148
  },
121
- resolveProp({ instance, key, initialValue, propMeta, scopeId, classMeta, customData, classConstructor, instantiate }) {
149
+ resolveProp({ instance, key: key$2, initialValue, propMeta, scopeId, classMeta, customData, classConstructor, instantiate }) {
122
150
  if (propMeta && customData?.pipes) return runPipes(customData.pipes, initialValue, {
123
151
  instance,
124
152
  type: classConstructor,
125
- key,
153
+ key: key$2,
126
154
  scopeId,
127
155
  propMeta,
128
156
  targetMeta: propMeta,
@@ -150,7 +178,8 @@ const scopeVarsMap = /* @__PURE__ */ new Map();
150
178
  }
151
179
  let logger;
152
180
  try {
153
- logger = event === "error" ? getDefaultLogger(INFACT_BANNER) : useEventLogger$1(INFACT_BANNER);
181
+ const ctxLogger = event === "error" ? getDefaultLogger(INFACT_BANNER) : useLogger$1();
182
+ logger = typeof ctxLogger.topic === "function" ? ctxLogger.topic(INFACT_BANNER) : ctxLogger;
154
183
  } catch (error) {
155
184
  logger = getDefaultLogger(INFACT_BANNER);
156
185
  }
@@ -192,19 +221,26 @@ const scopeVarsMap = /* @__PURE__ */ new Map();
192
221
 
193
222
  //#endregion
194
223
  //#region packages/moost/src/composables/controller.composable.ts
195
- function setControllerContext(controller, method, route) {
196
- const { store } = useAsyncEventContext$1();
197
- const { set } = store("controller");
198
- set("instance", controller);
199
- set("method", method);
200
- set("route", route);
201
- }
202
- function useControllerContext() {
203
- const { store } = useAsyncEventContext$1();
204
- const { get } = store("controller");
205
- const getController = () => get("instance");
206
- const getMethod = () => get("method");
207
- const getRoute = () => get("route");
224
+ const controllerInstanceKey = key$1("controller.instance");
225
+ const controllerMethodKey = key$1("controller.method");
226
+ const controllerRouteKey = key$1("controller.route");
227
+ /**
228
+ * Sets the controller context for the current event scope.
229
+ * Called internally by adapters when dispatching events to handlers.
230
+ */ function setControllerContext(controller, method, route, ctx) {
231
+ const _ctx = ctx || current$1();
232
+ _ctx.set(controllerInstanceKey, controller);
233
+ _ctx.set(controllerMethodKey, method);
234
+ _ctx.set(controllerRouteKey, route);
235
+ }
236
+ /**
237
+ * Provides access to the current controller context within an event handler.
238
+ * Returns utilities for accessing the controller instance, method metadata, and DI.
239
+ */ function useControllerContext(ctx) {
240
+ const _ctx = ctx || current$1();
241
+ const getController = () => _ctx.get(controllerInstanceKey);
242
+ const getMethod = () => _ctx.get(controllerMethodKey);
243
+ const getRoute = () => _ctx.get(controllerRouteKey);
208
244
  const getControllerMeta = () => getMoostMate().read(getController());
209
245
  const getMethodMeta = (name) => getMoostMate().read(getController(), name || getMethod());
210
246
  function instantiate(c) {
@@ -224,105 +260,204 @@ function useControllerContext() {
224
260
  };
225
261
  }
226
262
 
263
+ //#endregion
264
+ //#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 = key$1("interceptor.overtake"));
268
+ const interceptResultKey = (_g1 = g).__moost_interceptResultKey ?? (_g1.__moost_interceptResultKey = key$1("interceptor.result"));
269
+ /** Stores the overtake (reply) function in the current event context. */ function setOvertake(fn, ctx) {
270
+ (ctx || current$1()).set(overtakeKey, fn);
271
+ }
272
+ /** Retrieves the overtake (reply) function from the current event context. */ function useOvertake(ctx) {
273
+ return (ctx || current$1()).get(overtakeKey);
274
+ }
275
+ /** Stores the interceptor result value in the current event context. */ function setInterceptResult(value, ctx) {
276
+ (ctx || current$1()).set(interceptResultKey, value);
277
+ }
278
+ /** Retrieves the interceptor result value from the current event context. */ function useInterceptResult(ctx) {
279
+ return (ctx || current$1()).get(interceptResultKey);
280
+ }
281
+
227
282
  //#endregion
228
283
  //#region packages/moost/src/adapter-utils.ts
229
- const infact = getMoostInfact();
230
- function registerEventScope(scopeId) {
284
+ let _scopeChar = "a";
285
+ let _scopeNum = 0;
286
+ function nextScopeId() {
287
+ if (_scopeNum >= Number.MAX_SAFE_INTEGER) {
288
+ _scopeNum = 0;
289
+ _scopeChar = _scopeChar === "z" ? "a" : String.fromCodePoint((_scopeChar.codePointAt(0) ?? 97) + 1);
290
+ }
291
+ return `__moost_${_scopeChar}_${++_scopeNum}`;
292
+ }
293
+ /** Composable returning the moost scope ID for the current event. Generates on first call, caches for subsequent calls. */ const useScopeId = defineWook(() => nextScopeId());
294
+ /** Registers a DI scope for the given ID and returns an unscope function. */ function registerEventScope(scopeId) {
295
+ const infact = getMoostInfact();
231
296
  infact.registerScope(scopeId);
232
297
  return () => {
233
298
  infact.unregisterScope(scopeId);
234
299
  };
235
300
  }
236
- function defineMoostEventHandler(options) {
301
+ /**
302
+ * Creates the complete event handler lifecycle processor used by adapters.
303
+ * Handles scope registration, controller resolution, interceptors, argument resolution,
304
+ * handler invocation, and cleanup — optimised for sync-first execution.
305
+ */ function defineMoostEventHandler(options) {
237
306
  const ci = getContextInjector$1();
238
- return async () => {
239
- const scopeId = useEventId().getId();
240
- const logger = useEventLogger$1(options.loggerTitle);
307
+ const handlerSpanName = `Handler:${options.targetPath}`;
308
+ const handlerAttrs = {
309
+ "moost.handler": options.controllerMethod || "",
310
+ "moost.controller": options.controllerName || ""
311
+ };
312
+ return () => {
313
+ const ctx = current$1();
314
+ const scopeId = useScopeId(ctx);
315
+ const ctxLogger = useLogger$1(ctx);
316
+ const logger = typeof ctxLogger.topic === "function" ? ctxLogger.topic(options.loggerTitle) : ctxLogger;
241
317
  const unscope = registerEventScope(scopeId);
242
318
  let response;
243
- const hookOptions = {
244
- scopeId,
245
- logger,
246
- unscope,
247
- method: options.controllerMethod,
248
- getResponse: () => response,
249
- reply: (r) => response = r
250
- };
251
- if (options.hooks?.init) await options.hooks.init(hookOptions);
252
- const instance = await options.getControllerInstance();
253
- if (instance) {
254
- setControllerContext(instance, options.controllerMethod || "", options.targetPath);
255
- ci.hook(options.handlerType, "Controller:registered");
319
+ let hookOptions;
320
+ function getHookOptions() {
321
+ return hookOptions ?? (hookOptions = {
322
+ scopeId,
323
+ logger,
324
+ unscope,
325
+ method: options.controllerMethod,
326
+ getResponse: () => response,
327
+ reply: (r) => response = r
328
+ });
256
329
  }
257
- const interceptorHandler = await options.getIterceptorHandler();
258
- if (interceptorHandler?.count) try {
259
- response = await ci.with("Interceptors:init", () => interceptorHandler.init());
260
- if (response !== void 0) return await endWithResponse();
330
+ let interceptorHandler;
331
+ let raise = false;
332
+ try {
333
+ if (options.hooks?.init) {
334
+ const hookResult = options.hooks.init(getHookOptions());
335
+ if (isThenable(hookResult)) return hookResult.then(afterInit);
336
+ }
337
+ return afterInit();
261
338
  } catch (error) {
262
- options.logErrors && logger.error(error);
263
- response = error;
264
- return endWithResponse(true);
339
+ if (!options.manualUnscope) unscope();
340
+ throw error;
265
341
  }
266
- let args = [];
267
- if (options.resolveArgs) try {
268
- args = await ci.with("Arguments:resolve", () => options.resolveArgs());
269
- } catch (error) {
270
- options.logErrors && logger.error(error);
271
- response = error;
272
- return endWithResponse(true);
342
+ function afterInit() {
343
+ const instanceResult = options.getControllerInstance();
344
+ if (isThenable(instanceResult)) return instanceResult.then((inst) => afterInstance(inst));
345
+ return afterInstance(instanceResult);
273
346
  }
274
- if (interceptorHandler?.countBefore) {
275
- response = await ci.with("Interceptors:before", () => interceptorHandler.fireBefore(response));
276
- if (response !== void 0) return endWithResponse();
347
+ function afterInstance(instance) {
348
+ if (instance) {
349
+ setControllerContext(instance, options.controllerMethod || "", options.targetPath);
350
+ ci?.hook(options.handlerType, "Controller:registered");
351
+ }
352
+ interceptorHandler = options.getIterceptorHandler();
353
+ if (interceptorHandler?.count) try {
354
+ const initResult = ci ? ci.with("Interceptors:before", () => interceptorHandler?.before()) : interceptorHandler?.before();
355
+ if (isThenable(initResult)) return initResult.then((r) => {
356
+ response = r;
357
+ if (response !== void 0) return cleanup();
358
+ return afterInterceptors(instance);
359
+ }, handleError);
360
+ response = initResult;
361
+ if (response !== void 0) return cleanup();
362
+ } catch (error) {
363
+ if (options.logErrors) logger.error(String(error));
364
+ response = error;
365
+ raise = true;
366
+ return cleanup();
367
+ }
368
+ return afterInterceptors(instance);
277
369
  }
278
- const callControllerMethod = () => {
279
- if (options.callControllerMethod) return options.callControllerMethod(args);
280
- else if (instance && options.controllerMethod && typeof instance[options.controllerMethod] === "function") return instance[options.controllerMethod](...args);
281
- };
282
- try {
283
- response = await ci.with(`Handler:${options.targetPath}`, {
284
- "moost.handler": options.controllerMethod || "",
285
- "moost.controller": getConstructor$1(instance).name
286
- }, () => callControllerMethod());
287
- } catch (error) {
288
- options.logErrors && logger.error(error);
289
- response = error;
290
- return endWithResponse(true);
370
+ function afterInterceptors(instance) {
371
+ let args = [];
372
+ if (options.resolveArgs) try {
373
+ const argsResult = ci ? ci.with("Arguments:resolve", () => options.resolveArgs?.()) : options.resolveArgs?.();
374
+ if (isThenable(argsResult)) return argsResult.then((a) => {
375
+ args = a;
376
+ return callHandler(instance, args);
377
+ }, handleError);
378
+ args = argsResult;
379
+ } catch (error) {
380
+ if (options.logErrors) logger.error(String(error));
381
+ response = error;
382
+ raise = true;
383
+ return cleanup();
384
+ }
385
+ return callHandler(instance, args);
291
386
  }
292
- async function endWithResponse(raise = false) {
293
- if (interceptorHandler?.countAfter || interceptorHandler?.countOnError) try {
294
- response = await ci.with("Interceptors:after", () => interceptorHandler.fireAfter(response));
387
+ function callHandler(instance, args) {
388
+ const invoke = options.callControllerMethod ? () => options.callControllerMethod?.(args) : instance && options.controllerMethod && typeof instance[options.controllerMethod] === "function" ? () => instance[options.controllerMethod](...args) : void 0;
389
+ try {
390
+ const handlerResult = ci ? ci.with(handlerSpanName, handlerAttrs, () => invoke?.()) : invoke?.();
391
+ if (isThenable(handlerResult)) return handlerResult.then((r) => {
392
+ response = r;
393
+ return cleanup();
394
+ }, (error) => {
395
+ if (options.logErrors) logger.error(error);
396
+ response = error;
397
+ raise = true;
398
+ return cleanup();
399
+ });
400
+ response = handlerResult;
295
401
  } catch (error) {
296
- options.logErrors && logger.error(error);
297
- if (!options.manualUnscope) unscope();
298
- throw error;
402
+ if (options.logErrors) logger.error(error);
403
+ response = error;
404
+ raise = true;
405
+ }
406
+ return cleanup();
407
+ }
408
+ function handleError(error) {
409
+ if (options.logErrors) logger.error(String(error));
410
+ response = error;
411
+ raise = true;
412
+ return cleanup();
413
+ }
414
+ function cleanup() {
415
+ if (interceptorHandler?.countAfter || interceptorHandler?.countOnError) {
416
+ const afterResult = ci ? ci.with("Interceptors:after", () => interceptorHandler?.fireAfter(response)) : interceptorHandler?.fireAfter(response);
417
+ if (isThenable(afterResult)) return afterResult.then((r) => {
418
+ response = r;
419
+ return finalize();
420
+ }, (error) => {
421
+ if (options.logErrors) logger.error(String(error));
422
+ if (!options.manualUnscope) unscope();
423
+ throw error;
424
+ });
425
+ response = afterResult;
299
426
  }
427
+ return finalize();
428
+ }
429
+ function finalize() {
300
430
  if (!options.manualUnscope) unscope();
301
- if (options.hooks?.end) await options.hooks.end(hookOptions);
431
+ if (options.hooks?.end) {
432
+ const endResult = options.hooks.end(getHookOptions());
433
+ if (isThenable(endResult)) return endResult.then(() => {
434
+ if (raise) throw response;
435
+ return response;
436
+ });
437
+ }
302
438
  if (raise) throw response;
303
439
  return response;
304
440
  }
305
- return endWithResponse();
306
441
  };
307
442
  }
308
443
 
309
444
  //#endregion
310
445
  //#region packages/moost/src/binding/utils.ts
311
- function getInstanceOwnMethods(instance) {
446
+ /** Returns the own method names of an instance, including inherited methods from parent classes. */ function getInstanceOwnMethods(instance) {
312
447
  const proto = Object.getPrototypeOf(instance);
313
- return [
448
+ return [...new Set([
314
449
  ...getParentProps(getConstructor$1(instance)),
315
450
  ...Object.getOwnPropertyNames(proto),
316
451
  ...Object.getOwnPropertyNames(instance)
317
- ].filter((m) => typeof instance[m] === "function");
452
+ ])].filter((m) => typeof instance[m] === "function");
318
453
  }
319
- function getInstanceOwnProps(instance) {
454
+ /** Returns the own non-method property names of an instance, including inherited properties. */ function getInstanceOwnProps(instance) {
320
455
  const proto = Object.getPrototypeOf(instance);
321
- return [
456
+ return [...new Set([
322
457
  ...getParentProps(getConstructor$1(instance)),
323
458
  ...Object.getOwnPropertyNames(proto),
324
459
  ...Object.getOwnPropertyNames(instance)
325
- ].filter((m) => typeof instance[m] !== "function");
460
+ ])].filter((m) => typeof instance[m] !== "function");
326
461
  }
327
462
  const fnProto = Object.getPrototypeOf(Function);
328
463
  function getParentProps(constructor) {
@@ -332,99 +467,166 @@ function getParentProps(constructor) {
332
467
  }
333
468
 
334
469
  //#endregion
335
- //#region packages/moost/src/class-function/class-function.ts
336
- async function getCallableFn(targetInstance, fn, pipes, logger) {
337
- const mate$1 = getMoostMate();
338
- const meta = mate$1.read(fn);
339
- if (meta?.injectable) {
340
- const infact$1 = getMoostInfact();
341
- const instance = await infact$1.getForInstance(targetInstance, fn, { customData: { pipes: [...pipes || [], ...meta.pipes || []].sort((a, b) => a.priority - b.priority) } });
342
- return (...args) => instance.handler(...args);
343
- }
344
- if (typeof fn === "function") return fn;
345
- const e = /* @__PURE__ */ new Error(`getCallableFn failed for "${getConstructor$1(targetInstance).name}" because the passed arg is not a Function nor TClassFunction`);
346
- logger.error(e);
347
- throw e;
470
+ //#region packages/moost/src/resolve-arguments.ts
471
+ /**
472
+ * Builds an argument-resolver function from pre-computed per-parameter pipe lists.
473
+ *
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 void 0;
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
+ };
348
499
  }
349
500
 
350
501
  //#endregion
351
502
  //#region packages/moost/src/interceptor-handler.ts
352
- function _define_property$1(obj, key, value) {
353
- if (key in obj) Object.defineProperty(obj, key, {
503
+ function _define_property$1(obj, key$2, value) {
504
+ if (key$2 in obj) Object.defineProperty(obj, key$2, {
354
505
  value,
355
506
  enumerable: true,
356
507
  configurable: true,
357
508
  writable: true
358
509
  });
359
- else obj[key] = value;
510
+ else obj[key$2] = value;
360
511
  return obj;
361
512
  }
362
- var InterceptorHandler = class {
513
+ /**
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
+ }
363
523
  get count() {
364
524
  return this.handlers.length;
365
525
  }
366
- get countBefore() {
367
- return this.before.length;
368
- }
369
526
  get countAfter() {
370
- return this.after.length;
527
+ return this.after?.length ?? 0;
371
528
  }
372
529
  get countOnError() {
373
- return this.onError.length;
530
+ return this.onError?.length ?? 0;
374
531
  }
375
- replyFn(reply) {
376
- this.response = reply;
377
- this.responseOverwritten = true;
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
+ return void 0;
378
550
  }
379
- async init() {
551
+ before() {
380
552
  const ci = getContextInjector$1();
381
- for (const { handler, name } of this.handlers) {
382
- const response = await ci.with(`Interceptor:${name}`, { "moost.interceptor.stage": "init" }, () => handler((fn) => {
383
- this.before.push({
384
- name,
385
- fn
386
- });
387
- }, (fn) => {
388
- this.after.unshift({
389
- name,
390
- fn
391
- });
392
- }, (fn) => {
393
- this.onError.unshift({
394
- name,
395
- fn
396
- });
397
- }));
398
- if (response !== void 0) return response;
553
+ for (let i = 0; i < this.handlers.length; i++) {
554
+ const entry = this.handlers[i];
555
+ const { handler } = entry;
556
+ if (typeof handler === "function") {
557
+ const factoryResult = handler();
558
+ if (isThenable(factoryResult)) return this._beforeAsyncFactory(ci, factoryResult, i);
559
+ const pending = this.registerDef(factoryResult, entry, ci);
560
+ if (pending) return this._beforeAsyncPending(ci, pending, i);
561
+ if (this.responseOverwritten) return this.response;
562
+ } else {
563
+ const pending = this.registerDef(handler, entry, ci);
564
+ if (pending) return this._beforeAsyncPending(ci, pending, i);
565
+ if (this.responseOverwritten) return this.response;
566
+ }
399
567
  }
400
568
  }
401
- async fireBefore(response) {
402
- const ci = getContextInjector$1();
569
+ async _beforeAsyncFactory(ci, factoryPromise, startIndex) {
570
+ const def = await factoryPromise;
571
+ const entry = this.handlers[startIndex];
572
+ const pending = this.registerDef(def, entry, ci);
573
+ if (pending) await pending;
574
+ if (this.responseOverwritten) return this.response;
575
+ return this._beforeFrom(ci, startIndex + 1);
576
+ }
577
+ async _beforeAsyncPending(ci, pending, startIndex) {
578
+ await pending;
579
+ if (this.responseOverwritten) return this.response;
580
+ return this._beforeFrom(ci, startIndex + 1);
581
+ }
582
+ async _beforeFrom(ci, startIndex) {
583
+ for (let i = startIndex; i < this.handlers.length; i++) {
584
+ const entry = this.handlers[i];
585
+ const { handler } = entry;
586
+ let def;
587
+ if (typeof handler === "function") def = await handler();
588
+ else def = handler;
589
+ const pending = this.registerDef(def, entry, ci);
590
+ if (pending) await pending;
591
+ if (this.responseOverwritten) return this.response;
592
+ }
593
+ }
594
+ fireAfter(response) {
403
595
  this.response = response;
404
- for (const { name, fn } of this.before) {
405
- await ci.with(`Interceptor:${name}`, { "moost.interceptor.stage": "before" }, () => fn(this.replyFn.bind(this)));
406
- if (this.responseOverwritten) break;
596
+ const isError = response instanceof Error;
597
+ const handlers = isError ? this.onError : this.after;
598
+ if (!handlers) return this.response;
599
+ const ci = getContextInjector$1();
600
+ const stage = isError ? "onError" : "after";
601
+ for (let i = 0; i < handlers.length; i++) {
602
+ const { name, fn } = handlers[i];
603
+ const result = ci ? ci.with(`Interceptor:${name}`, { "moost.interceptor.stage": stage }, () => fn(response, this.getReplyFn())) : fn(response, this.getReplyFn());
604
+ if (isThenable(result)) return this._fireAfterAsync({
605
+ ci,
606
+ handlers,
607
+ stage,
608
+ response
609
+ }, result, i);
407
610
  }
408
611
  return this.response;
409
612
  }
410
- async fireAfter(response) {
411
- const ci = getContextInjector$1();
412
- this.response = response;
413
- if (response instanceof Error) for (const { name, fn } of this.onError) await ci.with(`Interceptor:${name}`, { "moost.interceptor.stage": "after" }, () => fn(response, this.replyFn.bind(this)));
414
- else for (const { name, fn } of this.after) await ci.with(`Interceptor:${name}`, { "moost.interceptor.stage": "onError" }, () => fn(response, this.replyFn.bind(this)));
613
+ async _fireAfterAsync(ctx, pending, startIndex) {
614
+ await pending;
615
+ for (let i = startIndex + 1; i < ctx.handlers.length; i++) {
616
+ const { name, fn } = ctx.handlers[i];
617
+ if (ctx.ci) await ctx.ci.with(`Interceptor:${name}`, { "moost.interceptor.stage": ctx.stage }, () => fn(ctx.response, this.getReplyFn()));
618
+ else await fn(ctx.response, this.getReplyFn());
619
+ }
415
620
  return this.response;
416
621
  }
417
622
  constructor(handlers) {
418
623
  _define_property$1(this, "handlers", void 0);
419
- _define_property$1(this, "before", void 0);
420
624
  _define_property$1(this, "after", void 0);
421
625
  _define_property$1(this, "onError", void 0);
422
626
  _define_property$1(this, "response", void 0);
423
627
  _define_property$1(this, "responseOverwritten", void 0);
628
+ _define_property$1(this, "_boundReplyFn", void 0);
424
629
  this.handlers = handlers;
425
- this.before = [];
426
- this.after = [];
427
- this.onError = [];
428
630
  this.responseOverwritten = false;
429
631
  }
430
632
  };
@@ -432,25 +634,129 @@ var InterceptorHandler = class {
432
634
  //#endregion
433
635
  //#region packages/moost/src/utils.ts
434
636
  const mate = getMoostMate();
435
- function getIterceptorHandlerFactory(interceptors, getTargetInstance, pipes, logger) {
637
+ const noInterceptors = () => void 0;
638
+ function findInterceptorMethods(handler) {
639
+ const fakeInstance = Object.create(handler.prototype);
640
+ const methods = getInstanceOwnMethods(fakeInstance);
641
+ const result = {};
642
+ for (const method of methods) {
643
+ const methodMeta = mate.read(fakeInstance, method);
644
+ const hook = methodMeta?.interceptorHook;
645
+ if (hook === "before" || hook === "after" || hook === "error") result[hook] = method;
646
+ }
647
+ return result;
648
+ }
649
+ function buildMethodResolver(opts) {
650
+ const fakeInstance = Object.create(opts.handler.prototype);
651
+ const methodMeta = mate.read(fakeInstance, opts.methodName) || {};
652
+ const argsPipes = [];
653
+ for (const p of methodMeta.params || []) argsPipes.push({
654
+ meta: p,
655
+ pipes: mergeSorted(opts.pipes, p.pipes)
656
+ });
657
+ if (argsPipes.length === 0) return void 0;
658
+ return resolveArguments(argsPipes, {
659
+ classMeta: opts.classMeta,
660
+ methodMeta,
661
+ type: opts.handler,
662
+ key: opts.methodName
663
+ });
664
+ }
665
+ function callWithArgs(instance, methodName, resolveArgs) {
666
+ const ci = getContextInjector$1();
667
+ const args = ci ? ci.with("Arguments:resolve", resolveArgs) : resolveArgs();
668
+ if (isThenable(args)) return args.then((a) => instance[methodName](...a));
669
+ return instance[methodName](...args);
670
+ }
671
+ function callMethod(instance, methodName, resolveArgs) {
672
+ if (resolveArgs) return callWithArgs(instance, methodName, resolveArgs);
673
+ return instance[methodName]();
674
+ }
675
+ function createClassInterceptorFactory(opts) {
676
+ const infact = getMoostInfact();
677
+ function buildDef(instance) {
678
+ const def = {};
679
+ if (opts.methods.before) {
680
+ const methodName = opts.methods.before;
681
+ const resolveArgs = opts.resolvers[methodName];
682
+ if (resolveArgs) def.before = (reply) => {
683
+ setOvertake(reply);
684
+ return callWithArgs(instance, methodName, resolveArgs);
685
+ };
686
+ else def.before = () => instance[methodName]();
687
+ }
688
+ if (opts.methods.after) {
689
+ const methodName = opts.methods.after;
690
+ const resolveArgs = opts.resolvers[methodName];
691
+ def.after = (response, reply) => {
692
+ setOvertake(reply);
693
+ setInterceptResult(response);
694
+ return callMethod(instance, methodName, resolveArgs);
695
+ };
696
+ }
697
+ if (opts.methods.error) {
698
+ const methodName = opts.methods.error;
699
+ const resolveArgs = opts.resolvers[methodName];
700
+ def.error = (error, reply) => {
701
+ setOvertake(reply);
702
+ setInterceptResult(error);
703
+ return callMethod(instance, methodName, resolveArgs);
704
+ };
705
+ }
706
+ return def;
707
+ }
708
+ function fromTarget(targetInstance) {
709
+ const result = infact.getForInstance(targetInstance, opts.handler, { customData: { pipes: opts.pipes } });
710
+ if (isThenable(result)) return result.then(buildDef);
711
+ return buildDef(result);
712
+ }
436
713
  return () => {
437
- const interceptorHandlers = [];
438
- for (const { handler, name } of interceptors) {
439
- const interceptorMeta = mate.read(handler);
440
- if (interceptorMeta?.injectable) interceptorHandlers.push({
441
- handler: async (...args) => {
442
- const targetInstance = await getTargetInstance();
443
- return (await getCallableFn(targetInstance, handler, pipes, logger))(...args);
444
- },
445
- name
446
- });
447
- else interceptorHandlers.push({
714
+ const result = opts.getTargetInstance();
715
+ if (isThenable(result)) return result.then(fromTarget);
716
+ return fromTarget(result);
717
+ };
718
+ }
719
+ function getIterceptorHandlerFactory(interceptors, getTargetInstance, pipes) {
720
+ if (interceptors.length === 0) return noInterceptors;
721
+ const precomputedHandlers = interceptors.map(({ handler, name }) => {
722
+ const spanName = `Interceptor:${name}`;
723
+ if (typeof handler !== "function") return {
724
+ handler,
725
+ name,
726
+ spanName
727
+ };
728
+ const interceptorMeta = mate.read(handler);
729
+ if (!interceptorMeta?.interceptor) throw new Error(`Invalid interceptor "${name}": must be TInterceptorDef or @Interceptor class`);
730
+ const classMeta = interceptorMeta;
731
+ const methods = findInterceptorMethods(handler);
732
+ const mergedPipes = mergeSorted(pipes, classMeta.pipes);
733
+ const resolvers = {};
734
+ for (const hook of [
735
+ "before",
736
+ "after",
737
+ "error"
738
+ ]) {
739
+ const methodName = methods[hook];
740
+ if (methodName) resolvers[methodName] = buildMethodResolver({
448
741
  handler,
449
- name
742
+ methodName,
743
+ pipes: mergedPipes,
744
+ classMeta
450
745
  });
451
746
  }
452
- return Promise.resolve(new InterceptorHandler(interceptorHandlers));
453
- };
747
+ return {
748
+ handler: createClassInterceptorFactory({
749
+ handler,
750
+ methods,
751
+ resolvers,
752
+ getTargetInstance,
753
+ pipes: mergedPipes
754
+ }),
755
+ name,
756
+ spanName
757
+ };
758
+ });
759
+ return () => new InterceptorHandler(precomputedHandlers);
454
760
  }
455
761
 
456
762
  //#endregion
@@ -477,32 +783,20 @@ async function bindControllerMethods(options) {
477
783
  for (const method of methods) {
478
784
  const methodMeta = getMoostMate().read(fakeInstance, method) || {};
479
785
  if (!methodMeta.handlers?.length) continue;
480
- const pipes = [...opts.pipes || [], ...methodMeta.pipes || []].sort((a, b) => a.priority - b.priority);
481
- const interceptors = [
482
- ...opts.interceptors || [],
483
- ...meta.interceptors || [],
484
- ...methodMeta.interceptors || []
485
- ].sort((a, b) => a.priority - b.priority);
486
- const getIterceptorHandler = getIterceptorHandlerFactory(interceptors, getInstance, pipes, options.logger);
786
+ const pipes = mergeSorted(opts.pipes, methodMeta.pipes);
787
+ const interceptors = mergeSorted(opts.interceptors, meta.interceptors, methodMeta.interceptors);
788
+ const getIterceptorHandler = getIterceptorHandlerFactory(interceptors, getInstance, pipes);
487
789
  const argsPipes = [];
488
790
  for (const p of methodMeta.params || []) argsPipes.push({
489
791
  meta: p,
490
- pipes: [...pipes, ...p.pipes || []].sort((a, b) => a.priority - b.priority)
792
+ pipes: mergeSorted(pipes, p.pipes)
793
+ });
794
+ const resolveArgs = resolveArguments(argsPipes, {
795
+ classMeta: meta,
796
+ methodMeta,
797
+ type: classConstructor,
798
+ key: method
491
799
  });
492
- const resolveArgs = async () => {
493
- const args = [];
494
- for (const [i, { pipes: pipes$1, meta: paramMeta }] of argsPipes.entries()) args[i] = await runPipes(pipes$1, void 0, {
495
- classMeta: meta,
496
- methodMeta,
497
- paramMeta,
498
- type: classConstructor,
499
- key: method,
500
- index: i,
501
- targetMeta: paramMeta,
502
- instantiate: (t) => useControllerContext().instantiate(t)
503
- }, "PARAM");
504
- return args;
505
- };
506
800
  const wm = /* @__PURE__ */ new WeakMap();
507
801
  controllerOverview.handlers.push(...methodMeta.handlers.map((h) => {
508
802
  const data = {
@@ -524,6 +818,7 @@ async function bindControllerMethods(options) {
524
818
  handlers: methodMeta.handlers,
525
819
  getIterceptorHandler,
526
820
  resolveArgs,
821
+ controllerName: classConstructor.name,
527
822
  logHandler: (eventName) => {
528
823
  options.moostInstance.logMappedHandler(eventName, classConstructor, method);
529
824
  },
@@ -541,7 +836,19 @@ async function bindControllerMethods(options) {
541
836
 
542
837
  //#endregion
543
838
  //#region packages/moost/src/decorators/circular.decorator.ts
544
- function Circular(resolver) {
839
+ /**
840
+ * Marks a constructor parameter dependency as circular.
841
+ * The resolver function is called lazily to break the circular reference.
842
+ *
843
+ * @param resolver - Factory that returns the class constructor (e.g. `() => MyService`).
844
+ *
845
+ * @example
846
+ * ```ts
847
+ * class ServiceA {
848
+ * constructor(@Circular(() => ServiceB) private b: ServiceB) {}
849
+ * }
850
+ * ```
851
+ */ function Circular(resolver) {
545
852
  return getMoostMate().decorate("circular", resolver);
546
853
  }
547
854
 
@@ -609,10 +916,10 @@ function Circular(resolver) {
609
916
  * Stores Required metadata
610
917
  */ function Required() {
611
918
  const mate$1 = getMoostMate();
612
- return mate$1.apply(mate$1.decorate("required", true), mate$1.decorateClass((meta, level, key, index) => {
613
- if (typeof index !== "number" && meta && ["string", "symbol"].includes(typeof key)) {
919
+ return mate$1.apply(mate$1.decorate("required", true), mate$1.decorateClass((meta, level, key$2, index) => {
920
+ if (typeof index !== "number" && meta && ["string", "symbol"].includes(typeof key$2)) {
614
921
  meta.requiredProps = meta.requiredProps || [];
615
- meta.requiredProps.push(key);
922
+ meta.requiredProps.push(key$2);
616
923
  }
617
924
  return meta;
618
925
  }));
@@ -679,15 +986,24 @@ var TInterceptorPriority = /* @__PURE__ */ function(TInterceptorPriority$1) {
679
986
  /**
680
987
  * ## Intercept
681
988
  * ### @Decorator
682
- * Set interceptor
683
- * @param handler interceptor fn (use defineInterceptorFn)
684
- * @param priority interceptor priority
685
- * @returns
989
+ * Attach an interceptor to a class or method.
990
+ * @param handler @Interceptor class constructor or TInterceptorDef object
991
+ * @param priority interceptor priority (overrides handler's own priority)
992
+ * @param name — interceptor name for tracing
686
993
  */ function Intercept(handler, priority, name) {
687
- return getMoostMate().decorate("interceptors", {
994
+ const mate$1 = getMoostMate();
995
+ if (typeof handler === "function") {
996
+ const interceptorMeta = mate$1.read(handler);
997
+ return mate$1.decorate("interceptors", {
998
+ handler,
999
+ priority: priority ?? interceptorMeta?.interceptor?.priority ?? 4,
1000
+ name: name || handler.name || "<anonymous>"
1001
+ }, true);
1002
+ }
1003
+ return mate$1.decorate("interceptors", {
688
1004
  handler,
689
- priority: priority || handler.priority || 4,
690
- name: name || handler._name || handler.name
1005
+ priority: priority ?? handler.priority ?? 4,
1006
+ name: name || handler._name || "<anonymous>"
691
1007
  }, true);
692
1008
  }
693
1009
 
@@ -700,14 +1016,14 @@ var TInterceptorPriority = /* @__PURE__ */ function(TInterceptorPriority$1) {
700
1016
  * @param label - field label
701
1017
  * @paramType unknown
702
1018
  */ function Resolve(resolver, label) {
703
- return (target, key, index) => {
1019
+ return (target, key$2, index) => {
704
1020
  const i = typeof index === "number" ? index : void 0;
705
1021
  getMoostMate().decorate("resolver", (metas, level) => {
706
1022
  let newLabel = label;
707
1023
  if (!newLabel && level === "PROP" && typeof metas.key === "string") newLabel = metas.key;
708
- fillLabel(target, key || "", i, newLabel);
1024
+ fillLabel(target, key$2 || "", i, newLabel);
709
1025
  return resolver(metas, level);
710
- })(target, key, i);
1026
+ })(target, key$2, i);
711
1027
  };
712
1028
  }
713
1029
  /**
@@ -743,23 +1059,90 @@ var TInterceptorPriority = /* @__PURE__ */ function(TInterceptorPriority$1) {
743
1059
  */ function ConstFactory(factory, label) {
744
1060
  return Resolve(async () => factory(), label);
745
1061
  }
746
- function fillLabel(target, key, index, name) {
1062
+ function fillLabel(target, key$2, index, name) {
747
1063
  if (name) {
748
- const meta = getMoostMate().read(target, key);
1064
+ const meta = getMoostMate().read(target, key$2);
749
1065
  if (typeof index === "number") {
750
- if (!meta?.params?.[index]?.label) Label(name)(target, key, index);
751
- } else if (!meta?.label) Label(name)(target, key);
1066
+ if (!meta?.params?.[index]?.label) Label(name)(target, key$2, index);
1067
+ } else if (!meta?.label) Label(name)(target, key$2);
752
1068
  }
753
1069
  }
754
1070
 
1071
+ //#endregion
1072
+ //#region packages/moost/src/decorators/interceptor.decorator.ts
1073
+ /**
1074
+ * ## @Interceptor
1075
+ * ### Class Decorator
1076
+ * Marks a class as a decorator-based interceptor.
1077
+ * Auto-adds @Injectable() if not already present.
1078
+ *
1079
+ * Use @Before(), @After(), and @OnError() method decorators
1080
+ * to register lifecycle hooks.
1081
+ *
1082
+ * @param priority — interceptor priority (default: INTERCEPTOR)
1083
+ * @param scope — DI scope ('FOR_EVENT' | 'SINGLETON' | true)
1084
+ */ function Interceptor(priority, scope) {
1085
+ const mate$1 = getMoostMate();
1086
+ const decorators = [insureInjectable, mate$1.decorate("interceptor", { priority: priority ?? TInterceptorPriority.INTERCEPTOR })];
1087
+ if (scope) decorators.push(mate$1.decorate("injectable", scope));
1088
+ return mate$1.apply(...decorators);
1089
+ }
1090
+ /**
1091
+ * ## @Before
1092
+ * ### Method Decorator
1093
+ * Marks a method as a before-phase interceptor hook.
1094
+ * Runs before argument resolution and handler execution.
1095
+ * Use @Overtake() param to get the reply fn for short-circuiting.
1096
+ */ function Before() {
1097
+ return getMoostMate().decorate("interceptorHook", "before");
1098
+ }
1099
+ /**
1100
+ * ## @After
1101
+ * ### Method Decorator
1102
+ * Marks a method as an after-phase interceptor hook.
1103
+ * Runs after successful handler execution.
1104
+ * Use @Response() param for the handler result, @Overtake() for the reply fn.
1105
+ */ function After() {
1106
+ return getMoostMate().decorate("interceptorHook", "after");
1107
+ }
1108
+ /**
1109
+ * ## @OnError
1110
+ * ### Method Decorator
1111
+ * Marks a method as an error-phase interceptor hook.
1112
+ * Runs when the handler throws or returns an Error.
1113
+ * Use @Response() param for the error, @Overtake() for the reply fn.
1114
+ */ function OnError() {
1115
+ return getMoostMate().decorate("interceptorHook", "error");
1116
+ }
1117
+ /**
1118
+ * ## @Overtake
1119
+ * ### Parameter Decorator
1120
+ * Injects the reply function into an interceptor method parameter.
1121
+ * Call the reply fn to short-circuit the handler or replace the response.
1122
+ */ function Overtake() {
1123
+ return Resolve(() => useOvertake(), "overtake");
1124
+ }
1125
+ /**
1126
+ * ## @Response
1127
+ * ### Parameter Decorator
1128
+ * Injects the handler result into an interceptor method parameter.
1129
+ * In @After methods, this is the successful response.
1130
+ * In @OnError methods, this is the Error that was thrown.
1131
+ */ function Response() {
1132
+ return Resolve(() => useInterceptResult(), "response");
1133
+ }
1134
+
755
1135
  //#endregion
756
1136
  //#region packages/moost/src/decorators/logger.decorator.ts
757
1137
  /**
758
1138
  * Resolves event logger from event context
759
1139
  * @param topic
760
- * @returns Resolver to '@wooksjs/event-core' (EventLogger)
1140
+ * @returns Resolver to '@wooksjs/event-core' (Logger)
761
1141
  */ function InjectEventLogger(topic) {
762
- return Resolve(() => useEventLogger$1(topic));
1142
+ return Resolve(() => {
1143
+ const l = useLogger$1();
1144
+ return topic && typeof l.topic === "function" ? l.topic(topic) : l;
1145
+ });
763
1146
  }
764
1147
  /**
765
1148
  * Resolves app-level logger
@@ -784,7 +1167,7 @@ function fillLabel(target, key, index, name) {
784
1167
 
785
1168
  //#endregion
786
1169
  //#region packages/moost/src/pipes/types.ts
787
- var TPipePriority = /* @__PURE__ */ function(TPipePriority$1) {
1170
+ /** Execution priority for pipes. Lower values run first. */ var TPipePriority = /* @__PURE__ */ function(TPipePriority$1) {
788
1171
  TPipePriority$1[TPipePriority$1["BEFORE_RESOLVE"] = 0] = "BEFORE_RESOLVE";
789
1172
  TPipePriority$1[TPipePriority$1["RESOLVE"] = 1] = "RESOLVE";
790
1173
  TPipePriority$1[TPipePriority$1["AFTER_RESOLVE"] = 2] = "AFTER_RESOLVE";
@@ -872,31 +1255,73 @@ var TPipePriority = /* @__PURE__ */ function(TPipePriority$1) {
872
1255
  //#endregion
873
1256
  //#region packages/moost/src/define.ts
874
1257
  /**
875
- * ### Define Interceptor Function
1258
+ * Define a before-phase interceptor.
1259
+ *
1260
+ * Runs before argument resolution and handler execution.
1261
+ * Call `reply(value)` to short-circuit the handler and respond early.
876
1262
  *
1263
+ * @example
877
1264
  * ```ts
878
- * defineInterceptorFn((before, after, onError) => {
879
- * //init
880
- * before(() => {
881
- * // before handler
882
- * })
883
- * after((response, reply) => {
884
- * // after handler
885
- * })
886
- * onError((error, reply) => {
887
- * // when error occured
888
- * })
889
- * },
890
- * TInterceptorPriority.INTERCEPTOR,
891
- * )
1265
+ * const authGuard = defineBeforeInterceptor((reply) => {
1266
+ * if (!isAuthenticated()) reply(new HttpError(401))
1267
+ * }, TInterceptorPriority.GUARD)
892
1268
  * ```
1269
+ */ function defineBeforeInterceptor(fn, priority = TInterceptorPriority.INTERCEPTOR) {
1270
+ return {
1271
+ before: fn,
1272
+ priority
1273
+ };
1274
+ }
1275
+ /**
1276
+ * Define an after-phase interceptor.
893
1277
  *
894
- * @param fn interceptor function
895
- * @param priority priority of the interceptor where BEFORE_ALL = 0, BEFORE_GUARD = 1, GUARD = 2, AFTER_GUARD = 3, INTERCEPTOR = 4, CATCH_ERROR = 5, AFTER_ALL = 6
896
- * @returns
897
- */ function defineInterceptorFn(fn, priority = TInterceptorPriority.INTERCEPTOR) {
898
- fn.priority = priority;
899
- return fn;
1278
+ * Runs after successful handler execution.
1279
+ * Call `reply(value)` to transform/replace the response.
1280
+ *
1281
+ * @example
1282
+ * ```ts
1283
+ * const setHeader = defineAfterInterceptor(() => {
1284
+ * useResponse().setHeader('x-server', 'my-server')
1285
+ * }, TInterceptorPriority.AFTER_ALL)
1286
+ * ```
1287
+ */ function defineAfterInterceptor(fn, priority = TInterceptorPriority.AFTER_ALL) {
1288
+ return {
1289
+ after: fn,
1290
+ priority
1291
+ };
1292
+ }
1293
+ /**
1294
+ * Define an error-phase interceptor.
1295
+ *
1296
+ * Runs when the handler throws or returns an Error.
1297
+ * Call `reply(value)` to recover from the error with a replacement response.
1298
+ *
1299
+ * @example
1300
+ * ```ts
1301
+ * const errorFormatter = defineErrorInterceptor((error, reply) => {
1302
+ * reply({ message: error.message, status: 500 })
1303
+ * }, TInterceptorPriority.CATCH_ERROR)
1304
+ * ```
1305
+ */ function defineErrorInterceptor(fn, priority = TInterceptorPriority.CATCH_ERROR) {
1306
+ return {
1307
+ error: fn,
1308
+ priority
1309
+ };
1310
+ }
1311
+ /**
1312
+ * Define a full interceptor with multiple lifecycle hooks.
1313
+ *
1314
+ * @example
1315
+ * ```ts
1316
+ * const myInterceptor = defineInterceptor({
1317
+ * before(reply) { ... },
1318
+ * after(response, reply) { ... },
1319
+ * error(error, reply) { ... },
1320
+ * }, TInterceptorPriority.INTERCEPTOR)
1321
+ * ```
1322
+ */ function defineInterceptor(def, priority = TInterceptorPriority.INTERCEPTOR) {
1323
+ def.priority = priority;
1324
+ return def;
900
1325
  }
901
1326
  /**
902
1327
  * ### Define Pipe Function
@@ -910,8 +1335,8 @@ var TPipePriority = /* @__PURE__ */ function(TPipePriority$1) {
910
1335
  * )
911
1336
  * ```
912
1337
  *
913
- * @param fn interceptor function
914
- * @param priority priority of the pipe where BEFORE_RESOLVE = 0, RESOLVE = 1, AFTER_RESOLVE = 2, BEFORE_TRANSFORM = 3, TRANSFORM = 4, AFTER_TRANSFORM = 5, BEFORE_VALIDATE = 6, VALIDATE = 7, AFTER_VALIDATE = 8
1338
+ * @param fn pipe function
1339
+ * @param priority priority of the pipe
915
1340
  * @returns
916
1341
  */ function definePipeFn(fn, priority = TPipePriority.TRANSFORM) {
917
1342
  fn.priority = priority;
@@ -935,14 +1360,14 @@ const sharedPipes = [{
935
1360
 
936
1361
  //#endregion
937
1362
  //#region packages/moost/src/moost.ts
938
- function _define_property(obj, key, value) {
939
- if (key in obj) Object.defineProperty(obj, key, {
1363
+ function _define_property(obj, key$2, value) {
1364
+ if (key$2 in obj) Object.defineProperty(obj, key$2, {
940
1365
  value,
941
1366
  enumerable: true,
942
1367
  configurable: true,
943
1368
  writable: true
944
1369
  });
945
- else obj[key] = value;
1370
+ else obj[key$2] = value;
946
1371
  return obj;
947
1372
  }
948
1373
  /**
@@ -1061,27 +1486,24 @@ function _define_property(obj, key, value) {
1061
1486
  async bindController(controller, provide, replace, globalPrefix, replaceOwnPrefix) {
1062
1487
  const mate$1 = getMoostMate();
1063
1488
  const classMeta = mate$1.read(controller);
1064
- const infact$1 = getMoostInfact();
1489
+ const infact = getMoostInfact();
1065
1490
  const isControllerConsructor = isConstructor$1(controller);
1066
- const pipes = [...this.pipes, ...classMeta?.pipes || []].sort((a, b) => a.priority - b.priority);
1491
+ const pipes = mergeSorted(this.pipes, classMeta?.pipes);
1067
1492
  let instance;
1068
1493
  const infactOpts = {
1069
1494
  provide,
1070
1495
  replace,
1071
1496
  customData: { pipes }
1072
1497
  };
1073
- if (isControllerConsructor && (classMeta?.injectable === "SINGLETON" || classMeta?.injectable === true)) await createAsyncEventContext({
1074
- event: { type: "init" },
1075
- options: {}
1076
- })(async () => {
1498
+ if (isControllerConsructor && (classMeta?.injectable === "SINGLETON" || classMeta?.injectable === true)) await createEventContext$1({ logger: this.logger }, async () => {
1077
1499
  setControllerContext(this, "bindController", "");
1078
- instance = await infact$1.get(controller, infactOpts);
1500
+ instance = await infact.get(controller, infactOpts);
1079
1501
  });
1080
1502
  else if (!isControllerConsructor) {
1081
1503
  instance = controller;
1082
- infact$1.setInstanceRegistries(instance, provide, replace, { pipes });
1504
+ infact.setInstanceRegistries(instance, provide, replace, { pipes });
1083
1505
  }
1084
- const getInstance = instance ? () => Promise.resolve(instance) : async () => await infact$1.get(controller, { ...infactOpts });
1506
+ const getInstance = instance ? () => instance : async () => await infact.get(controller, { ...infactOpts });
1085
1507
  const classConstructor = isConstructor$1(controller) ? controller : getConstructor$1(controller);
1086
1508
  this.controllersOverview.push(await bindControllerMethods({
1087
1509
  getInstance,
@@ -1137,22 +1559,26 @@ function _define_property(obj, key, value) {
1137
1559
  if (!this.globalInterceptorHandler) {
1138
1560
  const mate$1 = getMoostMate();
1139
1561
  const thisMeta = mate$1.read(this);
1140
- const pipes = [...this.pipes || [], ...thisMeta?.pipes || []].sort((a, b) => a.priority - b.priority);
1141
- const interceptors = [...this.interceptors, ...thisMeta?.interceptors || []].sort((a, b) => a.priority - b.priority);
1142
- this.globalInterceptorHandler = getIterceptorHandlerFactory(interceptors, () => Promise.resolve(this), pipes, this.logger);
1562
+ const pipes = mergeSorted(this.pipes, thisMeta?.pipes);
1563
+ const interceptors = mergeSorted(this.interceptors, thisMeta?.interceptors);
1564
+ this.globalInterceptorHandler = getIterceptorHandlerFactory(interceptors, () => this, pipes);
1143
1565
  }
1144
1566
  return this.globalInterceptorHandler();
1145
1567
  }
1146
1568
  applyGlobalInterceptors(...items) {
1147
- for (const item of items) if (typeof item === "function") this.interceptors.push({
1148
- handler: item,
1149
- priority: typeof item.priority === "number" ? item.priority : TInterceptorPriority.INTERCEPTOR,
1150
- name: item._name || item.name || "<anonymous>"
1151
- });
1569
+ const mate$1 = getMoostMate();
1570
+ for (const item of items) if (typeof item === "function") {
1571
+ const meta = mate$1.read(item);
1572
+ this.interceptors.push({
1573
+ handler: item,
1574
+ priority: meta?.interceptor?.priority ?? TInterceptorPriority.INTERCEPTOR,
1575
+ name: item.name || "<anonymous>"
1576
+ });
1577
+ } else if ("handler" in item) this.interceptors.push(item);
1152
1578
  else this.interceptors.push({
1153
- handler: item.handler,
1154
- priority: item.priority,
1155
- name: item.name || item.handler._name || item.handler.name || "<anonymous>"
1579
+ handler: item,
1580
+ priority: item.priority ?? TInterceptorPriority.INTERCEPTOR,
1581
+ name: item._name || "<anonymous>"
1156
1582
  });
1157
1583
  this.globalInterceptorHandler = void 0;
1158
1584
  return this;
@@ -1202,4 +1628,4 @@ function _define_property(obj, key, value) {
1202
1628
  };
1203
1629
 
1204
1630
  //#endregion
1205
- export { ApplyDecorators, Circular, Const, ConstFactory, ContextInjector, Controller, Description, EventLogger, Id, ImportController, Inherit, Inject, InjectEventLogger, InjectFromScope, InjectMoostLogger, InjectScopeVars, Injectable, Intercept, InterceptorHandler, Label, LoggerTopic, Moost, Optional, Param, Params, Pipe, ProstoLogger, Provide, Replace, Required, Resolve, TInterceptorPriority, TPipePriority, Value, clearGlobalWooks, createLogger, createProvideRegistry, createReplaceRegistry, defineInfactScope, defineInterceptorFn, defineMoostEventHandler, definePipeFn, getConstructor, getContextInjector, getGlobalWooks, getInfactScopeVars, getInstanceOwnMethods, getInstanceOwnProps, getMoostInfact, getMoostMate, getNewMoostInfact, isConstructor, loggerConsoleTransport, registerEventScope, replaceContextInjector, resolvePipe, setControllerContext, setInfactLoggingOptions, useAsyncEventContext, useControllerContext, useEventLogger };
1631
+ export { After, ApplyDecorators, Before, Circular, Const, ConstFactory, ContextInjector, Controller, Description, Id, ImportController, Inherit, Inject, InjectEventLogger, InjectFromScope, InjectMoostLogger, InjectScopeVars, Injectable, Intercept, Interceptor, InterceptorHandler, Label, LoggerTopic, Moost, OnError, Optional, Overtake, Param, Params, Pipe, ProstoLogger, Provide, Replace, Required, Resolve, Response, TInterceptorPriority, TPipePriority, Value, cached, clearGlobalWooks, createEventContext, createLogger, createProvideRegistry, createReplaceRegistry, current, defineAfterInterceptor, defineBeforeInterceptor, defineErrorInterceptor, defineInfactScope, defineInterceptor, defineMoostEventHandler, definePipeFn, eventTypeKey, getConstructor, getContextInjector, getGlobalWooks, getInfactScopeVars, getInstanceOwnMethods, getInstanceOwnProps, getMoostInfact, getMoostMate, getNewMoostInfact, isConstructor, isThenable, key, loggerConsoleTransport, mergeSorted, registerEventScope, replaceContextInjector, resetContextInjector, resolvePipe, run, setControllerContext, setInfactLoggingOptions, setInterceptResult, setOvertake, useControllerContext, useInterceptResult, useLogger, useOvertake, useScopeId };