@merkaly/nest 0.2.0 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/README.md +44 -24
  2. package/dist/events/aliexpress.d.ts +10 -0
  3. package/dist/events/aliexpress.d.ts.map +1 -0
  4. package/dist/events/aliexpress.js +12 -0
  5. package/dist/events/aliexpress.js.map +1 -0
  6. package/dist/events/index.d.ts +2 -2
  7. package/dist/events/index.d.ts.map +1 -1
  8. package/dist/events/index.js +2 -2
  9. package/dist/events/index.js.map +1 -1
  10. package/dist/events/merkaly-event.d.ts +96 -0
  11. package/dist/events/merkaly-event.d.ts.map +1 -0
  12. package/dist/events/merkaly-event.js +49 -0
  13. package/dist/events/merkaly-event.js.map +1 -0
  14. package/dist/http/request-logger.d.ts +1 -0
  15. package/dist/http/request-logger.d.ts.map +1 -1
  16. package/dist/http/request-logger.js +16 -5
  17. package/dist/http/request-logger.js.map +1 -1
  18. package/dist/logger/request-context.d.ts +7 -0
  19. package/dist/logger/request-context.d.ts.map +1 -1
  20. package/dist/logger/request-context.js +8 -0
  21. package/dist/logger/request-context.js.map +1 -1
  22. package/dist/rabbit/events.consumer.d.ts +19 -0
  23. package/dist/rabbit/events.consumer.d.ts.map +1 -0
  24. package/dist/rabbit/events.consumer.js +96 -0
  25. package/dist/rabbit/events.consumer.js.map +1 -0
  26. package/dist/rabbit/events.publisher.d.ts +8 -8
  27. package/dist/rabbit/events.publisher.d.ts.map +1 -1
  28. package/dist/rabbit/events.publisher.js +36 -41
  29. package/dist/rabbit/events.publisher.js.map +1 -1
  30. package/dist/rabbit/index.d.ts +1 -0
  31. package/dist/rabbit/index.d.ts.map +1 -1
  32. package/dist/rabbit/index.js +1 -0
  33. package/dist/rabbit/index.js.map +1 -1
  34. package/dist/rabbit/rabbit.consumer.d.ts +7 -10
  35. package/dist/rabbit/rabbit.consumer.d.ts.map +1 -1
  36. package/dist/rabbit/rabbit.consumer.js +38 -13
  37. package/dist/rabbit/rabbit.consumer.js.map +1 -1
  38. package/dist/rabbit/rabbit.module.d.ts +4 -2
  39. package/dist/rabbit/rabbit.module.d.ts.map +1 -1
  40. package/dist/rabbit/rabbit.module.js +35 -15
  41. package/dist/rabbit/rabbit.module.js.map +1 -1
  42. package/dist/rabbit/rabbit.options.d.ts +25 -5
  43. package/dist/rabbit/rabbit.options.d.ts.map +1 -1
  44. package/dist/rabbit/rabbit.options.js +6 -2
  45. package/dist/rabbit/rabbit.options.js.map +1 -1
  46. package/package.json +1 -1
  47. package/dist/events/webhook-event.d.ts +0 -12
  48. package/dist/events/webhook-event.d.ts.map +0 -1
  49. package/dist/events/webhook-event.js +0 -11
  50. package/dist/events/webhook-event.js.map +0 -1
  51. package/dist/events/webhook-patterns.d.ts +0 -52
  52. package/dist/events/webhook-patterns.d.ts.map +0 -1
  53. package/dist/events/webhook-patterns.js +0 -49
  54. package/dist/events/webhook-patterns.js.map +0 -1
package/README.md CHANGED
@@ -39,10 +39,11 @@ strict-peer-dependencies=false
39
39
  import {
40
40
  env, loadEnv, // config
41
41
  LoggerModule, LoggerService, // logger
42
- requestLogger, requestOrigin, requestContext, bodyParsers, bodyParserReason, // uma linha por request
42
+ requestLogger, requestOrigin, requestContext, noteRequest, bodyParsers, bodyParserReason, // uma linha por request
43
43
  AbstractExceptionFilter, ExceptionsFilter, rememberError, // filtros (motivo do erro para o log)
44
- RabbitModule, EventsPublisher, rabbitConsumerOptions, OnRabbitEvent, ackMessage, nackMessage,
45
- WebhookEvent, WebhookPatterns, WebhookPattern, AliexpressOrderStatus, createWebhookEvent, toPattern,
44
+ RabbitModule, EventsPublisher, EventsConsumer, EventRejectedException, // eventos
45
+ rabbitConsumerOptions, assertConsumerTopology, OnMerkalyEvent,
46
+ MerkalyEvent, MerkalyEventType, MerkalyProducer, EventDataMap, createMerkalyEvent, AliexpressOrderStatus,
46
47
  SignatureGuard, safeCompare, hmacDigest, verifyHmac,
47
48
  } from '@merkaly/nest';
