@haskou/ddd-kernel 0.1.1 → 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 +5 -10
  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 +89 -10
  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
@@ -93,6 +93,7 @@ var DependencyInjection = class _DependencyInjection {
93
93
  autowire;
94
94
  loader;
95
95
  container;
96
+ overrideTokenIds = /* @__PURE__ */ new Map();
96
97
  static configure(options) {
97
98
  _DependencyInjection.configuredInstance = new _DependencyInjection(options);
98
99
  return _DependencyInjection.configuredInstance;
@@ -117,6 +118,16 @@ var DependencyInjection = class _DependencyInjection {
117
118
  getServiceClassName(serviceName) {
118
119
  return typeof serviceName === "function" ? serviceName.name : void 0;
119
120
  }
121
+ getOverrideId(prefix, token) {
122
+ const tokenName = this.getServiceClassName(token) ?? String(token);
123
+ return `ddd-kernel.override.${prefix}.${tokenName}`;
124
+ }
125
+ ensureSyntheticService(id, value) {
126
+ const definition = this.container.register(id);
127
+ definition.public = true;
128
+ definition.synthetic = true;
129
+ this.container.set(id, value);
130
+ }
120
131
  parentMatchesService(parentId, serviceClassName) {
121
132
  if (!parentId) {
122
133
  return false;
@@ -128,6 +139,26 @@ var DependencyInjection = class _DependencyInjection {
128
139
  const serviceName = Buffer.from(serviceId, "base64").toString("utf8");
129
140
  return serviceName.endsWith(`__${serviceClassName}__${serviceClassName}`);
130
141
  }
142
+ serviceIdReferencesService(serviceId, serviceClassName) {
143
+ const serviceName = Buffer.from(serviceId, "base64").toString("utf8");
144
+ return serviceName.endsWith(`__${serviceClassName}`);
145
+ }
146
+ getReferenceId(value) {
147
+ if (typeof value === "object" && value !== null && "id" in value && typeof value.id === "string") {
148
+ return value.id;
149
+ }
150
+ return void 0;
151
+ }
152
+ getDefinitionArgumentReferences(definition) {
153
+ return [
154
+ ...definition._args ?? [],
155
+ ...definition._appendArgs ?? [],
156
+ ...definition._overrideArgs ?? []
157
+ ].flatMap((argument) => {
158
+ const referenceId = this.getReferenceId(argument);
159
+ return referenceId ? [referenceId] : [];
160
+ });
161
+ }
131
162
  findConcreteChildServiceId(serviceName) {
132
163
  const serviceClassName = this.getServiceClassName(serviceName);
133
164
  if (!serviceClassName) {
@@ -158,6 +189,86 @@ var DependencyInjection = class _DependencyInjection {
158
189
  );
159
190
  return matches[matches.length - 1];
160
191
  }
192
+ findReferencedServiceIds(serviceName) {
193
+ const serviceClassName = this.getServiceClassName(serviceName);
194
+ if (!serviceClassName) {
195
+ return [];
196
+ }
197
+ return [
198
+ ...new Set(
199
+ [...this.definitions.values()].flatMap(
200
+ (definition) => this.getDefinitionArgumentReferences(definition)
201
+ ).filter(
202
+ (id) => this.serviceIdReferencesService(id, serviceClassName)
203
+ )
204
+ )
205
+ ];
206
+ }
207
+ getOverrideTokenIds(token) {
208
+ const tokenIds = [
209
+ this.findRegisteredServiceId(token),
210
+ this.findAliasServiceId(token),
211
+ ...this.findReferencedServiceIds(token)
212
+ ].filter((id) => id !== void 0);
213
+ const existingTokenIds = [...new Set(tokenIds)];
214
+ if (existingTokenIds.length > 0) {
215
+ return existingTokenIds;
216
+ }
217
+ const overrideTokenId = this.getOverrideId("token", token);
218
+ this.ensureSyntheticService(overrideTokenId, void 0);
219
+ return [overrideTokenId];
220
+ }
221
+ getOverrideClassServiceId(ClassDefinition) {
222
+ const registeredServiceId = this.findRegisteredServiceId(ClassDefinition);
223
+ if (registeredServiceId) {
224
+ return registeredServiceId;
225
+ }
226
+ const overrideClassId = this.getOverrideId("class", ClassDefinition);
227
+ this.container.register(overrideClassId, ClassDefinition);
228
+ return overrideClassId;
229
+ }
230
+ applyClassOverride(override) {
231
+ if (!("useClass" in override)) {
232
+ return;
233
+ }
234
+ const tokenIds = this.getOverrideTokenIds(override.token);
235
+ const classId = this.getOverrideClassServiceId(override.useClass);
236
+ this.overrideTokenIds.set(override.token, tokenIds[0]);
237
+ for (const tokenId of tokenIds) {
238
+ this.container.setAlias(tokenId, classId);
239
+ }
240
+ }
241
+ applyFactoryOverride(override) {
242
+ if (!("useFactory" in override)) {
243
+ return;
244
+ }
245
+ const tokenIds = this.getOverrideTokenIds(override.token);
246
+ const factoryId = this.getOverrideId("factory", override.token);
247
+ this.ensureSyntheticService(factoryId, override.useFactory(this));
248
+ this.overrideTokenIds.set(override.token, tokenIds[0]);
249
+ for (const tokenId of tokenIds) {
250
+ this.container.setAlias(tokenId, factoryId);
251
+ }
252
+ }
253
+ applyValueOverride(override) {
254
+ if (!("useValue" in override)) {
255
+ return;
256
+ }
257
+ const tokenIds = this.getOverrideTokenIds(override.token);
258
+ const valueId = this.getOverrideId("value", override.token);
259
+ this.ensureSyntheticService(valueId, override.useValue);
260
+ this.overrideTokenIds.set(override.token, tokenIds[0]);
261
+ for (const tokenId of tokenIds) {
262
+ this.container.setAlias(tokenId, valueId);
263
+ }
264
+ }
265
+ applyOverrides() {
266
+ for (const override of this.options.overrides ?? []) {
267
+ this.applyClassOverride(override);
268
+ this.applyFactoryOverride(override);
269
+ this.applyValueOverride(override);
270
+ }
271
+ }
161
272
  registerParentAliases() {
162
273
  for (const [id, definition] of this.definitions.entries()) {
163
274
  if (definition._abstract === true || !definition._parent) {
@@ -180,17 +291,22 @@ var DependencyInjection = class _DependencyInjection {
180
291
  await this.loader.load(this.options.servicesYamlPath);
181
292
  }
182
293
  this.registerParentAliases();
294
+ this.applyOverrides();
183
295
  await this.container.compile();
184
296
  }
185
297
  getService(serviceName) {
186
- const childServiceId = this.findConcreteChildServiceId(serviceName);
187
- if (childServiceId) {
188
- return this.container.get(childServiceId);
298
+ const overrideTokenId = this.overrideTokenIds.get(serviceName);
299
+ if (overrideTokenId) {
300
+ return this.container.get(overrideTokenId);
189
301
  }
190
302
  const aliasServiceId = this.findAliasServiceId(serviceName);
191
303
  if (aliasServiceId) {
192
304
  return this.container.get(aliasServiceId);
193
305
  }
306
+ const childServiceId = this.findConcreteChildServiceId(serviceName);
307
+ if (childServiceId) {
308
+ return this.container.get(childServiceId);
309
+ }
194
310
  const registeredServiceId = this.findRegisteredServiceId(serviceName);
195
311
  if (registeredServiceId) {
196
312
  return this.container.get(registeredServiceId);
@@ -238,6 +354,9 @@ var Kernel = class _Kernel {
238
354
  static get logger() {
239
355
  return _Kernel.getActiveKernel().logger;
240
356
  }
357
+ static get active() {
358
+ return _Kernel.getActiveKernel();
359
+ }
241
360
  static get rootDirectory() {
242
361
  return process.cwd();
243
362
  }
@@ -306,13 +425,21 @@ var Kernel = class _Kernel {
306
425
  get schedulers() {
307
426
  return this.schedulersList;
308
427
  }
309
- async dependencyInjection() {
310
- this.dependencyInjectionInstance = this.dependencyInjectionInstance ?? DependencyInjection.configure({
311
- containerBuild: process.env.CONTAINER_BUILD === "true",
312
- servicesYamlPath: this.options.servicesYamlPath ?? path2.resolve(_Kernel.configDirectory, "container", "services.yaml"),
313
- sourceDirectory: this.options.sourceDirectory ?? _Kernel.sourceDirectory
314
- });
428
+ getDependencyInjectionOptions(options = {}) {
429
+ return {
430
+ containerBuild: options.containerBuild ?? process.env.CONTAINER_BUILD === "true",
431
+ overrides: options.overrides ?? [],
432
+ servicesYamlPath: options.servicesYamlPath ?? this.options.servicesYamlPath ?? path2.resolve(_Kernel.configDirectory, "container", "services.yaml"),
433
+ sourceDirectory: options.sourceDirectory ?? this.options.sourceDirectory ?? _Kernel.sourceDirectory
434
+ };
435
+ }
436
+ async dependencyInjection(options = {}) {
437
+ _Kernel.state.activeKernel = this;
438
+ this.dependencyInjectionInstance = this.dependencyInjectionInstance ?? DependencyInjection.configure(
439
+ this.getDependencyInjectionOptions(options)
440
+ );
315
441
  await this.dependencyInjectionInstance.compile();
442
+ _Kernel.state.activeKernel = this;
316
443
  }
317
444
  getRoutes() {
318
445
  return this.routes;
@@ -395,21 +522,52 @@ var Kernel = class _Kernel {
395
522
  }
396
523
  };
397
524
 
525
+ // src/adapters/pubsub/ConsumerMiddlewarePipeline.ts
526
+ var ConsumerMiddlewarePipeline = class {
527
+ constructor(middlewares) {
528
+ this.middlewares = middlewares;
529
+ }
530
+ middlewares;
531
+ async run(event, context, handler, index) {
532
+ const middleware = this.middlewares[index];
533
+ if (!middleware) {
534
+ await handler();
535
+ return;
536
+ }
537
+ await middleware.handle(
538
+ event,
539
+ () => this.run(event, context, handler, index + 1),
540
+ context
541
+ );
542
+ }
543
+ async execute(event, context, handler) {
544
+ await this.run(event, context, handler, 0);
545
+ }
546
+ };
547
+
398
548
  // src/adapters/pubsub/Consumer.ts
399
549
  var Consumer = class {
400
550
  constructor(consumer) {
401
551
  this.consumer = consumer;
402
552
  }
403
553
  consumer;
404
- async runMiddleware(event, middlewares, index) {
405
- const middleware = middlewares[index];
406
- if (!middleware) {
407
- await this.handler(event);
408
- return;
409
- }
410
- await middleware.handle(
554
+ async runMiddleware(event, consumerContext) {
555
+ const pipeline = new ConsumerMiddlewarePipeline(Kernel.consumerMiddleware);
556
+ const metadata = consumerContext?.metadata ?? {};
557
+ await pipeline.execute(
411
558
  event,
412
- () => this.runMiddleware(event, middlewares, index + 1)
559
+ {
560
+ causationId: event.getCausationId(),
561
+ correlationId: event.getCorrelationId(),
562
+ eventId: event.eventId,
563
+ eventName: this.eventName,
564
+ exchange: this.exchange,
565
+ kernel: Kernel.active,
566
+ metadata,
567
+ queueName: this.queueName,
568
+ rawMessage: metadata.rawMessage
569
+ },
570
+ () => this.handler(event)
413
571
  );
414
572
  }
415
573
  async init() {
@@ -418,7 +576,7 @@ var Consumer = class {
418
576
  this.eventName,
419
577
  this.domainEvent,
420
578
  this.exchange,
421
- (event) => this.runMiddleware(event, Kernel.consumerMiddleware, 0)
579
+ (event, context) => this.runMiddleware(event, context)
422
580
  );
423
581
  }
424
582
  get(service) {
@@ -426,6 +584,178 @@ var Consumer = class {
426
584
  }
427
585
  };
428
586
 
587
+ // src/adapters/pubsub/CorrelationConsumerMiddleware.ts
588
+ var CorrelationConsumerMiddleware = class {
589
+ constructor(options = {}) {
590
+ this.options = options;
591
+ }
592
+ options;
593
+ async handle(event, next, context) {
594
+ const correlationId = this.options.correlationId?.(event, context) ?? context.correlationId;
595
+ const causationId = this.options.causationId?.(event, context) ?? context.causationId;
596
+ if (correlationId) {
597
+ event.withCorrelationId(correlationId);
598
+ }
599
+ if (causationId) {
600
+ event.withCausationId(causationId);
601
+ }
602
+ await next();
603
+ }
604
+ };
605
+
606
+ // src/adapters/pubsub/DefaultPublisherHookErrorPolicy.ts
607
+ var DefaultPublisherHookErrorPolicy = class {
608
+ handleAfterPublishError(error, context) {
609
+ void error;
610
+ void context;
611
+ }
612
+ shouldFailAfterPublish(error, context) {
613
+ void error;
614
+ void context;
615
+ return false;
616
+ }
617
+ };
618
+
619
+ // src/adapters/pubsub/IdempotencyConsumerMiddleware.ts
620
+ var IdempotencyConsumerMiddleware = class {
621
+ constructor(options) {
622
+ this.options = options;
623
+ }
624
+ options;
625
+ async handleClaimedKey(key, next) {
626
+ const claimed = await this.options.store.claim?.(key);
627
+ if (!claimed) {
628
+ return;
629
+ }
630
+ try {
631
+ await next();
632
+ await (this.options.store.commit?.(key) ?? this.options.store.mark(key));
633
+ } catch (error) {
634
+ await this.options.store.release?.(key);
635
+ throw error;
636
+ }
637
+ }
638
+ async handleLegacyKey(key, next) {
639
+ if (await this.options.store.has(key)) {
640
+ return;
641
+ }
642
+ await next();
643
+ await this.options.store.mark(key);
644
+ }
645
+ async handle(event, next, context) {
646
+ const key = this.options.key?.(event, context) ?? context.eventId;
647
+ if (this.options.store.claim) {
648
+ await this.handleClaimedKey(key, next);
649
+ } else {
650
+ await this.handleLegacyKey(key, next);
651
+ }
652
+ }
653
+ };
654
+
655
+ // src/adapters/pubsub/InMemoryIdempotencyStore.ts
656
+ var InMemoryIdempotencyStore = class {
657
+ claimedKeys = /* @__PURE__ */ new Set();
658
+ handledKeys = /* @__PURE__ */ new Set();
659
+ claim(key) {
660
+ if (this.handledKeys.has(key) || this.claimedKeys.has(key)) {
661
+ return false;
662
+ }
663
+ this.claimedKeys.add(key);
664
+ return true;
665
+ }
666
+ commit(key) {
667
+ this.claimedKeys.delete(key);
668
+ this.handledKeys.add(key);
669
+ }
670
+ release(key) {
671
+ this.claimedKeys.delete(key);
672
+ }
673
+ has(key) {
674
+ return this.handledKeys.has(key);
675
+ }
676
+ mark(key) {
677
+ this.handledKeys.add(key);
678
+ }
679
+ };
680
+
681
+ // src/adapters/pubsub/PublisherHookPipeline.ts
682
+ var PublisherHookPipeline = class {
683
+ constructor(hooks = [], errorPolicy = new DefaultPublisherHookErrorPolicy()) {
684
+ this.errorPolicy = errorPolicy;
685
+ this.hooks.push(...hooks);
686
+ }
687
+ errorPolicy;
688
+ hooks = [];
689
+ async runAfterPublishHooks(context) {
690
+ for (const hook of this.hooks) {
691
+ await this.runAfterPublishHook(hook, context);
692
+ }
693
+ }
694
+ async runAfterPublishHook(hook, context) {
695
+ try {
696
+ await hook.afterPublish?.(context);
697
+ } catch (error) {
698
+ await this.errorPolicy.handleAfterPublishError(error, context);
699
+ if (this.errorPolicy.shouldFailAfterPublish(error, context)) {
700
+ throw error;
701
+ }
702
+ }
703
+ }
704
+ async runBeforePublishHooks(context) {
705
+ for (const hook of this.hooks) {
706
+ await hook.beforePublish?.(context);
707
+ }
708
+ }
709
+ async runPublishErrorHooks(error, context) {
710
+ for (const hook of this.hooks) {
711
+ await hook.onPublishError?.(error, context);
712
+ }
713
+ }
714
+ register(...hooks) {
715
+ this.hooks.push(...hooks);
716
+ }
717
+ async run(context, publish) {
718
+ await this.runBeforePublishHooks(context);
719
+ try {
720
+ const result = await publish();
721
+ await this.runAfterPublishHooks(context);
722
+ return result;
723
+ } catch (error) {
724
+ await this.runPublishErrorHooks(error, context);
725
+ throw error;
726
+ }
727
+ }
728
+ };
729
+
730
+ // src/adapters/pubsub/RetryConsumerMiddleware.ts
731
+ var RetryConsumerMiddleware = class {
732
+ constructor(options) {
733
+ this.options = options;
734
+ }
735
+ options;
736
+ async delay(attempt, error, context) {
737
+ const delayInMilliseconds = typeof this.options.delay === "function" ? await this.options.delay(attempt, error, context) : this.options.delay ?? 0;
738
+ if (delayInMilliseconds > 0) {
739
+ await new Promise((resolve) => setTimeout(resolve, delayInMilliseconds));
740
+ }
741
+ }
742
+ async handle(event, next, context) {
743
+ for (let attempt = 1; attempt <= this.options.maxAttempts; attempt++) {
744
+ try {
745
+ await next();
746
+ return;
747
+ } catch (error) {
748
+ const canRetry = attempt < this.options.maxAttempts && await (this.options.shouldRetry?.(error, attempt, context) ?? true);
749
+ if (!canRetry) {
750
+ throw error;
751
+ }
752
+ await this.options.onRetry?.(error, attempt, context);
753
+ await this.delay(attempt, error, context);
754
+ }
755
+ }
756
+ }
757
+ };
758
+
429
759
  // src/adapters/pubsub/amqp/AmqpMessageBusAdapter.ts
430
760
  import amqplib from "amqplib";
431
761
  import { randomUUID } from "crypto";
@@ -448,11 +778,16 @@ var NoFailedMessagesError = class extends Error {
448
778
 
449
779
  // src/adapters/pubsub/in-memory/InMemoryEventBus.ts
450
780
  var InMemoryEventBus = class {
451
- constructor(context) {
781
+ constructor(context, publisherHooks = [], publisherHookErrorPolicy) {
452
782
  this.context = context;
783
+ this.publisherHookPipeline = new PublisherHookPipeline(
784
+ publisherHooks,
785
+ publisherHookErrorPolicy
786
+ );
453
787
  }
454
788
  context;
455
789
  handlers = /* @__PURE__ */ new Map();
790
+ publisherHookPipeline;
456
791
  subscribe(name, handler) {
457
792
  const handlers = this.handlers.get(name) ?? [];
458
793
  handlers.push(handler);
@@ -460,24 +795,42 @@ var InMemoryEventBus = class {
460
795
  }
461
796
  async publish(event) {
462
797
  const handlers = this.handlers.get(event.name) ?? [];
463
- for (const handler of handlers) {
464
- await handler(event, this.context);
465
- }
798
+ await this.publisherHookPipeline.run(
799
+ { message: event, metadata: event.metadata ?? {}, topic: event.name },
800
+ async () => {
801
+ for (const handler of handlers) {
802
+ await handler(event, this.context);
803
+ }
804
+ }
805
+ );
806
+ }
807
+ registerPublisherHooks(...hooks) {
808
+ this.publisherHookPipeline.register(...hooks);
466
809
  }
467
810
  };
468
811
 
469
812
  // src/adapters/pubsub/in-memory/InMemoryPubSub.ts
470
813
  var InMemoryPubSub = class {
471
- constructor(context) {
814
+ constructor(context, publisherHooks = [], publisherHookErrorPolicy) {
472
815
  this.context = context;
816
+ this.publisherHookPipeline = new PublisherHookPipeline(
817
+ publisherHooks,
818
+ publisherHookErrorPolicy
819
+ );
473
820
  }
474
821
  context;
475
822
  consumers = /* @__PURE__ */ new Map();
823
+ publisherHookPipeline;
476
824
  async publish(topic, message) {
477
825
  const consumers = this.consumers.get(topic) ?? /* @__PURE__ */ new Set();
478
- for (const consumer of consumers) {
479
- await consumer(message, this.context);
480
- }
826
+ await this.publisherHookPipeline.run(
827
+ { message, metadata: message.metadata ?? {}, topic },
828
+ async () => {
829
+ for (const consumer of consumers) {
830
+ await consumer(message, this.context);
831
+ }
832
+ }
833
+ );
481
834
  }
482
835
  subscribe(topic, consumer) {
483
836
  const consumers = this.consumers.get(topic) ?? /* @__PURE__ */ new Set();
@@ -491,31 +844,100 @@ var InMemoryPubSub = class {
491
844
  }
492
845
  });
493
846
  }
847
+ registerPublisherHooks(...hooks) {
848
+ this.publisherHookPipeline.register(...hooks);
849
+ }
494
850
  };
495
851
 
496
852
  // src/adapters/ui/express/ExpressKernelServer.ts
497
- import { createExpressServer } from "routing-controllers";
853
+ import { createRequire } from "module";
854
+ import path3 from "path";
498
855
  var ExpressKernelServer = class {
499
856
  constructor(options) {
500
857
  this.options = options;
858
+ this.afterControllersHooks = this.copy(options.afterControllersHooks);
859
+ this.beforeControllersHooks = this.copy(options.beforeControllersHooks);
860
+ this.controllers = this.copy(options.controllers);
861
+ this.errorHandlers = this.copy(options.errorHandlers);
862
+ this.hooks = this.copy(options.hooks);
863
+ this.middlewares = this.copy(options.middlewares);
864
+ this.postControllerMiddlewares = this.copy(
865
+ options.postControllerMiddlewares
866
+ );
867
+ this.preControllerMiddlewares = this.copy(options.preControllerMiddlewares);
868
+ this.staticHooks = this.copy(options.staticHooks);
869
+ this.swaggerHooks = this.copy(options.swaggerHooks);
501
870
  }
502
871
  options;
872
+ applicationRequire = createRequire(
873
+ path3.resolve(process.cwd(), "package.json")
874
+ );
875
+ afterControllersHooks;
503
876
  appInstance;
877
+ beforeControllersHooks;
878
+ controllers;
879
+ errorHandlers;
880
+ hooks;
881
+ middlewares;
882
+ postControllerMiddlewares;
883
+ preControllerMiddlewares;
504
884
  serverInstance;
505
- registerErrorHandlers(app) {
506
- const handlers = this.options.errorHandlers ?? [this.defaultErrorHandler()];
885
+ staticHooks;
886
+ swaggerHooks;
887
+ copy(items) {
888
+ return [...items ?? []];
889
+ }
890
+ configureControllerContainer() {
891
+ const { useContainer } = this.getRoutingControllers();
892
+ useContainer(
893
+ {
894
+ /* c8 ignore next */
895
+ get: (ClassDefinition) => this.options.kernel.di.getService(ClassDefinition)
896
+ },
897
+ {
898
+ fallback: true,
899
+ fallbackOnErrors: true
900
+ }
901
+ );
902
+ }
903
+ getExpress() {
904
+ return this.applicationRequire("express");
905
+ }
906
+ getRoutingControllers() {
907
+ return this.applicationRequire("routing-controllers");
908
+ }
909
+ applyErrorHandlers(app) {
910
+ const handlers = this.errorHandlers.length > 0 ? this.errorHandlers : [this.defaultErrorHandler()];
507
911
  for (const handler of handlers) {
508
912
  app.use(handler);
509
913
  }
510
914
  }
511
915
  defaultErrorHandler() {
512
- return (error, request, response) => {
916
+ return (error, request, response, next) => {
917
+ void next;
513
918
  void request;
514
919
  response.status(500).json({
515
920
  error: error instanceof Error ? error.message : String(error)
516
921
  });
517
922
  };
518
923
  }
924
+ async runHooks(hooks, app) {
925
+ for (const hook of hooks) {
926
+ await hook(app);
927
+ }
928
+ }
929
+ async runPhaseHooks(phase, app) {
930
+ for (const hook of this.hooks) {
931
+ if (hook.phase === phase) {
932
+ await hook.handle(app);
933
+ }
934
+ }
935
+ }
936
+ applyMiddlewares(app, middlewares) {
937
+ for (const middleware of middlewares) {
938
+ app.use(middleware);
939
+ }
940
+ }
519
941
  get app() {
520
942
  if (!this.appInstance) {
521
943
  throw new Error("HTTP server is not running.");
@@ -528,6 +950,11 @@ var ExpressKernelServer = class {
528
950
  }
529
951
  return this.serverInstance;
530
952
  }
953
+ assertServerIsNotRunning() {
954
+ if (this.serverInstance) {
955
+ throw new Error("HTTP server is already running.");
956
+ }
957
+ }
531
958
  close() {
532
959
  if (!this.serverInstance) {
533
960
  return Promise.resolve();
@@ -544,15 +971,74 @@ var ExpressKernelServer = class {
544
971
  });
545
972
  });
546
973
  }
547
- run() {
548
- const app = createExpressServer({
549
- controllers: this.options.kernel.getRoutes(),
974
+ registerAfterControllersHooks(...hooks) {
975
+ this.assertServerIsNotRunning();
976
+ this.afterControllersHooks?.push(...hooks);
977
+ return this;
978
+ }
979
+ registerBeforeControllersHooks(...hooks) {
980
+ this.assertServerIsNotRunning();
981
+ this.beforeControllersHooks?.push(...hooks);
982
+ return this;
983
+ }
984
+ registerControllers(...controllers) {
985
+ this.assertServerIsNotRunning();
986
+ this.controllers.push(...controllers);
987
+ return this;
988
+ }
989
+ registerErrorHandlers(...handlers) {
990
+ this.assertServerIsNotRunning();
991
+ this.errorHandlers.push(...handlers);
992
+ return this;
993
+ }
994
+ registerHooks(...hooks) {
995
+ this.assertServerIsNotRunning();
996
+ this.hooks.push(...hooks);
997
+ return this;
998
+ }
999
+ registerMiddlewares(...middlewares) {
1000
+ this.assertServerIsNotRunning();
1001
+ this.middlewares.push(...middlewares);
1002
+ return this;
1003
+ }
1004
+ registerPostControllerMiddlewares(...middlewares) {
1005
+ this.assertServerIsNotRunning();
1006
+ this.postControllerMiddlewares.push(...middlewares);
1007
+ return this;
1008
+ }
1009
+ registerPreControllerMiddlewares(...middlewares) {
1010
+ this.assertServerIsNotRunning();
1011
+ this.preControllerMiddlewares.push(...middlewares);
1012
+ return this;
1013
+ }
1014
+ async run() {
1015
+ if (this.serverInstance) {
1016
+ throw new Error("HTTP server is already running.");
1017
+ }
1018
+ const controllers = [
1019
+ ...this.options.kernel.getRoutes(),
1020
+ ...this.controllers
1021
+ ];
1022
+ const express = this.getExpress();
1023
+ const { useExpressServer } = this.getRoutingControllers();
1024
+ const app = express();
1025
+ this.applyMiddlewares(app, this.middlewares);
1026
+ this.applyMiddlewares(app, this.preControllerMiddlewares);
1027
+ await this.runHooks(this.beforeControllersHooks, app);
1028
+ await this.runPhaseHooks("beforeControllers", app);
1029
+ this.configureControllerContainer();
1030
+ useExpressServer(app, {
1031
+ ...this.options.routingControllersOptions,
1032
+ controllers,
550
1033
  routePrefix: this.options.routePrefix
551
1034
  });
552
- for (const middleware of this.options.middlewares ?? []) {
553
- app.use(middleware);
554
- }
555
- this.registerErrorHandlers(app);
1035
+ this.applyMiddlewares(app, this.postControllerMiddlewares);
1036
+ await this.runHooks(this.afterControllersHooks, app);
1037
+ await this.runPhaseHooks("afterControllers", app);
1038
+ await this.runHooks(this.swaggerHooks, app);
1039
+ await this.runHooks(this.staticHooks, app);
1040
+ await this.runPhaseHooks("beforeErrors", app);
1041
+ this.applyErrorHandlers(app);
556
1042
  this.appInstance = app;
557
1043
  return new Promise((resolve) => {
558
1044
  this.serverInstance = app.listen(this.options.port ?? 3e3, () => {
@@ -562,6 +1048,134 @@ var ExpressKernelServer = class {
562
1048
  }
563
1049
  };
564
1050
 
1051
+ // src/adapters/ui/express/HttpErrorHandler.ts
1052
+ import {
1053
+ HttpError
1054
+ } from "routing-controllers";
1055
+
1056
+ // src/contracts/ui/HttpRouteStatusEnum.ts
1057
+ var HttpRouteStatusEnum = {
1058
+ BAD_REQUEST: 400,
1059
+ CONFLICT: 409,
1060
+ CREATED: 201,
1061
+ DEPRECATED: 299,
1062
+ FORBIDDEN: 403,
1063
+ INTERNAL_SERVER_ERROR: 500,
1064
+ NO_CONTENT: 204,
1065
+ NOT_FOUND: 404,
1066
+ OK: 200,
1067
+ PAYLOAD_TOO_LARGE: 413,
1068
+ SERVICE_UNAVAILABLE: 503,
1069
+ TOO_MANY_REQUESTS: 429,
1070
+ UNAUTHORIZED: 401,
1071
+ UNPROCESSABLE_ENTITY: 422
1072
+ };
1073
+
1074
+ // src/adapters/ui/express/HttpErrorHandler.ts
1075
+ var HttpErrorHandler = class {
1076
+ constructor(options = {}) {
1077
+ this.options = options;
1078
+ this.handlers = options.handlers ?? [];
1079
+ this.exposeUnhandledErrorsIn = options.exposeUnhandledErrorsIn ?? [
1080
+ "local",
1081
+ "test"
1082
+ ];
1083
+ }
1084
+ options;
1085
+ handlers;
1086
+ exposeUnhandledErrorsIn;
1087
+ formatValidationErrors(errors) {
1088
+ return errors.flatMap((error) => {
1089
+ if (error.children && error.children.length > 0) {
1090
+ return this.formatValidationErrors(error.children);
1091
+ }
1092
+ return [
1093
+ {
1094
+ details: error.constraints,
1095
+ property: error.property,
1096
+ value: error.value
1097
+ }
1098
+ ];
1099
+ });
1100
+ }
1101
+ getErrorMessage(error) {
1102
+ return error instanceof Error ? error.message : String(error);
1103
+ }
1104
+ getHttpStatus(error) {
1105
+ return error.httpCode ?? error.statusCode ?? error.status;
1106
+ }
1107
+ isPayloadTooLargeError(error) {
1108
+ return error.type === "entity.too.large" || this.getHttpStatus(error) === HttpRouteStatusEnum.PAYLOAD_TOO_LARGE;
1109
+ }
1110
+ logUnhandledError(error) {
1111
+ this.options.logger?.error(`Unhandled error: ${error.message}`);
1112
+ this.options.logger?.debug(error.stack ?? "No stack trace available");
1113
+ }
1114
+ handleSyntaxError(error, response) {
1115
+ if (!(error instanceof SyntaxError)) {
1116
+ return false;
1117
+ }
1118
+ response.status(HttpRouteStatusEnum.BAD_REQUEST).json({
1119
+ code: "SyntaxError",
1120
+ message: "Malformed JSON"
1121
+ });
1122
+ return true;
1123
+ }
1124
+ handlePayloadTooLargeError(error, response) {
1125
+ if (!this.isPayloadTooLargeError(error)) {
1126
+ return false;
1127
+ }
1128
+ response.status(HttpRouteStatusEnum.PAYLOAD_TOO_LARGE).json({
1129
+ code: "PayloadTooLargeError",
1130
+ httpStatus: HttpRouteStatusEnum.PAYLOAD_TOO_LARGE,
1131
+ message: "Request entity too large."
1132
+ });
1133
+ return true;
1134
+ }
1135
+ handleHttpError(error, response) {
1136
+ const httpError = error;
1137
+ const httpStatus = this.getHttpStatus(httpError);
1138
+ if (!httpStatus && !(error instanceof HttpError)) {
1139
+ return false;
1140
+ }
1141
+ response.status(httpStatus ?? HttpRouteStatusEnum.INTERNAL_SERVER_ERROR).json({
1142
+ code: error.name,
1143
+ errors: this.formatValidationErrors(
1144
+ error.errors ?? []
1145
+ ),
1146
+ httpStatus: httpStatus ?? HttpRouteStatusEnum.INTERNAL_SERVER_ERROR,
1147
+ message: error.message
1148
+ });
1149
+ return true;
1150
+ }
1151
+ handleUnhandledError(error, response) {
1152
+ if (this.exposeUnhandledErrorsIn.includes(process.env.NODE_ENV ?? "")) {
1153
+ this.logUnhandledError(error);
1154
+ }
1155
+ response.status(HttpRouteStatusEnum.INTERNAL_SERVER_ERROR).json({
1156
+ code: error.constructor.name || String(HttpRouteStatusEnum.INTERNAL_SERVER_ERROR),
1157
+ message: error.message || "Unknown error"
1158
+ });
1159
+ }
1160
+ error(error, request, response, next) {
1161
+ void request;
1162
+ const handlers = [
1163
+ this.handleSyntaxError.bind(this),
1164
+ this.handlePayloadTooLargeError.bind(this),
1165
+ ...this.handlers,
1166
+ this.handleHttpError.bind(this)
1167
+ ];
1168
+ if (handlers.some((handler) => handler(error, response))) {
1169
+ return;
1170
+ }
1171
+ void next;
1172
+ this.handleUnhandledError(error, response);
1173
+ }
1174
+ handle = (error, request, response, next) => {
1175
+ this.error(error, request, response, next);
1176
+ };
1177
+ };
1178
+
565
1179
  // src/adapters/ui/express/RoutePrefix.ts
566
1180
  var RoutePrefix = class _RoutePrefix {
567
1181
  value;
@@ -596,13 +1210,21 @@ var Route = class {
596
1210
  export {
597
1211
  ConsoleKernelLogger,
598
1212
  Consumer,
1213
+ ConsumerMiddlewarePipeline,
1214
+ CorrelationConsumerMiddleware,
1215
+ DefaultPublisherHookErrorPolicy,
599
1216
  ExpressKernelServer,
1217
+ HttpErrorHandler,
1218
+ IdempotencyConsumerMiddleware,
600
1219
  InMemoryEventBus,
1220
+ InMemoryIdempotencyStore,
601
1221
  InMemoryPubSub,
602
1222
  InMemoryRepository,
603
1223
  InvalidDomainEventError,
604
1224
  MongoRepository,
605
1225
  NoFailedMessagesError,
1226
+ PublisherHookPipeline,
1227
+ RetryConsumerMiddleware,
606
1228
  Route,
607
1229
  RoutePrefix
608
1230
  };