wsp-ms-core 1.0.7 → 1.0.8-7.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1,6 +1,8 @@
1
+ var __create = Object.create;
1
2
  var __defProp = Object.defineProperty;
2
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __getProtoOf = Object.getPrototypeOf;
4
6
  var __hasOwnProp = Object.prototype.hasOwnProperty;
5
7
  var __export = (target, all) => {
6
8
  for (var name in all)
@@ -14,26 +16,53 @@ var __copyProps = (to, from, except, desc) => {
14
16
  }
15
17
  return to;
16
18
  };
19
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
20
+ // If the importer is in node compatibility mode or this is not an ESM
21
+ // file that has been converted to a CommonJS file using a Babel-
22
+ // compatible transform (i.e. "__esModule" has not been set), then set
23
+ // "default" to the CommonJS "module.exports" for node compatibility.
24
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
25
+ mod
26
+ ));
17
27
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
18
28
 
19
29
  // src/index.ts
20
30
  var src_exports = {};
21
31
  __export(src_exports, {
32
+ BaseEvent: () => BaseEvent,
33
+ BaseObject: () => BaseObject,
34
+ BasicUnitOfWork: () => BasicUnitOfWork,
35
+ BasicUnitOfWorkFactory: () => BasicUnitOfWorkFactory,
36
+ Country: () => Country,
22
37
  Currency: () => Currency,
23
38
  DateTime: () => DateTime,
39
+ DefaultMysqlInboxRunner: () => DefaultMysqlInboxRunner,
40
+ DefaultMysqlOutboxRunner: () => DefaultMysqlOutboxRunner,
24
41
  DomainEntity: () => DomainEntity,
25
42
  DomainError: () => DomainError,
26
43
  DomainEvent: () => DomainEvent,
27
44
  Email: () => Email,
28
45
  ErrorManager: () => ErrorManager,
46
+ EventBus: () => EventBus,
47
+ EventBusMysqlRepository: () => EventBusMysqlRepository,
48
+ EventManager: () => EventManager,
49
+ ExchangeRates: () => ExchangeRates,
29
50
  FatalError: () => FatalError,
30
51
  HttpHealthCheckController: () => HttpHealthCheckController,
31
52
  HttpNotFoundController: () => HttpNotFoundController,
53
+ InboxRecord: () => InboxRecord,
54
+ IntegrationEvent: () => IntegrationEvent,
32
55
  InternalError: () => InternalError,
56
+ KafkaManager: () => KafkaManager,
33
57
  Language: () => Language,
34
58
  MysqlConnection: () => MysqlConnection,
35
59
  MysqlConnector: () => MysqlConnector,
60
+ OutboxRecord: () => OutboxRecord,
61
+ PaymentGateway: () => PaymentGateway,
62
+ PaymentStatus: () => PaymentStatus,
36
63
  Price: () => Price,
64
+ ProcessStatus: () => ProcessStatus,
65
+ StringVars: () => StringVars,
37
66
  UUID: () => UUID,
38
67
  UsageError: () => UsageError,
39
68
  ValueObject: () => ValueObject,
@@ -51,6 +80,9 @@ var ValueObject = class {
51
80
  this.validate(value);
52
81
  this._value = Object.freeze(value);
53
82
  }
83
+ toProps() {
84
+ return this._value;
85
+ }
54
86
  get value() {
55
87
  return this._value;
56
88
  }
@@ -144,6 +176,21 @@ var _DateTime = class _DateTime extends ValueObject {
144
176
  getWeekdayName(locale = "en") {
145
177
  return this._dt.setLocale(locale).toFormat("cccc");
146
178
  }
179
+ getDayName(locale = "en") {
180
+ return this._dt.setLocale(locale).toFormat("EEEE");
181
+ }
182
+ format(format) {
183
+ if (!_DateTime.FORMATS.hasOwnProperty(format)) {
184
+ throw new Error(`Invalid format: ${format}`);
185
+ }
186
+ const formatString = _DateTime.FORMATS[format];
187
+ return this._dt.toFormat(formatString);
188
+ }
189
+ toPrimitives() {
190
+ return {
191
+ value: this.value
192
+ };
193
+ }
147
194
  static create(input) {
148
195
  if (input === void 0) {
149
196
  return new _DateTime(_DateTime.toUtcFormat(import_luxon.DateTime.now()));
@@ -159,39 +206,106 @@ var _DateTime = class _DateTime extends ValueObject {
159
206
  }
160
207
  return new _DateTime(input);
161
208
  }
209
+ static now() {
210
+ return _DateTime.create();
211
+ }
162
212
  };
163
213
  _DateTime.DEFAULT_FORMAT = "yyyy-MM-dd HH:mm:ss";
214
+ _DateTime.FORMAT = "Y-M-D H:M:S";
215
+ _DateTime.FORMAT_Y_M = "Y-M";
216
+ _DateTime.FORMAT_Y_M_D = "Y-M-D";
217
+ _DateTime.FORMAT_Y_M_D_H_m_s = "Y-M-D H:M:S";
218
+ _DateTime.FORMAT_D_M_Y = "D-M-Y";
219
+ _DateTime.FORMATS = {
220
+ "Y-M": "yyyy-MM",
221
+ "Y-M-D": "yyyy-MM-dd",
222
+ "D/M/Y": "dd/MM/yyyy",
223
+ "Y/M/D": "yyyy/MM/dd",
224
+ "D-M-Y": "dd-MM-yyyy",
225
+ "Y.M.D": "yyyy.MM.dd",
226
+ "D.M.Y": "dd.MM.yyyy",
227
+ "Y-M-D H:M": "yyyy-MM-dd HH:mm",
228
+ "D/M/Y H:M": "dd/MM/yyyy HH:mm",
229
+ "Y/M/D H:M": "yyyy/MM/dd HH:mm",
230
+ "D-M-Y H:M": "dd-MM-yyyy HH:mm",
231
+ "Y.M.D H:M": "yyyy.MM.dd HH:mm",
232
+ "D.M.Y H:M": "dd.MM.yyyy HH:mm",
233
+ "Y-M-D H:M:S": "yyyy-MM-dd HH:mm:ss",
234
+ "Y-M-D H:M:S:SSS": "yyyy-MM-dd HH:mm:ss.SSS",
235
+ "D/M/Y H:M:S": "dd/MM/yyyy HH:mm:ss",
236
+ "Y/M/D H:M:S": "yyyy/MM/dd HH:mm:ss",
237
+ "D-M-Y H:M:S": "dd-MM-yyyy HH:mm:ss",
238
+ "Y.M.D H:M:S": "yyyy.MM.dd HH:mm:ss",
239
+ "D.M.Y H:M:S": "dd.MM.yyyy HH:mm:ss",
240
+ "H:M": "HH:mm",
241
+ "H:M:S": "HH:mm:ss"
242
+ };
164
243
  var DateTime = _DateTime;
165
244
 
245
+ // src/domain/contracts/BaseEvent.ts
246
+ var BaseEvent = class {
247
+ constructor(tenantUuid, version, type, payload) {
248
+ this._tenantUuid = tenantUuid;
249
+ this._version = version;
250
+ this._type = type;
251
+ this._payload = payload;
252
+ this._occurredAt = DateTime.now();
253
+ }
254
+ get tenantUuid() {
255
+ return this._tenantUuid;
256
+ }
257
+ get version() {
258
+ return this._version;
259
+ }
260
+ get type() {
261
+ return this._type;
262
+ }
263
+ get payload() {
264
+ return this._payload;
265
+ }
266
+ get ocurredAt() {
267
+ return this._occurredAt;
268
+ }
269
+ };
270
+
166
271
  // src/domain/contracts/DomainEntity.ts
167
272
  var DomainEntity = class {
168
- constructor(uuid, props, audit) {
169
- this.uuid = uuid;
273
+ constructor(props) {
274
+ this._events = [];
170
275
  this.props = props;
171
- this._createdAt = audit?.createdAt ?? DateTime.create();
172
- this._updatedAt = audit?.updatedAt ?? this.createdAt;
173
- this._deletedAt = audit?.deletedAt;
276
+ }
277
+ recordEvent(event) {
278
+ this._events.push(event);
174
279
  }
175
280
  touch() {
176
- this._updatedAt = DateTime.create();
281
+ this.props.updatedAt = DateTime.now();
282
+ }
283
+ get uuid() {
284
+ return this.props.uuid;
177
285
  }
178
286
  get createdAt() {
179
- return this._createdAt;
287
+ return this.props.createdAt;
180
288
  }
181
289
  get updatedAt() {
182
- return this._updatedAt;
290
+ return this.props.updatedAt;
183
291
  }
184
292
  get deletedAt() {
185
- return this._deletedAt;
293
+ return this.props.deletedAt;
186
294
  }
187
295
  get isDeleted() {
188
- return Boolean(this._deletedAt);
296
+ return Boolean(this.props.deletedAt);
189
297
  }
190
- softDelete() {
191
- if (!this._deletedAt) {
192
- this._deletedAt = DateTime.create();
193
- this.touch();
194
- }
298
+ pullEvents() {
299
+ const events = this._events;
300
+ this._events = [];
301
+ return events;
302
+ }
303
+ };
304
+
305
+ // src/domain/contracts/BaseObject.ts
306
+ var BaseObject = class {
307
+ constructor(props) {
308
+ this.props = props;
195
309
  }
196
310
  };
197
311
 
@@ -203,20 +317,6 @@ var DomainError = class extends Error {
203
317
  }
204
318
  };
205
319
 
206
- // src/domain/contracts/DomainEvent.ts
207
- var DomainEvent = class {
208
- constructor(payload) {
209
- this._payload = payload;
210
- this._occurredAt = DateTime.create();
211
- }
212
- get payload() {
213
- return this._payload;
214
- }
215
- get occurredAt() {
216
- return this._occurredAt;
217
- }
218
- };
219
-
220
320
  // src/domain/errors/FatalError.ts
221
321
  var FatalError = class extends DomainError {
222
322
  constructor(type, message = "") {
@@ -239,6 +339,424 @@ var UsageError = class extends DomainError {
239
339
  }
240
340
  };
241
341
 
342
+ // src/domain/events/DomainEvent.ts
343
+ var DomainEvent = class extends BaseEvent {
344
+ constructor(tenantUuid, version, type, payload, aggregateUuid, aggregateType) {
345
+ super(tenantUuid, version, type, payload);
346
+ this._aggregateUuid = aggregateUuid;
347
+ this._aggregateType = aggregateType;
348
+ }
349
+ get aggregateUuid() {
350
+ return this._aggregateUuid;
351
+ }
352
+ get aggregateType() {
353
+ return this._aggregateType;
354
+ }
355
+ };
356
+
357
+ // src/domain/value-objects/payments/PaymentGateway.ts
358
+ var _PaymentGateway = class _PaymentGateway extends ValueObject {
359
+ constructor(gateway) {
360
+ super(gateway);
361
+ }
362
+ validate(value) {
363
+ if (!_PaymentGateway.SUPPORTED.includes(value)) {
364
+ throw new InternalError(`Payment gateway <${value}> is not supported`);
365
+ }
366
+ }
367
+ isExternal() {
368
+ return _PaymentGateway.EXTERNALS.includes(this.value);
369
+ }
370
+ get isMercadoPago() {
371
+ return this.equals(_PaymentGateway.MERCADOPAGO);
372
+ }
373
+ get isHandy() {
374
+ return this.equals(_PaymentGateway.HANDY);
375
+ }
376
+ get isWonaDebit() {
377
+ return this.equals(_PaymentGateway.WONA_DEBIT);
378
+ }
379
+ get isWonaCard() {
380
+ return this.equals(_PaymentGateway.WONA_CARD);
381
+ }
382
+ get isWonaCash() {
383
+ return this.equals(_PaymentGateway.WONA_CASH);
384
+ }
385
+ get isWonaTransfer() {
386
+ return this.equals(_PaymentGateway.WONA_TRANSFER);
387
+ }
388
+ get isWonaMercadoPago() {
389
+ return this.equals(_PaymentGateway.WONA_MERCADOPAGO);
390
+ }
391
+ toPrimitives() {
392
+ return { value: this.value };
393
+ }
394
+ static create(gateway) {
395
+ return new _PaymentGateway(gateway.trim().toUpperCase());
396
+ }
397
+ };
398
+ _PaymentGateway.SUPPORTED = [
399
+ "MERCADOPAGO",
400
+ "HANDY",
401
+ "WONA_DEBIT",
402
+ "WONA_CARD",
403
+ "WONA_CASH",
404
+ "WONA_TRANSFER",
405
+ "WONA_MERCADOPAGO"
406
+ ];
407
+ _PaymentGateway.EXTERNALS = ["MERCADOPAGO", "STRIPE"];
408
+ _PaymentGateway.MERCADOPAGO = new _PaymentGateway("MERCADOPAGO");
409
+ _PaymentGateway.HANDY = new _PaymentGateway("HANDY");
410
+ _PaymentGateway.WONA_DEBIT = new _PaymentGateway("WONA_DEBIT");
411
+ _PaymentGateway.WONA_CARD = new _PaymentGateway("WONA_CARD");
412
+ _PaymentGateway.WONA_CASH = new _PaymentGateway("WONA_CASH");
413
+ _PaymentGateway.WONA_TRANSFER = new _PaymentGateway("WONA_TRANSFER");
414
+ _PaymentGateway.WONA_MERCADOPAGO = new _PaymentGateway("WONA_MERCADOPAGO");
415
+ var PaymentGateway = _PaymentGateway;
416
+
417
+ // src/domain/value-objects/payments/PaymentStatus.ts
418
+ var _PaymentStatus = class _PaymentStatus extends ValueObject {
419
+ constructor(status) {
420
+ super(status);
421
+ }
422
+ validate(status) {
423
+ if (!_PaymentStatus.SUPPORTED.includes(status)) {
424
+ throw new InternalError(`Payment status <${status}> is not supported`);
425
+ }
426
+ }
427
+ get isDone() {
428
+ return this.value === "DONE";
429
+ }
430
+ get isPending() {
431
+ return this.value === "PENDING";
432
+ }
433
+ get isInProgress() {
434
+ return this.value === "IN_PROGRESS";
435
+ }
436
+ get isFailed() {
437
+ return this.value === "FAILED";
438
+ }
439
+ get isCanceled() {
440
+ return this.value === "CANCELED";
441
+ }
442
+ get isHold() {
443
+ return this.value === "HOLD";
444
+ }
445
+ get isPendingRefund() {
446
+ return this.value === "PENDING_REFUND";
447
+ }
448
+ get isRefunded() {
449
+ return this.value === "REFUNDED";
450
+ }
451
+ toPrimitives() {
452
+ return { value: this.value };
453
+ }
454
+ static create(gateway) {
455
+ return new _PaymentStatus(gateway.trim().toUpperCase());
456
+ }
457
+ };
458
+ _PaymentStatus.SUPPORTED = ["DONE", "PENDING", "FAILED", "CANCELED", "HOLD", "PENDING_REFUND", "REFUNDED", "IN_PROGRESS"];
459
+ _PaymentStatus.DONE = new _PaymentStatus("DONE");
460
+ _PaymentStatus.PENDING = new _PaymentStatus("PENDING");
461
+ _PaymentStatus.IN_PROGRESS = new _PaymentStatus("IN_PROGRESS");
462
+ _PaymentStatus.FAILED = new _PaymentStatus("FAILED");
463
+ _PaymentStatus.CANCELED = new _PaymentStatus("CANCELED");
464
+ _PaymentStatus.HOLD = new _PaymentStatus("HOLD");
465
+ _PaymentStatus.PENDING_REFUND = new _PaymentStatus("PENDING_REFUND");
466
+ _PaymentStatus.REFUNDED = new _PaymentStatus("REFUNDED");
467
+ var PaymentStatus = _PaymentStatus;
468
+
469
+ // src/utils/StringVars.ts
470
+ var StringVars = class {
471
+ static parse(str, ob) {
472
+ const regex = /{{(.*?)}}/g;
473
+ return str.replace(regex, (match, variable) => {
474
+ if (ob.hasOwnProperty(variable.trim())) {
475
+ return ob[variable.trim()];
476
+ } else {
477
+ return match;
478
+ }
479
+ });
480
+ }
481
+ };
482
+
483
+ // src/infrastructure/errors/ErrorManager.ts
484
+ var _ErrorManager = class _ErrorManager {
485
+ constructor(logger = null) {
486
+ this.logger = logger;
487
+ }
488
+ getDefaultMessage(lang) {
489
+ return _ErrorManager.DEFAULT_MESSAGES[lang.value] || _ErrorManager.DEFAULT_MESSAGES[lang.base()] || "error";
490
+ }
491
+ onFatal(err, lang) {
492
+ this.logger?.fatal(err.type, err.message);
493
+ return { status: "ERROR", message: this.getDefaultMessage(lang) };
494
+ }
495
+ onInternal(err, lang) {
496
+ this.logger?.error(err.type, err.message);
497
+ return { status: "ERROR", message: this.getDefaultMessage(lang) };
498
+ }
499
+ onUsage(err, lang) {
500
+ const tmpl = _ErrorManager.TEMPLATES.get(err.type);
501
+ if (!tmpl) {
502
+ this.logger?.error("TEMPLATE_NOT_FOUND", `${err.type}`);
503
+ return { status: "ERROR", message: this.getDefaultMessage(lang) };
504
+ }
505
+ const code = lang.value;
506
+ const base = lang.base();
507
+ const rawMsg = tmpl.languages[code] ?? tmpl.languages[base] ?? this.getDefaultMessage(lang);
508
+ return {
509
+ status: 400,
510
+ message: StringVars.parse(rawMsg, err.vars)
511
+ };
512
+ }
513
+ onUnknown(err, lang) {
514
+ this.logger?.error("UNKNOWN_ERROR", err.message);
515
+ return { status: "ERROR", message: this.getDefaultMessage(lang) };
516
+ }
517
+ handle(err, lang) {
518
+ if (["local", "dev"].includes(process.env.ENVIRONMENT ?? "")) {
519
+ console.log(err);
520
+ }
521
+ if (err instanceof FatalError) {
522
+ return this.onFatal(err, lang);
523
+ }
524
+ if (err instanceof InternalError) {
525
+ return this.onInternal(err, lang);
526
+ }
527
+ if (err instanceof UsageError) {
528
+ return this.onUsage(err, lang);
529
+ }
530
+ return this.onUnknown(err, lang);
531
+ }
532
+ static addTemplate(template) {
533
+ _ErrorManager.TEMPLATES.set(template.type, template);
534
+ }
535
+ };
536
+ _ErrorManager.DEFAULT_MESSAGES = {
537
+ "es": "Ups, hemos encontrado un error. Nuestro equipo ya est\xE1 trabajando para solucionarlo",
538
+ "en": "Ups, we found an error. Our team is working on it.",
539
+ "pt": "Ops, encontramos um bug. Nossa equipe j\xE1 est\xE1 trabalhando para resolver isso."
540
+ };
541
+ _ErrorManager.APP_ERRORS = {
542
+ UNDEFINED: "UNDEFINED_ERROR",
543
+ PROCESS: "PROCESS_ERROR",
544
+ DATABASE: "DATABASE_ERROR"
545
+ };
546
+ _ErrorManager.TEMPLATES = /* @__PURE__ */ new Map();
547
+ var ErrorManager = _ErrorManager;
548
+
549
+ // src/domain/value-objects/Country.ts
550
+ ErrorManager.addTemplate({
551
+ type: "INVALID_COUNTRY",
552
+ languages: {
553
+ "es": "El pa\xEDs <{{country}}> no es v\xE1lido o no est\xE1 soportado",
554
+ "en": "Country <{{country}}> is not valid or not supported"
555
+ }
556
+ });
557
+ ErrorManager.addTemplate({
558
+ type: "COUNTRY_NOT_FOUND_BY_ALPHA2",
559
+ languages: {
560
+ "es": "No se encontr\xF3 pa\xEDs con c\xF3digo alpha2 <{{alpha2}}>",
561
+ "en": "Country not found with alpha2 code <{{alpha2}}>"
562
+ }
563
+ });
564
+ ErrorManager.addTemplate({
565
+ type: "COUNTRY_NOT_FOUND_BY_UUID",
566
+ languages: {
567
+ "es": "No se encontr\xF3 pa\xEDs con UUID <{{uuid}}>",
568
+ "en": "Country not found with UUID <{{uuid}}>"
569
+ }
570
+ });
571
+ var _Country = class _Country extends ValueObject {
572
+ constructor(country) {
573
+ const normalizedCountry = country.toUpperCase().trim();
574
+ super(normalizedCountry);
575
+ this._name = normalizedCountry;
576
+ this._alpha2 = _Country.COUNTRIES[normalizedCountry].alpha2;
577
+ this._alpha3 = _Country.COUNTRIES[normalizedCountry].alpha3;
578
+ this._numeric = _Country.COUNTRIES[normalizedCountry].numeric;
579
+ this._uuid = _Country.COUNTRIES[normalizedCountry].uuid;
580
+ this._phoneCode = _Country.COUNTRIES[normalizedCountry].phoneCode;
581
+ this._url = _Country.COUNTRIES[normalizedCountry].url;
582
+ }
583
+ validate(country) {
584
+ if (!_Country.NAMES.includes(country)) {
585
+ throw new UsageError("INVALID_COUNTRY", { country });
586
+ }
587
+ }
588
+ name() {
589
+ return this._name;
590
+ }
591
+ alpha2() {
592
+ return this._alpha2;
593
+ }
594
+ alpha3() {
595
+ return this._alpha3;
596
+ }
597
+ numeric() {
598
+ return this._numeric;
599
+ }
600
+ uuid() {
601
+ return this._uuid;
602
+ }
603
+ phoneCode() {
604
+ return this._phoneCode;
605
+ }
606
+ url() {
607
+ return this._url;
608
+ }
609
+ static findCountryByAlpha2(alpha2) {
610
+ for (const [country, codes] of Object.entries(_Country.COUNTRIES)) {
611
+ if (codes.alpha2 === alpha2.toUpperCase()) {
612
+ return new _Country(country);
613
+ }
614
+ }
615
+ throw new UsageError("COUNTRY_NOT_FOUND_BY_ALPHA2", { alpha2 });
616
+ }
617
+ static findCountryByUUID(uuid) {
618
+ for (const [country, codes] of Object.entries(_Country.COUNTRIES)) {
619
+ if (codes.uuid === uuid) {
620
+ return new _Country(country);
621
+ }
622
+ }
623
+ throw new UsageError("COUNTRY_NOT_FOUND_BY_UUID", { uuid });
624
+ }
625
+ static create(country) {
626
+ return new _Country(country);
627
+ }
628
+ static createOrDefault(country) {
629
+ try {
630
+ return new _Country(country);
631
+ } catch (error) {
632
+ return _Country.DEFAULT;
633
+ }
634
+ }
635
+ toPrimitives() {
636
+ return {
637
+ value: this.value,
638
+ name: this._name,
639
+ alpha2: this._alpha2,
640
+ alpha3: this._alpha3,
641
+ numeric: this._numeric,
642
+ uuid: this._uuid,
643
+ phoneCode: this._phoneCode,
644
+ url: this._url
645
+ };
646
+ }
647
+ static isValid(country) {
648
+ try {
649
+ _Country.create(country);
650
+ return true;
651
+ } catch {
652
+ return false;
653
+ }
654
+ }
655
+ };
656
+ _Country.COUNTRIES = {
657
+ URUGUAY: {
658
+ alpha2: "UY",
659
+ alpha3: "URY",
660
+ numeric: "858",
661
+ phoneCode: "+598",
662
+ uuid: "5739ecc0-d12b-4db5-8897-e03a04a95c72",
663
+ url: "https://dev-wonasports.s3.us-east-2.amazonaws.com/assets/uruguay.png"
664
+ },
665
+ ARGENTINA: {
666
+ alpha2: "AR",
667
+ alpha3: "ARG",
668
+ numeric: "032",
669
+ phoneCode: "+54",
670
+ uuid: "66663efe-ab7a-4166-b971-9f36fd0ea6b2",
671
+ url: "https://dev-wonasports.s3.us-east-2.amazonaws.com/assets/argentina.png"
672
+ },
673
+ ECUADOR: {
674
+ alpha2: "EC",
675
+ alpha3: "ECU",
676
+ numeric: "218",
677
+ phoneCode: "+593",
678
+ uuid: "ee109239-0150-4e5f-9ff2-a85f270092b1",
679
+ url: "https://dev-wonasports.s3.us-east-2.amazonaws.com/assets/ecuador.png"
680
+ },
681
+ PERU: {
682
+ alpha2: "PE",
683
+ alpha3: "PER",
684
+ numeric: "604",
685
+ phoneCode: "+51",
686
+ uuid: "e4d61ef5-b92d-4f9c-8ec1-23f4beb50abd",
687
+ url: "https://dev-wonasports.s3.us-east-2.amazonaws.com/assets/peru.png"
688
+ },
689
+ BRASIL: {
690
+ alpha2: "BR",
691
+ alpha3: "BRA",
692
+ numeric: "076",
693
+ phoneCode: "+55",
694
+ uuid: "b7b91d72-deaf-4641-957c-a65003e33104",
695
+ url: "https://dev-wonasports.s3.us-east-2.amazonaws.com/assets/brasil.png"
696
+ },
697
+ CHILE: {
698
+ alpha2: "CL",
699
+ alpha3: "CHL",
700
+ numeric: "152",
701
+ phoneCode: "+56",
702
+ uuid: "f69b35f4-d734-4c76-866c-29a18bf000fb",
703
+ url: "https://dev-wonasports.s3.us-east-2.amazonaws.com/assets/chile.png"
704
+ },
705
+ VENEZUELA: {
706
+ alpha2: "VE",
707
+ alpha3: "VEN",
708
+ numeric: "862",
709
+ phoneCode: "+58",
710
+ uuid: "31b6c591-63f6-43db-8ea9-829bb03746c5",
711
+ url: "https://dev-wonasports.s3.us-east-2.amazonaws.com/assets/venezuela.png"
712
+ },
713
+ COLOMBIA: {
714
+ alpha2: "CO",
715
+ alpha3: "COL",
716
+ numeric: "170",
717
+ phoneCode: "+57",
718
+ uuid: "6fdfe34b-6726-4604-96af-665ea5fc9239",
719
+ url: "https://dev-wonasports.s3.us-east-2.amazonaws.com/assets/colombia.png"
720
+ },
721
+ BOLIVIA: {
722
+ alpha2: "BO",
723
+ alpha3: "BOL",
724
+ numeric: "068",
725
+ phoneCode: "+591",
726
+ uuid: "948886db-c280-4ba7-a777-a76c180b295b",
727
+ url: "https://dev-wonasports.s3.us-east-2.amazonaws.com/assets/bolivia.png"
728
+ },
729
+ PARAGUAY: {
730
+ alpha2: "PY",
731
+ alpha3: "PRY",
732
+ numeric: "600",
733
+ phoneCode: "+595",
734
+ uuid: "d67b3472-e38d-4900-8ae3-02d99cd1d884",
735
+ url: "https://dev-wonasports.s3.us-east-2.amazonaws.com/assets/paraguay.png"
736
+ },
737
+ USA: {
738
+ alpha2: "US",
739
+ alpha3: "USA",
740
+ numeric: "840",
741
+ phoneCode: "+1",
742
+ uuid: "16ac3b9b-8f7b-4c54-91d5-d62c7cd74ad4",
743
+ url: "https://dev-wonasports.s3.us-east-2.amazonaws.com/assets/usa.png"
744
+ }
745
+ };
746
+ _Country.NAMES = Object.keys(_Country.COUNTRIES);
747
+ _Country.DEFAULT = new _Country("URUGUAY");
748
+ _Country.ARGENTINA = new _Country("ARGENTINA");
749
+ _Country.ECUADOR = new _Country("ECUADOR");
750
+ _Country.PERU = new _Country("PERU");
751
+ _Country.BRASIL = new _Country("BRASIL");
752
+ _Country.CHILE = new _Country("CHILE");
753
+ _Country.VENEZUELA = new _Country("VENEZUELA");
754
+ _Country.COLOMBIA = new _Country("COLOMBIA");
755
+ _Country.BOLIVIA = new _Country("BOLIVIA");
756
+ _Country.PARAGUAY = new _Country("PARAGUAY");
757
+ _Country.USA = new _Country("USA");
758
+ var Country = _Country;
759
+
242
760
  // src/domain/value-objects/Currency.ts
243
761
  var _Currency = class _Currency extends ValueObject {
244
762
  constructor(alpha) {
@@ -254,6 +772,11 @@ var _Currency = class _Currency extends ValueObject {
254
772
  throw new Error(`Currency <${code}> is not supported`);
255
773
  }
256
774
  }
775
+ toPrimitives() {
776
+ return {
777
+ value: this.value
778
+ };
779
+ }
257
780
  static create(raw) {
258
781
  if (typeof raw === "number" || _Currency.NUM_REGEX.test(raw)) {
259
782
  const num = Number(raw);
@@ -307,6 +830,11 @@ var _Email = class _Email extends ValueObject {
307
830
  throw new Error(`Email <${value}> is not a valid address`);
308
831
  }
309
832
  }
833
+ toPrimitives() {
834
+ return {
835
+ value: this.value
836
+ };
837
+ }
310
838
  static create(raw) {
311
839
  return new _Email(raw);
312
840
  }
@@ -324,12 +852,18 @@ var _Language = class _Language extends ValueObject {
324
852
  }
325
853
  validate(value) {
326
854
  if (!_Language.SUPPORTED.includes(value)) {
327
- throw new Error(`Language <${value}> is not supported`);
855
+ throw new InternalError(`Language <${value}> is not supported`);
328
856
  }
329
857
  }
330
858
  base() {
331
859
  return this.value.split("-")[0];
332
860
  }
861
+ toPrimitives() {
862
+ return {
863
+ base: this.base(),
864
+ value: this.value
865
+ };
866
+ }
333
867
  static create(raw) {
334
868
  const normalized = raw.trim().toLowerCase().replace("_", "-");
335
869
  try {
@@ -410,25 +944,43 @@ _Language.SPANISH_PUERTO_RICO = new _Language("es-pr");
410
944
  var Language = _Language;
411
945
 
412
946
  // src/domain/value-objects/Price.ts
947
+ ErrorManager.addTemplate({
948
+ type: "INVALID_PRICE_AMOUNT",
949
+ languages: {
950
+ "es": "El precio <{{amount}}> no es v\xE1lido",
951
+ "en": "Price amount <{{amount}}> is not a valid number"
952
+ }
953
+ });
954
+ ErrorManager.addTemplate({
955
+ type: "INVALID_PRICE_RANGE",
956
+ languages: {
957
+ "es": "El precio <{{amount}}> debe ser \u2265 {{min}} y \u2264 {{max}}",
958
+ "en": "Price amount <{{amount}}> must be \u2265 {{min}} and \u2264 {{max}}"
959
+ }
960
+ });
413
961
  var _Price = class _Price extends ValueObject {
414
962
  constructor(amount, currency) {
415
963
  super({ amount, currency });
416
- this.amount = amount;
417
- this.currency = currency;
418
964
  }
419
965
  validate(props) {
420
966
  const { amount, currency } = props;
421
967
  if (typeof amount !== "number" || Number.isNaN(amount) || !Number.isFinite(amount)) {
422
- throw new Error(`Price amount <${amount}> is not a valid number`);
968
+ throw new UsageError("INVALID_PRICE_AMOUNT", { amount });
423
969
  }
424
- if (amount < _Price.MIN_AMOUNT) {
425
- throw new Error(`Price amount <${amount}> must be \u2265 ${_Price.MIN_AMOUNT}`);
970
+ if (amount < _Price.MIN_AMOUNT || amount > _Price.MAX_AMOUNT) {
971
+ throw new UsageError("INVALID_PRICE_RANGE", { amount, min: _Price.MIN_AMOUNT, max: _Price.MAX_AMOUNT });
426
972
  }
427
973
  }
974
+ get amount() {
975
+ return this._value.amount;
976
+ }
977
+ get currency() {
978
+ return this._value.currency;
979
+ }
428
980
  equals(other) {
429
981
  if (!other)
430
982
  return false;
431
- return this.amount === other.amount && this.currency.equals(other.currency);
983
+ return this._value.amount === other.amount && this.currency.equals(other.currency);
432
984
  }
433
985
  assertSameCurrency(other) {
434
986
  if (!this.currency.equals(other.currency)) {
@@ -443,190 +995,537 @@ var _Price = class _Price extends ValueObject {
443
995
  this.assertSameCurrency(other);
444
996
  return _Price.create(this.amount - other.amount, this.currency);
445
997
  }
998
+ toPrimitives() {
999
+ return {
1000
+ amount: this.amount,
1001
+ currency: this.currency.value
1002
+ };
1003
+ }
446
1004
  static create(amount, currency) {
447
1005
  const cur = currency instanceof Currency ? currency : Currency.create(currency);
448
1006
  return new _Price(amount, cur);
449
1007
  }
1008
+ static createFromPrimitives(data) {
1009
+ return new _Price(Number(data.amount), Currency.create(String(data.currency)));
1010
+ }
450
1011
  };
451
1012
  _Price.MIN_AMOUNT = -1e6;
1013
+ _Price.MAX_AMOUNT = 1e9;
452
1014
  var Price = _Price;
453
1015
 
1016
+ // src/domain/value-objects/ProcessStatus.ts
1017
+ var _ProcessStatus = class _ProcessStatus extends ValueObject {
1018
+ constructor(status) {
1019
+ super(status.trim().toUpperCase());
1020
+ }
1021
+ validate(value) {
1022
+ if (!_ProcessStatus.SUPPORTED.includes(value)) {
1023
+ throw new InternalError(`Domain event status <${value}> is not supported`);
1024
+ }
1025
+ }
1026
+ toPrimitives() {
1027
+ return void 0;
1028
+ }
1029
+ static create(status) {
1030
+ return new _ProcessStatus(status);
1031
+ }
1032
+ };
1033
+ _ProcessStatus.SUPPORTED = ["PENDING", "PROCESSING", "PROCESSED", "FAILED", "DEAD"];
1034
+ _ProcessStatus.PENDING = new _ProcessStatus("PENDING");
1035
+ _ProcessStatus.PROCESSING = new _ProcessStatus("PROCESSING");
1036
+ _ProcessStatus.PROCESSED = new _ProcessStatus("PROCESSED");
1037
+ _ProcessStatus.FAILED = new _ProcessStatus("FAILED");
1038
+ _ProcessStatus.DEAD = new _ProcessStatus("DEAD");
1039
+ var ProcessStatus = _ProcessStatus;
1040
+
454
1041
  // src/domain/value-objects/UUID.ts
455
- var UUID = class _UUID extends ValueObject {
1042
+ var crypto = __toESM(require("crypto"));
1043
+ var _UUID = class _UUID extends ValueObject {
456
1044
  constructor(value) {
457
1045
  super(value);
458
1046
  }
459
1047
  validate(uuid) {
460
1048
  if (!_UUID.isValid(uuid)) {
461
- throw new Error(`Invalid uuid ${uuid}`);
1049
+ throw new InternalError(`Invalid uuid <${uuid}>`);
462
1050
  }
463
1051
  }
1052
+ toPrimitives() {
1053
+ return { value: this.value };
1054
+ }
464
1055
  static create(uuid) {
465
1056
  return new _UUID(uuid ?? crypto.randomUUID());
466
1057
  }
467
- static isValid(uuid) {
468
- return /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(uuid);
1058
+ static version(uuid) {
1059
+ const m = /^[0-9a-f]{8}-[0-9a-f]{4}-([1-8])[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.exec(uuid);
1060
+ return m ? Number(m[1]) : void 0;
1061
+ }
1062
+ static isNil(uuid) {
1063
+ return /^0{8}-0{4}-0{4}-0{4}-0{12}$/i.test(uuid);
1064
+ }
1065
+ static isRFCStyle(uuid) {
1066
+ return /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(uuid);
1067
+ }
1068
+ static isValid(uuid, opts = {}) {
1069
+ const allowed = opts.allowedVersions ?? [1, 2, 3, 4, 5, 6, 7, 8];
1070
+ const allowNil = opts.allowNil ?? false;
1071
+ if (allowNil && _UUID.isNil(uuid))
1072
+ return true;
1073
+ if (!_UUID.isRFCStyle(uuid))
1074
+ return false;
1075
+ const v = _UUID.version(uuid);
1076
+ return !!v && allowed.includes(v);
469
1077
  }
470
1078
  };
1079
+ _UUID.NIL = "00000000-0000-0000-0000-000000000000";
1080
+ var UUID = _UUID;
471
1081
 
472
- // src/utils/StringVars.ts
473
- var StringVars = class {
474
- static parse(str, ob) {
475
- const regex = /{{(.*?)}}/g;
476
- return str.replace(regex, (match, variable) => {
477
- if (ob.hasOwnProperty(variable.trim())) {
478
- return ob[variable.trim()];
479
- } else {
480
- return match;
481
- }
482
- });
1082
+ // src/application/contracts/IntegrationEvent.ts
1083
+ var IntegrationEvent = class extends BaseEvent {
1084
+ constructor(tenantUuid, version, type, payload, aggregateUuid, aggregateType) {
1085
+ super(tenantUuid, version, type, payload);
1086
+ this._aggregateUuid = aggregateUuid;
1087
+ this._aggregateType = aggregateType;
1088
+ }
1089
+ get aggregateUuid() {
1090
+ return this._aggregateUuid;
1091
+ }
1092
+ get aggregateType() {
1093
+ return this._aggregateType;
483
1094
  }
484
1095
  };
485
1096
 
486
- // src/infrastructure/errors/ErrorManager.ts
487
- var _ErrorManager = class _ErrorManager {
488
- constructor(logger = null) {
489
- this.logger = logger;
1097
+ // src/application/event-bus/EventBus.ts
1098
+ var _EventBus = class _EventBus {
1099
+ constructor(repository) {
1100
+ this.repository = repository;
1101
+ }
1102
+ async publish(event) {
1103
+ const mapper = _EventBus.MAPPERS.get(event.type);
1104
+ if (!mapper) {
1105
+ throw new InternalError(ErrorManager.APP_ERRORS.PROCESS, `Eventbus mapper not found for <${event.type}>`);
1106
+ }
1107
+ await this.repository.create(mapper(event));
490
1108
  }
491
- getDefaultMessage(lang) {
492
- return _ErrorManager.DEFAULT_MESSAGES[lang.value] || _ErrorManager.DEFAULT_MESSAGES[lang.base()] || "error";
1109
+ async publishMany(events) {
1110
+ for (let event of events) {
1111
+ await this.publish(event);
1112
+ }
493
1113
  }
494
- onFatal(err, lang) {
495
- this.logger?.fatal(err.type, err.message);
496
- return { status: "ERROR", message: this.getDefaultMessage(lang) };
1114
+ static addMapper(eventType, mapper) {
1115
+ _EventBus.MAPPERS.set(eventType, mapper);
497
1116
  }
498
- onInternal(err, lang) {
499
- this.logger?.error(err.type, err.message);
500
- return { status: "ERROR", message: this.getDefaultMessage(lang) };
1117
+ };
1118
+ _EventBus.MAPPERS = /* @__PURE__ */ new Map();
1119
+ var EventBus = _EventBus;
1120
+
1121
+ // src/application/unit-of-work/BasicUnitOfWork.ts
1122
+ var BasicUnitOfWork = class {
1123
+ constructor(conn) {
1124
+ this.connection = conn;
501
1125
  }
502
- onUsage(err, lang) {
503
- const tmpl = _ErrorManager.TEMPLATES.get(err.type);
504
- if (!tmpl) {
505
- this.logger?.error("TEMPLATE_NOT_FOUND", `${err.type}`);
506
- return { status: "ERROR", message: this.getDefaultMessage(lang) };
1126
+ async execute(fn) {
1127
+ await this.connection.begin();
1128
+ try {
1129
+ const result = await fn();
1130
+ await this.connection.commit();
1131
+ return result;
1132
+ } catch (err) {
1133
+ await this.connection.rollback();
1134
+ throw err;
1135
+ } finally {
1136
+ await this.connection.close();
507
1137
  }
508
- const code = lang.value;
509
- const base = lang.base();
510
- const rawMsg = tmpl.languages[code] ?? tmpl.languages[base] ?? this.getDefaultMessage(lang);
511
- return {
512
- status: "ERROR",
513
- message: StringVars.parse(rawMsg, err.vars)
514
- };
515
1138
  }
516
- onUnknown(err, lang) {
517
- this.logger?.error("UNKNOWN_ERROR", err.message);
518
- return { status: "ERROR", message: this.getDefaultMessage(lang) };
1139
+ };
1140
+
1141
+ // src/application/unit-of-work/BasicUnitOfWorkFactory.ts
1142
+ var BasicUnitOfWorkFactory = class {
1143
+ constructor(connector) {
1144
+ this.connector = connector;
519
1145
  }
520
- handle(err, lang) {
521
- if (["local", "dev"].includes(process.env.ENVIRONMENT ?? "")) {
522
- console.log(err);
523
- }
524
- if (err instanceof FatalError) {
525
- return this.onFatal(err, lang);
526
- }
527
- if (err instanceof InternalError) {
528
- return this.onInternal(err, lang);
1146
+ async create() {
1147
+ const conn = await this.connector.getConnection();
1148
+ return new BasicUnitOfWork(conn);
1149
+ }
1150
+ };
1151
+
1152
+ // src/infrastructure/contracts/EventManager.ts
1153
+ var EventManager = class {
1154
+ constructor(connection) {
1155
+ this._connection = connection;
1156
+ this._topics = [];
1157
+ this._callbackList = {};
1158
+ this._onStart = null;
1159
+ this._onConnected = null;
1160
+ this._onSubscribe = null;
1161
+ this._onMessage = null;
1162
+ this._onError = null;
1163
+ this._onCrash = null;
1164
+ this._onReconnect = null;
1165
+ }
1166
+ async execRoute(topic, event) {
1167
+ if (this._callbackList[topic]) {
1168
+ await this._callbackList[topic](event);
529
1169
  }
530
- if (err instanceof UsageError) {
531
- return this.onUsage(err, lang);
1170
+ }
1171
+ async execCallback(callback, data) {
1172
+ if (callback) {
1173
+ await callback(data);
532
1174
  }
533
- return this.onUnknown(err, lang);
534
1175
  }
535
- static addTemplate(template) {
536
- _ErrorManager.TEMPLATES.set(template.type, template);
1176
+ route(topic, callback) {
1177
+ this._topics.push(topic);
1178
+ this._callbackList[topic] = callback;
1179
+ }
1180
+ onStart(callback) {
1181
+ this._onStart = callback;
1182
+ }
1183
+ onConnected(callback) {
1184
+ this._onConnected = callback;
1185
+ }
1186
+ onSubscribe(callback) {
1187
+ this._onSubscribe = callback;
1188
+ }
1189
+ onMessage(callback) {
1190
+ this._onMessage = callback;
1191
+ }
1192
+ onError(callback) {
1193
+ this._onError = callback;
1194
+ }
1195
+ onCrash(callback) {
1196
+ this._onCrash = callback;
1197
+ }
1198
+ onReconnect(callback) {
1199
+ this._onReconnect = callback;
1200
+ }
1201
+ get topics() {
1202
+ return this._topics;
1203
+ }
1204
+ get callbackList() {
1205
+ return this._callbackList;
537
1206
  }
538
1207
  };
539
- _ErrorManager.DEFAULT_MESSAGES = {
540
- "es": "Ups, hemos encontrado un error. Nuestro equipo ya est\xE1 trabajando para solucionarlo",
541
- "en": "Ups, we found an error. Our team is working on it.",
542
- "pt": "Ops, encontramos um bug. Nossa equipe j\xE1 est\xE1 trabalhando para resolver isso."
543
- };
544
- _ErrorManager.APP_ERRORS = {
545
- UNDEFINED: "UNDEFINED_ERROR",
546
- PROCESS: "PROCESS_ERROR",
547
- DATABASE: "DATABASE_ERROR"
548
- };
549
- _ErrorManager.TEMPLATES = /* @__PURE__ */ new Map();
550
- var ErrorManager = _ErrorManager;
551
-
552
- // src/infrastructure/mysql/MysqlConnector.ts
553
- var import_promise = require("mysql2/promise");
554
1208
 
555
- // src/infrastructure/mysql/MysqlConnection.ts
556
- var MysqlConnection = class {
557
- constructor(conn) {
558
- this._conn = conn;
1209
+ // src/infrastructure/contracts/OutboxRecord.ts
1210
+ var OutboxRecord = class _OutboxRecord {
1211
+ constructor(eventUuid, eventType, tenantUuid, aggregateUuid, aggregateType, topic, payload, status, attempts, errorMessage, publishedAt, lastAttempt, createdAt) {
1212
+ this._eventUuid = eventUuid;
1213
+ this._tenantUuid = tenantUuid;
1214
+ this._aggregateUuid = aggregateUuid;
1215
+ this._aggregateType = aggregateType;
1216
+ this._eventType = eventType;
1217
+ this._topic = topic;
1218
+ this._payload = payload;
1219
+ this._status = status;
1220
+ this._attempts = attempts;
1221
+ this._errorMessage = errorMessage;
1222
+ this._publishedAt = publishedAt;
1223
+ this._lastAttempt = lastAttempt;
1224
+ this._createdAt = createdAt ?? DateTime.now();
559
1225
  }
560
- async query(statement, params = []) {
561
- const [rows] = await this._conn.query(statement, params);
562
- return rows;
1226
+ get eventUuid() {
1227
+ return this._eventUuid;
563
1228
  }
564
- async begin() {
565
- await this._conn.beginTransaction();
1229
+ get tenantUuid() {
1230
+ return this._tenantUuid;
566
1231
  }
567
- async commit() {
568
- await this._conn.commit();
1232
+ get aggregateUuid() {
1233
+ return this._aggregateUuid;
569
1234
  }
570
- async rollback() {
571
- await this._conn.rollback();
1235
+ get aggregateType() {
1236
+ return this._aggregateType;
572
1237
  }
573
- async transaction(fn) {
574
- await this.begin();
575
- try {
576
- const result = await fn(this);
577
- await this.commit();
578
- return result;
579
- } catch (err) {
580
- await this.rollback();
581
- throw err;
582
- }
1238
+ get eventType() {
1239
+ return this._eventType;
583
1240
  }
584
- async close() {
585
- this._conn.release();
1241
+ get topic() {
1242
+ return this._topic;
1243
+ }
1244
+ get payload() {
1245
+ return this._payload;
1246
+ }
1247
+ get status() {
1248
+ return this._status;
1249
+ }
1250
+ get attempts() {
1251
+ return this._attempts;
1252
+ }
1253
+ get errorMessage() {
1254
+ return this._errorMessage;
1255
+ }
1256
+ get publishedAt() {
1257
+ return this._publishedAt;
1258
+ }
1259
+ get lastAttempt() {
1260
+ return this._lastAttempt;
1261
+ }
1262
+ get createdAt() {
1263
+ return this._createdAt;
1264
+ }
1265
+ incrementAttempts() {
1266
+ this._attempts++;
1267
+ this._lastAttempt = DateTime.now();
1268
+ }
1269
+ markProcessed() {
1270
+ this._status = ProcessStatus.PROCESSED;
1271
+ this._publishedAt = DateTime.now();
1272
+ }
1273
+ markProcessing() {
1274
+ this.incrementAttempts();
1275
+ this._status = ProcessStatus.PROCESSING;
1276
+ }
1277
+ markWithError(error) {
1278
+ this._status = this.attempts < 5 ? ProcessStatus.FAILED : ProcessStatus.DEAD;
1279
+ this._errorMessage = error;
1280
+ }
1281
+ toPrimitives() {
1282
+ return {
1283
+ eventUuid: this.eventUuid.value,
1284
+ eventType: this.eventType,
1285
+ tenantUuid: this.tenantUuid.value,
1286
+ aggregateUuid: this.aggregateUuid.value,
1287
+ aggregateType: this.aggregateType,
1288
+ topic: this.topic,
1289
+ payload: this.payload,
1290
+ status: this.status.value,
1291
+ attempts: this.attempts,
1292
+ errorMessage: this.errorMessage ?? void 0,
1293
+ publishedAt: this.publishedAt?.value ?? void 0,
1294
+ lastAttempt: this.lastAttempt?.value ?? void 0,
1295
+ createdAt: this.createdAt.value
1296
+ };
1297
+ }
1298
+ static reconstitute(data) {
1299
+ return new _OutboxRecord(
1300
+ UUID.create(data.event_uuid),
1301
+ String(data.event_type),
1302
+ UUID.create(data.tenant_uuid),
1303
+ UUID.create(data.aggregate_uuid),
1304
+ String(data.aggregate_type),
1305
+ String(data.topic),
1306
+ String(data.payload),
1307
+ ProcessStatus.create(data.status),
1308
+ Number(data.attempts),
1309
+ data.error_message ?? void 0,
1310
+ data.published_at ? DateTime.create(data.published_at) : void 0,
1311
+ data.last_attempt ? DateTime.create(data.last_attempt) : void 0,
1312
+ data.created_at ? DateTime.create(data.created_at) : void 0
1313
+ );
586
1314
  }
587
1315
  };
588
1316
 
589
- // src/infrastructure/mysql/MysqlConnector.ts
590
- var _MysqlConnector = class _MysqlConnector {
591
- constructor(pool) {
592
- this._pool = pool ?? (0, import_promise.createPool)({
593
- host: process.env.DB_HOST,
594
- port: Number(process.env.DB_PORT ?? 3306),
595
- user: process.env.DB_USER,
596
- password: process.env.DB_PASSWORD,
597
- database: process.env.DB_NAME,
598
- connectionLimit: Number(process.env.DB_POOL_SIZE) || _MysqlConnector.DEFAULT_POOL_SIZE,
599
- decimalNumbers: true
600
- });
1317
+ // src/infrastructure/contracts/InboxRecord.ts
1318
+ var InboxRecord = class _InboxRecord {
1319
+ constructor(eventUuid, tenantUuid, topic, producer, payload, status, attempts, errorMessage, lastAttempt, processedAt, createdAt) {
1320
+ this._eventUuid = eventUuid;
1321
+ this._tenantUuid = tenantUuid;
1322
+ this._topic = topic;
1323
+ this._producer = producer;
1324
+ this._payload = payload;
1325
+ this._status = status;
1326
+ this._attempts = attempts ?? 0;
1327
+ this._errorMessage = errorMessage ?? void 0;
1328
+ this._lastAttempt = lastAttempt ?? void 0;
1329
+ this._processedAt = processedAt ?? void 0;
1330
+ this._createdAt = createdAt ?? DateTime.now();
601
1331
  }
602
- async wrap(conn) {
603
- return new MysqlConnection(conn);
1332
+ get eventUuid() {
1333
+ return this._eventUuid;
604
1334
  }
605
- async getConnection() {
606
- const conn = await this._pool.getConnection();
607
- return this.wrap(conn);
1335
+ get tenantUuid() {
1336
+ return this._tenantUuid;
608
1337
  }
609
- async closePool() {
610
- await this._pool.end();
1338
+ get topic() {
1339
+ return this._topic;
611
1340
  }
612
- static async ping() {
613
- const connector = new _MysqlConnector();
614
- try {
615
- const conn = await connector._pool.getConnection();
616
- await conn.ping();
617
- conn.release();
618
- return true;
619
- } catch {
620
- return false;
621
- } finally {
622
- await connector.closePool();
623
- }
1341
+ get producer() {
1342
+ return this._producer;
1343
+ }
1344
+ get payload() {
1345
+ return this._payload;
1346
+ }
1347
+ get status() {
1348
+ return this._status;
1349
+ }
1350
+ get attempts() {
1351
+ return this._attempts;
1352
+ }
1353
+ get errorMessage() {
1354
+ return this._errorMessage;
1355
+ }
1356
+ get lastAttempt() {
1357
+ return this._lastAttempt;
1358
+ }
1359
+ get processedAt() {
1360
+ return this._processedAt;
1361
+ }
1362
+ get createdAt() {
1363
+ return this._createdAt;
1364
+ }
1365
+ incrementAttempts() {
1366
+ this._attempts++;
1367
+ this._lastAttempt = DateTime.now();
1368
+ }
1369
+ markProcessed() {
1370
+ this._status = ProcessStatus.PROCESSED;
1371
+ this._processedAt = DateTime.now();
1372
+ }
1373
+ markProcessing() {
1374
+ this.incrementAttempts();
1375
+ this._status = ProcessStatus.PROCESSING;
1376
+ }
1377
+ markWithError(error) {
1378
+ this._status = this.attempts < 5 ? ProcessStatus.FAILED : ProcessStatus.DEAD;
1379
+ this._errorMessage = error;
1380
+ }
1381
+ toPrimitives() {
1382
+ return {
1383
+ eventUuid: this.eventUuid.value,
1384
+ tenantUuid: this.tenantUuid.value,
1385
+ topic: this.topic,
1386
+ producer: this.producer,
1387
+ payload: this.payload,
1388
+ status: this.status.value,
1389
+ attempts: this.attempts,
1390
+ errorMessage: this.errorMessage ?? void 0,
1391
+ lastAttempt: this.lastAttempt?.value ?? void 0,
1392
+ processedAt: this.processedAt?.value ?? void 0,
1393
+ createdAt: this.createdAt.value
1394
+ };
1395
+ }
1396
+ static reconstitute(data) {
1397
+ return new _InboxRecord(
1398
+ UUID.create(data.event_uuid),
1399
+ UUID.create(data.tenant_uuid),
1400
+ String(data.topic),
1401
+ String(data.producer),
1402
+ String(data.payload),
1403
+ ProcessStatus.create(data.status),
1404
+ Number(data.attempts),
1405
+ data.error_message ?? void 0,
1406
+ data.last_attempt ? DateTime.create(data.last_attempt) : void 0,
1407
+ data.processed_at ? DateTime.create(data.processed_at) : void 0,
1408
+ data.created_at ? DateTime.create(data.created_at) : void 0
1409
+ );
1410
+ }
1411
+ };
1412
+
1413
+ // src/infrastructure/event-bus/EventBusMysqlRepository.ts
1414
+ var EventBusMysqlRepository = class {
1415
+ constructor(connection) {
1416
+ this.connection = connection;
1417
+ }
1418
+ recordToRowValues(record) {
1419
+ return [
1420
+ record.eventUuid.value,
1421
+ record.eventType,
1422
+ record.tenantUuid.value,
1423
+ record.aggregateUuid.value,
1424
+ record.aggregateType,
1425
+ record.topic,
1426
+ record.payload,
1427
+ record.status.value,
1428
+ record.attempts,
1429
+ record.errorMessage,
1430
+ record.publishedAt?.value,
1431
+ record.lastAttempt?.value,
1432
+ record.createdAt.value
1433
+ ];
1434
+ }
1435
+ async create(record) {
1436
+ const values = this.recordToRowValues(record);
1437
+ await this.connection.query(
1438
+ `INSERT INTO events_outbox (event_uuid, event_type, tenant_uuid, aggregate_uuid, aggregate_type, topic,
1439
+ payload, status, attempts, error_message, published_at, last_attempt, created_at)
1440
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
1441
+ values
1442
+ );
1443
+ }
1444
+ async update(record) {
1445
+ const values = [record.status.value, record.attempts, record.errorMessage, record.publishedAt?.value, record.lastAttempt?.value, record.eventUuid.value];
1446
+ await this.connection.query(
1447
+ `UPDATE events_outbox
1448
+ SET status = ?,
1449
+ attempts = ?,
1450
+ error_message = ?,
1451
+ published_at = ?,
1452
+ last_attempt = ?
1453
+ WHERE event_uuid = ?`,
1454
+ values
1455
+ );
1456
+ }
1457
+ async listPending(limit) {
1458
+ const result = await this.connection.query(
1459
+ `SELECT * FROM events_outbox WHERE status IN ('PENDING','FAILED') AND published_at IS NULL LIMIT ${limit}`,
1460
+ []
1461
+ );
1462
+ return result.length > 0 ? result.map((r) => OutboxRecord.reconstitute(r)) : [];
624
1463
  }
625
1464
  };
626
- _MysqlConnector.DEFAULT_POOL_SIZE = 10;
627
- var MysqlConnector = _MysqlConnector;
628
1465
 
629
1466
  // src/infrastructure/express/ExpressAdapters.ts
1467
+ var BOOL_TRUE = /* @__PURE__ */ new Set(["1", "true", "yes", "y", "on"]);
1468
+ var BOOL_FALSE = /* @__PURE__ */ new Set(["0", "false", "no", "n", "off"]);
1469
+ var NUMERIC_KEY_RE = /^(?:page|qty|limit|offset|total|amount|price|count|rounding|min[A-Z_].*|max[A-Z_].*)$/i;
1470
+ var DATE_KEY_RE = /(At|Date|_at|_date)$/i;
1471
+ function toUtcDateTimeString(raw) {
1472
+ const s = raw.trim();
1473
+ if (/^\d{4}-\d{2}-\d{2}$/.test(s))
1474
+ return `${s} 00:00:00`;
1475
+ if (/^\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2}$/.test(s))
1476
+ return s;
1477
+ const d = new Date(s);
1478
+ if (Number.isNaN(d.getTime()))
1479
+ return void 0;
1480
+ const pad = (n) => String(n).padStart(2, "0");
1481
+ return `${d.getUTCFullYear()}-${pad(d.getUTCMonth() + 1)}-${pad(d.getUTCDate())} ${pad(d.getUTCHours())}:${pad(d.getUTCMinutes())}:${pad(d.getUTCSeconds())}`;
1482
+ }
1483
+ function coerceScalar(key, v) {
1484
+ if (v == null)
1485
+ return void 0;
1486
+ if (typeof v !== "string")
1487
+ return v;
1488
+ const s = v.trim();
1489
+ if (s === "")
1490
+ return void 0;
1491
+ if (s.toLowerCase() === "null")
1492
+ return null;
1493
+ const low = s.toLowerCase();
1494
+ if (BOOL_TRUE.has(low))
1495
+ return true;
1496
+ if (BOOL_FALSE.has(low))
1497
+ return false;
1498
+ if (NUMERIC_KEY_RE.test(key) && /^-?\d+(\.\d+)?$/.test(s)) {
1499
+ const n = Number(s);
1500
+ if (Number.isFinite(n))
1501
+ return n;
1502
+ }
1503
+ if (DATE_KEY_RE.test(key)) {
1504
+ return toUtcDateTimeString(s) ?? s;
1505
+ }
1506
+ return s;
1507
+ }
1508
+ function normalizeQuery(q) {
1509
+ const out = {};
1510
+ for (const [key, raw] of Object.entries(q)) {
1511
+ const values = Array.isArray(raw) ? raw : typeof raw === "string" && raw.includes(",") ? raw.split(",").map((s) => s.trim()).filter(Boolean) : [raw];
1512
+ const coerced = values.map((v) => coerceScalar(key, v)).filter((v) => v !== void 0);
1513
+ if (coerced.length === 1)
1514
+ out[key] = coerced[0];
1515
+ else if (coerced.length > 1)
1516
+ out[key] = coerced;
1517
+ }
1518
+ return out;
1519
+ }
1520
+ function isReadableStream(x) {
1521
+ return x && typeof x.pipe === "function";
1522
+ }
1523
+ function hasHeader(headers, name) {
1524
+ if (!headers)
1525
+ return false;
1526
+ const lname = name.toLowerCase();
1527
+ return Object.keys(headers).some((h) => h.toLowerCase() === lname);
1528
+ }
630
1529
  function adaptExpressRoute(Controller) {
631
1530
  return async (req, res, next) => {
632
1531
  const rawLangHeader = req.headers["accept-language"] ?? req.headers["Accept-Language"] ?? "es";
@@ -635,14 +1534,49 @@ function adaptExpressRoute(Controller) {
635
1534
  const httpRequest = {
636
1535
  headers: req.headers,
637
1536
  params: req.params,
638
- query: Object.fromEntries(Object.entries(req.query).map(([k, v]) => [k, String(v)])),
1537
+ query: normalizeQuery(req.query),
639
1538
  lang,
640
1539
  body: req.body
641
1540
  };
642
1541
  try {
643
1542
  const controller = new Controller();
644
- const { statusCode, body } = await controller.handle(httpRequest);
645
- res.status(statusCode).json(body);
1543
+ const { statusCode, body, headers } = await controller.handle(httpRequest);
1544
+ if (headers)
1545
+ res.set(headers);
1546
+ if (isReadableStream(body)) {
1547
+ if (!hasHeader(headers, "Content-Type"))
1548
+ res.set("Content-Type", "application/octet-stream");
1549
+ res.status(statusCode);
1550
+ body.on("error", next);
1551
+ body.pipe(res);
1552
+ return;
1553
+ }
1554
+ const isArrayBuffer = typeof body === "object" && body instanceof ArrayBuffer;
1555
+ const isArrayBufferView = typeof body === "object" && body != null && ArrayBuffer.isView(body);
1556
+ if (Buffer.isBuffer(body) || isArrayBuffer || isArrayBufferView) {
1557
+ let buf;
1558
+ if (Buffer.isBuffer(body)) {
1559
+ buf = body;
1560
+ } else if (isArrayBuffer) {
1561
+ buf = Buffer.from(body);
1562
+ } else {
1563
+ const view = body;
1564
+ buf = Buffer.from(view.buffer, view.byteOffset, view.byteLength);
1565
+ }
1566
+ if (!hasHeader(headers, "Content-Type"))
1567
+ res.set("Content-Type", "application/octet-stream");
1568
+ if (!res.getHeader("Content-Length"))
1569
+ res.set("Content-Length", String(buf.length));
1570
+ res.status(statusCode);
1571
+ res.end(buf);
1572
+ return;
1573
+ }
1574
+ const hasCT = hasHeader(headers, "Content-Type");
1575
+ if (typeof body === "string" || hasCT) {
1576
+ res.status(statusCode).send(body);
1577
+ return;
1578
+ }
1579
+ res.status(statusCode).json(body ?? {});
646
1580
  } catch (err) {
647
1581
  next(err);
648
1582
  }
@@ -659,49 +1593,461 @@ function adaptExpressErrorHandler(errorManager) {
659
1593
  };
660
1594
  }
661
1595
 
662
- // src/infrastructure/http/HttpNotFoundController.ts
1596
+ // src/infrastructure/http/DefaultController.ts
1597
+ var HttpHealthCheckController = class {
1598
+ async handle(request) {
1599
+ return {
1600
+ statusCode: 200,
1601
+ body: {
1602
+ date: DateTime.create().value,
1603
+ code: 200
1604
+ }
1605
+ };
1606
+ }
1607
+ };
663
1608
  var HttpNotFoundController = class {
664
1609
  async handle(request) {
665
1610
  return {
666
1611
  statusCode: 404,
667
1612
  body: {
668
- status: "NOT_FOUND",
669
1613
  message: `Route ${request.headers.location} not found`
670
1614
  }
671
1615
  };
672
1616
  }
673
1617
  };
674
1618
 
675
- // src/infrastructure/http/HttpHealthCheckController.ts
676
- var HttpHealthCheckController = class {
677
- async handle(request) {
678
- return {
679
- statusCode: 200,
680
- body: {
681
- date: DateTime.create().value,
682
- code: 200,
683
- status: "OK"
1619
+ // src/infrastructure/kafka/KafkaManager.ts
1620
+ var import_kafkajs = require("kafkajs");
1621
+ var KafkaManager = class extends EventManager {
1622
+ constructor(connection) {
1623
+ super(connection);
1624
+ this.kafka = new import_kafkajs.Kafka({
1625
+ brokers: this._connection.brokers,
1626
+ ssl: true,
1627
+ sasl: {
1628
+ mechanism: "scram-sha-256",
1629
+ username: this._connection.userName,
1630
+ password: this._connection.password
1631
+ },
1632
+ logLevel: import_kafkajs.logLevel.ERROR
1633
+ });
1634
+ this.consumer = this.kafka.consumer({ groupId: process.env.KAFKA_GROUP || "default" });
1635
+ this.producer = this.kafka.producer();
1636
+ }
1637
+ async run(autocommit) {
1638
+ const __run = async () => {
1639
+ await this.execCallback(this._onStart, { message: `--- ${this.constructor.name} started ---` });
1640
+ await this.consumer.connect();
1641
+ await this.execCallback(this._onConnected, { message: `--- ${this.constructor.name} connected to ${this._connection.brokers} ---` });
1642
+ for (let topic of this._topics) {
1643
+ try {
1644
+ await this.consumer.subscribe({ topic, fromBeginning: true });
1645
+ await this.execCallback(this._onSubscribe, { message: `--- ${this.constructor.name} subscribed to ${topic} ---` });
1646
+ } catch (error) {
1647
+ await this.execCallback(this._onConnected, { message: `Error on subscribe to kafka topic: ${topic}` });
1648
+ }
684
1649
  }
1650
+ await this.consumer.run({
1651
+ autoCommit: autocommit,
1652
+ // @ts-ignore
1653
+ eachMessage: async ({ topic, partition, message, heartbeat }) => {
1654
+ try {
1655
+ await this.execCallback(this._onMessage, `[New message detected for ${topic}]: ${message.value?.toString()}`);
1656
+ const json = JSON.parse(String(message.value?.toString()));
1657
+ await this.execRoute(topic, {
1658
+ topic: String(json.topic),
1659
+ producer: String(json.producer),
1660
+ tenant: UUID.create(String(json.tenant)),
1661
+ message: json.message
1662
+ });
1663
+ const next = (BigInt(message.offset) + 1n).toString();
1664
+ await this.consumer.commitOffsets([{ topic, partition, offset: next }]);
1665
+ await heartbeat();
1666
+ } catch (error) {
1667
+ await this.execCallback(this._onError, error);
1668
+ }
1669
+ }
1670
+ });
1671
+ };
1672
+ try {
1673
+ await __run();
1674
+ } catch (error) {
1675
+ await this.execCallback(this._onError, new InternalError(ErrorManager.APP_ERRORS.PROCESS, error.toString()));
1676
+ }
1677
+ }
1678
+ async send(e) {
1679
+ const evt = {
1680
+ date: DateTime.now().value,
1681
+ tenant: e.tenant.value,
1682
+ producer: e.producer,
1683
+ data: JSON.parse(e.message)
685
1684
  };
1685
+ try {
1686
+ if (!this.producer) {
1687
+ throw new InternalError(ErrorManager.APP_ERRORS.PROCESS, "Producer not initialized");
1688
+ }
1689
+ await this.producer.connect();
1690
+ await this.producer.send({
1691
+ topic: e.topic,
1692
+ messages: [{ value: JSON.stringify(evt) }]
1693
+ });
1694
+ await this.producer.disconnect();
1695
+ } catch (error) {
1696
+ throw new InternalError(error.toString());
1697
+ }
1698
+ }
1699
+ async start(autocommit = false) {
1700
+ this.consumer.on(this.consumer.events.CRASH, async (error) => {
1701
+ await this.execCallback(this._onError, new InternalError(ErrorManager.APP_ERRORS.PROCESS, error.payload.error.stack));
1702
+ });
1703
+ await this.run(autocommit);
1704
+ }
1705
+ async pause() {
1706
+ }
1707
+ async restart() {
1708
+ await this.consumer.stop();
1709
+ await this.start();
1710
+ }
1711
+ async stop() {
1712
+ await this.consumer.stop();
1713
+ }
1714
+ };
1715
+
1716
+ // src/infrastructure/mysql/Mysql.ts
1717
+ var import_promise = require("mysql2/promise");
1718
+ var _MysqlConnector = class _MysqlConnector {
1719
+ constructor(pool) {
1720
+ this._pool = pool ?? (0, import_promise.createPool)({
1721
+ host: process.env.DB_HOST,
1722
+ port: Number(process.env.DB_PORT ?? 3306),
1723
+ user: process.env.DB_USER,
1724
+ password: process.env.DB_PASSWORD,
1725
+ database: process.env.DB_DATABASE,
1726
+ dateStrings: true,
1727
+ connectionLimit: Number(process.env.DB_POOL_SIZE ?? _MysqlConnector.DEFAULT_POOL_SIZE),
1728
+ decimalNumbers: true
1729
+ });
1730
+ }
1731
+ async wrap(conn) {
1732
+ return new MysqlConnection(conn);
1733
+ }
1734
+ async query(sql, params = []) {
1735
+ const [rows] = await this._pool.query(sql, params);
1736
+ return rows;
1737
+ }
1738
+ async getConnection() {
1739
+ const conn = await this._pool.getConnection();
1740
+ return this.wrap(conn);
1741
+ }
1742
+ async closePool() {
1743
+ await this._pool.end();
1744
+ }
1745
+ static async ping() {
1746
+ const connector = new _MysqlConnector();
1747
+ try {
1748
+ const conn = await connector._pool.getConnection();
1749
+ await conn.ping();
1750
+ conn.release();
1751
+ return true;
1752
+ } catch {
1753
+ return false;
1754
+ } finally {
1755
+ await connector.closePool();
1756
+ }
1757
+ }
1758
+ };
1759
+ _MysqlConnector.DEFAULT_POOL_SIZE = 20;
1760
+ var MysqlConnector = _MysqlConnector;
1761
+ var MysqlConnection = class {
1762
+ constructor(conn) {
1763
+ this._conn = conn;
1764
+ }
1765
+ async query(statement, params = []) {
1766
+ const [rows] = await this._conn.query(statement, params);
1767
+ return rows;
1768
+ }
1769
+ async begin() {
1770
+ await this._conn.beginTransaction();
1771
+ }
1772
+ async commit() {
1773
+ await this._conn.commit();
1774
+ }
1775
+ async rollback() {
1776
+ await this._conn.rollback();
1777
+ }
1778
+ async transaction(fn) {
1779
+ await this.begin();
1780
+ try {
1781
+ const result = await fn(this);
1782
+ await this.commit();
1783
+ return result;
1784
+ } catch (err) {
1785
+ await this.rollback();
1786
+ throw err;
1787
+ }
1788
+ }
1789
+ async close() {
1790
+ await this._conn.release();
1791
+ }
1792
+ };
1793
+
1794
+ // src/infrastructure/runners/default-mysql-outbox-runner.ts
1795
+ var DefaultMysqlOutboxRunner = class {
1796
+ constructor(uowFactory, eventManager) {
1797
+ this.errors = [];
1798
+ this.uowFactory = uowFactory;
1799
+ this.eventManager = eventManager;
1800
+ this.interval = Number(process.env.OUTBOX_RUNNER_INTERVAL_MS || 5e3);
1801
+ this.maxEvents = Number(process.env.OUTBOX_RUNNER_MAX_EVENTS || 20);
1802
+ }
1803
+ addError(error) {
1804
+ this.errors.push(error);
1805
+ }
1806
+ logErrors() {
1807
+ if (this.errors.length > 0) {
1808
+ console.error(this.errors);
1809
+ this.errors = [];
1810
+ }
1811
+ }
1812
+ async sleep() {
1813
+ return new Promise((r) => setTimeout(r, this.maxEvents));
1814
+ }
1815
+ async processOutboxRecord(e, eventBusRepository) {
1816
+ try {
1817
+ e.markProcessing();
1818
+ await eventBusRepository.update(e);
1819
+ await this.eventManager.send({
1820
+ topic: e.topic,
1821
+ producer: process.env.NAME && process.env.ENVIRONMENT ? `${process.env.NAME}-${process.env.ENVIRONMENT}` : "unknown",
1822
+ tenant: e.tenantUuid,
1823
+ message: e.payload
1824
+ });
1825
+ e.markProcessed();
1826
+ } catch (error) {
1827
+ const type = String(error.type);
1828
+ e.markWithError(type);
1829
+ throw new Error(type);
1830
+ } finally {
1831
+ await eventBusRepository.update(e);
1832
+ }
1833
+ }
1834
+ async process() {
1835
+ const uow = await this.uowFactory.create();
1836
+ const eventBusRepository = new EventBusMysqlRepository(uow.connection);
1837
+ await uow.execute(async () => {
1838
+ const records = await eventBusRepository.listPending(this.maxEvents);
1839
+ for (let record of records) {
1840
+ try {
1841
+ await this.processOutboxRecord(record, eventBusRepository);
1842
+ } catch (error) {
1843
+ this.addError({ eventUuid: record.eventUuid.value, error: error.toString() });
1844
+ }
1845
+ }
1846
+ this.logErrors();
1847
+ });
1848
+ }
1849
+ async start() {
1850
+ console.log("[outbox-runner]: start");
1851
+ for (; ; ) {
1852
+ await this.process();
1853
+ await this.sleep();
1854
+ }
1855
+ }
1856
+ };
1857
+
1858
+ // src/infrastructure/inbox/InboxMysqlRepository.ts
1859
+ var InboxMysqlRepository = class {
1860
+ constructor(connection) {
1861
+ this.connection = connection;
1862
+ }
1863
+ recordToRowValues(record) {
1864
+ return [
1865
+ record.eventUuid.value,
1866
+ record.tenantUuid.value,
1867
+ record.topic,
1868
+ record.producer,
1869
+ record.payload,
1870
+ record.status.value,
1871
+ record.attempts,
1872
+ record.errorMessage,
1873
+ record.lastAttempt?.value ?? null,
1874
+ record.processedAt?.value ?? null,
1875
+ record.createdAt.value
1876
+ ];
1877
+ }
1878
+ async create(record) {
1879
+ const values = this.recordToRowValues(record);
1880
+ await this.connection.query(
1881
+ `INSERT INTO events_inbox (event_uuid, tenant_uuid, topic, producer,
1882
+ payload, status, attempts, error_message, last_attempt, processed_at, created_at)
1883
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
1884
+ values
1885
+ );
1886
+ }
1887
+ async update(record) {
1888
+ const values = [record.status.value, record.attempts, record.errorMessage, record.processedAt?.value, record.lastAttempt?.value, record.eventUuid.value];
1889
+ await this.connection.query(
1890
+ `UPDATE events_inbox
1891
+ SET status = ?,
1892
+ attempts = ?,
1893
+ error_message = ?,
1894
+ processed_at = ?,
1895
+ last_attempt = ?
1896
+ WHERE event_uuid = ?`,
1897
+ values
1898
+ );
1899
+ }
1900
+ async listPending(limit) {
1901
+ const result = await this.connection.query(
1902
+ `SELECT * FROM events_inbox WHERE status IN ('PENDING','FAILED') AND events_inbox.processed_at IS NULL LIMIT ${limit}`,
1903
+ []
1904
+ );
1905
+ return result.length > 0 ? result.map((r) => InboxRecord.reconstitute(r)) : [];
1906
+ }
1907
+ };
1908
+
1909
+ // src/infrastructure/runners/default-mysql-inbox-runner.ts
1910
+ var DefaultMysqlInboxRunner = class {
1911
+ constructor(uowFactory, eventManager) {
1912
+ this.topics = [];
1913
+ this.uowFactory = uowFactory;
1914
+ this.eventManager = eventManager;
1915
+ this.interval = Number(process.env.OUTBOX_RUNNER_INTERVAL_MS || 5e3);
1916
+ this.maxEvents = Number(process.env.OUTBOX_RUNNER_MAX_EVENTS || 20);
1917
+ }
1918
+ async saveEvent(e, repository) {
1919
+ let record = new InboxRecord(
1920
+ UUID.create(),
1921
+ e.tenant,
1922
+ e.topic,
1923
+ e.producer,
1924
+ JSON.stringify(e.message),
1925
+ ProcessStatus.PENDING
1926
+ );
1927
+ try {
1928
+ await repository.create(record);
1929
+ } catch (error) {
1930
+ const type = String(error.type);
1931
+ record.markWithError(type);
1932
+ throw new Error(type);
1933
+ } finally {
1934
+ await repository.update(record);
1935
+ }
1936
+ }
1937
+ subscribeTo(topic) {
1938
+ this.topics.push(topic);
1939
+ }
1940
+ async process() {
1941
+ const uow = await this.uowFactory.create();
1942
+ const inboxRepository = new InboxMysqlRepository(uow.connection);
1943
+ await uow.execute(async () => {
1944
+ for (let topic of this.topics) {
1945
+ try {
1946
+ this.eventManager.route(topic, async (e) => {
1947
+ await this.saveEvent(e, inboxRepository);
1948
+ });
1949
+ } catch (error) {
1950
+ console.log(error.toString());
1951
+ }
1952
+ }
1953
+ await this.eventManager.start();
1954
+ });
1955
+ }
1956
+ async start() {
1957
+ console.log("[inbox-runner]: start");
1958
+ for (; ; ) {
1959
+ await this.process();
1960
+ }
1961
+ }
1962
+ };
1963
+
1964
+ // src/utils/ExchangeRates.ts
1965
+ var ExchangeRates = class _ExchangeRates extends BaseObject {
1966
+ constructor(props) {
1967
+ super(props);
1968
+ }
1969
+ getRate(currency) {
1970
+ if (Object.keys(this.props.rates).includes(currency.value)) {
1971
+ return this.props.rates[currency.value];
1972
+ }
1973
+ return null;
1974
+ }
1975
+ get base() {
1976
+ return this.props.base;
1977
+ }
1978
+ get rates() {
1979
+ return this.props.rates;
1980
+ }
1981
+ get date() {
1982
+ return this.props.date;
1983
+ }
1984
+ toProps() {
1985
+ return this.props;
1986
+ }
1987
+ toPrimitives() {
1988
+ return {
1989
+ base: this.props.base.value,
1990
+ rates: this.props.rates,
1991
+ date: this.props.date.value
1992
+ };
1993
+ }
1994
+ exchangeToBase(price) {
1995
+ const rate = this.getRate(price.currency);
1996
+ if (!rate) {
1997
+ throw new InternalError("INVALID_EXCHANGE_RATE_CURRENCY", `Avaiable rates: ${this.props.rates} - Base Currency:${this.props.base.value} - Price Currency: ${price.currency.value}`);
1998
+ }
1999
+ if (price.currency.value === this.props.base.value) {
2000
+ return price;
2001
+ }
2002
+ return Price.create(parseFloat((price.amount / rate).toFixed(2)), this.props.base.value);
2003
+ }
2004
+ static create(props) {
2005
+ return new _ExchangeRates(props);
2006
+ }
2007
+ static createFromPrimitives(data) {
2008
+ return _ExchangeRates.create({
2009
+ base: Currency.create(data.base),
2010
+ rates: data.rates ?? [],
2011
+ date: DateTime.create(data.date ?? "")
2012
+ });
686
2013
  }
687
2014
  };
688
2015
  // Annotate the CommonJS export names for ESM import in node:
689
2016
  0 && (module.exports = {
2017
+ BaseEvent,
2018
+ BaseObject,
2019
+ BasicUnitOfWork,
2020
+ BasicUnitOfWorkFactory,
2021
+ Country,
690
2022
  Currency,
691
2023
  DateTime,
2024
+ DefaultMysqlInboxRunner,
2025
+ DefaultMysqlOutboxRunner,
692
2026
  DomainEntity,
693
2027
  DomainError,
694
2028
  DomainEvent,
695
2029
  Email,
696
2030
  ErrorManager,
2031
+ EventBus,
2032
+ EventBusMysqlRepository,
2033
+ EventManager,
2034
+ ExchangeRates,
697
2035
  FatalError,
698
2036
  HttpHealthCheckController,
699
2037
  HttpNotFoundController,
2038
+ InboxRecord,
2039
+ IntegrationEvent,
700
2040
  InternalError,
2041
+ KafkaManager,
701
2042
  Language,
702
2043
  MysqlConnection,
703
2044
  MysqlConnector,
2045
+ OutboxRecord,
2046
+ PaymentGateway,
2047
+ PaymentStatus,
704
2048
  Price,
2049
+ ProcessStatus,
2050
+ StringVars,
705
2051
  UUID,
706
2052
  UsageError,
707
2053
  ValueObject,