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