@haskou/ddd-kernel 0.1.0 → 1.0.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 (120) hide show
  1. package/README.md +44 -115
  2. package/dist/DomainEventConsumer-Bg-bOwmh.d.cts +11 -0
  3. package/dist/DomainEventConsumer-BroJmVty.d.ts +11 -0
  4. package/dist/DomainMessageBus-3jYk7TPw.d.ts +13 -0
  5. package/dist/DomainMessageBus-OyliPu3Z.d.cts +13 -0
  6. package/dist/MessageBus-BtUXnd0Y.d.cts +10 -0
  7. package/dist/MessageBus-oQ9BnW84.d.ts +10 -0
  8. package/dist/{NoFailedMessagesError-0YJKRWPF.d.ts → NoFailedMessagesError-BLpGI-G4.d.ts} +6 -1
  9. package/dist/{NoFailedMessagesError-Kz7CYWpT.d.cts → NoFailedMessagesError-BjxYoKTR.d.cts} +6 -1
  10. package/dist/PublisherHookErrorPolicy-CjouTcSR.d.cts +8 -0
  11. package/dist/PublisherHookErrorPolicy-DSsCNE6O.d.ts +8 -0
  12. package/dist/RetryPredicate-U7dYnQ4N.d.ts +15 -0
  13. package/dist/RetryPredicate-yT_z9zk1.d.cts +15 -0
  14. package/dist/{Scheduler-oigqNOUJ.d.ts → Scheduler-BW-U5Ccg.d.cts} +1 -1
  15. package/dist/{Scheduler-oigqNOUJ.d.cts → Scheduler-BW-U5Ccg.d.ts} +1 -1
  16. package/dist/ServiceClass-BkEHcXDi.d.cts +72 -0
  17. package/dist/ServiceClass-Bq_fBC5R.d.ts +72 -0
  18. package/dist/{Kernel-BWUOUWWI.d.cts → ShutdownHook-BjbnCKzr.d.cts} +49 -7
  19. package/dist/{Kernel-CUaqHa1s.d.ts → ShutdownHook-CMWLsfu-.d.ts} +49 -7
  20. package/dist/Subscription-4vuAAxax.d.cts +23 -0
  21. package/dist/Subscription-vtF0lEHP.d.ts +23 -0
  22. package/dist/adapters/index.cjs +665 -37
  23. package/dist/adapters/index.cjs.map +1 -1
  24. package/dist/adapters/index.d.cts +15 -13
  25. package/dist/adapters/index.d.ts +15 -13
  26. package/dist/adapters/index.js +659 -37
  27. package/dist/adapters/index.js.map +1 -1
  28. package/dist/adapters/pubsub/amqp/index.cjs +241 -16
  29. package/dist/adapters/pubsub/amqp/index.cjs.map +1 -1
  30. package/dist/adapters/pubsub/amqp/index.d.cts +16 -8
  31. package/dist/adapters/pubsub/amqp/index.d.ts +16 -8
  32. package/dist/adapters/pubsub/amqp/index.js +241 -16
  33. package/dist/adapters/pubsub/amqp/index.js.map +1 -1
  34. package/dist/adapters/pubsub/in-memory/index.cjs +96 -8
  35. package/dist/adapters/pubsub/in-memory/index.cjs.map +1 -1
  36. package/dist/adapters/pubsub/in-memory/index.d.cts +9 -3
  37. package/dist/adapters/pubsub/in-memory/index.d.ts +9 -3
  38. package/dist/adapters/pubsub/in-memory/index.js +96 -8
  39. package/dist/adapters/pubsub/in-memory/index.js.map +1 -1
  40. package/dist/adapters/pubsub/index.cjs +397 -27
  41. package/dist/adapters/pubsub/index.cjs.map +1 -1
  42. package/dist/adapters/pubsub/index.d.cts +89 -7
  43. package/dist/adapters/pubsub/index.d.ts +89 -7
  44. package/dist/adapters/pubsub/index.js +389 -26
  45. package/dist/adapters/pubsub/index.js.map +1 -1
  46. package/dist/adapters/ui/express/index.cjs +279 -11
  47. package/dist/adapters/ui/express/index.cjs.map +1 -1
  48. package/dist/adapters/ui/express/index.d.cts +127 -12
  49. package/dist/adapters/ui/express/index.d.ts +127 -12
  50. package/dist/adapters/ui/express/index.js +270 -11
  51. package/dist/adapters/ui/express/index.js.map +1 -1
  52. package/dist/adapters/ui/index.cjs +412 -27
  53. package/dist/adapters/ui/index.cjs.map +1 -1
  54. package/dist/adapters/ui/index.d.cts +7 -8
  55. package/dist/adapters/ui/index.d.ts +7 -8
  56. package/dist/adapters/ui/index.js +413 -27
  57. package/dist/adapters/ui/index.js.map +1 -1
  58. package/dist/adapters/ui/routes/index.cjs +136 -9
  59. package/dist/adapters/ui/routes/index.cjs.map +1 -1
  60. package/dist/adapters/ui/routes/index.js +136 -9
  61. package/dist/adapters/ui/routes/index.js.map +1 -1
  62. package/dist/contracts/index.cjs +16 -17
  63. package/dist/contracts/index.cjs.map +1 -1
  64. package/dist/contracts/index.d.cts +10 -2
  65. package/dist/contracts/index.d.ts +10 -2
  66. package/dist/contracts/index.js +16 -17
  67. package/dist/contracts/index.js.map +1 -1
  68. package/dist/contracts/kernel/index.cjs.map +1 -1
  69. package/dist/contracts/kernel/index.d.cts +7 -1
  70. package/dist/contracts/kernel/index.d.ts +7 -1
  71. package/dist/contracts/pubsub/index.cjs.map +1 -1
  72. package/dist/contracts/pubsub/index.d.cts +5 -1
  73. package/dist/contracts/pubsub/index.d.ts +5 -1
  74. package/dist/contracts/ui/index.cjs +16 -17
  75. package/dist/contracts/ui/index.cjs.map +1 -1
  76. package/dist/contracts/ui/index.d.cts +16 -16
  77. package/dist/contracts/ui/index.d.ts +16 -16
  78. package/dist/contracts/ui/index.js +16 -17
  79. package/dist/contracts/ui/index.js.map +1 -1
  80. package/dist/domain/index.cjs.map +1 -1
  81. package/dist/domain/index.d.cts +6 -2
  82. package/dist/domain/index.d.ts +6 -2
  83. package/dist/domain/index.js.map +1 -1
  84. package/dist/index.cjs +152 -26
  85. package/dist/index.cjs.map +1 -1
  86. package/dist/index.d.cts +8 -7
  87. package/dist/index.d.ts +8 -7
  88. package/dist/index.js +152 -26
  89. package/dist/index.js.map +1 -1
  90. package/dist/infrastructure/dependency-injection/index.cjs +119 -3
  91. package/dist/infrastructure/dependency-injection/index.cjs.map +1 -1
  92. package/dist/infrastructure/dependency-injection/index.d.cts +4 -1
  93. package/dist/infrastructure/dependency-injection/index.d.ts +4 -1
  94. package/dist/infrastructure/dependency-injection/index.js +119 -3
  95. package/dist/infrastructure/dependency-injection/index.js.map +1 -1
  96. package/dist/infrastructure/express/index.cjs +279 -11
  97. package/dist/infrastructure/express/index.cjs.map +1 -1
  98. package/dist/infrastructure/express/index.d.cts +7 -8
  99. package/dist/infrastructure/express/index.d.ts +7 -8
  100. package/dist/infrastructure/express/index.js +270 -11
  101. package/dist/infrastructure/express/index.js.map +1 -1
  102. package/dist/infrastructure/scheduler/index.cjs +136 -9
  103. package/dist/infrastructure/scheduler/index.cjs.map +1 -1
  104. package/dist/infrastructure/scheduler/index.d.cts +2 -2
  105. package/dist/infrastructure/scheduler/index.d.ts +2 -2
  106. package/dist/infrastructure/scheduler/index.js +136 -9
  107. package/dist/infrastructure/scheduler/index.js.map +1 -1
  108. package/package.json +101 -11
  109. package/dist/Consumer-CC8ZRCsd.d.cts +0 -17
  110. package/dist/Consumer-CeT0Wbxb.d.ts +0 -17
  111. package/dist/DomainEventConsumer-3WBMSSr2.d.cts +0 -7
  112. package/dist/DomainEventConsumer-B4hkIUmP.d.ts +0 -7
  113. package/dist/DomainEventPublisher-8G0lvmdy.d.cts +0 -7
  114. package/dist/DomainEventPublisher-DhGgM3f2.d.ts +0 -7
  115. package/dist/ServiceClass-BmNw8fJj.d.cts +0 -37
  116. package/dist/ServiceClass-C7NCKdSS.d.ts +0 -37
  117. package/dist/ShutdownHook-BGskq2-q.d.ts +0 -9
  118. package/dist/ShutdownHook-Dib5uNKB.d.cts +0 -9
  119. package/dist/Subscription-Bwkb_did.d.ts +0 -9
  120. package/dist/Subscription-P9WROD_6.d.cts +0 -9