48
49
  ```
@@ -54,9 +55,8 @@ import {
54
55
  imports: [
55
56
  LoggerModule.forRoot({ service: 'webhooks.merkaly.io' }), // global
56
57
  RabbitModule.forRoot({
57
- url: env.string('RABBITMQ_URL'), // sem url: publish rejeita com 503
58
- queue: env.string('RABBITMQ_QUEUE', 'api.merkaly.io-queue'), // <repo>-queue
59
- routes: { [WebhookPatterns.asaas.PAYMENT_CONFIRMED]: 'payments.merkaly.io-queue' }, // opcional
58
+ url: env.string('RABBITMQ_URL'), // sem url: emit rejeita com 503
59
+ producer: MerkalyProducer.WEBHOOKS, // vai em todo evento
60
60
  timeout: env.number('RABBITMQ_PUBLISH_TIMEOUT', 5000),
61
61
  required: env.isProduction, // produção não sobe sem url
62
62
  }),
@@ -68,7 +68,7 @@ export class AppModule {}
68
68
 
69
69
  `RabbitModule.forRootAsync({ imports, inject, useFactory })` aceita a config injetada.
70
70
 
71
- **Filas**: o nome é o do repositório do serviço que consome, `<repo>-queue` (ex.: `api.merkaly.io-queue`, `payments.merkaly.io-queue`). O pacote não tem fila padrão: cada app passa a sua.
71
+ **Exchange e filas**: o produtor publica no exchange topic `merkaly.events` com o tipo do evento como routing key e nunca conhece uma fila. Cada consumidor declara a sua fila, com o nome do repositório (`api.merkaly.io`, `payments.merkaly.io`), e o Nest a liga a cada tipo que ele escuta. Um consumidor novo não toca no produtor.
72
72
 
73
73
  **Health check**: o pacote não registra controllers. O Swagger servido em `/` responde 200 e funciona como health check.
74
74
 
@@ -89,16 +89,17 @@ Error: boom
89
89
 
90
90
  - **ORIGIN** (24): quem fez o request, nas linhas de request e nos logs internos emitidos durante ele: `x-merkaly-host`, senão o host do `Origin`, `cf-connecting-ip`, o primeiro `x-forwarded-for` ou o socket (`requestOrigin`). Fora de um request, o `service`.
91
91
  - **SCOPE** (22): contexto de negócio definido pelo app (`requestLogger({ scope })` ou `LoggerModule({ scope })`, ex.: id da organização). Nas linhas de request é resolvido ao terminar a resposta, então pega o que os guards definiram.
92
- - **KIND** (16): método HTTP nas linhas de request; nos logs internos, o `group` de quem loga.
92
+ - **KIND** (16): método HTTP nas linhas de request; nos logs internos, o `group` de quem loga. Com `requestLogger({ kind })` a linha de request usa esse valor (ex.: o evento do webhook) e o método passa a abrir a mensagem: `PAYMENT_RECEIVED POST /asaas 200 44ms`.
93
93
  - **MESSAGE**: `/path STATUS Nms "motivo"` (motivo cortado em 200, sem query, body nem headers) ou o texto. Nível: `ERROR` em 5xx com o stack embaixo, `WARN` em 4xx, `LOG` no resto.
94
94
 
95
95
  ```ts
96
96
  // main.ts
97
- app.use(requestLogger(app)); // antes do Swagger; opcional { scope: (req) => 'stripe' }
97
+ app.use(requestLogger(app)); // antes do Swagger; opcional { scope: (req) => 'stripe', kind: (req) => req.body?.event }
98
98
  bodyParsers(app, { raw: ['/webhook/stripe'] }); // json + urlencoded com motivo seguro ("Invalid JSON body")
99
99
  SwaggerModule.setup('/', app, document);
100
100
  ```
101
101
 
102
+ - `noteRequest(texto, nível?)` soma um resultado ao fim da linha do request (`-> payment.received`, `"discarded"`) em vez de uma linha separada; o nível só sobe (`warn`/`error`). Fora de um request devolve `false`. O `EventsPublisher` usa isso: dentro de um request o `emit` vira `-> <tipo>` na linha do request, fora dele continua com linha própria.
102
103
  - O ORIGIN do request fica num `AsyncLocalStorage` do pacote (`requestContext`). O `requestLogger` cria o contexto e o `LoggerModule` registra um middleware que volta a entrar nele depois dos body parsers (que podem perder o contexto assíncrono), igual ao `nestjs-cls`.
103
104
  - `bodyParsers(app, { raw?, rawBody? })`: `raw` mantém o body como Buffer nesses paths; `rawBody: true` preenche `req.rawBody` (igual ao `NestFactory.create(..., { rawBody: true })`, que deixa de registrar os próprios parsers).
104
105
  - SCOPE com CLS, sem o pacote depender de `nestjs-cls`:
@@ -114,32 +115,51 @@ LoggerModule.forRootAsync({
114
115
 
115
116
  ### Eventos
116
117
 
117
- Envelope `WebhookEvent<T>`: `{ id, source, type, receivedAt, organization?, data }`. Padrão do nome: `<source>.<evento>` em minúsculas (`toPattern('asaas', 'PAYMENT_CONFIRMED')` = `asaas.payment_confirmed`). Os padrões conhecidos ficam em `WebhookPatterns` e são a fonte única para produtores e consumidores, iguais ao que o webhooks publica:
118
-
119
- - Asaas: `asaas.<evento em minúsculas>` (ex.: `asaas.payment_received`).
120
- - Stripe: tipo pontuado original (ex.: `stripe.invoice.paid`, `stripe.customer.subscription.updated`).
121
- - Auth0: nome legível do código de log (ex.: `auth0.success_signup`, `auth0.success_login`, `auth0.user_deleted`) e `auth0.user_login` da Action.
122
- - AliExpress: tópico do `message_type` (53 `aliexpress.dropshipper_order_status_update`, 18 `aliexpress.waybill_changed`, 15 `aliexpress.product_deleted`); status do pedido DS em `AliexpressOrderStatus`.
118
+ Contrato completo em [`docs/events.md`](docs/events.md). Eventos de negócio normalizados (`payment.received`, nunca `asaas.payment_received`): se o provedor muda, o consumidor não muda.
123
119
 
124
120
  ```ts
125
- await this.events.publish(
126
- WebhookPatterns.asaas.PAYMENT_CONFIRMED,
127
- createWebhookEvent({ id: body.id, source: 'asaas', type: body.event, data: body.payment }),
128
- ); // resolve só com ack do broker; timeout ou falha: ServiceUnavailableException
121
+ // produtor: data tipado pelo tipo; resolve com o evento ou ServiceUnavailableException (o provedor reintenta)
122
+ await this.events.emit(MerkalyEventType.PAYMENT_RECEIVED, { ...paymentRef, receivedAt }, {
123
+ id: `asaas:${body.id}`, source: 'asaas', organization, occurredAt, raw: { type: body.event, payload },
124
+ });
129
125
  ```
130
126
 
131
- Consumidor (quando o api ou o payments passarem a consumir):
127
+ Consumidor:
132
128
 
133
129
  ```ts
134
- app.connectMicroservice(rabbitConsumerOptions({ url, queue: 'api.merkaly.io-queue' }));
130
+ // main.ts
131
+ const consumer = { url, queue: 'api.merkaly.io' };
132
+ await assertConsumerTopology(consumer); // cria .retry e .dlq
133
+ app.connectMicroservice(rabbitConsumerOptions(consumer));
135
134
  await app.startAllMicroservices();
