awilixify 3.0.0 → 3.1.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.
Files changed (55) hide show
  1. package/README.md +5 -2
  2. package/dist/index.d.ts +2 -1
  3. package/dist/lib/devtools/devtools.types.d.ts +63 -0
  4. package/dist/lib/devtools/devtools.types.js +1 -0
  5. package/dist/lib/devtools/helpers.d.ts +2 -0
  6. package/dist/lib/devtools/helpers.js +18 -0
  7. package/dist/lib/devtools/index.d.ts +4 -0
  8. package/dist/lib/devtools/index.js +3 -0
  9. package/dist/lib/di/contexts/container-context-base.d.ts +13 -3
  10. package/dist/lib/di/contexts/container-context-base.js +45 -7
  11. package/dist/lib/di/contexts/di-context-async.js +15 -9
  12. package/dist/lib/di/contexts/di-context-base.js +5 -4
  13. package/dist/lib/di/contexts/module-graph-ensurer.d.ts +1 -0
  14. package/dist/lib/di/contexts/module-graph-ensurer.js +5 -0
  15. package/dist/lib/di/errors.d.ts +3 -0
  16. package/dist/lib/di/errors.js +6 -0
  17. package/dist/lib/di/modules/module-factories.js +9 -1
  18. package/dist/lib/di/modules/runtime-module.types.d.ts +7 -0
  19. package/dist/lib/di/processors/handler-processor.d.ts +5 -1
  20. package/dist/lib/di/processors/handler-processor.js +41 -7
  21. package/dist/lib/di/processors/initializer-processor.d.ts +3 -2
  22. package/dist/lib/di/processors/initializer-processor.js +33 -29
  23. package/dist/lib/di/processors/interceptor-processor.d.ts +4 -1
  24. package/dist/lib/di/processors/interceptor-processor.js +18 -2
  25. package/dist/lib/di/processors/keyed-feature-registrar.js +1 -1
  26. package/dist/lib/di/processors/lifecycle-processor.d.ts +0 -1
  27. package/dist/lib/di/processors/lifecycle-processor.js +1 -4
  28. package/dist/lib/di/providers/provider-resolver.d.ts +7 -3
  29. package/dist/lib/di/providers/provider-resolver.js +56 -16
  30. package/dist/lib/di/providers/types/provider.types.d.ts +5 -5
  31. package/dist/lib/mediator/mediator.d.ts +4 -1
  32. package/dist/lib/mediator/mediator.js +13 -2
  33. package/dist/lib/react/index.d.ts +4 -0
  34. package/dist/lib/react/index.js +4 -0
  35. package/dist/lib/react/react-di-context.d.ts +2 -7
  36. package/dist/lib/react/react-di-context.js +5 -8
  37. package/dist/lib/react/react-module.types.d.ts +5 -19
  38. package/dist/tsconfig.tsbuildinfo +1 -1
  39. package/package.json +7 -3
  40. package/dist/lib/di/contexts/context-overrides.d.ts +0 -22
  41. package/dist/lib/di/contexts/context-overrides.js +0 -93
  42. package/dist/lib/di/contexts/module-overrides.d.ts +0 -70
  43. package/dist/lib/di/contexts/module-overrides.js +0 -3
  44. package/dist/lib/di/contexts/override-value.types.d.ts +0 -6
  45. package/dist/lib/di/contexts/override-value.types.js +0 -1
  46. package/dist/lib/di/contexts/overrides-processor.d.ts +0 -22
  47. package/dist/lib/di/contexts/overrides-processor.js +0 -93
  48. package/dist/lib/di/contexts/provider-overrides.types.d.ts +0 -19
  49. package/dist/lib/di/contexts/provider-overrides.types.js +0 -1
  50. package/dist/lib/di/modules/provider-overrides.types.d.ts +0 -19
  51. package/dist/lib/di/modules/provider-overrides.types.js +0 -1
  52. package/dist/lib/di/providers/basic-provider-resolver.d.ts +0 -35
  53. package/dist/lib/di/providers/basic-provider-resolver.js +0 -88
  54. package/dist/react.d.ts +0 -4
  55. package/dist/react.js +0 -4
