@nestjs-transactional/outbox 1.0.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (126) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +396 -0
  3. package/dist/api/completed-event-publications.d.ts +31 -0
  4. package/dist/api/completed-event-publications.js +58 -0
  5. package/dist/api/completed-event-publications.js.map +1 -0
  6. package/dist/api/failed-event-publications.d.ts +36 -0
  7. package/dist/api/failed-event-publications.js +75 -0
  8. package/dist/api/failed-event-publications.js.map +1 -0
  9. package/dist/api/incomplete-event-publications.d.ts +28 -0
  10. package/dist/api/incomplete-event-publications.js +72 -0
  11. package/dist/api/incomplete-event-publications.js.map +1 -0
  12. package/dist/decorators/inject-decorators.d.ts +85 -0
  13. package/dist/decorators/inject-decorators.js +100 -0
  14. package/dist/decorators/inject-decorators.js.map +1 -0
  15. package/dist/decorators/outbox-events-handler.decorator.d.ts +97 -0
  16. package/dist/decorators/outbox-events-handler.decorator.js +58 -0
  17. package/dist/decorators/outbox-events-handler.decorator.js.map +1 -0
  18. package/dist/dispatcher/data-source-outbox-publisher.d.ts +69 -0
  19. package/dist/dispatcher/data-source-outbox-publisher.js +127 -0
  20. package/dist/dispatcher/data-source-outbox-publisher.js.map +1 -0
  21. package/dist/dispatcher/event-publication-processor.d.ts +69 -0
  22. package/dist/dispatcher/event-publication-processor.js +192 -0
  23. package/dist/dispatcher/event-publication-processor.js.map +1 -0
  24. package/dist/dispatcher/outbox-event-publisher.d.ts +129 -0
  25. package/dist/dispatcher/outbox-event-publisher.js +172 -0
  26. package/dist/dispatcher/outbox-event-publisher.js.map +1 -0
  27. package/dist/dispatcher/processor-options.d.ts +24 -0
  28. package/dist/dispatcher/processor-options.js +15 -0
  29. package/dist/dispatcher/processor-options.js.map +1 -0
  30. package/dist/externalization/errors.d.ts +21 -0
  31. package/dist/externalization/errors.js +30 -0
  32. package/dist/externalization/errors.js.map +1 -0
  33. package/dist/externalization/event-externalizer.d.ts +49 -0
  34. package/dist/externalization/event-externalizer.js +14 -0
  35. package/dist/externalization/event-externalizer.js.map +1 -0
  36. package/dist/externalization/externalization-registry.d.ts +56 -0
  37. package/dist/externalization/externalization-registry.js +103 -0
  38. package/dist/externalization/externalization-registry.js.map +1 -0
  39. package/dist/externalization/externalized.decorator.d.ts +121 -0
  40. package/dist/externalization/externalized.decorator.js +87 -0
  41. package/dist/externalization/externalized.decorator.js.map +1 -0
  42. package/dist/externalization/types.d.ts +39 -0
  43. package/dist/externalization/types.js +3 -0
  44. package/dist/externalization/types.js.map +1 -0
  45. package/dist/index.d.ts +38 -0
  46. package/dist/index.js +58 -0
  47. package/dist/index.js.map +1 -0
  48. package/dist/interfaces/outbox-event-handler.interface.d.ts +32 -0
  49. package/dist/interfaces/outbox-event-handler.interface.js +3 -0
  50. package/dist/interfaces/outbox-event-handler.interface.js.map +1 -0
  51. package/dist/module/outbox-processing.module.d.ts +29 -0
  52. package/dist/module/outbox-processing.module.js +66 -0
  53. package/dist/module/outbox-processing.module.js.map +1 -0
  54. package/dist/module/outbox.module.d.ts +196 -0
  55. package/dist/module/outbox.module.js +604 -0
  56. package/dist/module/outbox.module.js.map +1 -0
  57. package/dist/recovery/staleness-monitor.d.ts +39 -0
  58. package/dist/recovery/staleness-monitor.js +118 -0
  59. package/dist/recovery/staleness-monitor.js.map +1 -0
  60. package/dist/recovery/startup-recovery.d.ts +37 -0
  61. package/dist/recovery/startup-recovery.js +55 -0
  62. package/dist/recovery/startup-recovery.js.map +1 -0
  63. package/dist/registry/event-publication-registry.d.ts +70 -0
  64. package/dist/registry/event-publication-registry.js +138 -0
  65. package/dist/registry/event-publication-registry.js.map +1 -0
  66. package/dist/registry/listener-registry.d.ts +52 -0
  67. package/dist/registry/listener-registry.js +67 -0
  68. package/dist/registry/listener-registry.js.map +1 -0
  69. package/dist/registry/multi-ds-listener-registrar.d.ts +78 -0
  70. package/dist/registry/multi-ds-listener-registrar.js +112 -0
  71. package/dist/registry/multi-ds-listener-registrar.js.map +1 -0
  72. package/dist/registry/outbox-listener-scanner.d.ts +66 -0
  73. package/dist/registry/outbox-listener-scanner.js +192 -0
  74. package/dist/registry/outbox-listener-scanner.js.map +1 -0
  75. package/dist/repository/event-publication-repository.d.ts +112 -0
  76. package/dist/repository/event-publication-repository.js +6 -0
  77. package/dist/repository/event-publication-repository.js.map +1 -0
  78. package/dist/serialization/event-serializer.d.ts +28 -0
  79. package/dist/serialization/event-serializer.js +6 -0
  80. package/dist/serialization/event-serializer.js.map +1 -0
  81. package/dist/serialization/event-type-registry.d.ts +41 -0
  82. package/dist/serialization/event-type-registry.js +79 -0
  83. package/dist/serialization/event-type-registry.js.map +1 -0
  84. package/dist/serialization/event-type-resolver.d.ts +43 -0
  85. package/dist/serialization/event-type-resolver.js +66 -0
  86. package/dist/serialization/event-type-resolver.js.map +1 -0
  87. package/dist/serialization/json-event-serializer.d.ts +20 -0
  88. package/dist/serialization/json-event-serializer.js +64 -0
  89. package/dist/serialization/json-event-serializer.js.map +1 -0
  90. package/dist/testing/assertable-published-events.d.ts +71 -0
  91. package/dist/testing/assertable-published-events.js +124 -0
  92. package/dist/testing/assertable-published-events.js.map +1 -0
  93. package/dist/testing/in-memory-repository.d.ts +52 -0
  94. package/dist/testing/in-memory-repository.js +230 -0
  95. package/dist/testing/in-memory-repository.js.map +1 -0
  96. package/dist/testing/index.d.ts +4 -0
  97. package/dist/testing/index.js +20 -0
  98. package/dist/testing/index.js.map +1 -0
  99. package/dist/testing/published-events.d.ts +62 -0
  100. package/dist/testing/published-events.js +110 -0
  101. package/dist/testing/published-events.js.map +1 -0
  102. package/dist/tokens/index.d.ts +2 -0
  103. package/dist/tokens/index.js +14 -0
  104. package/dist/tokens/index.js.map +1 -0
  105. package/dist/tokens/token-utils.d.ts +68 -0
  106. package/dist/tokens/token-utils.js +98 -0
  107. package/dist/tokens/token-utils.js.map +1 -0
  108. package/dist/types/completion-mode.d.ts +16 -0
  109. package/dist/types/completion-mode.js +20 -0
  110. package/dist/types/completion-mode.js.map +1 -0
  111. package/dist/types/errors.d.ts +50 -0
  112. package/dist/types/errors.js +64 -0
  113. package/dist/types/errors.js.map +1 -0
  114. package/dist/types/event-publication.d.ts +47 -0
  115. package/dist/types/event-publication.js +3 -0
  116. package/dist/types/event-publication.js.map +1 -0
  117. package/dist/types/publication-status.d.ts +26 -0
  118. package/dist/types/publication-status.js +30 -0
  119. package/dist/types/publication-status.js.map +1 -0
  120. package/dist/types/resubmission-options.d.ts +37 -0
  121. package/dist/types/resubmission-options.js +62 -0
  122. package/dist/types/resubmission-options.js.map +1 -0
  123. package/dist/types/staleness-config.d.ts +18 -0
  124. package/dist/types/staleness-config.js +10 -0
  125. package/dist/types/staleness-config.js.map +1 -0
  126. package/package.json +77 -0
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ExternalizationError = void 0;
4
+ const errors_1 = require("../types/errors");
5
+ /**
6
+ * Raised when an {@link EventExternalizer} fails to deliver a
7
+ * publication to its external broker target. Carries diagnostic
8
+ * context (event type, broker target, optional underlying cause) so
9
+ * operators can correlate a `FAILED` publication row with the broker
10
+ * issue without parsing free-form messages.
11
+ *
12
+ * Implementations are encouraged to wrap transport-specific failures
13
+ * in this error, but the `EventPublicationProcessor` does not
14
+ * require it — any rejection from `externalize()` is recorded on the
15
+ * publication.
16
+ */
17
+ class ExternalizationError extends errors_1.OutboxError {
18
+ eventType;
19
+ target;
20
+ cause;
21
+ code = 'EXTERNALIZATION_ERROR';
22
+ constructor(message, eventType, target, cause) {
23
+ super(message);
24
+ this.eventType = eventType;
25
+ this.target = target;
26
+ this.cause = cause;
27
+ }
28
+ }
29
+ exports.ExternalizationError = ExternalizationError;
30
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/externalization/errors.ts"],"names":[],"mappings":";;;AAAA,4CAA8C;AAE9C;;;;;;;;;;;GAWG;AACH,MAAa,oBAAqB,SAAQ,oBAAW;IAKxC;IACA;IACA;IANF,IAAI,GAAG,uBAAuB,CAAC;IAExC,YACE,OAAe,EACN,SAAiB,EACjB,MAAc,EACd,KAAa;QAEtB,KAAK,CAAC,OAAO,CAAC,CAAC;QAJN,cAAS,GAAT,SAAS,CAAQ;QACjB,WAAM,GAAN,MAAM,CAAQ;QACd,UAAK,GAAL,KAAK,CAAQ;IAGxB,CAAC;CACF;AAXD,oDAWC"}
@@ -0,0 +1,49 @@
1
+ import type { ExternalizationMetadata } from './types';
2
+ /**
3
+ * SPI for routing events that have an `@Externalized` mapping to an
4
+ * external message broker.
5
+ *
6
+ * Implementations are provided by extension packages — primarily
7
+ * `@nestjs-transactional/outbox-microservices` (Phase 11.3), which
8
+ * delegates to `@nestjs/microservices` `ClientProxy` and therefore
9
+ * covers every transport that NestJS already supports (Kafka,
10
+ * RabbitMQ, NATS, JMS, gRPC, custom). Future native (broker-specific)
11
+ * adapters can register under the same {@link EVENT_EXTERNALIZER}
12
+ * token without touching `outbox`.
13
+ *
14
+ * `EventPublicationProcessor` invokes `externalize()` AFTER the local
15
+ * outbox listener has succeeded for the publication and BEFORE the
16
+ * publication is finalized as `COMPLETED` (DD-019). If `externalize()`
17
+ * rejects, the publication is recorded as `FAILED` and can be
18
+ * resubmitted via {@link FailedEventPublications.resubmit} — the
19
+ * single-unit atomicity contract from DD-019 means a successful local
20
+ * listener may run again on retry, hence the documented idempotency
21
+ * requirement on listeners.
22
+ *
23
+ * Errors surface to the processor as ordinary rejections; wrap
24
+ * transport-specific failures in {@link ExternalizationError} for
25
+ * structured diagnostics if needed.
26
+ */
27
+ export interface EventExternalizer {
28
+ /**
29
+ * Route the event to its broker-side target.
30
+ *
31
+ * @param event Deserialized event payload (the same instance that
32
+ * was passed to the local listener).
33
+ * @param metadata Resolved routing metadata for this event. The
34
+ * processor obtains it from the `ExternalizationRegistry`
35
+ * (Phase 11.2).
36
+ */
37
+ externalize(event: unknown, metadata: ExternalizationMetadata): Promise<void>;
38
+ }
39
+ /**
40
+ * DI token for {@link EventExternalizer} bindings.
41
+ *
42
+ * Consumed by `EventPublicationProcessor` via `@Optional()` injection
43
+ * (DD-018) — the outbox runs in internal-only mode when no
44
+ * implementation is bound. Bind a concrete implementation via
45
+ * `useClass`, `useExisting`, or `useFactory` in the application's
46
+ * module configuration.
47
+ */
48
+ export declare const EVENT_EXTERNALIZER: unique symbol;
49
+ //# sourceMappingURL=event-externalizer.d.ts.map
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.EVENT_EXTERNALIZER = void 0;
4
+ /**
5
+ * DI token for {@link EventExternalizer} bindings.
6
+ *
7
+ * Consumed by `EventPublicationProcessor` via `@Optional()` injection
8
+ * (DD-018) — the outbox runs in internal-only mode when no
9
+ * implementation is bound. Bind a concrete implementation via
10
+ * `useClass`, `useExisting`, or `useFactory` in the application's
11
+ * module configuration.
12
+ */
13
+ exports.EVENT_EXTERNALIZER = Symbol('EVENT_EXTERNALIZER');
14
+ //# sourceMappingURL=event-externalizer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-externalizer.js","sourceRoot":"","sources":["../../src/externalization/event-externalizer.ts"],"names":[],"mappings":";;;AAwCA;;;;;;;;GAQG;AACU,QAAA,kBAAkB,GAAG,MAAM,CAAC,oBAAoB,CAAC,CAAC"}
@@ -0,0 +1,56 @@
1
+ import { type OnModuleInit } from '@nestjs/common';
2
+ import { EventTypeRegistry } from '../serialization/event-type-registry';
3
+ import { type ExternalizedMetadata } from './externalized.decorator';
4
+ import type { ExternalizationMetadata } from './types';
5
+ /**
6
+ * Registry mapping event type names to their {@link ExternalizedMetadata}.
7
+ *
8
+ * Built by scanning {@link EventTypeRegistry} at module init: every
9
+ * registered class that carries `@Externalized` metadata is indexed
10
+ * by its constructor name (matching `EventPublication.eventType`).
11
+ * Event classes registered AFTER `onModuleInit` (e.g. via
12
+ * `EventTypeRegistry.register` from a custom provider's
13
+ * `OnApplicationBootstrap`) will not appear in the index — register
14
+ * event types via `OutboxModule.forFeature([...])` so the feature-
15
+ * module factory provider populates the registry during eager
16
+ * provider instantiation, before any `onModuleInit` hook runs.
17
+ *
18
+ * Consumed by `EventPublicationProcessor.tryExternalize` to resolve
19
+ * the per-publication {@link ExternalizationMetadata} that the bound
20
+ * `EventExternalizer` then translates into a transport-specific call.
21
+ *
22
+ * Always provided by `OutboxModule`. When no events are decorated
23
+ * with `@Externalized` the registry is simply empty — `get()` returns
24
+ * `undefined` for every type and the externalization step in the
25
+ * processor becomes a no-op.
26
+ */
27
+ export declare class ExternalizationRegistry implements OnModuleInit {
28
+ private readonly eventTypeRegistry;
29
+ private readonly logger;
30
+ private readonly mapping;
31
+ constructor(eventTypeRegistry: EventTypeRegistry);
32
+ onModuleInit(): void;
33
+ /** Resolve {@link ExternalizedMetadata} for an event type, or `undefined`. */
34
+ get(eventType: string): ExternalizedMetadata | undefined;
35
+ /** Whether the given event type has an `@Externalized` mapping. */
36
+ has(eventType: string): boolean;
37
+ /**
38
+ * Build the per-publication {@link ExternalizationMetadata} that the
39
+ * processor passes to the bound `EventExternalizer`. Resolves
40
+ * dynamic `routingKey` and `headers` callbacks by invoking them with
41
+ * the actual event instance.
42
+ *
43
+ * @returns Resolved metadata, or `undefined` if `eventType` has no
44
+ * `@Externalized` mapping (the processor then skips the
45
+ * externalization call entirely).
46
+ */
47
+ buildMetadata(eventType: string, event: unknown): ExternalizationMetadata | undefined;
48
+ }
49
+ /**
50
+ * DI token for {@link ExternalizationRegistry}. Use the class token
51
+ * for direct injection — this Symbol is available for code that
52
+ * prefers token-based wiring or needs to override the registry in
53
+ * tests.
54
+ */
55
+ export declare const EXTERNALIZATION_REGISTRY: unique symbol;
56
+ //# sourceMappingURL=externalization-registry.d.ts.map
@@ -0,0 +1,103 @@
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 ExternalizationRegistry_1;
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.EXTERNALIZATION_REGISTRY = exports.ExternalizationRegistry = void 0;
14
+ const common_1 = require("@nestjs/common");
15
+ const event_type_registry_1 = require("../serialization/event-type-registry");
16
+ const externalized_decorator_1 = require("./externalized.decorator");
17
+ /**
18
+ * Registry mapping event type names to their {@link ExternalizedMetadata}.
19
+ *
20
+ * Built by scanning {@link EventTypeRegistry} at module init: every
21
+ * registered class that carries `@Externalized` metadata is indexed
22
+ * by its constructor name (matching `EventPublication.eventType`).
23
+ * Event classes registered AFTER `onModuleInit` (e.g. via
24
+ * `EventTypeRegistry.register` from a custom provider's
25
+ * `OnApplicationBootstrap`) will not appear in the index — register
26
+ * event types via `OutboxModule.forFeature([...])` so the feature-
27
+ * module factory provider populates the registry during eager
28
+ * provider instantiation, before any `onModuleInit` hook runs.
29
+ *
30
+ * Consumed by `EventPublicationProcessor.tryExternalize` to resolve
31
+ * the per-publication {@link ExternalizationMetadata} that the bound
32
+ * `EventExternalizer` then translates into a transport-specific call.
33
+ *
34
+ * Always provided by `OutboxModule`. When no events are decorated
35
+ * with `@Externalized` the registry is simply empty — `get()` returns
36
+ * `undefined` for every type and the externalization step in the
37
+ * processor becomes a no-op.
38
+ */
39
+ let ExternalizationRegistry = ExternalizationRegistry_1 = class ExternalizationRegistry {
40
+ eventTypeRegistry;
41
+ logger = new common_1.Logger(ExternalizationRegistry_1.name);
42
+ mapping = new Map();
43
+ constructor(eventTypeRegistry) {
44
+ this.eventTypeRegistry = eventTypeRegistry;
45
+ }
46
+ onModuleInit() {
47
+ for (const [name, type] of this.eventTypeRegistry.getAll()) {
48
+ const metadata = (0, externalized_decorator_1.getExternalizedMetadata)(type);
49
+ if (metadata !== undefined) {
50
+ this.mapping.set(name, metadata);
51
+ this.logger.debug(`Registered externalization for ${name} → ${metadata.target}`);
52
+ }
53
+ }
54
+ if (this.mapping.size > 0) {
55
+ this.logger.log(`Externalization configured for ${this.mapping.size} event type(s)`);
56
+ }
57
+ }
58
+ /** Resolve {@link ExternalizedMetadata} for an event type, or `undefined`. */
59
+ get(eventType) {
60
+ return this.mapping.get(eventType);
61
+ }
62
+ /** Whether the given event type has an `@Externalized` mapping. */
63
+ has(eventType) {
64
+ return this.mapping.has(eventType);
65
+ }
66
+ /**
67
+ * Build the per-publication {@link ExternalizationMetadata} that the
68
+ * processor passes to the bound `EventExternalizer`. Resolves
69
+ * dynamic `routingKey` and `headers` callbacks by invoking them with
70
+ * the actual event instance.
71
+ *
72
+ * @returns Resolved metadata, or `undefined` if `eventType` has no
73
+ * `@Externalized` mapping (the processor then skips the
74
+ * externalization call entirely).
75
+ */
76
+ buildMetadata(eventType, event) {
77
+ const config = this.mapping.get(eventType);
78
+ if (config === undefined) {
79
+ return undefined;
80
+ }
81
+ const headers = typeof config.headers === 'function' ? config.headers(event) : config.headers;
82
+ return {
83
+ eventType,
84
+ target: config.target,
85
+ client: config.client,
86
+ routingKey: config.routingKey?.(event),
87
+ headers,
88
+ };
89
+ }
90
+ };
91
+ exports.ExternalizationRegistry = ExternalizationRegistry;
92
+ exports.ExternalizationRegistry = ExternalizationRegistry = ExternalizationRegistry_1 = __decorate([
93
+ (0, common_1.Injectable)(),
94
+ __metadata("design:paramtypes", [event_type_registry_1.EventTypeRegistry])
95
+ ], ExternalizationRegistry);
96
+ /**
97
+ * DI token for {@link ExternalizationRegistry}. Use the class token
98
+ * for direct injection — this Symbol is available for code that
99
+ * prefers token-based wiring or needs to override the registry in
100
+ * tests.
101
+ */
102
+ exports.EXTERNALIZATION_REGISTRY = Symbol('EXTERNALIZATION_REGISTRY');
103
+ //# sourceMappingURL=externalization-registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"externalization-registry.js","sourceRoot":"","sources":["../../src/externalization/externalization-registry.ts"],"names":[],"mappings":";;;;;;;;;;;;;AAAA,2CAAuE;AAEvE,8EAAyE;AAEzE,qEAGkC;AAGlC;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEI,IAAM,uBAAuB,+BAA7B,MAAM,uBAAuB;IAIL;IAHZ,MAAM,GAAG,IAAI,eAAM,CAAC,yBAAuB,CAAC,IAAI,CAAC,CAAC;IAClD,OAAO,GAAG,IAAI,GAAG,EAAgC,CAAC;IAEnE,YAA6B,iBAAoC;QAApC,sBAAiB,GAAjB,iBAAiB,CAAmB;IAAG,CAAC;IAErE,YAAY;QACV,KAAK,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,IAAI,CAAC,iBAAiB,CAAC,MAAM,EAAE,EAAE,CAAC;YAC3D,MAAM,QAAQ,GAAG,IAAA,gDAAuB,EAAC,IAAI,CAAC,CAAC;YAC/C,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;gBAC3B,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;gBACjC,IAAI,CAAC,MAAM,CAAC,KAAK,CACf,kCAAkC,IAAI,MAAM,QAAQ,CAAC,MAAM,EAAE,CAC9D,CAAC;YACJ,CAAC;QACH,CAAC;QAED,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC;YAC1B,IAAI,CAAC,MAAM,CAAC,GAAG,CACb,kCAAkC,IAAI,CAAC,OAAO,CAAC,IAAI,gBAAgB,CACpE,CAAC;QACJ,CAAC;IACH,CAAC;IAED,8EAA8E;IAC9E,GAAG,CAAC,SAAiB;QACnB,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IACrC,CAAC;IAED,mEAAmE;IACnE,GAAG,CAAC,SAAiB;QACnB,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IACrC,CAAC;IAED;;;;;;;;;OASG;IACH,aAAa,CAAC,SAAiB,EAAE,KAAc;QAC7C,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAC3C,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACzB,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,MAAM,OAAO,GACX,OAAO,MAAM,CAAC,OAAO,KAAK,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC;QAEhF,OAAO;YACL,SAAS;YACT,MAAM,EAAE,MAAM,CAAC,MAAM;YACrB,MAAM,EAAE,MAAM,CAAC,MAAM;YACrB,UAAU,EAAE,MAAM,CAAC,UAAU,EAAE,CAAC,KAAK,CAAC;YACtC,OAAO;SACR,CAAC;IACJ,CAAC;CACF,CAAA;AA7DY,0DAAuB;kCAAvB,uBAAuB;IADnC,IAAA,mBAAU,GAAE;qCAKqC,uCAAiB;GAJtD,uBAAuB,CA6DnC;AAED;;;;;GAKG;AACU,QAAA,wBAAwB,GAAG,MAAM,CAAC,0BAA0B,CAAC,CAAC"}
@@ -0,0 +1,121 @@
1
+ import 'reflect-metadata';
2
+ /**
3
+ * Metadata key under which {@link ExternalizedMetadata} is stored on
4
+ * classes decorated with {@link Externalized}.
5
+ *
6
+ * Fresh `Symbol` (not `Symbol.for`) — externalization metadata is read
7
+ * only by `ExternalizationRegistry` inside outbox; cross-package
8
+ * sharing is not required.
9
+ */
10
+ export declare const EXTERNALIZED_METADATA: unique symbol;
11
+ /**
12
+ * Options accepted by {@link Externalized}. The optional `TEvent`
13
+ * generic types the `routingKey` and `headers` callbacks so users get
14
+ * IDE assistance when extracting fields from the event:
15
+ *
16
+ * ```ts
17
+ * @Externalized<OrderPlacedEvent>({
18
+ * target: 'orders',
19
+ * routingKey: (e) => e.tenantId, // `e` is OrderPlacedEvent
20
+ * })
21
+ * class OrderPlacedEvent { ... }
22
+ * ```
23
+ *
24
+ * The generic is erased at storage time — the
25
+ * {@link ExternalizationRegistry} sees the callbacks as `(event:
26
+ * unknown) => ...` (see {@link ExternalizedMetadata}).
27
+ */
28
+ export interface ExternalizedOptions<TEvent = unknown> {
29
+ /**
30
+ * Broker-side destination — Kafka topic, RabbitMQ exchange, NATS
31
+ * subject, gRPC service, etc. Required, must be a non-empty string.
32
+ * Interpretation is delegated to the
33
+ * `EventExternalizer` implementation.
34
+ */
35
+ readonly target: string;
36
+ /**
37
+ * Optional override for which `ClientProxy` registration the
38
+ * externalizer should use when more than one is bound. Resolution
39
+ * semantics are owned by the externalizer; `outbox-microservices`
40
+ * (Phase 11.3) interprets it as a token in the user's
41
+ * `ClientsModule` registration (DD-017).
42
+ */
43
+ readonly client?: string | symbol;
44
+ /**
45
+ * Optional callback that derives a routing key from the event
46
+ * instance. Used by brokers that support secondary routing
47
+ * (RabbitMQ routing key, Kafka message key, ...). Implementations
48
+ * that do not understand routing keys ignore the field.
49
+ */
50
+ readonly routingKey?: (event: TEvent) => string;
51
+ /**
52
+ * Optional message headers. Either a static record, or a callback
53
+ * that derives headers from the event instance.
54
+ */
55
+ readonly headers?: Record<string, string> | ((event: TEvent) => Record<string, string>);
56
+ }
57
+ /**
58
+ * Stored shape of {@link ExternalizedOptions} after decoration. The
59
+ * `TEvent` generic from the input is erased — callbacks accept
60
+ * `unknown` and are invoked with the original event instance by
61
+ * {@link ExternalizationRegistry.buildMetadata}.
62
+ */
63
+ export interface ExternalizedMetadata {
64
+ readonly target: string;
65
+ readonly client?: string | symbol;
66
+ readonly routingKey?: (event: unknown) => string;
67
+ readonly headers?: Record<string, string> | ((event: unknown) => Record<string, string>);
68
+ }
69
+ /**
70
+ * Mark an event class for externalization to a message broker.
71
+ *
72
+ * After a local outbox listener completes successfully for a
73
+ * publication of this event type, the
74
+ * `EventPublicationProcessor` invokes the bound
75
+ * `EventExternalizer` (see DD-018) with the resolved
76
+ * {@link ExternalizationMetadata}. Reliability — retry on broker
77
+ * failure, recovery on restart — is provided by the existing outbox
78
+ * machinery (single-unit atomicity per DD-019). For Phase 11.1 / 11.2
79
+ * the actual broker delivery requires a concrete externalizer
80
+ * implementation (e.g.
81
+ * `@nestjs-transactional/outbox-microservices`, Phase 11.3); without
82
+ * one, decorated events are processed locally and the externalization
83
+ * step is skipped without error.
84
+ *
85
+ * @throws {Error} If `target` is missing, not a string, or empty.
86
+ *
87
+ * @example
88
+ * Static target:
89
+ * ```ts
90
+ * @Externalized({ target: 'orders.placed' })
91
+ * export class OrderPlacedEvent {
92
+ * constructor(public readonly orderId: string) {}
93
+ * }
94
+ * ```
95
+ *
96
+ * @example
97
+ * Per-event routing key and headers:
98
+ * ```ts
99
+ * @Externalized<OrderPlacedEvent>({
100
+ * target: 'orders',
101
+ * routingKey: (e) => e.tenantId,
102
+ * headers: (e) => ({ 'x-correlation-id': e.correlationId }),
103
+ * client: 'KAFKA_CLIENT', // forwarded to the externalizer
104
+ * })
105
+ * export class OrderPlacedEvent {
106
+ * constructor(
107
+ * readonly orderId: string,
108
+ * readonly tenantId: string,
109
+ * readonly correlationId: string,
110
+ * ) {}
111
+ * }
112
+ * ```
113
+ */
114
+ export declare function Externalized<TEvent = unknown>(options: ExternalizedOptions<TEvent>): ClassDecorator;
115
+ /**
116
+ * Read the {@link ExternalizedMetadata} attached to `target` by
117
+ * {@link Externalized}. Returns `undefined` when the class was not
118
+ * decorated.
119
+ */
120
+ export declare function getExternalizedMetadata(target: object): ExternalizedMetadata | undefined;
121
+ //# sourceMappingURL=externalized.decorator.d.ts.map
@@ -0,0 +1,87 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.EXTERNALIZED_METADATA = void 0;
4
+ exports.Externalized = Externalized;
5
+ exports.getExternalizedMetadata = getExternalizedMetadata;
6
+ require("reflect-metadata");
7
+ /**
8
+ * Metadata key under which {@link ExternalizedMetadata} is stored on
9
+ * classes decorated with {@link Externalized}.
10
+ *
11
+ * Fresh `Symbol` (not `Symbol.for`) — externalization metadata is read
12
+ * only by `ExternalizationRegistry` inside outbox; cross-package
13
+ * sharing is not required.
14
+ */
15
+ exports.EXTERNALIZED_METADATA = Symbol('EXTERNALIZED_METADATA');
16
+ /**
17
+ * Mark an event class for externalization to a message broker.
18
+ *
19
+ * After a local outbox listener completes successfully for a
20
+ * publication of this event type, the
21
+ * `EventPublicationProcessor` invokes the bound
22
+ * `EventExternalizer` (see DD-018) with the resolved
23
+ * {@link ExternalizationMetadata}. Reliability — retry on broker
24
+ * failure, recovery on restart — is provided by the existing outbox
25
+ * machinery (single-unit atomicity per DD-019). For Phase 11.1 / 11.2
26
+ * the actual broker delivery requires a concrete externalizer
27
+ * implementation (e.g.
28
+ * `@nestjs-transactional/outbox-microservices`, Phase 11.3); without
29
+ * one, decorated events are processed locally and the externalization
30
+ * step is skipped without error.
31
+ *
32
+ * @throws {Error} If `target` is missing, not a string, or empty.
33
+ *
34
+ * @example
35
+ * Static target:
36
+ * ```ts
37
+ * @Externalized({ target: 'orders.placed' })
38
+ * export class OrderPlacedEvent {
39
+ * constructor(public readonly orderId: string) {}
40
+ * }
41
+ * ```
42
+ *
43
+ * @example
44
+ * Per-event routing key and headers:
45
+ * ```ts
46
+ * @Externalized<OrderPlacedEvent>({
47
+ * target: 'orders',
48
+ * routingKey: (e) => e.tenantId,
49
+ * headers: (e) => ({ 'x-correlation-id': e.correlationId }),
50
+ * client: 'KAFKA_CLIENT', // forwarded to the externalizer
51
+ * })
52
+ * export class OrderPlacedEvent {
53
+ * constructor(
54
+ * readonly orderId: string,
55
+ * readonly tenantId: string,
56
+ * readonly correlationId: string,
57
+ * ) {}
58
+ * }
59
+ * ```
60
+ */
61
+ function Externalized(options) {
62
+ if (typeof options.target !== 'string' || options.target.length === 0) {
63
+ throw new Error('@Externalized requires "target" option as a non-empty string');
64
+ }
65
+ // The `TEvent` generic is erased on storage — the registry invokes
66
+ // callbacks with `unknown` and passes the original event instance
67
+ // captured at publish time.
68
+ const metadata = {
69
+ target: options.target,
70
+ client: options.client,
71
+ routingKey: options.routingKey,
72
+ headers: options.headers,
73
+ };
74
+ return (target) => {
75
+ Reflect.defineMetadata(exports.EXTERNALIZED_METADATA, metadata, target);
76
+ };
77
+ }
78
+ /**
79
+ * Read the {@link ExternalizedMetadata} attached to `target` by
80
+ * {@link Externalized}. Returns `undefined` when the class was not
81
+ * decorated.
82
+ */
83
+ function getExternalizedMetadata(target) {
84
+ const value = Reflect.getMetadata(exports.EXTERNALIZED_METADATA, target);
85
+ return value;
86
+ }
87
+ //# sourceMappingURL=externalized.decorator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"externalized.decorator.js","sourceRoot":"","sources":["../../src/externalization/externalized.decorator.ts"],"names":[],"mappings":";;;AAqHA,oCAuBC;AAOD,0DAGC;AAtJD,4BAA0B;AAE1B;;;;;;;GAOG;AACU,QAAA,qBAAqB,GAAG,MAAM,CAAC,uBAAuB,CAAC,CAAC;AA8DrE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AACH,SAAgB,YAAY,CAC1B,OAAoC;IAEpC,IAAI,OAAO,OAAO,CAAC,MAAM,KAAK,QAAQ,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtE,MAAM,IAAI,KAAK,CAAC,8DAA8D,CAAC,CAAC;IAClF,CAAC;IAED,mEAAmE;IACnE,kEAAkE;IAClE,4BAA4B;IAC5B,MAAM,QAAQ,GAAyB;QACrC,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,UAAU,EAAE,OAAO,CAAC,UAAsD;QAC1E,OAAO,EAAE,OAAO,CAAC,OAGJ;KACd,CAAC;IAEF,OAAO,CAAC,MAAc,EAAQ,EAAE;QAC9B,OAAO,CAAC,cAAc,CAAC,6BAAqB,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;IAClE,CAAC,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,SAAgB,uBAAuB,CAAC,MAAc;IACpD,MAAM,KAAK,GAAY,OAAO,CAAC,WAAW,CAAC,6BAAqB,EAAE,MAAM,CAAC,CAAC;IAC1E,OAAO,KAAyC,CAAC;AACnD,CAAC"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Metadata describing how a single event should be routed to an
3
+ * external message broker.
4
+ *
5
+ * Produced by the `ExternalizationRegistry` (Phase 11.2) by resolving
6
+ * the event's class against the metadata stored by the `@Externalized`
7
+ * decorator. Consumed by an {@link EventExternalizer} implementation
8
+ * (e.g. `MicroservicesEventExternalizer` from
9
+ * `@nestjs-transactional/outbox-microservices`, Phase 11.3) which
10
+ * translates the abstract `target` / `routingKey` / `headers` shape
11
+ * into transport-specific calls.
12
+ *
13
+ * Field semantics:
14
+ * - `eventType` matches `EventPublication.eventType` and is filled in
15
+ * for diagnostics — externalizer implementations should not need to
16
+ * re-derive it from the payload.
17
+ * - `target` is the broker-side destination (Kafka topic, RabbitMQ
18
+ * exchange, NATS subject, gRPC method, ...). Interpretation is
19
+ * delegated to the externalizer.
20
+ * - `routingKey` is the optional sub-routing token for brokers that
21
+ * support it (RabbitMQ routing key, Kafka partition key when used
22
+ * that way). Implementations that do not understand routing keys
23
+ * should ignore the field.
24
+ * - `headers` is an optional flat string map propagated to the
25
+ * transport's headers / metadata if the transport supports them.
26
+ * - `client` is an optional override for which `ClientProxy`
27
+ * registration the externalizer should use when more than one is
28
+ * bound. Resolution semantics are owned by the externalizer
29
+ * implementation — `outbox-microservices` interprets it as a token
30
+ * in the user's `ClientsModule` registration (DD-017).
31
+ */
32
+ export interface ExternalizationMetadata {
33
+ readonly eventType: string;
34
+ readonly target: string;
35
+ readonly routingKey?: string;
36
+ readonly headers?: Record<string, string>;
37
+ readonly client?: string | symbol;
38
+ }
39
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/externalization/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,38 @@
1
+ export * from './types/publication-status';
2
+ export * from './types/event-publication';
3
+ export * from './types/completion-mode';
4
+ export * from './types/resubmission-options';
5
+ export * from './types/staleness-config';
6
+ export * from './types/errors';
7
+ export * from './serialization/event-serializer';
8
+ export * from './serialization/event-type-registry';
9
+ export * from './serialization/event-type-resolver';
10
+ export * from './serialization/json-event-serializer';
11
+ export * from './repository/event-publication-repository';
12
+ export * from './registry/event-publication-registry';
13
+ export * from './registry/listener-registry';
14
+ export * from './registry/multi-ds-listener-registrar';
15
+ export * from './registry/outbox-listener-scanner';
16
+ export * from './decorators/outbox-events-handler.decorator';
17
+ export * from './decorators/inject-decorators';
18
+ export * from './tokens';
19
+ export type { IOutboxEventHandler } from './interfaces/outbox-event-handler.interface';
20
+ export * from './dispatcher/outbox-event-publisher';
21
+ export * from './dispatcher/data-source-outbox-publisher';
22
+ export * from './dispatcher/event-publication-processor';
23
+ export * from './dispatcher/processor-options';
24
+ export type { ExternalizationMetadata } from './externalization/types';
25
+ export type { EventExternalizer } from './externalization/event-externalizer';
26
+ export { EVENT_EXTERNALIZER } from './externalization/event-externalizer';
27
+ export { ExternalizationError } from './externalization/errors';
28
+ export { Externalized, getExternalizedMetadata, EXTERNALIZED_METADATA, } from './externalization/externalized.decorator';
29
+ export type { ExternalizedOptions, ExternalizedMetadata, } from './externalization/externalized.decorator';
30
+ export { ExternalizationRegistry, EXTERNALIZATION_REGISTRY, } from './externalization/externalization-registry';
31
+ export * from './recovery/staleness-monitor';
32
+ export * from './api/failed-event-publications';
33
+ export * from './api/incomplete-event-publications';
34
+ export * from './api/completed-event-publications';
35
+ export * from './recovery/startup-recovery';
36
+ export * from './module/outbox.module';
37
+ export * from './module/outbox-processing.module';
38
+ //# sourceMappingURL=index.d.ts.map
package/dist/index.js ADDED
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.EXTERNALIZATION_REGISTRY = exports.ExternalizationRegistry = exports.EXTERNALIZED_METADATA = exports.getExternalizedMetadata = exports.Externalized = exports.ExternalizationError = exports.EVENT_EXTERNALIZER = void 0;
18
+ __exportStar(require("./types/publication-status"), exports);
19
+ __exportStar(require("./types/event-publication"), exports);
20
+ __exportStar(require("./types/completion-mode"), exports);
21
+ __exportStar(require("./types/resubmission-options"), exports);
22
+ __exportStar(require("./types/staleness-config"), exports);
23
+ __exportStar(require("./types/errors"), exports);
24
+ __exportStar(require("./serialization/event-serializer"), exports);
25
+ __exportStar(require("./serialization/event-type-registry"), exports);
26
+ __exportStar(require("./serialization/event-type-resolver"), exports);
27
+ __exportStar(require("./serialization/json-event-serializer"), exports);
28
+ __exportStar(require("./repository/event-publication-repository"), exports);
29
+ __exportStar(require("./registry/event-publication-registry"), exports);
30
+ __exportStar(require("./registry/listener-registry"), exports);
31
+ __exportStar(require("./registry/multi-ds-listener-registrar"), exports);
32
+ __exportStar(require("./registry/outbox-listener-scanner"), exports);
33
+ __exportStar(require("./decorators/outbox-events-handler.decorator"), exports);
34
+ __exportStar(require("./decorators/inject-decorators"), exports);
35
+ __exportStar(require("./tokens"), exports);
36
+ __exportStar(require("./dispatcher/outbox-event-publisher"), exports);
37
+ __exportStar(require("./dispatcher/data-source-outbox-publisher"), exports);
38
+ __exportStar(require("./dispatcher/event-publication-processor"), exports);
39
+ __exportStar(require("./dispatcher/processor-options"), exports);
40
+ var event_externalizer_1 = require("./externalization/event-externalizer");
41
+ Object.defineProperty(exports, "EVENT_EXTERNALIZER", { enumerable: true, get: function () { return event_externalizer_1.EVENT_EXTERNALIZER; } });
42
+ var errors_1 = require("./externalization/errors");
43
+ Object.defineProperty(exports, "ExternalizationError", { enumerable: true, get: function () { return errors_1.ExternalizationError; } });
44
+ var externalized_decorator_1 = require("./externalization/externalized.decorator");
45
+ Object.defineProperty(exports, "Externalized", { enumerable: true, get: function () { return externalized_decorator_1.Externalized; } });
46
+ Object.defineProperty(exports, "getExternalizedMetadata", { enumerable: true, get: function () { return externalized_decorator_1.getExternalizedMetadata; } });
47
+ Object.defineProperty(exports, "EXTERNALIZED_METADATA", { enumerable: true, get: function () { return externalized_decorator_1.EXTERNALIZED_METADATA; } });
48
+ var externalization_registry_1 = require("./externalization/externalization-registry");
49
+ Object.defineProperty(exports, "ExternalizationRegistry", { enumerable: true, get: function () { return externalization_registry_1.ExternalizationRegistry; } });
50
+ Object.defineProperty(exports, "EXTERNALIZATION_REGISTRY", { enumerable: true, get: function () { return externalization_registry_1.EXTERNALIZATION_REGISTRY; } });
51
+ __exportStar(require("./recovery/staleness-monitor"), exports);
52
+ __exportStar(require("./api/failed-event-publications"), exports);
53
+ __exportStar(require("./api/incomplete-event-publications"), exports);
54
+ __exportStar(require("./api/completed-event-publications"), exports);
55
+ __exportStar(require("./recovery/startup-recovery"), exports);
56
+ __exportStar(require("./module/outbox.module"), exports);
57
+ __exportStar(require("./module/outbox-processing.module"), exports);
58
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAAA,6DAA2C;AAC3C,4DAA0C;AAC1C,0DAAwC;AACxC,+DAA6C;AAC7C,2DAAyC;AACzC,iDAA+B;AAE/B,mEAAiD;AACjD,sEAAoD;AACpD,sEAAoD;AACpD,wEAAsD;AAEtD,4EAA0D;AAE1D,wEAAsD;AACtD,+DAA6C;AAC7C,yEAAuD;AACvD,qEAAmD;AAEnD,+EAA6D;AAC7D,iEAA+C;AAE/C,2CAAyB;AAIzB,sEAAoD;AACpD,4EAA0D;AAC1D,2EAAyD;AACzD,iEAA+C;AAI/C,2EAA0E;AAAjE,wHAAA,kBAAkB,OAAA;AAC3B,mDAAgE;AAAvD,8GAAA,oBAAoB,OAAA;AAC7B,mFAIkD;AAHhD,sHAAA,YAAY,OAAA;AACZ,iIAAA,uBAAuB,OAAA;AACvB,+HAAA,qBAAqB,OAAA;AAMvB,uFAGoD;AAFlD,mIAAA,uBAAuB,OAAA;AACvB,oIAAA,wBAAwB,OAAA;AAG1B,+DAA6C;AAE7C,kEAAgD;AAChD,sEAAoD;AACpD,qEAAmD;AAEnD,8DAA4C;AAE5C,yDAAuC;AACvC,oEAAkD"}