@@ -1,4 +1,4 @@
1
- export { D as DependencyInjection, a as DependencyInjectionOptions, S as ServiceClass } from '../../ServiceClass-BmNw8fJj.cjs';
1
+ export { C as ClassDependencyOverride, D as DependencyInjection, a as DependencyInjectionOptions, b as DependencyOverride, c as DependencyOverrideFactory, d as DependencyOverrideToken, F as FactoryDependencyOverride, S as ServiceClass, V as ValueDependencyOverride } from '../../ServiceClass-BkEHcXDi.cjs';
2
2
  import '../../ServiceResolver-D2Jz-l_Z.cjs';
3
3
 
4
4
  type ExplicitServiceClass = {
@@ -11,6 +11,9 @@ type ContainerDefinition = {
11
11
 
12
12
  type DefinitionMetadata = {
13
13
  readonly _abstract?: boolean;
14
+ readonly _appendArgs?: unknown[];
15
+ readonly _args?: unknown[];
16
+ readonly _overrideArgs?: unknown[];
14
17
  readonly _parent?: string | null;
15
18
  };
16
19
 
@@ -1,4 +1,4 @@
1
- export { D as DependencyInjection, a as DependencyInjectionOptions, S as ServiceClass } from '../../ServiceClass-C7NCKdSS.js';
1
+ export { C as ClassDependencyOverride, D as DependencyInjection, a as DependencyInjectionOptions, b as DependencyOverride, c as DependencyOverrideFactory, d as DependencyOverrideToken, F as FactoryDependencyOverride, S as ServiceClass, V as ValueDependencyOverride } from '../../ServiceClass-Bq_fBC5R.js';
2
2
  import '../../ServiceResolver-D2Jz-l_Z.js';
3
3
 
4
4
  type ExplicitServiceClass = {
@@ -11,6 +11,9 @@ type ContainerDefinition = {
11
11
 
12
12
  type DefinitionMetadata = {
13
13
  readonly _abstract?: boolean;
14
+ readonly _appendArgs?: unknown[];
15
+ readonly _args?: unknown[];
16
+ readonly _overrideArgs?: unknown[];
14
17
  readonly _parent?: string | null;
15
18
  };
16
19
 
@@ -26,6 +26,7 @@ var DependencyInjection = class _DependencyInjection {
26
26
  autowire;
27
27
  loader;
28
28
  container;
29
+ overrideTokenIds = /* @__PURE__ */ new Map();
29
30
  static configure(options) {
30
31
  _DependencyInjection.configuredInstance = new _DependencyInjection(options);
31
32
  return _DependencyInjection.configuredInstance;
@@ -50,6 +51,16 @@ var DependencyInjection = class _DependencyInjection {
50
51
  getServiceClassName(serviceName) {
51
52
  return typeof serviceName === "function" ? serviceName.name : void 0;
52
53
  }
54
+ getOverrideId(prefix, token) {
55
+ const tokenName = this.getServiceClassName(token) ?? String(token);
56
+ return `ddd-kernel.override.${prefix}.${tokenName}`;
57
+ }
58
+ ensureSyntheticService(id, value) {
59
+ const definition = this.container.register(id);
60
+ definition.public = true;
61
+ definition.synthetic = true;
62
+ this.container.set(id, value);
63
+ }
53
64
  parentMatchesService(parentId, serviceClassName) {
54
65
  if (!parentId) {
55
66
  return false;
@@ -61,6 +72,26 @@ var DependencyInjection = class _DependencyInjection {
61
72
  const serviceName = Buffer.from(serviceId, "base64").toString("utf8");
62
73
  return serviceName.endsWith(`__${serviceClassName}__${serviceClassName}`);
63
74
  }
75
+ serviceIdReferencesService(serviceId, serviceClassName) {
76
+ const serviceName = Buffer.from(serviceId, "base64").toString("utf8");
77
+ return serviceName.endsWith(`__${serviceClassName}`);
78
+ }
79
+ getReferenceId(value) {
80
+ if (typeof value === "object" && value !== null && "id" in value && typeof value.id === "string") {
81
+ return value.id;
82
+ }
83
+ return void 0;
84
+ }
85
+ getDefinitionArgumentReferences(definition) {
86
+ return [
87
+ ...definition._args ?? [],
88
+ ...definition._appendArgs ?? [],
89
+ ...definition._overrideArgs ?? []
90
+ ].flatMap((argument) => {
91
+ const referenceId = this.getReferenceId(argument);
92
+ return referenceId ? [referenceId] : [];
93
+ });
94
+ }
64
95
  findConcreteChildServiceId(serviceName) {
65
96
  const serviceClassName = this.getServiceClassName(serviceName);
66
97
  if (!serviceClassName) {
@@ -91,6 +122,86 @@ var DependencyInjection = class _DependencyInjection {
91
122
  );
92
123
  return matches[matches.length - 1];
93
124
  }
125
+ findReferencedServiceIds(serviceName) {
126
+ const serviceClassName = this.getServiceClassName(serviceName);
127
+ if (!serviceClassName) {
128
+ return [];
129
+ }
130
+ return [
131
+ ...new Set(
132
+ [...this.definitions.values()].flatMap(
133
+ (definition) => this.getDefinitionArgumentReferences(definition)
134
+ ).filter(
135
+ (id) => this.serviceIdReferencesService(id, serviceClassName)
136
+ )
137
+ )
138
+ ];
139
+ }
140
+ getOverrideTokenIds(token) {
141
+ const tokenIds = [
142
+ this.findRegisteredServiceId(token),
143
+ this.findAliasServiceId(token),
144
+ ...this.findReferencedServiceIds(token)
145
+ ].filter((id) => id !== void 0);
146
+ const existingTokenIds = [...new Set(tokenIds)];
147
+ if (existingTokenIds.length > 0) {
148
+ return existingTokenIds;
149
+ }
150
+ const overrideTokenId = this.getOverrideId("token", token);
151
+ this.ensureSyntheticService(overrideTokenId, void 0);
152
+ return [overrideTokenId];
153
+ }
154
+ getOverrideClassServiceId(ClassDefinition) {
155
+ const registeredServiceId = this.findRegisteredServiceId(ClassDefinition);
156
+ if (registeredServiceId) {
157
+ return registeredServiceId;
158
+ }
159
+ const overrideClassId = this.getOverrideId("class", ClassDefinition);
160
+ this.container.register(overrideClassId, ClassDefinition);
161
+ return overrideClassId;
162
+ }
163
+ applyClassOverride(override) {
164
+ if (!("useClass" in override)) {
165
+ return;
166
+ }
167
+ const tokenIds = this.getOverrideTokenIds(override.token);
168
+ const classId = this.getOverrideClassServiceId(override.useClass);
169
+ this.overrideTokenIds.set(override.token, tokenIds[0]);
170
+ for (const tokenId of tokenIds) {
171
+ this.container.setAlias(tokenId, classId);
172
+ }
173
+ }
174
+ applyFactoryOverride(override) {
175
+ if (!("useFactory" in override)) {
176
+ return;
177
+ }
178
+ const tokenIds = this.getOverrideTokenIds(override.token);
179
+ const factoryId = this.getOverrideId("factory", override.token);
180
+ this.ensureSyntheticService(factoryId, override.useFactory(this));
181
+ this.overrideTokenIds.set(override.token, tokenIds[0]);
182
+ for (const tokenId of tokenIds) {
183
+ this.container.setAlias(tokenId, factoryId);
184
+ }
185
+ }
186
+ applyValueOverride(override) {
187
+ if (!("useValue" in override)) {
188
+ return;
189
+ }
190
+ const tokenIds = this.getOverrideTokenIds(override.token);
191
+ const valueId = this.getOverrideId("value", override.token);
192
+ this.ensureSyntheticService(valueId, override.useValue);
193
+ this.overrideTokenIds.set(override.token, tokenIds[0]);
194
+ for (const tokenId of tokenIds) {
195
+ this.container.setAlias(tokenId, valueId);
196
+ }
197
+ }
198
+ applyOverrides() {
199
+ for (const override of this.options.overrides ?? []) {
200
+ this.applyClassOverride(override);
201
+ this.applyFactoryOverride(override);
202
+ this.applyValueOverride(override);
203
+ }
204
+ }
94
205
  registerParentAliases() {
95
206
  for (const [id, definition] of this.definitions.entries()) {
96
207
  if (definition._abstract === true || !definition._parent) {
@@ -113,17 +224,22 @@ var DependencyInjection = class _DependencyInjection {
113
224
  await this.loader.load(this.options.servicesYamlPath);
114
225
  }
115
226
  this.registerParentAliases();
227
+ this.applyOverrides();
116
228
  await this.container.compile();
117
229
  }
118
230
  getService(serviceName) {
119
- const childServiceId = this.findConcreteChildServiceId(serviceName);
120
- if (childServiceId) {
121
- return this.container.get(childServiceId);
231
+ const overrideTokenId = this.overrideTokenIds.get(serviceName);
232
+ if (overrideTokenId) {
233
+ return this.container.get(overrideTokenId);
122
234
  }
123
235
  const aliasServiceId = this.findAliasServiceId(serviceName);
124
236
  if (aliasServiceId) {
125
237
  return this.container.get(aliasServiceId);
126
238
  }
239
+ const childServiceId = this.findConcreteChildServiceId(serviceName);
240
+ if (childServiceId) {
241
+ return this.container.get(childServiceId);
242
+ }
127
243
  const registeredServiceId = this.findRegisteredServiceId(serviceName);
128
244
  if (registeredServiceId) {
129
245
  return this.container.get(registeredServiceId);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/infrastructure/dependency-injection/DependencyInjection.ts"],"sourcesContent":["import fs from 'fs-extra';\nimport {\n Autowire,\n ContainerBuilder,\n ServiceFile,\n YamlFileLoader,\n} from 'node-dependency-injection';\nimport path from 'node:path';\n\nimport type { ServiceResolver } from '../../contracts/index.js';\nimport type { ContainerInternals } from './ContainerInternals.js';\nimport type { DefinitionMetadata } from './DefinitionMetadata.js';\nimport type { DependencyInjectionOptions } from './DependencyInjectionOptions.js';\n\nexport class DependencyInjection implements ServiceResolver {\n private static configuredInstance: DependencyInjection | undefined;\n private autowire: Autowire | undefined;\n private loader: YamlFileLoader | undefined;\n private readonly container: ContainerBuilder;\n\n public static configure(\n options: DependencyInjectionOptions,\n ): DependencyInjection {\n DependencyInjection.configuredInstance = new DependencyInjection(options);\n\n return DependencyInjection.configuredInstance;\n }\n\n public static get instance(): DependencyInjection {\n if (!DependencyInjection.configuredInstance) {\n throw new Error('DependencyInjection has not been configured.');\n }\n\n return DependencyInjection.configuredInstance;\n }\n\n constructor(\n private readonly options: DependencyInjectionOptions = {\n containerBuild: process.env.CONTAINER_BUILD === 'true',\n servicesYamlPath: path.resolve(\n process.cwd(),\n 'config',\n 'container',\n 'services.yaml',\n ),\n sourceDirectory: path.resolve(process.cwd(), 'src'),\n },\n ) {\n this.container = new ContainerBuilder(false, this.options.sourceDirectory);\n }\n\n private get definitions(): Map<string, DefinitionMetadata> {\n const container = this.container as unknown as ContainerInternals;\n\n return container._definitions || new Map();\n }\n\n private get aliases(): Map<string, string> {\n const container = this.container as unknown as ContainerInternals;\n\n return container._alias || new Map();\n }\n\n private async ensureFolderExists(filePath: string): Promise<void> {\n await fs.mkdir(path.dirname(filePath), { recursive: true });\n }\n\n private getServiceClassName(serviceName: unknown): string | undefined {\n return typeof serviceName === 'function' ? serviceName.name : undefined;\n }\n\n private parentMatchesService(\n parentId: string | null | undefined,\n serviceClassName: string,\n ): boolean {\n if (!parentId) {\n return false;\n }\n\n const parentName = Buffer.from(parentId, 'base64').toString('utf8');\n\n return parentName.endsWith(`__${serviceClassName}__${serviceClassName}`);\n }\n\n private serviceIdMatchesService(\n serviceId: string,\n serviceClassName: string,\n ): boolean {\n const serviceName = Buffer.from(serviceId, 'base64').toString('utf8');\n\n return serviceName.endsWith(`__${serviceClassName}__${serviceClassName}`);\n }\n\n private findConcreteChildServiceId(serviceName: unknown): string | undefined {\n const serviceClassName = this.getServiceClassName(serviceName);\n\n if (!serviceClassName) {\n return undefined;\n }\n\n const matches = [...this.definitions.entries()]\n .filter(([, definition]) => definition._abstract !== true)\n .filter(([, definition]) =>\n this.parentMatchesService(definition._parent, serviceClassName),\n )\n .map(([id]) => id);\n\n return matches[matches.length - 1];\n }\n\n private findRegisteredServiceId(serviceName: unknown): string | undefined {\n const serviceClassName = this.getServiceClassName(serviceName);\n\n if (!serviceClassName) {\n return undefined;\n }\n\n const matches = [...this.definitions.keys()].filter((id) =>\n this.serviceIdMatchesService(id, serviceClassName),\n );\n\n return matches[matches.length - 1];\n }\n\n private findAliasServiceId(serviceName: unknown): string | undefined {\n const serviceClassName = this.getServiceClassName(serviceName);\n\n if (!serviceClassName) {\n return undefined;\n }\n\n const matches = [...this.aliases.keys()].filter((id) =>\n this.serviceIdMatchesService(id, serviceClassName),\n );\n\n return matches[matches.length - 1];\n }\n\n private registerParentAliases(): void {\n for (const [id, definition] of this.definitions.entries()) {\n if (definition._abstract === true || !definition._parent) {\n continue;\n }\n\n this.container.setAlias(definition._parent, id);\n }\n }\n\n public async compile(): Promise<void> {\n if (this.options.containerBuild) {\n await this.ensureFolderExists(this.options.servicesYamlPath);\n this.autowire = new Autowire(this.container);\n this.autowire.serviceFile = new ServiceFile(\n this.options.servicesYamlPath,\n false,\n );\n await this.autowire.process();\n } else {\n this.loader = new YamlFileLoader(this.container);\n await this.loader.load(this.options.servicesYamlPath);\n }\n\n this.registerParentAliases();\n await this.container.compile();\n }\n\n public getService<T>(serviceName: unknown): T {\n const childServiceId = this.findConcreteChildServiceId(serviceName);\n\n if (childServiceId) {\n return this.container.get<T>(childServiceId);\n }\n\n const aliasServiceId = this.findAliasServiceId(serviceName);\n\n if (aliasServiceId) {\n return this.container.get<T>(aliasServiceId);\n }\n\n const registeredServiceId = this.findRegisteredServiceId(serviceName);\n\n if (registeredServiceId) {\n return this.container.get<T>(registeredServiceId);\n }\n\n return this.container.get<T>(serviceName);\n }\n}\n\nexport default DependencyInjection;\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../src/infrastructure/dependency-injection/DependencyInjection.ts"],"sourcesContent":["import fs from 'fs-extra';\nimport {\n Autowire,\n ContainerBuilder,\n ServiceFile,\n YamlFileLoader,\n} from 'node-dependency-injection';\nimport path from 'node:path';\n\nimport type { ServiceResolver } from '../../contracts/index.js';\nimport type { ContainerInternals } from './ContainerInternals.js';\nimport type { DefinitionMetadata } from './DefinitionMetadata.js';\nimport type { DependencyInjectionOptions } from './DependencyInjectionOptions.js';\nimport type { DependencyOverride } from './DependencyOverride.js';\n\nexport class DependencyInjection implements ServiceResolver {\n private static configuredInstance: DependencyInjection | undefined;\n private autowire: Autowire | undefined;\n private loader: YamlFileLoader | undefined;\n private readonly container: ContainerBuilder;\n private readonly overrideTokenIds = new Map<unknown, string>();\n\n public static configure(\n options: DependencyInjectionOptions,\n ): DependencyInjection {\n DependencyInjection.configuredInstance = new DependencyInjection(options);\n\n return DependencyInjection.configuredInstance;\n }\n\n public static get instance(): DependencyInjection {\n if (!DependencyInjection.configuredInstance) {\n throw new Error('DependencyInjection has not been configured.');\n }\n\n return DependencyInjection.configuredInstance;\n }\n\n constructor(\n private readonly options: DependencyInjectionOptions = {\n containerBuild: process.env.CONTAINER_BUILD === 'true',\n servicesYamlPath: path.resolve(\n process.cwd(),\n 'config',\n 'container',\n 'services.yaml',\n ),\n sourceDirectory: path.resolve(process.cwd(), 'src'),\n },\n ) {\n this.container = new ContainerBuilder(false, this.options.sourceDirectory);\n }\n\n private get definitions(): Map<string, DefinitionMetadata> {\n const container = this.container as unknown as ContainerInternals;\n\n return container._definitions || new Map();\n }\n\n private get aliases(): Map<string, string> {\n const container = this.container as unknown as ContainerInternals;\n\n return container._alias || new Map();\n }\n\n private async ensureFolderExists(filePath: string): Promise<void> {\n await fs.mkdir(path.dirname(filePath), { recursive: true });\n }\n\n private getServiceClassName(serviceName: unknown): string | undefined {\n return typeof serviceName === 'function' ? serviceName.name : undefined;\n }\n\n private getOverrideId(prefix: string, token: unknown): string {\n const tokenName = this.getServiceClassName(token) ?? String(token);\n\n return `ddd-kernel.override.${prefix}.${tokenName}`;\n }\n\n private ensureSyntheticService(id: string, value: unknown): void {\n const definition = this.container.register(id);\n\n definition.public = true;\n definition.synthetic = true;\n this.container.set(id, value);\n }\n\n private parentMatchesService(\n parentId: string | null | undefined,\n serviceClassName: string,\n ): boolean {\n if (!parentId) {\n return false;\n }\n\n const parentName = Buffer.from(parentId, 'base64').toString('utf8');\n\n return parentName.endsWith(`__${serviceClassName}__${serviceClassName}`);\n }\n\n private serviceIdMatchesService(\n serviceId: string,\n serviceClassName: string,\n ): boolean {\n const serviceName = Buffer.from(serviceId, 'base64').toString('utf8');\n\n return serviceName.endsWith(`__${serviceClassName}__${serviceClassName}`);\n }\n\n private serviceIdReferencesService(\n serviceId: string,\n serviceClassName: string,\n ): boolean {\n const serviceName = Buffer.from(serviceId, 'base64').toString('utf8');\n\n return serviceName.endsWith(`__${serviceClassName}`);\n }\n\n private getReferenceId(value: unknown): string | undefined {\n if (\n typeof value === 'object' &&\n value !== null &&\n 'id' in value &&\n typeof value.id === 'string'\n ) {\n return value.id;\n }\n\n return undefined;\n }\n\n private getDefinitionArgumentReferences(\n definition: DefinitionMetadata,\n ): string[] {\n return [\n ...(definition._args ?? []),\n ...(definition._appendArgs ?? []),\n ...(definition._overrideArgs ?? []),\n ].flatMap((argument) => {\n const referenceId = this.getReferenceId(argument);\n\n return referenceId ? [referenceId] : [];\n });\n }\n\n private findConcreteChildServiceId(serviceName: unknown): string | undefined {\n const serviceClassName = this.getServiceClassName(serviceName);\n\n if (!serviceClassName) {\n return undefined;\n }\n\n const matches = [...this.definitions.entries()]\n .filter(([, definition]) => definition._abstract !== true)\n .filter(([, definition]) =>\n this.parentMatchesService(definition._parent, serviceClassName),\n )\n .map(([id]) => id);\n\n return matches[matches.length - 1];\n }\n\n private findRegisteredServiceId(serviceName: unknown): string | undefined {\n const serviceClassName = this.getServiceClassName(serviceName);\n\n if (!serviceClassName) {\n return undefined;\n }\n\n const matches = [...this.definitions.keys()].filter((id) =>\n this.serviceIdMatchesService(id, serviceClassName),\n );\n\n return matches[matches.length - 1];\n }\n\n private findAliasServiceId(serviceName: unknown): string | undefined {\n const serviceClassName = this.getServiceClassName(serviceName);\n\n if (!serviceClassName) {\n return undefined;\n }\n\n const matches = [...this.aliases.keys()].filter((id) =>\n this.serviceIdMatchesService(id, serviceClassName),\n );\n\n return matches[matches.length - 1];\n }\n\n private findReferencedServiceIds(serviceName: unknown): string[] {\n const serviceClassName = this.getServiceClassName(serviceName);\n\n if (!serviceClassName) {\n return [];\n }\n\n return [\n ...new Set(\n [...this.definitions.values()]\n .flatMap((definition) =>\n this.getDefinitionArgumentReferences(definition),\n )\n .filter((id) =>\n this.serviceIdReferencesService(id, serviceClassName),\n ),\n ),\n ];\n }\n\n private getOverrideTokenIds(token: unknown): string[] {\n const tokenIds = [\n this.findRegisteredServiceId(token),\n this.findAliasServiceId(token),\n ...this.findReferencedServiceIds(token),\n ].filter((id): id is string => id !== undefined);\n const existingTokenIds = [...new Set(tokenIds)];\n\n if (existingTokenIds.length > 0) {\n return existingTokenIds;\n }\n\n const overrideTokenId = this.getOverrideId('token', token);\n\n this.ensureSyntheticService(overrideTokenId, undefined);\n\n return [overrideTokenId];\n }\n\n private getOverrideClassServiceId(\n ClassDefinition: new () => unknown,\n ): string {\n const registeredServiceId = this.findRegisteredServiceId(ClassDefinition);\n\n if (registeredServiceId) {\n return registeredServiceId;\n }\n\n const overrideClassId = this.getOverrideId('class', ClassDefinition);\n\n this.container.register(overrideClassId, ClassDefinition);\n\n return overrideClassId;\n }\n\n private applyClassOverride(override: DependencyOverride): void {\n if (!('useClass' in override)) {\n return;\n }\n\n const tokenIds = this.getOverrideTokenIds(override.token);\n const classId = this.getOverrideClassServiceId(override.useClass);\n\n this.overrideTokenIds.set(override.token, tokenIds[0]);\n for (const tokenId of tokenIds) {\n this.container.setAlias(tokenId, classId);\n }\n }\n\n private applyFactoryOverride(override: DependencyOverride): void {\n if (!('useFactory' in override)) {\n return;\n }\n\n const tokenIds = this.getOverrideTokenIds(override.token);\n const factoryId = this.getOverrideId('factory', override.token);\n\n this.ensureSyntheticService(factoryId, override.useFactory(this));\n this.overrideTokenIds.set(override.token, tokenIds[0]);\n for (const tokenId of tokenIds) {\n this.container.setAlias(tokenId, factoryId);\n }\n }\n\n private applyValueOverride(override: DependencyOverride): void {\n if (!('useValue' in override)) {\n return;\n }\n\n const tokenIds = this.getOverrideTokenIds(override.token);\n const valueId = this.getOverrideId('value', override.token);\n\n this.ensureSyntheticService(valueId, override.useValue);\n this.overrideTokenIds.set(override.token, tokenIds[0]);\n for (const tokenId of tokenIds) {\n this.container.setAlias(tokenId, valueId);\n }\n }\n\n private applyOverrides(): void {\n for (const override of this.options.overrides ?? []) {\n this.applyClassOverride(override);\n this.applyFactoryOverride(override);\n this.applyValueOverride(override);\n }\n }\n\n private registerParentAliases(): void {\n for (const [id, definition] of this.definitions.entries()) {\n if (definition._abstract === true || !definition._parent) {\n continue;\n }\n\n this.container.setAlias(definition._parent, id);\n }\n }\n\n public async compile(): Promise<void> {\n if (this.options.containerBuild) {\n await this.ensureFolderExists(this.options.servicesYamlPath);\n this.autowire = new Autowire(this.container);\n this.autowire.serviceFile = new ServiceFile(\n this.options.servicesYamlPath,\n false,\n );\n await this.autowire.process();\n } else {\n this.loader = new YamlFileLoader(this.container);\n await this.loader.load(this.options.servicesYamlPath);\n }\n\n this.registerParentAliases();\n this.applyOverrides();\n await this.container.compile();\n }\n\n public getService<T>(serviceName: unknown): T {\n const overrideTokenId = this.overrideTokenIds.get(serviceName);\n\n if (overrideTokenId) {\n return this.container.get<T>(overrideTokenId);\n }\n\n const aliasServiceId = this.findAliasServiceId(serviceName);\n\n if (aliasServiceId) {\n return this.container.get<T>(aliasServiceId);\n }\n\n const childServiceId = this.findConcreteChildServiceId(serviceName);\n\n if (childServiceId) {\n return this.container.get<T>(childServiceId);\n }\n\n const registeredServiceId = this.findRegisteredServiceId(serviceName);\n\n if (registeredServiceId) {\n return this.container.get<T>(registeredServiceId);\n }\n\n return this.container.get<T>(serviceName);\n }\n}\n\nexport default DependencyInjection;\n"],"mappings":"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","names":[]}
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
+ var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
5
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
8
  var __export = (target, all) => {
7
9
  for (var name in all)
@@ -15,39 +17,114 @@ var __copyProps = (to, from, except, desc) => {
15
17
  }
16
18
  return to;
17
19
  };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
18
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
29
 
20
30
  // src/infrastructure/express/index.ts
21
31
  var express_exports = {};
22
32
  __export(express_exports, {
23
33
  ExpressKernelServer: () => ExpressKernelServer,
34
+ HttpErrorHandler: () => HttpErrorHandler,
24
35
  RoutePrefix: () => RoutePrefix
25
36
  });
26
37
  module.exports = __toCommonJS(express_exports);
27
38
 
28
39
  // src/adapters/ui/express/ExpressKernelServer.ts
29
- var import_routing_controllers = require("routing-controllers");
40
+ var import_node_module = require("module");
41
+ var import_node_path = __toESM(require("path"), 1);
30
42
  var ExpressKernelServer = class {
31
43
  constructor(options) {
32
44
  this.options = options;
45
+ this.afterControllersHooks = this.copy(options.afterControllersHooks);
46
+ this.beforeControllersHooks = this.copy(options.beforeControllersHooks);
47
+ this.controllers = this.copy(options.controllers);
48
+ this.errorHandlers = this.copy(options.errorHandlers);
49
+ this.hooks = this.copy(options.hooks);
50
+ this.middlewares = this.copy(options.middlewares);
51
+ this.postControllerMiddlewares = this.copy(
52
+ options.postControllerMiddlewares
53
+ );
54
+ this.preControllerMiddlewares = this.copy(options.preControllerMiddlewares);
55
+ this.staticHooks = this.copy(options.staticHooks);
56
+ this.swaggerHooks = this.copy(options.swaggerHooks);
33
57
  }
34
58
  options;
59
+ applicationRequire = (0, import_node_module.createRequire)(
60
+ import_node_path.default.resolve(process.cwd(), "package.json")
61
+ );
62
+ afterControllersHooks;
35
63
  appInstance;
64
+ beforeControllersHooks;
65
+ controllers;
66
+ errorHandlers;
67
+ hooks;
68
+ middlewares;
69
+ postControllerMiddlewares;
70
+ preControllerMiddlewares;
36
71
  serverInstance;
37
- registerErrorHandlers(app) {
38
- const handlers = this.options.errorHandlers ?? [this.defaultErrorHandler()];
72
+ staticHooks;
73
+ swaggerHooks;
74
+ copy(items) {
75
+ return [...items ?? []];
76
+ }
77
+ configureControllerContainer() {
78
+ const { useContainer } = this.getRoutingControllers();
79
+ useContainer(
80
+ {
81
+ /* c8 ignore next */
82
+ get: (ClassDefinition) => this.options.kernel.di.getService(ClassDefinition)
83
+ },
84
+ {
85
+ fallback: true,
86
+ fallbackOnErrors: true
87
+ }
88
+ );
89
+ }
90
+ getExpress() {
91
+ return this.applicationRequire("express");
92
+ }
93
+ getRoutingControllers() {
94
+ return this.applicationRequire("routing-controllers");
95
+ }
96
+ applyErrorHandlers(app) {
97
+ const handlers = this.errorHandlers.length > 0 ? this.errorHandlers : [this.defaultErrorHandler()];
39
98
  for (const handler of handlers) {
40
99
  app.use(handler);
41
100
  }
42
101
  }
43
102
  defaultErrorHandler() {
44
- return (error, request, response) => {
103
+ return (error, request, response, next) => {
104
+ void next;
45
105
  void request;
46
106
  response.status(500).json({
47
107
  error: error instanceof Error ? error.message : String(error)
48
108
  });
49
109
  };
50
110
  }
111
+ async runHooks(hooks, app) {
112
+ for (const hook of hooks) {
113
+ await hook(app);
114
+ }
115
+ }
116
+ async runPhaseHooks(phase, app) {
117
+ for (const hook of this.hooks) {
118
+ if (hook.phase === phase) {
119
+ await hook.handle(app);
120
+ }
121
+ }
122
+ }
123
+ applyMiddlewares(app, middlewares) {
124
+ for (const middleware of middlewares) {
125
+ app.use(middleware);
126
+ }
127
+ }
51
128
  get app() {
52
129
  if (!this.appInstance) {
53
130
  throw new Error("HTTP server is not running.");
@@ -60,6 +137,11 @@ var ExpressKernelServer = class {
60
137
  }
61
138
  return this.serverInstance;
62
139
  }
140
+ assertServerIsNotRunning() {
141
+ if (this.serverInstance) {
142
+ throw new Error("HTTP server is already running.");
143
+ }
144
+ }
63
145
  close() {
64
146
  if (!this.serverInstance) {
65
147
  return Promise.resolve();
@@ -76,15 +158,74 @@ var ExpressKernelServer = class {
76
158
  });
77
159
  });
78
160
  }
79
- run() {
80
- const app = (0, import_routing_controllers.createExpressServer)({
81
- controllers: this.options.kernel.getRoutes(),
161
+ registerAfterControllersHooks(...hooks) {
162
+ this.assertServerIsNotRunning();
163
+ this.afterControllersHooks?.push(...hooks);
164
+ return this;
165
+ }
166
+ registerBeforeControllersHooks(...hooks) {
167
+ this.assertServerIsNotRunning();
168
+ this.beforeControllersHooks?.push(...hooks);
169
+ return this;
170
+ }
171
+ registerControllers(...controllers) {
172
+ this.assertServerIsNotRunning();
173
+ this.controllers.push(...controllers);
174
+ return this;
175
+ }
176
+ registerErrorHandlers(...handlers) {
177
+ this.assertServerIsNotRunning();
178
+ this.errorHandlers.push(...handlers);
179
+ return this;
180
+ }
181
+ registerHooks(...hooks) {
182
+ this.assertServerIsNotRunning();
183
+ this.hooks.push(...hooks);
184
+ return this;
185
+ }
186
+ registerMiddlewares(...middlewares) {
187
+ this.assertServerIsNotRunning();
188
+ this.middlewares.push(...middlewares);
189
+ return this;
190
+ }
191
+ registerPostControllerMiddlewares(...middlewares) {
192
+ this.assertServerIsNotRunning();
193
+ this.postControllerMiddlewares.push(...middlewares);
194
+ return this;
195
+ }
196
+ registerPreControllerMiddlewares(...middlewares) {
197
+ this.assertServerIsNotRunning();
198
+ this.preControllerMiddlewares.push(...middlewares);
199
+ return this;
200
+ }
201
+ async run() {
202
+ if (this.serverInstance) {
203
+ throw new Error("HTTP server is already running.");
204
+ }
205
+ const controllers = [
206
+ ...this.options.kernel.getRoutes(),
207
+ ...this.controllers
208
+ ];
209
+ const express = this.getExpress();
210
+ const { useExpressServer } = this.getRoutingControllers();
211
+ const app = express();
212
+ this.applyMiddlewares(app, this.middlewares);
213
+ this.applyMiddlewares(app, this.preControllerMiddlewares);
214
+ await this.runHooks(this.beforeControllersHooks, app);
215
+ await this.runPhaseHooks("beforeControllers", app);
216
+ this.configureControllerContainer();
217
+ useExpressServer(app, {
218
+ ...this.options.routingControllersOptions,
219
+ controllers,
82
220
  routePrefix: this.options.routePrefix
83
221
  });
84
- for (const middleware of this.options.middlewares ?? []) {
85
- app.use(middleware);
86
- }
87
- this.registerErrorHandlers(app);
222
+ this.applyMiddlewares(app, this.postControllerMiddlewares);
223
+ await this.runHooks(this.afterControllersHooks, app);
224
+ await this.runPhaseHooks("afterControllers", app);
225
+ await this.runHooks(this.swaggerHooks, app);
226
+ await this.runHooks(this.staticHooks, app);
227
+ await this.runPhaseHooks("beforeErrors", app);
228
+ this.applyErrorHandlers(app);
88
229
  this.appInstance = app;
89
230
  return new Promise((resolve) => {
90
231
  this.serverInstance = app.listen(this.options.port ?? 3e3, () => {
@@ -94,6 +235,132 @@ var ExpressKernelServer = class {
94
235
  }
95
236
  };
96
237
 
238
+ // src/adapters/ui/express/HttpErrorHandler.ts
239
+ var import_routing_controllers = require("routing-controllers");
240
+
241
+ // src/contracts/ui/HttpRouteStatusEnum.ts
242
+ var HttpRouteStatusEnum = {
243
+ BAD_REQUEST: 400,
244
+ CONFLICT: 409,
245
+ CREATED: 201,
246
+ DEPRECATED: 299,
247
+ FORBIDDEN: 403,
248
+ INTERNAL_SERVER_ERROR: 500,
249
+ NO_CONTENT: 204,
250
+ NOT_FOUND: 404,
251
+ OK: 200,
252
+ PAYLOAD_TOO_LARGE: 413,
253
+ SERVICE_UNAVAILABLE: 503,
254
+ TOO_MANY_REQUESTS: 429,
255
+ UNAUTHORIZED: 401,
256
+ UNPROCESSABLE_ENTITY: 422
257
+ };
258
+
259
+ // src/adapters/ui/express/HttpErrorHandler.ts
260
+ var HttpErrorHandler = class {
261
+ constructor(options = {}) {
262
+ this.options = options;
263
+ this.handlers = options.handlers ?? [];
264
+ this.exposeUnhandledErrorsIn = options.exposeUnhandledErrorsIn ?? [
265
+ "local",
266
+ "test"
267
+ ];
268
+ }
269
+ options;
270
+ handlers;
271
+ exposeUnhandledErrorsIn;
272
+ formatValidationErrors(errors) {
273
+ return errors.flatMap((error) => {
274
+ if (error.children && error.children.length > 0) {
275
+ return this.formatValidationErrors(error.children);
276
+ }
277
+ return [
278
+ {
279
+ details: error.constraints,
280
+ property: error.property,
281
+ value: error.value
282
+ }
283
+ ];
284
+ });
285
+ }
286
+ getErrorMessage(error) {
287
+ return error instanceof Error ? error.message : String(error);
288
+ }
289
+ getHttpStatus(error) {
290
+ return error.httpCode ?? error.statusCode ?? error.status;
291
+ }
292
+ isPayloadTooLargeError(error) {
293
+ return error.type === "entity.too.large" || this.getHttpStatus(error) === HttpRouteStatusEnum.PAYLOAD_TOO_LARGE;
294
+ }
295
+ logUnhandledError(error) {
296
+ this.options.logger?.error(`Unhandled error: ${error.message}`);
297
+ this.options.logger?.debug(error.stack ?? "No stack trace available");
298
+ }
299
+ handleSyntaxError(error, response) {
300
+ if (!(error instanceof SyntaxError)) {
301
+ return false;
302
+ }
303
+ response.status(HttpRouteStatusEnum.BAD_REQUEST).json({
304
+ code: "SyntaxError",
305
+ message: "Malformed JSON"
306
+ });
307
+ return true;
308
+ }
309
+ handlePayloadTooLargeError(error, response) {
310
+ if (!this.isPayloadTooLargeError(error)) {
311
+ return false;
312
+ }
313
+ response.status(HttpRouteStatusEnum.PAYLOAD_TOO_LARGE).json({
314
+ code: "PayloadTooLargeError",
315
+ httpStatus: HttpRouteStatusEnum.PAYLOAD_TOO_LARGE,
316
+ message: "Request entity too large."
317
+ });
318
+ return true;
319
+ }
320
+ handleHttpError(error, response) {
321
+ const httpError = error;
322
+ const httpStatus = this.getHttpStatus(httpError);
323
+ if (!httpStatus && !(error instanceof import_routing_controllers.HttpError)) {
324
+ return false;
325
+ }
326
+ response.status(httpStatus ?? HttpRouteStatusEnum.INTERNAL_SERVER_ERROR).json({
327
+ code: error.name,
328
+ errors: this.formatValidationErrors(
329
+ error.errors ?? []
330
+ ),
331
+ httpStatus: httpStatus ?? HttpRouteStatusEnum.INTERNAL_SERVER_ERROR,
332
+ message: error.message
333
+ });
334
+ return true;
335
+ }
336
+ handleUnhandledError(error, response) {
337
+ if (this.exposeUnhandledErrorsIn.includes(process.env.NODE_ENV ?? "")) {
338
+ this.logUnhandledError(error);
339
+ }
340
+ response.status(HttpRouteStatusEnum.INTERNAL_SERVER_ERROR).json({
341
+ code: error.constructor.name || String(HttpRouteStatusEnum.INTERNAL_SERVER_ERROR),
342
+ message: error.message || "Unknown error"
343
+ });
344
+ }
345
+ error(error, request, response, next) {
346
+ void request;
347
+ const handlers = [
348
+ this.handleSyntaxError.bind(this),
349
+ this.handlePayloadTooLargeError.bind(this),
350
+ ...this.handlers,
351
+ this.handleHttpError.bind(this)
352
+ ];
353
+ if (handlers.some((handler) => handler(error, response))) {
354
+ return;
355
+ }
356
+ void next;
357
+ this.handleUnhandledError(error, response);
358
+ }
359
+ handle = (error, request, response, next) => {
360
+ this.error(error, request, response, next);
361
+ };
362
+ };
363
+
97
364
  // src/adapters/ui/express/RoutePrefix.ts
98
365
  var RoutePrefix = class _RoutePrefix {
99
366
  value;
@@ -121,6 +388,7 @@ var RoutePrefix = class _RoutePrefix {
121
388
  // Annotate the CommonJS export names for ESM import in node:
122
389
  0 && (module.exports = {
123
390
  ExpressKernelServer,
391
+ HttpErrorHandler,
124
392
  RoutePrefix
125
393
  });
126
394
  //# sourceMappingURL=index.cjs.map