package/README.md CHANGED
@@ -7,8 +7,11 @@ Transport-agnostic, type-safe, modular DI and CQRS framework on top of [Awilix](
7
7
  [![Build Status](https://github.com/wildstyles/awilixify/workflows/ci/badge.svg)](https://github.com/wildstyles/awilixify/actions)
8
8
  [![codecov](https://codecov.io/gh/wildstyles/awilixify/branch/main/graph/badge.svg)](https://codecov.io/gh/wildstyles/awilixify)
9
9
 
10
- > [!WARNING]
11
- > This project is in active development and is not production-ready yet.
10
+ > [!TIP]
11
+ > 🤔 “Another JS framework?” Here are my attempts to convince you to give Awilixify a try:
12
+ >
13
+ > - [Awilixify: NestJS-Like Modular DI for Legacy Applications](https://medium.com/@r.vanzhula/awilixify-nestjs-like-modular-di-for-legacy-applications-db2a1e29c7de)
14
+ > - [Awilixify: Making Middlewares End-to-End Type-Safe](https://medium.com/@r.vanzhula/awilixify-making-middlewares-end-to-end-type-safe-b3f4dbfb6b42)
12
15
 
13
16
  📚 Documentation: https://wildstyles.github.io/awilixify/
14
17
 
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export * from "./lib/decorators/decorator-state.js";
2
+ export type { DecoratorToken } from "./lib/decorators/decorator-state.types.js";
2
3
  export type { ModuleScope } from "./lib/di/contexts/container-context-base.js";
3
4
  export * from "./lib/di/contexts/di-context.js";
4
5
  export * from "./lib/di/contexts/di-context-async.js";
@@ -7,7 +8,7 @@ export * from "./lib/di/modules/module-def.types.js";
7
8
  export * from "./lib/di/modules/module-factories.js";
8
9
  export * from "./lib/di/modules/module-overrides.js";
9
10
  export * from "./lib/di/modules/module-ref.types.js";
10
- export type { Controller, InitializerContext, InterceptContext, Interceptor, ProviderInit, } from "./lib/di/providers/provider.types.js";
11
+ export type { ConstructorController, Controller, InitializerContext, InterceptContext, Interceptor, ProviderInit, } from "./lib/di/providers/provider.types.js";
11
12
  export { Initializer } from "./lib/di/providers/provider.types.js";
12
13
  export { isResultLike } from "./lib/di/type-guards.js";
13
14
  export * from "./lib/mediator/contract.types.js";
@@ -0,0 +1,63 @@
1
+ import type * as Awilix from "awilix";
2
+ import type { InternalModuleLike } from "../di/modules/runtime-module.types.js";
3
+ import type { ConstructorController, Initializer } from "../di/providers/provider.types.js";
4
+ export declare const AWILIXIFY_DEVTOOLS_PROCESSOR = "__awilixifyDevtoolsProcessor";
5
+ export type TraceSpanKind = "http" | "provider" | "handler" | "prehandler" | "interceptor" | "initializer";
6
+ export type RecordSpanInput<T> = {
7
+ kind: TraceSpanKind;
8
+ moduleName: string;
9
+ providerKey: string;
10
+ methodName: string;
11
+ args: unknown[];
12
+ callback: () => T | Promise<T>;
13
+ moduleId?: string;
14
+ };
15
+ export type WrapResolverInput = {
16
+ kind: TraceSpanKind;
17
+ module: InternalModuleLike;
18
+ options: Awilix.BuildResolverOptions<any>;
19
+ providerKey: string;
20
+ resolver: Awilix.Resolver<any>;
21
+ moduleId?: string;
22
+ isFactory?: boolean;
23
+ };
24
+ export type TraceInitializerInput<T = unknown> = {
25
+ args: unknown[];
26
+ callback: () => T | Promise<T>;
27
+ controllerName: string;
28
+ getStatusCode: () => number | undefined;
29
+ methodName: string;
30
+ moduleName: string;
31
+ };
32
+ export interface Tracer {
33
+ recordSpan<T>(input: RecordSpanInput<T>): T | Promise<T>;
34
+ traceInitializer<T>(input: TraceInitializerInput<T>): T | Promise<T>;
35
+ wrapResolver(input: WrapResolverInput): Awilix.Resolver<any>;
36
+ }
37
+ export type ModuleDecoratorMetadata = {
38
+ module: InternalModuleLike;
39
+ controllers: readonly {
40
+ controllerClass: ConstructorController;
41
+ }[];
42
+ initializers: readonly [string, () => Initializer<any, boolean>][];
43
+ };
44
+ export interface GraphCollector {
45
+ registerModule(input: {
46
+ module: InternalModuleLike;
47
+ scope: Awilix.AwilixContainer;
48
+ importedModules: readonly InternalModuleLike[];
49
+ }): string;
50
+ collectModuleRoutes(metadata: ModuleDecoratorMetadata): void;
51
+ }
52
+ export type DevtoolsProcessorContext = {
53
+ rootModule: InternalModuleLike;
54
+ globalModules: readonly InternalModuleLike[];
55
+ };
56
+ export interface DevtoolsProcessor {
57
+ graphCollector: GraphCollector;
58
+ tracer: Tracer;
59
+ initialize(context: DevtoolsProcessorContext): void;
60
+ }
61
+ export type DevtoolsProcessorRef = {
62
+ current?: DevtoolsProcessor;
63
+ };
@@ -0,0 +1 @@
1
+ export const AWILIXIFY_DEVTOOLS_PROCESSOR = "__awilixifyDevtoolsProcessor";
@@ -0,0 +1,2 @@
1
+ import type { ConstructorController } from "../di/providers/provider.types.js";
2
+ export declare function getControllerMethodNames(controllerClass: ConstructorController): Array<string | symbol>;
@@ -0,0 +1,18 @@
1
+ export function getControllerMethodNames(controllerClass) {
2
+ const collected = new Set();
3
+ let proto = controllerClass.prototype;
4
+ while (proto && proto !== Object.prototype) {
5
+ Object.getOwnPropertyNames(proto)
6
+ .filter((name) => name !== "constructor" && typeof proto[name] === "function")
7
+ .forEach((name) => {
8
+ collected.add(name);
9
+ });
10
+ Object.getOwnPropertySymbols(proto)
11
+ .filter((symbol) => typeof proto[symbol] === "function")
12
+ .forEach((symbol) => {
13
+ collected.add(symbol);
14
+ });
15
+ proto = Object.getPrototypeOf(proto);
16
+ }
17
+ return [...collected];
18
+ }
@@ -0,0 +1,4 @@
1
+ export type { InternalModuleLike } from "../di/modules/runtime-module.types.js";
2
+ export { hasInitAfter, hasUseClass, isCostructorProvider, isCostructorProvider as isConstructorProvider, isEagerProvider, isPromiseLike, isResultLike, } from "../di/type-guards.js";
3
+ export * from "./devtools.types.js";
4
+ export { getControllerMethodNames } from "./helpers.js";
@@ -0,0 +1,3 @@
1
+ export { hasInitAfter, hasUseClass, isCostructorProvider, isCostructorProvider as isConstructorProvider, isEagerProvider, isPromiseLike, isResultLike, } from "../di/type-guards.js";
2
+ export * from "./devtools.types.js";
3
+ export { getControllerMethodNames } from "./helpers.js";
@@ -1,4 +1,5 @@
1
1
  import * as Awilix from "awilix";
2
+ import { type DevtoolsProcessorRef } from "../../devtools/devtools.types.js";
2
3
  import type { InternalModuleLike as M } from "../modules/runtime-module.types.js";
3
4
  import { type LifecycleMethods, LifecycleProcessor } from "../processors/lifecycle-processor.js";
4
5
  import { type OverrideOptions, OverridesProcessor } from "../processors/overrides-processor.js";
@@ -9,9 +10,9 @@ interface ScopeContext {
9
10
  hasScopeContext: () => boolean;
10
11
  resolveFromScope: <T>(scope: Awilix.AwilixContainer, key: string | symbol) => T;
11
12
  }
12
- export interface ContainerContextOptions extends OverrideOptions {
13
+ export interface ContainerContextOptions<Options extends Awilix.BuildResolverOptions<any> = Awilix.BuildResolverOptions<any>> extends OverrideOptions {
13
14
  containerOptions?: Awilix.ContainerOptions;
14
- providerOptions?: Partial<Awilix.BuildResolverOptions<any>>;
15
+ providerOptions?: Options;
15
16
  globalModules?: readonly M[];
16
17
  }
17
18
  export interface ModuleScope<S extends Awilix.AwilixContainer = Awilix.AwilixContainer> extends LifecycleMethods {
@@ -27,6 +28,7 @@ export declare abstract class ContainerContextBase<TOptions extends ContainerCon
27
28
  private readonly scopeContext?;
28
29
  protected readonly forwardRefModules: WeakSet<M>;
29
30
  protected readonly moduleScopeMap: WeakMap<M, Awilix.AwilixContainer<{}>>;
31
+ protected readonly devtoolsProcessorRef: DevtoolsProcessorRef;
30
32
  protected readonly moduleTreeMap: WeakMap<M, ModuleScope<Awilix.AwilixContainer<{}>>>;
31
33
  protected readonly options: TOptions;
32
34
  protected readonly sorter: ProviderDependencySorter;
@@ -39,15 +41,23 @@ export declare abstract class ContainerContextBase<TOptions extends ContainerCon
39
41
  protected bootstrapModule(module: M): ModuleScope;
40
42
  protected createContainer(module?: M): Awilix.AwilixContainer;
41
43
  protected registerModuleWithScope(m: M, scope: Awilix.AwilixContainer, moduleChain: M[]): ModuleScope;
42
- protected initializeGlobalModules(): void;
44
+ protected initializeGlobalModules(rootModule: M): void;
43
45
  protected registerImportedFeatures(scope: Awilix.AwilixContainer, importedModulesWithScope: RegisteredModuleScope[]): void;
44
46
  protected registerExportedProviders(scope: Awilix.AwilixContainer, importedModulesWithScope: RegisteredModuleScope[]): void;
45
47
  protected getExportedProviderKeys(module: M): string[];
46
48
  protected markModuleIfImportsUseForwardRef(m: M): void;
47
49
  protected createModuleScope(name: string, scope: Awilix.AwilixContainer, importedModulesWithScope: RegisteredModuleScope[]): ModuleScope;
48
50
  protected resolveImports(m: M): M[];
51
+ protected registerModuleInGraph({ module, scope, importedModules, }: {
52
+ module: M;
53
+ scope: Awilix.AwilixContainer;
54
+ importedModules: readonly M[];
55
+ }): string | undefined;
49
56
  protected ensureAdditionalNameConflicts(_module: M, _resolvedImports: M[]): void;
50
57
  protected beforeRegisterProviders(_module: M, _scope: Awilix.AwilixContainer, _importedModulesWithScope: RegisteredModuleScope[]): void;
51
58
  protected afterRegisterProviders(_module: M, _scope: Awilix.AwilixContainer, _importedModulesWithScope: RegisteredModuleScope[]): void;
59
+ protected hasDevtoolsProcessorProvider(module: M): boolean;
60
+ protected get globalModules(): M[];
61
+ protected attachDevtools(rootModule: M): void;
52
62
  }
53
63
  export {};
@@ -1,4 +1,5 @@
1
1
  import * as Awilix from "awilix";
2
+ import { AWILIXIFY_DEVTOOLS_PROCESSOR, } from "../../devtools/devtools.types.js";
2
3
  import * as ERRORS from "../errors.js";
3
4
  import { LifecycleProcessor, } from "../processors/lifecycle-processor.js";
4
5
  import { OverridesProcessor, } from "../processors/overrides-processor.js";
@@ -9,6 +10,7 @@ export class ContainerContextBase {
9
10
  scopeContext;
10
11
  forwardRefModules = new WeakSet();
11
12
  moduleScopeMap = new WeakMap();
13
+ devtoolsProcessorRef = {};
12
14
  moduleTreeMap = new WeakMap();
13
15
  options;
14
16
  sorter = new ProviderDependencySorter();
@@ -37,7 +39,7 @@ export class ContainerContextBase {
37
39
  this.lifecycleProcessor = new LifecycleProcessor(this.options.providerOptions || {});
38
40
  }
39
41
  bootstrapModule(module) {
40
- this.initializeGlobalModules();
42
+ this.initializeGlobalModules(module);
41
43
  const moduleTree = this.registerModuleWithScope(module, this.createContainer(module), []);
42
44
  this.overridesProcessor.ensureAllModuleOverridesApplied();
43
45
  return moduleTree;
@@ -57,6 +59,11 @@ export class ContainerContextBase {
57
59
  }
58
60
  const imports = this.resolveImports(m);
59
61
  const moduleWithOverrides = this.overridesProcessor.applyModuleOverrides(m);
62
+ const moduleId = this.registerModuleInGraph({
63
+ module: moduleWithOverrides,
64
+ scope,
65
+ importedModules: imports,
66
+ });
60
67
  this.moduleGraphEnsurer.ensureImportedModulesUniqueness({
61
68
  module: moduleWithOverrides,
62
69
  resolvedImports: imports,
@@ -100,6 +107,7 @@ export class ContainerContextBase {
100
107
  scope.register({
101
108
  [key]: this.providerResolver.resolveProvider({
102
109
  key,
110
+ moduleId,
103
111
  provider,
104
112
  resolutionScope: scope,
105
113
  module: moduleWithOverrides,
@@ -112,21 +120,21 @@ export class ContainerContextBase {
112
120
  this.moduleTreeMap.set(m, moduleTree);
113
121
  return moduleTree;
114
122
  }
115
- initializeGlobalModules() {
116
- const globalModules = this.options.globalModules || [];
117
- const globalModuleImports = globalModules.flatMap((module) => this.resolveImports(module).map((importedModule) => ({
123
+ initializeGlobalModules(rootModule) {
124
+ const globalModuleImports = this.globalModules.flatMap((module) => this.resolveImports(module).map((importedModule) => ({
118
125
  module,
119
126
  importedModule,
120
127
  })));
121
128
  this.moduleGraphEnsurer.ensureGlobalModulesDoNotImportGlobalModules({
122
- globalModules,
129
+ globalModules: this.globalModules,
123
130
  globalModuleImports,
124
131
  });
125
132
  this.globalModulesWithScope = [];
126
- for (const module of globalModules) {
133
+ this.attachDevtools(rootModule);
134
+ for (const module of this.globalModules) {
127
135
  const moduleTree = this.registerModuleWithScope(module, this.createContainer(module), []);
128
136
  this.globalModulesWithScope.push({
129
- module: this.overridesProcessor.getModuleWithOverrides(module),
137
+ module,
130
138
  moduleScope: moduleTree,
131
139
  });
132
140
  }
@@ -188,7 +196,37 @@ export class ContainerContextBase {
188
196
  return resolvedImport;
189
197
  });
190
198
  }
199
+ registerModuleInGraph({ module, scope, importedModules, }) {
200
+ if (!this.devtoolsProcessorRef.current) {
201
+ return undefined;
202
+ }
203
+ const moduleId = this.devtoolsProcessorRef.current.graphCollector.registerModule({
204
+ module,
205
+ scope,
206
+ importedModules,
207
+ });
208
+ return moduleId;
209
+ }
191
210
  ensureAdditionalNameConflicts(_module, _resolvedImports) { }
192
211
  beforeRegisterProviders(_module, _scope, _importedModulesWithScope) { }
193
212
  afterRegisterProviders(_module, _scope, _importedModulesWithScope) { }
213
+ hasDevtoolsProcessorProvider(module) {
214
+ return Boolean(module.providers?.[AWILIXIFY_DEVTOOLS_PROCESSOR]);
215
+ }
216
+ get globalModules() {
217
+ return (this.options.globalModules || [])
218
+ .filter((module) => !this.hasDevtoolsProcessorProvider(module))
219
+ .map((el) => this.overridesProcessor.getModuleWithOverrides(el));
220
+ }
221
+ attachDevtools(rootModule) {
222
+ const module = this.moduleGraphEnsurer.ensureSingleDevtoolsModule((this.options.globalModules || []).filter((module) => this.hasDevtoolsProcessorProvider(module)));
223
+ if (!module)
224
+ return;
225
+ const moduleTree = this.registerModuleWithScope(module, this.createContainer(module), []);
226
+ this.devtoolsProcessorRef.current = moduleTree.scope.resolve(AWILIXIFY_DEVTOOLS_PROCESSOR);
227
+ this.devtoolsProcessorRef.current.initialize({
228
+ rootModule,
229
+ globalModules: this.globalModules,
230
+ });
231
+ }
194
232
  }
@@ -10,26 +10,26 @@ export class AsyncDIContext extends DIContextBase {
10
10
  }
11
11
  async bootstrap(module) {
12
12
  const rootModule = await module;
13
- await this.initializeGlobalModulesAsync();
13
+ await this.initializeGlobalModulesAsync(rootModule);
14
14
  const moduleTree = await this.registerModuleWithScopeAsync(rootModule, this.createContainer(rootModule), []);
15
15
  this.overridesProcessor.ensureAllModuleOverridesApplied();
16
16
  return moduleTree;
17
17
  }
18
- async initializeGlobalModulesAsync() {
19
- const globalModules = this.options.globalModules || [];
20
- const globalModuleImports = (await Promise.all(globalModules.map(async (module) => (await this.resolveImportsAsync(module)).map((importedModule) => ({
18
+ async initializeGlobalModulesAsync(rootModule) {
19
+ const globalModuleImports = (await Promise.all(this.globalModules.map(async (module) => (await this.resolveImportsAsync(module)).map((importedModule) => ({
21
20
  module,
22
21
  importedModule,
23
22
  }))))).flat();
24
23
  this.moduleGraphEnsurer.ensureGlobalModulesDoNotImportGlobalModules({
25
- globalModules,
24
+ globalModules: this.globalModules,
26
25
  globalModuleImports,
27
26
  });
28
27
  this.globalModulesWithScope = [];
29
- for (const module of globalModules) {
28
+ this.attachDevtools(rootModule);
29
+ for (const module of this.globalModules) {
30
30
  const moduleTree = await this.registerModuleWithScopeAsync(module, this.createContainer(module), []);
31
31
  this.globalModulesWithScope.push({
32
- module: this.overridesProcessor.getModuleWithOverrides(module),
32
+ module,
33
33
  moduleScope: moduleTree,
34
34
  });
35
35
  }
@@ -41,6 +41,11 @@ export class AsyncDIContext extends DIContextBase {
41
41
  }
42
42
  const imports = await this.resolveImportsAsync(m);
43
43
  const moduleWithOverrides = this.overridesProcessor.applyModuleOverrides(m);
44
+ const moduleId = this.registerModuleInGraph({
45
+ module: moduleWithOverrides,
46
+ scope,
47
+ importedModules: imports,
48
+ });
44
49
  this.moduleGraphEnsurer.ensureImportedModulesUniqueness({
45
50
  module: moduleWithOverrides,
46
51
  resolvedImports: imports,
@@ -66,11 +71,11 @@ export class AsyncDIContext extends DIContextBase {
66
71
  if (pendingTree) {
67
72
  return pendingTree;
68
73
  }
69
- const moduleTreePromise = this.registerNewModuleWithScopeAsync(m, moduleWithOverrides, scope, imports, moduleChain);
74
+ const moduleTreePromise = this.registerNewModuleWithScopeAsync(m, moduleWithOverrides, moduleId, scope, imports, moduleChain);
70
75
  this.moduleTreePromiseMap.set(m, moduleTreePromise);
71
76
  return moduleTreePromise;
72
77
  }
73
- async registerNewModuleWithScopeAsync(m, moduleWithOverrides, scope, imports, moduleChain) {
78
+ async registerNewModuleWithScopeAsync(m, moduleWithOverrides, moduleId, scope, imports, moduleChain) {
74
79
  this.moduleScopeMap.set(m, scope);
75
80
  const importedModulesWithScope = [
76
81
  ...this.globalModulesWithScope,
@@ -93,6 +98,7 @@ export class AsyncDIContext extends DIContextBase {
93
98
  scope.register({
94
99
  [key]: await this.providerResolver.resolveProviderAsync({
95
100
  key,
101
+ moduleId,
96
102
  provider,
97
103
  resolutionScope: scope,
98
104
  module: moduleWithOverrides,
@@ -9,16 +9,17 @@ export class DIContextBase extends ContainerContextBase {
9
9
  controllerProcessor;
10
10
  handlerProcessor;
11
11
  interceptorProcessor;
12
- initializerProcessor = new InitializerProcessor();
12
+ initializerProcessor;
13
13
  constructor(options) {
14
14
  super(options, {
15
15
  hasScopeContext: hasRequestScopeContext,
16
16
  resolveFromScope: resolveFromRequestScope,
17
17
  });
18
- this.handlerProcessor = new HandlerProcessor(this.options.providerOptions || {});
19
- this.interceptorProcessor = new InterceptorProcessor(this.options.providerOptions || {});
18
+ this.handlerProcessor = new HandlerProcessor(this.options.providerOptions || {}, this.devtoolsProcessorRef);
19
+ this.interceptorProcessor = new InterceptorProcessor(this.options.providerOptions || {}, this.devtoolsProcessorRef);
20
+ this.initializerProcessor = new InitializerProcessor(this.devtoolsProcessorRef);
20
21
  this.controllerProcessor = new ControllerProcessor(this.interceptorProcessor, this.options.providerOptions || {}, this.options.skipRegisterRoutes === true);
21
- this.providerResolver = new ProviderResolver(this.interceptorProcessor, this.options.providerOptions || {}, getOrCreateRequestScope);
22
+ this.providerResolver = new ProviderResolver(this.interceptorProcessor, this.options.providerOptions || {}, this.devtoolsProcessorRef, getOrCreateRequestScope);
22
23
  }
23
24
  beforeRegisterProviders(m, scope, importedModulesWithScope) {
24
25
  this.interceptorProcessor.processInterceptors(m, scope, importedModulesWithScope);
@@ -27,5 +27,6 @@ export declare class ModuleGraphEnsurer {
27
27
  ensureGlobalModulesDoNotImportGlobalModules({ globalModules, globalModuleImports, }: EnsureGlobalModulesDoNotImportGlobalModulesParams): void;
28
28
  ensureNoProviderNameConflicts({ module, resolvedImports, globalModules, getExportedProviderKeys, }: EnsureNoProviderNameConflictsParams): void;
29
29
  ensureCircularDependencyHasForwardRef({ module, moduleChain, forwardRefModules, }: EnsureCircularDependencyHasForwardRefParams): void;
30
+ ensureSingleDevtoolsModule(devtoolsModules: M[]): M | undefined;
30
31
  }
31
32
  export {};
@@ -42,4 +42,9 @@ export class ModuleGraphEnsurer {
42
42
  const chainNames = moduleChain.map((chainModule) => chainModule.name);
43
43
  throw new ERRORS.CircularModuleDependencyError(module.name, chainNames);
44
44
  }
45
+ ensureSingleDevtoolsModule(devtoolsModules) {
46
+ if (devtoolsModules.length <= 1)
47
+ return devtoolsModules[0];
48
+ throw new ERRORS.DuplicateDevtoolsModuleError(devtoolsModules.map((module) => module.name));
49
+ }
45
50
  }
@@ -13,6 +13,9 @@ export declare class CircularDependencyError extends Error {
13
13
  export declare class DuplicateModuleImportError extends Error {
14
14
  constructor(parentModuleName: string, importedModuleName: string);
15
15
  }
16
+ export declare class DuplicateDevtoolsModuleError extends Error {
17
+ constructor(moduleNames: string[]);
18
+ }
16
19
  export declare class ProviderNameConflictError extends Error {
17
20
  constructor(moduleName: string, conflictingKeys: string[]);
18
21
  }
@@ -30,6 +30,12 @@ export class DuplicateModuleImportError extends Error {
30
30
  this.name = "DuplicateModuleImportError";
31
31
  }
32
32
  }
33
+ export class DuplicateDevtoolsModuleError extends Error {
34
+ constructor(moduleNames) {
35
+ super(`Only one devtools module can be registered. Found: ${moduleNames.join(", ")}`);
36
+ this.name = "DuplicateDevtoolsModuleError";
37
+ }
38
+ }
33
39
  export class ProviderNameConflictError extends Error {
34
40
  constructor(moduleName, conflictingKeys) {
35
41
  super(`Module "${moduleName}" has provider name conflicts with imported modules: ${conflictingKeys.join(", ")}`);
@@ -18,13 +18,21 @@ export function createModule(module, options) {
18
18
  if (options?.hashNameFrom === undefined) {
19
19
  return withOptions;
20
20
  }
21
+ const hashSource = stableStringify(options.hashNameFrom);
21
22
  const hash = createHash("sha256")
22
- .update(stableStringify(options.hashNameFrom))
23
+ .update(hashSource)
23
24
  .digest("hex")
24
25
  .slice(0, Math.max(4, options.hashLength ?? 8));
25
26
  return {
26
27
  ...withOptions,
27
28
  name: `${withOptions.name}_${hash}`,
29
+ __devtools: {
30
+ baseName: withOptions.name,
31
+ dynamic: {
32
+ hash,
33
+ paramsPreview: hashSource,
34
+ },
35
+ },
28
36
  };
29
37
  }
30
38
  function stableStringify(value) {
@@ -3,6 +3,13 @@ import type { AnyController, AnyInitializer, AnyInterceptor, AnyMiddleware, AnyP
3
3
  import type { ForwardRef } from "./module-ref.types.js";
4
4
  export interface InternalModuleLike {
5
5
  name: string;
6
+ __devtools?: {
7
+ baseName?: string;
8
+ dynamic?: {
9
+ hash: string;
10
+ paramsPreview: string;
11
+ };
12
+ };
6
13
  imports?: readonly (InternalModuleLike | Promise<InternalModuleLike> | ForwardRef<InternalModuleLike>)[];
7
14
  providers?: Record<string, AnyProvider>;
8
15
  exports?: readonly string[];
@@ -1,4 +1,5 @@
1
1
  import * as Awilix from "awilix";
2
+ import type { DevtoolsProcessorRef } from "../../devtools/devtools.types.js";
2
3
  import type { RegisteredModuleScope } from "../contexts/container-context-base.js";
3
4
  import type { InternalModuleLike as M } from "../modules/runtime-module.types.js";
4
5
  export declare const HandlerType: {
@@ -8,8 +9,11 @@ export declare const HandlerType: {
8
9
  export type HandlerType = (typeof HandlerType)[keyof typeof HandlerType];
9
10
  export declare class HandlerProcessor {
10
11
  private readonly providerOptions;
12
+ private readonly devtoolsProcessorRef;
11
13
  private readonly keyedFeatureRegistrar;
12
14
  private static readonly handlerConfig;
13
- constructor(providerOptions: Partial<Awilix.BuildResolverOptions<any>>);
15
+ constructor(providerOptions: Partial<Awilix.BuildResolverOptions<any>>, devtoolsProcessorRef: DevtoolsProcessorRef);
14
16
  processHandlers(m: M, scope: Awilix.AwilixContainer, importedModulesWithScope: RegisteredModuleScope[], handlerType: HandlerType): void;
17
+ private createTracedHandlerResolver;
18
+ private get devtoolsTracer();
15
19
  }
@@ -11,6 +11,7 @@ export const HandlerType = {
11
11
  };
12
12
  export class HandlerProcessor {
13
13
  providerOptions;
14
+ devtoolsProcessorRef;
14
15
  keyedFeatureRegistrar;
15
16
  static handlerConfig = {
16
17
  query: {
@@ -24,8 +25,9 @@ export class HandlerProcessor {
24
25
  mediatorKey: "commandMediator",
25
26
  },
26
27
  };
27
- constructor(providerOptions) {
28
+ constructor(providerOptions, devtoolsProcessorRef) {
28
29
  this.providerOptions = providerOptions;
30
+ this.devtoolsProcessorRef = devtoolsProcessorRef;
29
31
  this.keyedFeatureRegistrar = new KeyedFeatureRegistrar(providerOptions);
30
32
  }
31
33
  processHandlers(m, scope, importedModulesWithScope, handlerType) {
@@ -39,7 +41,7 @@ export class HandlerProcessor {
39
41
  const handlers = m[handlersKey];
40
42
  if (!handlers?.length)
41
43
  return;
42
- const mediator = new Mediator(middlewareResolvers, m.name);
44
+ const mediator = new Mediator(middlewareResolvers, m.name, handlerType, this.devtoolsProcessorRef);
43
45
  for (const h of handlers) {
44
46
  const { useClass: HandlerClass, ...handlerOptions } = hasUseClass(h)
45
47
  ? h
@@ -50,20 +52,52 @@ export class HandlerProcessor {
50
52
  }
51
53
  const options = ProviderResolver.mergeResolverOptions(m, this.providerOptions, handlerOptions);
52
54
  const handlerSymbol = Symbol(`${handlerKey}_${HandlerClass.name}`);
55
+ const handlerResolver = Awilix.asClass(HandlerClass, options);
53
56
  scope.register({
54
- [handlerSymbol]: Awilix.asClass(HandlerClass, options),
57
+ [handlerSymbol]: this.createTracedHandlerResolver({
58
+ handlerClassName: HandlerClass.name,
59
+ module: m,
60
+ options,
61
+ resolver: handlerResolver,
62
+ }),
55
63
  });
56
- mediator.register(handlerKey, (payload, context) => {
64
+ mediator.register(handlerKey, async (payload, context) => {
57
65
  const requestScope = options.lifetime === Awilix.Lifetime.SINGLETON
58
66
  ? scope
59
67
  : getOrCreateRequestScope(scope);
60
- return requestScope
61
- .resolve(handlerSymbol)
62
- .executor(payload, context);
68
+ if (!this.devtoolsTracer) {
69
+ return requestScope
70
+ .resolve(handlerSymbol)
71
+ .executor(payload, context);
72
+ }
73
+ return this.devtoolsTracer.recordSpan({
74
+ kind: "handler",
75
+ moduleName: m.name,
76
+ providerKey: `${handlerType}:${handlerKey}`,
77
+ methodName: "executor",
78
+ args: [payload, context],
79
+ callback: () => requestScope
80
+ .resolve(handlerSymbol)
81
+ .executor(payload, context),
82
+ });
63
83
  });
64
84
  }
65
85
  scope.register({
66
86
  [mediatorKey]: Awilix.asValue(mediator),
67
87
  });
68
88
  }
89
+ createTracedHandlerResolver({ handlerClassName, module, options, resolver, }) {
90
+ if (!this.devtoolsTracer)
91
+ return resolver;
92
+ return this.devtoolsTracer.wrapResolver({
93
+ kind: "handler",
94
+ module,
95
+ options,
96
+ providerKey: handlerClassName,
97
+ resolver,
98
+ });
99
+ }
100
+ get devtoolsTracer() {
101
+ return this.devtoolsProcessorRef.current?.tracer;
102
+ }
69
103
  }
@@ -1,4 +1,5 @@
1
1
  import type * as Awilix from "awilix";
2
+ import type { DevtoolsProcessorRef } from "../../devtools/devtools.types.js";
2
3
  import type { RegisteredModuleScope } from "../contexts/container-context-base.js";
3
4
  import type { InternalModuleLike as M } from "../modules/runtime-module.types.js";
4
5
  import type { ConstructorController } from "../providers/provider.types.js";
@@ -9,11 +10,11 @@ export type ControllerRuntimeEntry = {
9
10
  };
10
11
  export type InitializerTask = (options?: ModuleInitOptions) => Promise<void>;
11
12
  export declare class InitializerProcessor {
13
+ private readonly devtoolsProcessorRef;
12
14
  private readonly keyedFeatureRegistrar;
13
15
  private readonly resolversByModule;
16
+ constructor(devtoolsProcessorRef: DevtoolsProcessorRef);
14
17
  collectInitializers(m: M, scope: Awilix.AwilixContainer, importedModulesWithScope: RegisteredModuleScope[], controllers: ControllerRuntimeEntry[]): InitializerTask | null;
15
18
  private ensureNoMultiInvokableInitializersPerMethod;
16
19
  private filterActiveInitializers;
17
- private processInitializerResolvers;
18
- private getControllerMethodNames;
19
20
  }