136
135
 
137
- @OnRabbitEvent(WebhookPatterns.asaas.PAYMENT_CONFIRMED)
138
- handle(@Payload() event: WebhookEvent, @Ctx() context: RmqContext) {
139
- return this.service.confirm(event).then(() => ackMessage(context));
136
+ // módulo: `around` roda cada mensagem dentro do contexto do app (ex.: CLS com a organização)
137
+ RabbitModule.forConsumerAsync({
138
+ inject: [ClsService],
139
+ useFactory: (cls: ClsService) => ({
140
+ queue: 'api.merkaly.io',
141
+ around: (event, next) => cls.run(() => { cls.set('identity', event.organization); return next(); }),
142
+ }),
143
+ });
144
+
145
+ // handler
146
+ @OnMerkalyEvent(MerkalyEventType.PAYMENT_RECEIVED)
147
+ received(@Payload() event: MerkalyEvent<MerkalyEventType.PAYMENT_RECEIVED>, @Ctx() context: RmqContext) {
148
+ return this.consumer.handle(event, context, (e) => this.payments.refresh(e.data.saleId));
140
149
  }
141
150
  ```
142
151
 
152
+ `@OnMerkalyEvent(...types)` aceita vários tipos no mesmo handler. `EventsConsumer.handle` fecha a mensagem e loga uma linha (`ORIGIN` = provedor, `SCOPE` = organização, `KIND` = `EVENT`):
153
+
154
+ | Resultado | O que acontece |
155
+ | --- | --- |
156
+ | Sucesso | `ack`. |
157
+ | `EventRejectedException` (venda inexistente, valor diferente...) | `ack` + `WARN`: reintentar não resolve. |
158
+ | Outro erro (Mongo, rede, provedor fora) | Vai para `<fila>.retry` (30 s) e volta; depois de 5 tentativas, `<fila>.dlq`. |
159
+ | Malformado ou `version` maior que a do handler | `<fila>.dlq` direto. |
160
+
161
+ A fila principal descarta para a `.dlq` o que ficar 7 dias sem consumo ou passar de 10.000 mensagens.
162
+
143
163
  ### Env
144
164
 
145
165
  `env.string`, `env.number`, `env.boolean`, `env.list`, `env.required`, `env.isProduction`. Carrega o `.env` (dotenv) no primeiro acesso. Valor obrigatório ausente ou inválido: **erro em produção**, aviso no log fora dela.
@@ -0,0 +1,10 @@
1
+ export declare const AliexpressOrderStatus: {
2
+ readonly CREATED: "OrderCreated";
3
+ readonly PAYMENT_AUTHORIZED: "PaymentAuthorized";
4
+ readonly PAYMENT_FAILED: "paymentFailedEvent";
5
+ readonly SHIPPED: "OrderShipped";
6
+ readonly CONFIRMED: "OrderConfirmed";
7
+ readonly CLOSED: "OrderClosed";
8
+ };
9
+ export type AliexpressOrderStatus = (typeof AliexpressOrderStatus)[keyof typeof AliexpressOrderStatus];
10
+ //# sourceMappingURL=aliexpress.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aliexpress.d.ts","sourceRoot":"","sources":["../../src/events/aliexpress.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,qBAAqB;;;;;;;CAOxB,CAAC;AAEX,MAAM,MAAM,qBAAqB,GAC/B,CAAC,OAAO,qBAAqB,CAAC,CAAC,MAAM,OAAO,qBAAqB,CAAC,CAAC"}
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AliexpressOrderStatus = void 0;
4
+ exports.AliexpressOrderStatus = {
5
+ CREATED: 'OrderCreated',
6
+ PAYMENT_AUTHORIZED: 'PaymentAuthorized',
7
+ PAYMENT_FAILED: 'paymentFailedEvent',
8
+ SHIPPED: 'OrderShipped',
9
+ CONFIRMED: 'OrderConfirmed',
10
+ CLOSED: 'OrderClosed',
11
+ };
12
+ //# sourceMappingURL=aliexpress.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aliexpress.js","sourceRoot":"","sources":["../../src/events/aliexpress.ts"],"names":[],"mappings":";;;AACa,QAAA,qBAAqB,GAAG;IACnC,OAAO,EAAE,cAAc;IACvB,kBAAkB,EAAE,mBAAmB;IACvC,cAAc,EAAE,oBAAoB;IACpC,OAAO,EAAE,cAAc;IACvB,SAAS,EAAE,gBAAgB;IAC3B,MAAM,EAAE,aAAa;CACb,CAAC"}
@@ -1,3 +1,3 @@
1
- export * from './webhook-event';
2
- export * from './webhook-patterns';
1
+ export * from './aliexpress';
2
+ export * from './merkaly-event';
3
3
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/events/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,cAAc,oBAAoB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/events/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC"}
@@ -14,6 +14,6 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
- __exportStar(require("./webhook-event"), exports);
18
- __exportStar(require("./webhook-patterns"), exports);
17
+ __exportStar(require("./aliexpress"), exports);
18
+ __exportStar(require("./merkaly-event"), exports);
19
19
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/events/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,kDAAgC;AAChC,qDAAmC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/events/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,+CAA6B;AAC7B,kDAAgC"}
@@ -0,0 +1,96 @@
1
+ export declare enum MerkalyEventType {
2
+ PAYMENT_CONFIRMED = "payment.confirmed",
3
+ PAYMENT_RECEIVED = "payment.received",
4
+ PAYMENT_FAILED = "payment.failed",
5
+ PAYMENT_OVERDUE = "payment.overdue",
6
+ PAYMENT_CANCELLED = "payment.cancelled",
7
+ PAYMENT_REFUNDED = "payment.refunded",
8
+ PAYMENT_CHARGEBACK_OPENED = "payment.chargeback.opened",
9
+ PAYMENT_CHARGEBACK_UPDATED = "payment.chargeback.updated",
10
+ PAYMENT_ACCOUNT_APPROVED = "payment.account.approved",
11
+ PAYMENT_ACCOUNT_REJECTED = "payment.account.rejected",
12
+ PAYMENT_ACCOUNT_STATUS_CHANGED = "payment.account.status_changed"
13
+ }
14
+ export declare enum MerkalyProducer {
15
+ WEBHOOKS = "webhooks.merkaly.io",
16
+ API = "api.merkaly.io",
17
+ PAYMENTS = "payments.merkaly.io"
18
+ }
19
+ export type MerkalyEventSource = 'asaas' | 'stripe' | 'auth0' | 'aliexpress';
20
+ export type PaymentProvider = 'asaas';
21
+ export type PaymentMethod = 'PIX' | 'CARD' | 'BOLETO';
22
+ export interface PaymentRef {
23
+ provider: PaymentProvider;
24
+ paymentId: string;
25
+ saleId: string | null;
26
+ method: PaymentMethod;
27
+ amount: number;
28
+ netAmount: number | null;
29
+ }
30
+ export type PaymentAccountSection = 'general' | 'commercialInfo' | 'documentation' | 'bankAccountInfo';
31
+ export type PaymentAccountStatus = 'PENDING' | 'AWAITING_APPROVAL' | 'APPROVED' | 'REJECTED' | 'EXPIRING_SOON' | 'EXPIRED';
32
+ export interface PaymentAccountRef {
33
+ provider: PaymentProvider;
34
+ accountId: string;
35
+ }
36
+ export interface EventDataMap {
37
+ [MerkalyEventType.PAYMENT_CONFIRMED]: PaymentRef & {
38
+ confirmedAt: string;
39
+ };
40
+ [MerkalyEventType.PAYMENT_RECEIVED]: PaymentRef & {
41
+ receivedAt: string;
42
+ };
43
+ [MerkalyEventType.PAYMENT_FAILED]: PaymentRef & {
44
+ reason: 'card_refused' | 'risk_rejected';
45
+ };
46
+ [MerkalyEventType.PAYMENT_OVERDUE]: PaymentRef & {
47
+ dueDate: string;
48
+ };
49
+ [MerkalyEventType.PAYMENT_CANCELLED]: PaymentRef;
50
+ [MerkalyEventType.PAYMENT_REFUNDED]: PaymentRef & {
51
+ refundedAmount: number;
52
+ partial: boolean;
53
+ };
54
+ [MerkalyEventType.PAYMENT_CHARGEBACK_OPENED]: PaymentRef & {
55
+ reason: string | null;
56
+ };
57
+ [MerkalyEventType.PAYMENT_CHARGEBACK_UPDATED]: PaymentRef & {
58
+ stage: 'disputed' | 'awaiting_reversal';
59
+ };
60
+ [MerkalyEventType.PAYMENT_ACCOUNT_APPROVED]: PaymentAccountRef;
61
+ [MerkalyEventType.PAYMENT_ACCOUNT_REJECTED]: PaymentAccountRef;
62
+ [MerkalyEventType.PAYMENT_ACCOUNT_STATUS_CHANGED]: PaymentAccountRef & {
63
+ section: PaymentAccountSection;
64
+ status: PaymentAccountStatus;
65
+ };
66
+ }
67
+ export interface MerkalyRawEvent {
68
+ type: string;
69
+ payload?: unknown;
70
+ truncated?: boolean;
71
+ }
72
+ export interface MerkalyEvent<K extends MerkalyEventType = MerkalyEventType> {
73
+ id: string;
74
+ type: K;
75
+ version: number;
76
+ producer: MerkalyProducer;
77
+ source?: MerkalyEventSource;
78
+ organization?: string;
79
+ occurredAt: string;
80
+ publishedAt: string;
81
+ correlationId?: string;
82
+ data: EventDataMap[K];
83
+ raw?: MerkalyRawEvent;
84
+ }
85
+ export interface MerkalyEventMeta {
86
+ id?: string;
87
+ version?: number;
88
+ source?: MerkalyEventSource;
89
+ organization?: string;
90
+ occurredAt?: string;
91
+ correlationId?: string;
92
+ raw?: MerkalyRawEvent;
93
+ }
94
+ export declare const RAW_PAYLOAD_LIMIT: number;
95
+ export declare function createMerkalyEvent<K extends MerkalyEventType>(producer: MerkalyProducer, type: K, data: EventDataMap[K], meta?: MerkalyEventMeta): MerkalyEvent<K>;
96
+ //# sourceMappingURL=merkaly-event.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"merkaly-event.d.ts","sourceRoot":"","sources":["../../src/events/merkaly-event.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RAW_PAYLOAD_LIMIT = exports.MerkalyProducer = exports.MerkalyEventType = void 0;
4
+ exports.createMerkalyEvent = createMerkalyEvent;
5
+ const crypto_1 = require("crypto");
6
+ var MerkalyEventType;
7
+ (function (MerkalyEventType) {
8
+ MerkalyEventType["PAYMENT_CONFIRMED"] = "payment.confirmed";
9
+ MerkalyEventType["PAYMENT_RECEIVED"] = "payment.received";
10
+ MerkalyEventType["PAYMENT_FAILED"] = "payment.failed";
11
+ MerkalyEventType["PAYMENT_OVERDUE"] = "payment.overdue";
12
+ MerkalyEventType["PAYMENT_CANCELLED"] = "payment.cancelled";
13
+ MerkalyEventType["PAYMENT_REFUNDED"] = "payment.refunded";
14
+ MerkalyEventType["PAYMENT_CHARGEBACK_OPENED"] = "payment.chargeback.opened";
15
+ MerkalyEventType["PAYMENT_CHARGEBACK_UPDATED"] = "payment.chargeback.updated";
16
+ MerkalyEventType["PAYMENT_ACCOUNT_APPROVED"] = "payment.account.approved";
17
+ MerkalyEventType["PAYMENT_ACCOUNT_REJECTED"] = "payment.account.rejected";
18
+ MerkalyEventType["PAYMENT_ACCOUNT_STATUS_CHANGED"] = "payment.account.status_changed";
19
+ })(MerkalyEventType || (exports.MerkalyEventType = MerkalyEventType = {}));
20
+ var MerkalyProducer;
21
+ (function (MerkalyProducer) {
22
+ MerkalyProducer["WEBHOOKS"] = "webhooks.merkaly.io";
23
+ MerkalyProducer["API"] = "api.merkaly.io";
24
+ MerkalyProducer["PAYMENTS"] = "payments.merkaly.io";
25
+ })(MerkalyProducer || (exports.MerkalyProducer = MerkalyProducer = {}));
26
+ exports.RAW_PAYLOAD_LIMIT = 16 * 1024;
27
+ function capRaw(raw) {
28
+ if (!raw?.payload)
29
+ return raw;
30
+ const size = Buffer.byteLength(JSON.stringify(raw.payload) ?? '');
31
+ return size > exports.RAW_PAYLOAD_LIMIT ? { type: raw.type, truncated: true } : raw;
32
+ }
33
+ function createMerkalyEvent(producer, type, data, meta = {}) {
34
+ const publishedAt = new Date().toISOString();
35
+ return {
36
+ id: meta.id ?? (0, crypto_1.randomUUID)(),
37
+ type,
38
+ version: meta.version ?? 1,
39
+ producer,
40
+ source: meta.source,
41
+ organization: meta.organization || undefined,
42
+ occurredAt: meta.occurredAt ?? publishedAt,
43
+ publishedAt,
44
+ correlationId: meta.correlationId,
45
+ data,
46
+ raw: capRaw(meta.raw),
47
+ };
48
+ }
49
+ //# sourceMappingURL=merkaly-event.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"merkaly-event.js","sourceRoot":"","sources":["../../src/events/merkaly-event.ts"],"names":[],"mappings":";;;AA6GA,gDAqBC;AAlID,mCAAoC;AAGpC,IAAY,gBAYX;AAZD,WAAY,gBAAgB;IAC1B,2DAAuC,CAAA;IACvC,yDAAqC,CAAA;IACrC,qDAAiC,CAAA;IACjC,uDAAmC,CAAA;IACnC,2DAAuC,CAAA;IACvC,yDAAqC,CAAA;IACrC,2EAAuD,CAAA;IACvD,6EAAyD,CAAA;IACzD,yEAAqD,CAAA;IACrD,yEAAqD,CAAA;IACrD,qFAAiE,CAAA;AACnE,CAAC,EAZW,gBAAgB,gCAAhB,gBAAgB,QAY3B;AAED,IAAY,eAIX;AAJD,WAAY,eAAe;IACzB,mDAAgC,CAAA;IAChC,yCAAsB,CAAA;IACtB,mDAAgC,CAAA;AAClC,CAAC,EAJW,eAAe,+BAAf,eAAe,QAI1B;AA6EY,QAAA,iBAAiB,GAAG,EAAE,GAAG,IAAI,CAAC;AAG3C,SAAS,MAAM,CAAC,GAAqB;IACnC,IAAI,CAAC,GAAG,EAAE,OAAO;QAAE,OAAO,GAAG,CAAC;IAE9B,MAAM,IAAI,GAAG,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;IAElE,OAAO,IAAI,GAAG,yBAAiB,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC;AAC9E,CAAC;AAED,SAAgB,kBAAkB,CAChC,QAAyB,EACzB,IAAO,EACP,IAAqB,EACrB,OAAyB,EAAE;IAE3B,MAAM,WAAW,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAE7C,OAAO;QACL,EAAE,EAAE,IAAI,CAAC,EAAE,IAAI,IAAA,mBAAU,GAAE;QAC3B,IAAI;QACJ,OAAO,EAAE,IAAI,CAAC,OAAO,IAAI,CAAC;QAC1B,QAAQ;QACR,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,YAAY,EAAE,IAAI,CAAC,YAAY,IAAI,SAAS;QAC5C,UAAU,EAAE,IAAI,CAAC,UAAU,IAAI,WAAW;QAC1C,WAAW;QACX,aAAa,EAAE,IAAI,CAAC,aAAa;QACjC,IAAI;QACJ,GAAG,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC;KACtB,CAAC;AACJ,CAAC"}
@@ -6,6 +6,7 @@ export interface RequestLogLocals {
6
6
  }
7
7
  export interface RequestLoggerOptions {
8
8
  scope?: (request: Request) => string | undefined;
9
+ kind?: (request: Request) => string | undefined;
9
10
  }
10
11
  export declare function requestLogger(app: INestApplicationContext, options?: RequestLoggerOptions): RequestHandler;
11
12
  //# sourceMappingURL=request-logger.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"request-logger.d.ts","sourceRoot":"","sources":["../../src/http/request-logger.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC;AAC9D,OAAO,KAAK,EAAgB,OAAO,EAAE,cAAc,EAAY,MAAM,SAAS,CAAC;AAE/E,MAAM,WAAW,gBAAgB;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,oBAAoB;IACnC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,MAAM,GAAG,SAAS,CAAC;CAClD;AAQD,wBAAgB,aAAa,CAC3B,GAAG,EAAE,uBAAuB,EAC5B,OAAO,GAAE,oBAAyB,GACjC,cAAc,CA8BhB"}
1
+ {"version":3,"file":"request-logger.d.ts","sourceRoot":"","sources":["../../src/http/request-logger.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC;AAC9D,OAAO,KAAK,EAAgB,OAAO,EAAE,cAAc,EAAY,MAAM,SAAS,CAAC;AAE/E,MAAM,WAAW,gBAAgB;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,oBAAoB;IACnC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,MAAM,GAAG,SAAS,CAAC;IACjD,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,MAAM,GAAG,SAAS,CAAC;CACjD;AAaD,wBAAgB,aAAa,CAC3B,GAAG,EAAE,uBAAuB,EAC5B,OAAO,GAAE,oBAAyB,GACjC,cAAc,CAuChB"}
@@ -2,6 +2,8 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.requestLogger = requestLogger;
4
4
  const logger_1 = require("../logger");
5
+ const LEVELS = ['log', 'warn', 'error'];
6
+ const higher = (a, b) => LEVELS.indexOf(a) >= LEVELS.indexOf(b) ? a : b;
5
7
  const MAX_REASON = 200;
6
8
  const capped = (reason) => reason.length > MAX_REASON ? `${reason.slice(0, MAX_REASON)}…` : reason;
7
9
  function requestLogger(app, options = {}) {
@@ -15,12 +17,21 @@ function requestLogger(app, options = {}) {
15
17
  const ms = Number(process.hrtime.bigint() - started) / 1e6;
16
18
  const path = request.originalUrl.split('?')[0];
17
19
  const { error, trace } = response.locals;
18
- const line = `${path} ${response.statusCode} ${ms.toFixed(0)}ms`;
19
- const message = error ? `${line} ${JSON.stringify(capped(error))}` : line;
20
- const tier = response.statusCode >= 500 ? 'error' : response.statusCode >= 400 ? 'warn' : 'log';
20
+ const kind = options.kind?.(request);
21
+ const target = kind ? `${request.method} ${path}` : path;
22
+ const notes = context.notes ?? [];
23
+ const line = [
24
+ `${target} ${response.statusCode} ${ms.toFixed(0)}ms`,
25
+ error ? JSON.stringify(capped(error)) : '',
26
+ ...notes.map((note) => note.text),
27
+ ]
28
+ .filter(Boolean)
29
+ .join(' ');
30
+ const status = response.statusCode >= 500 ? 'error' : response.statusCode >= 400 ? 'warn' : 'log';
31
+ const tier = notes.reduce((level, note) => higher(level, note.level), status);
21
32
  logger_1.requestContext.run(context, () => logger.request(tier, {
22
- method: request.method,
23
- message,
33
+ method: kind || request.method,
34
+ message: line,
24
35
  trace: tier === 'error' ? trace : undefined,
25
36
  }));
26
37
  });
@@ -1 +1 @@
1
- {"version":3,"file":"request-logger.js","sourceRoot":"","sources":["../../src/http/request-logger.ts"],"names":[],"mappings":";;AAmBA,sCAiCC;AApDD,sCAAqE;AAarE,MAAM,UAAU,GAAG,GAAG,CAAC;AAEvB,MAAM,MAAM,GAAG,CAAC,MAAc,EAAE,EAAE,CAChC,MAAM,CAAC,MAAM,GAAG,UAAU,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC;AAG1E,SAAgB,aAAa,CAC3B,GAA4B,EAC5B,UAAgC,EAAE;IAElC,MAAM,MAAM,GAAG,GAAG,CAAC,GAAG,CAAC,sBAAa,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;IAEzD,OAAO,CAAC,OAAgB,EAAE,QAAkB,EAAE,IAAkB,EAAE,EAAE;QAClE,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;QACxC,MAAM,OAAO,GAAG,IAAA,kBAAS,EAAC,OAAO,CAAC,CAAC;QAEnC,IAAI,OAAO,CAAC,KAAK;YAAE,OAAO,CAAC,KAAK,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC,OAAO,CAAC,CAAC;QAElE,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE;YACzB,MAAM,EAAE,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,OAAO,CAAC,GAAG,GAAG,CAAC;YAC3D,MAAM,IAAI,GAAG,OAAO,CAAC,WAAW,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;YAC/C,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,QAAQ,CAAC,MAA0B,CAAC;YAC7D,MAAM,IAAI,GAAG,GAAG,IAAI,IAAI,QAAQ,CAAC,UAAU,IAAI,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;YACjE,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,IAAI,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;YAC1E,MAAM,IAAI,GACR,QAAQ,CAAC,UAAU,IAAI,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,UAAU,IAAI,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC;YAGrF,uBAAc,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,EAAE,CAC/B,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE;gBACnB,MAAM,EAAE,OAAO,CAAC,MAAM;gBACtB,OAAO;gBACP,KAAK,EAAE,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS;aAC5C,CAAC,CACH,CAAC;QACJ,CAAC,CAAC,CAAC;QAEH,uBAAc,CAAC,GAAG,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IACpC,CAAC,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"request-logger.js","sourceRoot":"","sources":["../../src/http/request-logger.ts"],"names":[],"mappings":"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"}
@@ -1,11 +1,18 @@
1
1
  import { AsyncLocalStorage } from 'async_hooks';
2
2
  import { NestMiddleware } from '@nestjs/common';
3
3
  import type { NextFunction, Request, Response } from 'express';
4
+ export type RequestNoteLevel = 'log' | 'warn' | 'error';
5
+ export interface RequestNote {
6
+ text: string;
7
+ level: RequestNoteLevel;
8
+ }
4
9
  export interface RequestContext {
5
10
  origin: string;
6
11
  scope?: () => string | undefined;
12
+ notes?: RequestNote[];
7
13
  }
8
14
  export declare const requestContext: AsyncLocalStorage<RequestContext>;
15
+ export declare function noteRequest(text: string, level?: RequestNoteLevel): boolean;
9
16
  export declare function requestOrigin(request: Request): string;
10
17
  export declare function contextOf(request: Request): RequestContext;
11
18
  export declare class RequestContextMiddleware implements NestMiddleware {
@@ -1 +1 @@
1
- {"version":3,"file":"request-context.d.ts","sourceRoot":"","sources":["../../src/logger/request-context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAc,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAC5D,OAAO,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAE/D,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,MAAM,GAAG,SAAS,CAAC;CAClC;AAED,eAAO,MAAM,cAAc,mCAA0C,CAAC;AAStE,wBAAgB,aAAa,CAAC,OAAO,EAAE,OAAO,UAS7C;AAGD,wBAAgB,SAAS,CAAC,OAAO,EAAE,OAAO,GAAG,cAAc,CAI1D;AAGD,qBACa,wBAAyB,YAAW,cAAc;IAC7D,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY;CAGtD"}
1
+ {"version":3,"file":"request-context.d.ts","sourceRoot":"","sources":["../../src/logger/request-context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAc,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAC5D,OAAO,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAE/D,MAAM,MAAM,gBAAgB,GAAG,KAAK,GAAG,MAAM,GAAG,OAAO,CAAC;AAExD,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,gBAAgB,CAAC;CACzB;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,MAAM,GAAG,SAAS,CAAC;IACjC,KAAK,CAAC,EAAE,WAAW,EAAE,CAAC;CACvB;AAED,eAAO,MAAM,cAAc,mCAA0C,CAAC;AAGtE,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,GAAE,gBAAwB,WAQxE;AASD,wBAAgB,aAAa,CAAC,OAAO,EAAE,OAAO,UAS7C;AAGD,wBAAgB,SAAS,CAAC,OAAO,EAAE,OAAO,GAAG,cAAc,CAI1D;AAGD,qBACa,wBAAyB,YAAW,cAAc;IAC7D,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY;CAGtD"}
@@ -7,11 +7,19 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
7
7
  };
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
9
  exports.RequestContextMiddleware = exports.requestContext = void 0;
10
+ exports.noteRequest = noteRequest;
10
11
  exports.requestOrigin = requestOrigin;
11
12
  exports.contextOf = contextOf;
12
13
  const async_hooks_1 = require("async_hooks");
13
14
  const common_1 = require("@nestjs/common");
14
15
  exports.requestContext = new async_hooks_1.AsyncLocalStorage();
16
+ function noteRequest(text, level = 'log') {
17
+ const context = exports.requestContext.getStore();
18
+ if (!context)
19
+ return false;
20
+ (context.notes ??= []).push({ level, text });
21
+ return true;
22
+ }
15
23
  const CONTEXT = Symbol('merkaly.request.context');
16
24
  const hostOf = (origin) => origin?.split('/').pop() || undefined;
17
25
  function requestOrigin(request) {
@@ -1 +1 @@
1
- {"version":3,"file":"request-context.js","sourceRoot":"","sources":["../../src/logger/request-context.ts"],"names":[],"mappings":";;;;;;;;;AAkBA,sCASC;AAGD,8BAIC;AAlCD,6CAAgD;AAChD,2CAA4D;AAQ/C,QAAA,cAAc,GAAG,IAAI,+BAAiB,EAAkB,CAAC;AAEtE,MAAM,OAAO,GAAG,MAAM,CAAC,yBAAyB,CAAC,CAAC;AAIlD,MAAM,MAAM,GAAG,CAAC,MAAe,EAAE,EAAE,CAAC,MAAM,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,IAAI,SAAS,CAAC;AAG1E,SAAgB,aAAa,CAAC,OAAgB;IAC5C,OAAO,CACL,OAAO,CAAC,MAAM,CAAC,gBAAgB,CAAC;QAChC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAChC,OAAO,CAAC,MAAM,CAAC,kBAAkB,CAAC;QAClC,OAAO,CAAC,MAAM,CAAC,iBAAiB,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE;QACvD,OAAO,CAAC,MAAM,EAAE,aAAa,EAAE,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC;QACtD,SAAS,CACV,CAAC;AACJ,CAAC;AAGD,SAAgB,SAAS,CAAC,OAAgB;IACxC,MAAM,GAAG,GAAG,OAAyB,CAAC;IAEtC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,aAAa,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;AAC/D,CAAC;AAIM,IAAM,wBAAwB,GAA9B,MAAM,wBAAwB;IACnC,GAAG,CAAC,OAAgB,EAAE,CAAW,EAAE,IAAkB;QACnD,sBAAc,CAAC,GAAG,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC,CAAC;IAC/C,CAAC;CACF,CAAA;AAJY,4DAAwB;mCAAxB,wBAAwB;IADpC,IAAA,mBAAU,GAAE;GACA,wBAAwB,CAIpC"}
1
+ {"version":3,"file":"request-context.js","sourceRoot":"","sources":["../../src/logger/request-context.ts"],"names":[],"mappings":";;;;;;;;;AAoBA,kCAQC;AASD,sCASC;AAGD,8BAIC;AArDD,6CAAgD;AAChD,2CAA4D;AAgB/C,QAAA,cAAc,GAAG,IAAI,+BAAiB,EAAkB,CAAC;AAGtE,SAAgB,WAAW,CAAC,IAAY,EAAE,QAA0B,KAAK;IACvE,MAAM,OAAO,GAAG,sBAAc,CAAC,QAAQ,EAAE,CAAC;IAE1C,IAAI,CAAC,OAAO;QAAE,OAAO,KAAK,CAAC;IAE3B,CAAC,OAAO,CAAC,KAAK,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IAE7C,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,OAAO,GAAG,MAAM,CAAC,yBAAyB,CAAC,CAAC;AAIlD,MAAM,MAAM,GAAG,CAAC,MAAe,EAAE,EAAE,CAAC,MAAM,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,IAAI,SAAS,CAAC;AAG1E,SAAgB,aAAa,CAAC,OAAgB;IAC5C,OAAO,CACL,OAAO,CAAC,MAAM,CAAC,gBAAgB,CAAC;QAChC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAChC,OAAO,CAAC,MAAM,CAAC,kBAAkB,CAAC;QAClC,OAAO,CAAC,MAAM,CAAC,iBAAiB,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE;QACvD,OAAO,CAAC,MAAM,EAAE,aAAa,EAAE,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC;QACtD,SAAS,CACV,CAAC;AACJ,CAAC;AAGD,SAAgB,SAAS,CAAC,OAAgB;IACxC,MAAM,GAAG,GAAG,OAAyB,CAAC;IAEtC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,aAAa,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;AAC/D,CAAC;AAIM,IAAM,wBAAwB,GAA9B,MAAM,wBAAwB;IACnC,GAAG,CAAC,OAAgB,EAAE,CAAW,EAAE,IAAkB;QACnD,sBAAc,CAAC,GAAG,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC,CAAC;IAC/C,CAAC;CACF,CAAA;AAJY,4DAAwB;mCAAxB,wBAAwB;IADpC,IAAA,mBAAU,GAAE;GACA,wBAAwB,CAIpC"}
@@ -0,0 +1,19 @@
1
+ import { RmqContext } from '@nestjs/microservices';
2
+ import { MerkalyEvent, MerkalyEventType } from '../events';
3
+ import { LoggerService } from '../logger';
4
+ import { EventsConsumerOptions } from './rabbit.options';
5
+ export declare class EventRejectedException extends Error {
6
+ }
7
+ export interface HandleOptions {
8
+ version?: number;
9
+ }
10
+ export declare class EventsConsumer {
11
+ private readonly options;
12
+ private readonly logger;
13
+ constructor(options: EventsConsumerOptions, logger?: LoggerService);
14
+ handle<K extends MerkalyEventType>(event: MerkalyEvent<K>, context: RmqContext, handler: (event: MerkalyEvent<K>) => Promise<unknown>, { version }?: HandleOptions): Promise<void>;
15
+ private ack;
16
+ private deadLetter;
17
+ private retry;
18
+ }
19
+ //# sourceMappingURL=events.consumer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events.consumer.d.ts","sourceRoot":"","sources":["../../src/rabbit/events.consumer.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACnD,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAkB,MAAM,WAAW,CAAC;AAG1D,OAAO,EAAqC,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AAM5F,qBAAa,sBAAuB,SAAQ,KAAK;CAAG;AAEpD,MAAM,WAAW,aAAa;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,qBACa,cAAc;IAIG,OAAO,CAAC,QAAQ,CAAC,OAAO;IAHpD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAgB;gBAGM,OAAO,EAAE,qBAAqB,EAC7D,MAAM,CAAC,EAAE,aAAa;IAM7B,MAAM,CAAC,CAAC,SAAS,gBAAgB,EACtC,KAAK,EAAE,YAAY,CAAC,CAAC,CAAC,EACtB,OAAO,EAAE,UAAU,EACnB,OAAO,EAAE,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,OAAO,CAAC,EACrD,EAAE,OAAW,EAAE,GAAE,aAAkB,GAClC,OAAO,CAAC,IAAI,CAAC;IAoChB,OAAO,CAAC,GAAG;IAKX,OAAO,CAAC,UAAU;IASlB,OAAO,CAAC,KAAK;CAiBd"}
@@ -0,0 +1,96 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
12
+ return function (target, key) { decorator(target, key, paramIndex); }
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.EventsConsumer = exports.EventRejectedException = void 0;
16
+ const common_1 = require("@nestjs/common");
17
+ const logger_1 = require("../logger");
18
+ const events_publisher_1 = require("./events.publisher");
19
+ const rabbit_consumer_1 = require("./rabbit.consumer");
20
+ const rabbit_options_1 = require("./rabbit.options");
21
+ const GROUP = 'EVENT';
22
+ const RETRIES_HEADER = 'x-merkaly-retries';
23
+ class EventRejectedException extends Error {
24
+ }
25
+ exports.EventRejectedException = EventRejectedException;
26
+ let EventsConsumer = class EventsConsumer {
27
+ options;
28
+ logger;
29
+ constructor(options, logger) {
30
+ this.options = options;
31
+ this.logger = logger ?? new logger_1.LoggerService();
32
+ }
33
+ handle(event, context, handler, { version = 1 } = {}) {
34
+ const started = Date.now();
35
+ const scope = {
36
+ origin: event?.source ?? event?.producer ?? 'unknown',
37
+ scope: () => event?.organization,
38
+ };
39
+ const line = (text) => `${event?.type} (${event?.id}) ${text} ${Date.now() - started}ms`;
40
+ return logger_1.requestContext.run(scope, async () => {
41
+ if (!event?.id || !event?.type)
42
+ return this.deadLetter(context, line('malformed'));
43
+ if (event.version > version)
44
+ return this.deadLetter(context, line(`unknown version ${event.version}`));
45
+ const next = () => handler(event);
46
+ const run = this.options.around ? () => this.options.around(event, next) : next;
47
+ return Promise.resolve()
48
+ .then(run)
49
+ .then(() => {
50
+ this.ack(context);
51
+ this.logger.log({ group: GROUP, message: line('ok') });
52
+ })
53
+ .catch((error) => {
54
+ if (error instanceof EventRejectedException) {
55
+ this.ack(context);
56
+ return this.logger.warn({ group: GROUP, message: line(`rejected "${error.message}"`) });
57
+ }
58
+ return this.retry(context, line(`failed "${(0, events_publisher_1.describeError)(error)}"`), error);
59
+ });
60
+ });
61
+ }
62
+ ack(context) {
63
+ context.getChannelRef().ack(context.getMessage());
64
+ }
65
+ deadLetter(context, message, trace) {
66
+ context.getChannelRef().nack(context.getMessage(), false, false);
67
+ this.logger.error({
68
+ group: GROUP,
69
+ message: `${message} -> ${(0, rabbit_consumer_1.deadLetterQueue)(this.options.queue)}`,
70
+ trace,
71
+ });
72
+ }
73
+ retry(context, message, trace) {
74
+ const msg = context.getMessage();
75
+ const attempt = Number(msg.properties.headers?.[RETRIES_HEADER] ?? 0) + 1;
76
+ const max = this.options.retries ?? rabbit_options_1.DEFAULT_RETRIES;
77
+ if (attempt > max)
78
+ return this.deadLetter(context, `${message}, ${max} retries`, trace);
79
+ const channel = context.getChannelRef();
80
+ channel.sendToQueue((0, rabbit_consumer_1.retryQueue)(this.options.queue), msg.content, {
81
+ ...msg.properties,
82
+ headers: { ...msg.properties.headers, [RETRIES_HEADER]: attempt },
83
+ persistent: true,
84
+ });
85
+ channel.ack(msg);
86
+ this.logger.error({ group: GROUP, message: `${message}, retry ${attempt}/${max}`, trace });
87
+ }
88
+ };
89
+ exports.EventsConsumer = EventsConsumer;
90
+ exports.EventsConsumer = EventsConsumer = __decorate([
91
+ (0, common_1.Injectable)(),
92
+ __param(0, (0, common_1.Inject)(rabbit_options_1.CONSUMER_OPTIONS)),
93
+ __param(1, (0, common_1.Optional)()),
94
+ __metadata("design:paramtypes", [Object, logger_1.LoggerService])
95
+ ], EventsConsumer);
96
+ //# sourceMappingURL=events.consumer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events.consumer.js","sourceRoot":"","sources":["../../src/rabbit/events.consumer.ts"],"names":[],"mappings":"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"}