@nestjs-transactional/outbox-microservices 1.0.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +71 -36
- package/dist/externalizer/microservices-event-externalizer.d.ts +1 -1
- package/dist/externalizer/microservices-event-externalizer.js +19 -22
- package/dist/externalizer/microservices-event-externalizer.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +3 -9
- package/dist/index.js.map +1 -1
- package/dist/module/outbox-microservices.module.d.ts +1 -1
- package/dist/module/outbox-microservices.module.js +15 -18
- package/dist/module/outbox-microservices.module.js.map +1 -1
- package/dist/types/options.js +1 -4
- package/dist/types/options.js.map +1 -1
- package/package.json +29 -22
package/README.md
CHANGED
|
@@ -10,27 +10,44 @@ Mark an event `@Externalized`, and once its local handlers have
|
|
|
10
10
|
completed, [`@nestjs-transactional/outbox`](https://www.npmjs.com/package/@nestjs-transactional/outbox)
|
|
11
11
|
hands it to this package, which emits it over a `ClientProxy` you
|
|
12
12
|
already configured. One implementation covers every transport
|
|
13
|
-
`@nestjs/microservices` supports — Kafka, RabbitMQ,
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
13
|
+
`@nestjs/microservices` supports — Kafka, RabbitMQ, MQTT, Redis, NATS,
|
|
14
|
+
and custom strategies. gRPC is the exception; see below.
|
|
15
|
+
|
|
16
|
+
## What a successful publish means on your transport
|
|
17
|
+
|
|
18
|
+
A publication is marked `COMPLETED` when `emit()` resolves, and what
|
|
19
|
+
`emit()` waits for is not the same on every transport. Kafka and
|
|
20
|
+
RabbitMQ wait for a real broker acknowledgement, so an unreachable
|
|
21
|
+
broker marks the publication `FAILED` and the outbox's retry and
|
|
22
|
+
resubmit machinery engages. NATS and TCP do not wait for anything.
|
|
23
|
+
|
|
24
|
+
| Transport | `emit()` resolves when | Acknowledged by the broker? |
|
|
25
|
+
| --- | --- | --- |
|
|
26
|
+
| Kafka | `producer.send()` settles; `kafkajs` defaults to `acks: -1`, every in-sync replica | Yes |
|
|
27
|
+
| RabbitMQ | the publisher confirm arrives (`amqp-connection-manager` enables confirms by default) | Yes, but set `persistent: true` |
|
|
28
|
+
| MQTT | PUBACK at QoS 1 and above, immediately at QoS 0 | At QoS 1 and above |
|
|
29
|
+
| Redis | the `PUBLISH` command replies | The server got it, but Redis pub/sub does not persist: only live subscribers receive it |
|
|
30
|
+
| TCP | the message is written to the socket | No |
|
|
31
|
+
| NATS | immediately: core `publish()` returns `void`, and the client resolves unconditionally | No |
|
|
32
|
+
| gRPC | never: `dispatchEvent` throws `Method is not supported in gRPC mode` | Not usable for externalization |
|
|
33
|
+
|
|
34
|
+
Two things worth acting on:
|
|
35
|
+
|
|
36
|
+
- **RabbitMQ publishes non-persistent by default.** NestJS defaults
|
|
37
|
+
`persistent` to `false`, and RabbitMQ confirms a non-persistent
|
|
38
|
+
message without writing it to disk, so a broker restart loses it.
|
|
39
|
+
Pass `persistent: true` in your `ClientsModule.register()` options.
|
|
40
|
+
- **NATS gives you no delivery signal at all.** Core NATS publish is
|
|
41
|
+
fire-and-forget by protocol. If you need a guarantee there, this
|
|
42
|
+
externalizer is not the right one; the `EVENT_EXTERNALIZER` SPI is
|
|
43
|
+
public and a JetStream-based implementation slots into the same
|
|
44
|
+
place.
|
|
45
|
+
|
|
46
|
+
The outbox itself guarantees crash-consistent **enqueueing** and
|
|
47
|
+
at-least-once delivery to **local** handlers regardless of transport.
|
|
48
|
+
Measurements, the reading of each client's `dispatchEvent`, and what
|
|
49
|
+
remains genuinely unguaranteed:
|
|
50
|
+
[ADR-021](https://github.com/igorgolovanov/nestjs-transactional/blob/main/docs/adr/021-externalization-acknowledgement-per-transport.md).
|
|
34
51
|
|
|
35
52
|
## Install
|
|
36
53
|
|
|
@@ -38,6 +55,20 @@ gRPC, and custom strategies.
|
|
|
38
55
|
pnpm add @nestjs-transactional/outbox-microservices @nestjs-transactional/outbox @nestjs/microservices
|
|
39
56
|
```
|
|
40
57
|
|
|
58
|
+
## Module format
|
|
59
|
+
|
|
60
|
+
This package ships **ESM only**, matching NestJS 12, which is ESM-only
|
|
61
|
+
across its own packages. There is no CommonJS build.
|
|
62
|
+
|
|
63
|
+
A CommonJS application still works: Node loads ESM from `require()`
|
|
64
|
+
since 22.12.0, which is why `engines.node` is `>=22.13.0`. What does not
|
|
65
|
+
follow Node here is tooling with its own module loader — Jest above all,
|
|
66
|
+
which needs `NODE_OPTIONS=--experimental-vm-modules` and a few config
|
|
67
|
+
settings. The 19 example applications in the repository all run their
|
|
68
|
+
suites that way and can be copied from.
|
|
69
|
+
|
|
70
|
+
Reasoning and measurements: [ADR-022](https://github.com/igorgolovanov/nestjs-transactional/blob/main/docs/adr/022-esm-only-packaging.md).
|
|
71
|
+
|
|
41
72
|
## Quick start
|
|
42
73
|
|
|
43
74
|
This package does not create broker connections — you register clients
|
|
@@ -97,34 +128,38 @@ publication, so a typo surfaces on deploy instead of in the middle of
|
|
|
97
128
|
the night. Pass `validateOnBootstrap: false` to defer resolution if you
|
|
98
129
|
register clients late.
|
|
99
130
|
|
|
100
|
-
##
|
|
131
|
+
## Hardening the delivery
|
|
101
132
|
|
|
102
|
-
|
|
103
|
-
|
|
133
|
+
The defaults are reasonable on Kafka and RabbitMQ. What is left is
|
|
134
|
+
mostly about not weakening them, and about the consumer side.
|
|
104
135
|
|
|
105
|
-
1. **
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
not
|
|
110
|
-
|
|
136
|
+
1. **Do not configure the acknowledgement away.** Kafka `acks: 0`,
|
|
137
|
+
MQTT QoS 0, and RabbitMQ without `persistent: true` each give up a
|
|
138
|
+
guarantee you had for free. `producer.idempotent: true` on Kafka
|
|
139
|
+
additionally protects against duplicates from producer retries.
|
|
140
|
+
This package reuses whatever proxy you registered and does not
|
|
141
|
+
interfere with any of it.
|
|
142
|
+
2. **Deduplicate on the consumer.** At-least-once means duplicates are
|
|
143
|
+
expected, not exceptional. Track processed message ids on the
|
|
111
144
|
receiving side and alert on gaps. The listener id plus the event id
|
|
112
145
|
is enough to identify a message.
|
|
113
|
-
3. **
|
|
114
|
-
`
|
|
115
|
-
|
|
116
|
-
|
|
146
|
+
3. **Watch the `FAILED` publications.** A broker rejection now reaches
|
|
147
|
+
you as a `FAILED` row with a readable `failureReason`, which is what
|
|
148
|
+
`FailedEventPublications.resubmit` and the retry scheduler act on.
|
|
149
|
+
That path is only useful if someone is looking at it.
|
|
117
150
|
|
|
118
151
|
## Limitations
|
|
119
152
|
|
|
120
153
|
- **Headers and `routingKey`** are accepted by `@Externalized` but not
|
|
121
154
|
yet applied to the emitted payload.
|
|
122
|
-
- **
|
|
155
|
+
- **gRPC cannot be used** as an externalization transport:
|
|
156
|
+
`ClientGrpcProxy.dispatchEvent` throws.
|
|
157
|
+
- **NATS and TCP give no delivery signal** — see the table at the top.
|
|
123
158
|
|
|
124
159
|
## Documentation
|
|
125
160
|
|
|
126
161
|
- [Getting started and full docs](https://github.com/igorgolovanov/nestjs-transactional#readme)
|
|
127
|
-
- [
|
|
162
|
+
- [Acknowledgement per transport (ADR-021)](https://github.com/igorgolovanov/nestjs-transactional/blob/main/docs/adr/021-externalization-acknowledgement-per-transport.md)
|
|
128
163
|
- [Externalization architecture (ADR-015)](https://github.com/igorgolovanov/nestjs-transactional/blob/main/docs/adr/015-event-externalization-architecture.md)
|
|
129
164
|
- [Architecture: event externalization](https://github.com/igorgolovanov/nestjs-transactional/blob/main/docs/architecture/event-externalization.md)
|
|
130
165
|
- Runnable examples:
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type OnApplicationBootstrap } from '@nestjs/common';
|
|
2
2
|
import { ModuleRef } from '@nestjs/core';
|
|
3
3
|
import { type EventExternalizer, type ExternalizationMetadata } from '@nestjs-transactional/outbox';
|
|
4
|
-
import { type OutboxMicroservicesOptions } from '../types/options';
|
|
4
|
+
import { type OutboxMicroservicesOptions } from '../types/options.js';
|
|
5
5
|
/**
|
|
6
6
|
* {@link EventExternalizer} implementation backed by `@nestjs/microservices`
|
|
7
7
|
* `ClientProxy`. Routes externalized events to whichever transport
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
2
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
3
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
@@ -12,13 +11,11 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
|
12
11
|
return function (target, key) { decorator(target, key, paramIndex); }
|
|
13
12
|
};
|
|
14
13
|
var MicroservicesEventExternalizer_1;
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
const rxjs_1 = require("rxjs");
|
|
21
|
-
const options_1 = require("../types/options");
|
|
14
|
+
import { Inject, Injectable, Logger, } from '@nestjs/common';
|
|
15
|
+
import { ModuleRef } from '@nestjs/core';
|
|
16
|
+
import { describeThrown, ExternalizationError, } from '@nestjs-transactional/outbox';
|
|
17
|
+
import { firstValueFrom } from 'rxjs';
|
|
18
|
+
import { OUTBOX_MICROSERVICES_OPTIONS } from '../types/options.js';
|
|
22
19
|
/**
|
|
23
20
|
* {@link EventExternalizer} implementation backed by `@nestjs/microservices`
|
|
24
21
|
* `ClientProxy`. Routes externalized events to whichever transport
|
|
@@ -45,7 +42,7 @@ const options_1 = require("../types/options");
|
|
|
45
42
|
let MicroservicesEventExternalizer = MicroservicesEventExternalizer_1 = class MicroservicesEventExternalizer {
|
|
46
43
|
moduleRef;
|
|
47
44
|
options;
|
|
48
|
-
logger = new
|
|
45
|
+
logger = new Logger(MicroservicesEventExternalizer_1.name);
|
|
49
46
|
constructor(moduleRef, options) {
|
|
50
47
|
this.moduleRef = moduleRef;
|
|
51
48
|
this.options = options;
|
|
@@ -64,18 +61,18 @@ let MicroservicesEventExternalizer = MicroservicesEventExternalizer_1 = class Mi
|
|
|
64
61
|
this.logger.log(`Externalization configured with default client: ${formatToken(this.options.defaultClient)}`);
|
|
65
62
|
}
|
|
66
63
|
catch (err) {
|
|
67
|
-
const
|
|
64
|
+
const reason = describeThrown(err);
|
|
68
65
|
throw new Error(`OutboxMicroservicesModule: defaultClient '${formatToken(this.options.defaultClient)}' ` +
|
|
69
66
|
`is not registered in the DI container. Register the ClientProxy via ` +
|
|
70
67
|
`ClientsModule.register() / ClientsModule.registerAsync(), or pass ` +
|
|
71
68
|
`validateOnBootstrap: false to defer resolution to the first event. ` +
|
|
72
|
-
`Original error: ${cause}
|
|
69
|
+
`Original error: ${reason}`, { cause: err });
|
|
73
70
|
}
|
|
74
71
|
}
|
|
75
72
|
async externalize(event, metadata) {
|
|
76
73
|
const clientToken = metadata.client ?? this.options.defaultClient;
|
|
77
74
|
if (clientToken === undefined) {
|
|
78
|
-
throw new
|
|
75
|
+
throw new ExternalizationError(`No ClientProxy specified for event ${metadata.eventType}. ` +
|
|
79
76
|
`Set defaultClient on OutboxMicroservicesModule.forRoot() or pass ` +
|
|
80
77
|
`'client' in the @Externalized() decorator options.`, metadata.eventType, metadata.target);
|
|
81
78
|
}
|
|
@@ -85,8 +82,8 @@ let MicroservicesEventExternalizer = MicroservicesEventExternalizer_1 = class Mi
|
|
|
85
82
|
}
|
|
86
83
|
catch (err) {
|
|
87
84
|
const cause = err instanceof Error ? err : undefined;
|
|
88
|
-
const causeMessage =
|
|
89
|
-
throw new
|
|
85
|
+
const causeMessage = describeThrown(err);
|
|
86
|
+
throw new ExternalizationError(`ClientProxy '${formatToken(clientToken)}' not found in the DI container. ` +
|
|
90
87
|
`Ensure it is registered via ClientsModule.register() / registerAsync(). ` +
|
|
91
88
|
`Original error: ${causeMessage}`, metadata.eventType, metadata.target, cause);
|
|
92
89
|
}
|
|
@@ -98,13 +95,13 @@ let MicroservicesEventExternalizer = MicroservicesEventExternalizer_1 = class Mi
|
|
|
98
95
|
this.logger.debug(`${metadata.eventType}: headers/routingKey are not applied to the wire payload in this version (current limitation): ${JSON.stringify({ headers: metadata.headers, routingKey: metadata.routingKey })}`);
|
|
99
96
|
}
|
|
100
97
|
try {
|
|
101
|
-
await
|
|
98
|
+
await firstValueFrom(client.emit(metadata.target, event));
|
|
102
99
|
this.logger.debug(`Externalized ${metadata.eventType} → ${metadata.target} (client: ${formatToken(clientToken)})`);
|
|
103
100
|
}
|
|
104
101
|
catch (err) {
|
|
105
102
|
const cause = err instanceof Error ? err : undefined;
|
|
106
|
-
const causeMessage =
|
|
107
|
-
throw new
|
|
103
|
+
const causeMessage = describeThrown(err);
|
|
104
|
+
throw new ExternalizationError(`Failed to publish ${metadata.eventType} to ${metadata.target}: ${causeMessage}`, metadata.eventType, metadata.target, cause);
|
|
108
105
|
}
|
|
109
106
|
}
|
|
110
107
|
resolveClient(token) {
|
|
@@ -115,12 +112,12 @@ let MicroservicesEventExternalizer = MicroservicesEventExternalizer_1 = class Mi
|
|
|
115
112
|
return proxy;
|
|
116
113
|
}
|
|
117
114
|
};
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
(
|
|
121
|
-
|
|
122
|
-
__metadata("design:paramtypes", [core_1.ModuleRef, Object])
|
|
115
|
+
MicroservicesEventExternalizer = MicroservicesEventExternalizer_1 = __decorate([
|
|
116
|
+
Injectable(),
|
|
117
|
+
__param(1, Inject(OUTBOX_MICROSERVICES_OPTIONS)),
|
|
118
|
+
__metadata("design:paramtypes", [ModuleRef, Object])
|
|
123
119
|
], MicroservicesEventExternalizer);
|
|
120
|
+
export { MicroservicesEventExternalizer };
|
|
124
121
|
function formatToken(token) {
|
|
125
122
|
if (typeof token === 'symbol') {
|
|
126
123
|
return token.toString();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"microservices-event-externalizer.js","sourceRoot":"","sources":["../../src/externalizer/microservices-event-externalizer.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"microservices-event-externalizer.js","sourceRoot":"","sources":["../../src/externalizer/microservices-event-externalizer.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { MicroservicesEventExternalizer } from './externalizer/microservices-event-externalizer';
|
|
2
|
-
export { OutboxMicroservicesModule, type OutboxMicroservicesAsyncOptions, } from './module/outbox-microservices.module';
|
|
3
|
-
export { OUTBOX_MICROSERVICES_OPTIONS, type OutboxMicroservicesOptions } from './types/options';
|
|
1
|
+
export { MicroservicesEventExternalizer } from './externalizer/microservices-event-externalizer.js';
|
|
2
|
+
export { OutboxMicroservicesModule, type OutboxMicroservicesAsyncOptions, } from './module/outbox-microservices.module.js';
|
|
3
|
+
export { OUTBOX_MICROSERVICES_OPTIONS, type OutboxMicroservicesOptions } from './types/options.js';
|
|
4
4
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
var microservices_event_externalizer_1 = require("./externalizer/microservices-event-externalizer");
|
|
5
|
-
Object.defineProperty(exports, "MicroservicesEventExternalizer", { enumerable: true, get: function () { return microservices_event_externalizer_1.MicroservicesEventExternalizer; } });
|
|
6
|
-
var outbox_microservices_module_1 = require("./module/outbox-microservices.module");
|
|
7
|
-
Object.defineProperty(exports, "OutboxMicroservicesModule", { enumerable: true, get: function () { return outbox_microservices_module_1.OutboxMicroservicesModule; } });
|
|
8
|
-
var options_1 = require("./types/options");
|
|
9
|
-
Object.defineProperty(exports, "OUTBOX_MICROSERVICES_OPTIONS", { enumerable: true, get: function () { return options_1.OUTBOX_MICROSERVICES_OPTIONS; } });
|
|
1
|
+
export { MicroservicesEventExternalizer } from './externalizer/microservices-event-externalizer.js';
|
|
2
|
+
export { OutboxMicroservicesModule, } from './module/outbox-microservices.module.js';
|
|
3
|
+
export { OUTBOX_MICROSERVICES_OPTIONS } from './types/options.js';
|
|
10
4
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,8BAA8B,EAAE,MAAM,oDAAoD,CAAC;AACpG,OAAO,EACL,yBAAyB,GAE1B,MAAM,yCAAyC,CAAC;AACjD,OAAO,EAAE,4BAA4B,EAAmC,MAAM,oBAAoB,CAAC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type DynamicModule, type InjectionToken, type ModuleMetadata } from '@nestjs/common';
|
|
2
|
-
import { type OutboxMicroservicesOptions } from '../types/options';
|
|
2
|
+
import { type OutboxMicroservicesOptions } from '../types/options.js';
|
|
3
3
|
/**
|
|
4
4
|
* Async-options shape for {@link OutboxMicroservicesModule.forRootAsync}.
|
|
5
5
|
* `imports` follows NestJS convention so the factory can pull values
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
2
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
3
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
@@ -6,12 +5,10 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
6
5
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
6
|
};
|
|
8
7
|
var OutboxMicroservicesModule_1;
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
const microservices_event_externalizer_1 = require("../externalizer/microservices-event-externalizer");
|
|
14
|
-
const options_1 = require("../types/options");
|
|
8
|
+
import { Module, } from '@nestjs/common';
|
|
9
|
+
import { EVENT_EXTERNALIZER } from '@nestjs-transactional/outbox';
|
|
10
|
+
import { MicroservicesEventExternalizer } from '../externalizer/microservices-event-externalizer.js';
|
|
11
|
+
import { OUTBOX_MICROSERVICES_OPTIONS } from '../types/options.js';
|
|
15
12
|
/**
|
|
16
13
|
* NestJS module that wires the
|
|
17
14
|
* {@link MicroservicesEventExternalizer} as the
|
|
@@ -58,38 +55,38 @@ const options_1 = require("../types/options");
|
|
|
58
55
|
let OutboxMicroservicesModule = OutboxMicroservicesModule_1 = class OutboxMicroservicesModule {
|
|
59
56
|
static forRoot(options = {}) {
|
|
60
57
|
const providers = [
|
|
61
|
-
{ provide:
|
|
62
|
-
|
|
63
|
-
{ provide:
|
|
58
|
+
{ provide: OUTBOX_MICROSERVICES_OPTIONS, useValue: options },
|
|
59
|
+
MicroservicesEventExternalizer,
|
|
60
|
+
{ provide: EVENT_EXTERNALIZER, useExisting: MicroservicesEventExternalizer },
|
|
64
61
|
];
|
|
65
62
|
return {
|
|
66
63
|
module: OutboxMicroservicesModule_1,
|
|
67
64
|
global: true,
|
|
68
65
|
providers,
|
|
69
|
-
exports: [
|
|
66
|
+
exports: [EVENT_EXTERNALIZER, MicroservicesEventExternalizer],
|
|
70
67
|
};
|
|
71
68
|
}
|
|
72
69
|
static forRootAsync(options) {
|
|
73
70
|
const providers = [
|
|
74
71
|
{
|
|
75
|
-
provide:
|
|
72
|
+
provide: OUTBOX_MICROSERVICES_OPTIONS,
|
|
76
73
|
useFactory: options.useFactory,
|
|
77
74
|
inject: options.inject ? [...options.inject] : undefined,
|
|
78
75
|
},
|
|
79
|
-
|
|
80
|
-
{ provide:
|
|
76
|
+
MicroservicesEventExternalizer,
|
|
77
|
+
{ provide: EVENT_EXTERNALIZER, useExisting: MicroservicesEventExternalizer },
|
|
81
78
|
];
|
|
82
79
|
return {
|
|
83
80
|
module: OutboxMicroservicesModule_1,
|
|
84
81
|
global: true,
|
|
85
82
|
imports: options.imports ?? [],
|
|
86
83
|
providers,
|
|
87
|
-
exports: [
|
|
84
|
+
exports: [EVENT_EXTERNALIZER, MicroservicesEventExternalizer],
|
|
88
85
|
};
|
|
89
86
|
}
|
|
90
87
|
};
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
(0, common_1.Module)({})
|
|
88
|
+
OutboxMicroservicesModule = OutboxMicroservicesModule_1 = __decorate([
|
|
89
|
+
Module({})
|
|
94
90
|
], OutboxMicroservicesModule);
|
|
91
|
+
export { OutboxMicroservicesModule };
|
|
95
92
|
//# sourceMappingURL=outbox-microservices.module.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"outbox-microservices.module.js","sourceRoot":"","sources":["../../src/module/outbox-microservices.module.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"outbox-microservices.module.js","sourceRoot":"","sources":["../../src/module/outbox-microservices.module.ts"],"names":[],"mappings":";;;;;;;AAAA,OAAO,EAGL,MAAM,GAGP,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AAElE,OAAO,EAAE,8BAA8B,EAAE,MAAM,qDAAqD,CAAC;AACrG,OAAO,EAAE,4BAA4B,EAAmC,MAAM,qBAAqB,CAAC;AAepG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AAEI,IAAM,yBAAyB,iCAA/B,MAAM,yBAAyB;IACpC,MAAM,CAAC,OAAO,CAAC,UAAsC,EAAE;QACrD,MAAM,SAAS,GAAe;YAC5B,EAAE,OAAO,EAAE,4BAA4B,EAAE,QAAQ,EAAE,OAAO,EAAE;YAC5D,8BAA8B;YAC9B,EAAE,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,8BAA8B,EAAE;SAC7E,CAAC;QAEF,OAAO;YACL,MAAM,EAAE,2BAAyB;YACjC,MAAM,EAAE,IAAI;YACZ,SAAS;YACT,OAAO,EAAE,CAAC,kBAAkB,EAAE,8BAA8B,CAAC;SAC9D,CAAC;IACJ,CAAC;IAED,MAAM,CAAC,YAAY,CAAC,OAAwC;QAC1D,MAAM,SAAS,GAAe;YAC5B;gBACE,OAAO,EAAE,4BAA4B;gBACrC,UAAU,EAAE,OAAO,CAAC,UAAU;gBAC9B,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS;aACzD;YACD,8BAA8B;YAC9B,EAAE,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,8BAA8B,EAAE;SAC7E,CAAC;QAEF,OAAO;YACL,MAAM,EAAE,2BAAyB;YACjC,MAAM,EAAE,IAAI;YACZ,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,EAAE;YAC9B,SAAS;YACT,OAAO,EAAE,CAAC,kBAAkB,EAAE,8BAA8B,CAAC;SAC9D,CAAC;IACJ,CAAC;CACF,CAAA;AAnCY,yBAAyB;IADrC,MAAM,CAAC,EAAE,CAAC;GACE,yBAAyB,CAmCrC"}
|
package/dist/types/options.js
CHANGED
|
@@ -1,11 +1,8 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.OUTBOX_MICROSERVICES_OPTIONS = void 0;
|
|
4
1
|
/**
|
|
5
2
|
* DI token for the resolved {@link OutboxMicroservicesOptions} object.
|
|
6
3
|
* Internal — consumers configure the module via
|
|
7
4
|
* `OutboxMicroservicesModule.forRoot()` rather than injecting the
|
|
8
5
|
* token directly.
|
|
9
6
|
*/
|
|
10
|
-
|
|
7
|
+
export const OUTBOX_MICROSERVICES_OPTIONS = Symbol('OUTBOX_MICROSERVICES_OPTIONS');
|
|
11
8
|
//# sourceMappingURL=options.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"options.js","sourceRoot":"","sources":["../../src/types/options.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"options.js","sourceRoot":"","sources":["../../src/types/options.ts"],"names":[],"mappings":"AAmCA;;;;;GAKG;AACH,MAAM,CAAC,MAAM,4BAA4B,GAAG,MAAM,CAAC,8BAA8B,CAAC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nestjs-transactional/outbox-microservices",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "Event externalization for @nestjs-transactional/outbox via @nestjs/microservices ClientProxy — Spring Modulith @Externalized parity",
|
|
5
5
|
"license": "MIT",
|
|
6
|
-
"type": "
|
|
6
|
+
"type": "module",
|
|
7
7
|
"sideEffects": false,
|
|
8
8
|
"author": "Igor Golovanov",
|
|
9
9
|
"repository": {
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"nats"
|
|
28
28
|
],
|
|
29
29
|
"engines": {
|
|
30
|
-
"node": ">=22.
|
|
30
|
+
"node": ">=22.13.0"
|
|
31
31
|
},
|
|
32
32
|
"main": "dist/index.js",
|
|
33
33
|
"types": "dist/index.d.ts",
|
|
@@ -48,37 +48,44 @@
|
|
|
48
48
|
"provenance": true
|
|
49
49
|
},
|
|
50
50
|
"peerDependencies": {
|
|
51
|
-
"@nestjs/common": "^10.0.0 || ^11.0.0",
|
|
52
|
-
"@nestjs/core": "^10.0.0 || ^11.0.0",
|
|
53
|
-
"@nestjs/microservices": "^10.0.0 || ^11.0.0",
|
|
51
|
+
"@nestjs/common": "^10.0.0 || ^11.0.0 || ^12.0.0",
|
|
52
|
+
"@nestjs/core": "^10.0.0 || ^11.0.0 || ^12.0.0",
|
|
53
|
+
"@nestjs/microservices": "^10.0.0 || ^11.0.0 || ^12.0.0",
|
|
54
54
|
"reflect-metadata": "^0.1.13 || ^0.2.0",
|
|
55
55
|
"rxjs": "^7.0.0",
|
|
56
|
-
"@nestjs-transactional/outbox": "^
|
|
56
|
+
"@nestjs-transactional/outbox": "^2.0.0"
|
|
57
57
|
},
|
|
58
58
|
"devDependencies": {
|
|
59
|
-
"@
|
|
60
|
-
"@nestjs/
|
|
61
|
-
"@nestjs/
|
|
62
|
-
"@nestjs/
|
|
63
|
-
"@
|
|
64
|
-
"
|
|
59
|
+
"@jest/globals": "^30.4.2",
|
|
60
|
+
"@nestjs/common": "^11.2.3",
|
|
61
|
+
"@nestjs/core": "^11.2.3",
|
|
62
|
+
"@nestjs/microservices": "^11.2.3",
|
|
63
|
+
"@nestjs/testing": "^11.2.3",
|
|
64
|
+
"@testcontainers/kafka": "^12.1.0",
|
|
65
|
+
"@types/jest": "^30.0.0",
|
|
66
|
+
"@types/node": "^22.20.1",
|
|
67
|
+
"amqplib": "^2.0.1",
|
|
68
|
+
"jest": "^30.5.1",
|
|
69
|
+
"kafkajs": "^2.2.4",
|
|
65
70
|
"reflect-metadata": "^0.2.2",
|
|
66
71
|
"rxjs": "^7.8.1",
|
|
67
|
-
"
|
|
68
|
-
"
|
|
69
|
-
"
|
|
70
|
-
"@nestjs-transactional/
|
|
72
|
+
"testcontainers": "^12.1.0",
|
|
73
|
+
"ts-jest": "^29.4.12",
|
|
74
|
+
"typescript": "^6.0.3",
|
|
75
|
+
"@nestjs-transactional/core": "2.0.0",
|
|
76
|
+
"@nestjs-transactional/outbox": "2.0.0"
|
|
71
77
|
},
|
|
72
78
|
"scripts": {
|
|
73
79
|
"build": "tsc -p tsconfig.build.json",
|
|
74
80
|
"clean": "rimraf dist *.tsbuildinfo coverage",
|
|
75
|
-
"test": "jest",
|
|
76
|
-
"test:watch": "jest --watch",
|
|
77
|
-
"test:
|
|
81
|
+
"test": "NODE_OPTIONS=--experimental-vm-modules jest",
|
|
82
|
+
"test:watch": "NODE_OPTIONS=--experimental-vm-modules jest --watch",
|
|
83
|
+
"test:integration": "NODE_OPTIONS=--experimental-vm-modules jest --config jest.integration.config.cjs",
|
|
84
|
+
"test:cov": "NODE_OPTIONS=--experimental-vm-modules jest --coverage",
|
|
78
85
|
"type-check": "tsc --noEmit",
|
|
79
|
-
"lint": "eslint
|
|
86
|
+
"lint": "eslint .",
|
|
80
87
|
"api:check": "api-extractor run",
|
|
81
88
|
"api:update": "api-extractor run --local",
|
|
82
|
-
"publish:check": "publint && attw --pack ."
|
|
89
|
+
"publish:check": "publint && attw --pack . --profile esm-only"
|
|
83
90
|
}
|
|
84
91
|
}
|