@haskou/ddd-kernel 0.1.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/LICENSE +21 -0
- package/README.md +143 -0
- package/dist/Constructor-fyZLLIh8.d.cts +5 -0
- package/dist/Constructor-fyZLLIh8.d.ts +5 -0
- package/dist/Consumer-CC8ZRCsd.d.cts +17 -0
- package/dist/Consumer-CeT0Wbxb.d.ts +17 -0
- package/dist/DomainEvent-mXWEtr_J.d.cts +26 -0
- package/dist/DomainEvent-mXWEtr_J.d.ts +26 -0
- package/dist/DomainEventConsumer-3WBMSSr2.d.cts +7 -0
- package/dist/DomainEventConsumer-B4hkIUmP.d.ts +7 -0
- package/dist/DomainEventPublisher-8G0lvmdy.d.cts +7 -0
- package/dist/DomainEventPublisher-DhGgM3f2.d.ts +7 -0
- package/dist/HandlerContext-DUUExVo3.d.ts +20 -0
- package/dist/HandlerContext-Lm89pSeG.d.cts +20 -0
- package/dist/Kernel-BWUOUWWI.d.cts +70 -0
- package/dist/Kernel-CUaqHa1s.d.ts +70 -0
- package/dist/KernelLogger-BESOFrpW.d.cts +8 -0
- package/dist/KernelLogger-BESOFrpW.d.ts +8 -0
- package/dist/Log-36L3Z84a.d.cts +8 -0
- package/dist/Log-36L3Z84a.d.ts +8 -0
- package/dist/NoFailedMessagesError-0YJKRWPF.d.ts +45 -0
- package/dist/NoFailedMessagesError-Kz7CYWpT.d.cts +45 -0
- package/dist/Repository-D9CuAyCV.d.cts +7 -0
- package/dist/Repository-D9CuAyCV.d.ts +7 -0
- package/dist/Scheduler-oigqNOUJ.d.cts +27 -0
- package/dist/Scheduler-oigqNOUJ.d.ts +27 -0
- package/dist/ServiceClass-BmNw8fJj.d.cts +37 -0
- package/dist/ServiceClass-C7NCKdSS.d.ts +37 -0
- package/dist/ServiceResolver-D2Jz-l_Z.d.cts +5 -0
- package/dist/ServiceResolver-D2Jz-l_Z.d.ts +5 -0
- package/dist/ShutdownHook-BGskq2-q.d.ts +9 -0
- package/dist/ShutdownHook-Dib5uNKB.d.cts +9 -0
- package/dist/Subscription-Bwkb_did.d.ts +9 -0
- package/dist/Subscription-P9WROD_6.d.cts +9 -0
- package/dist/adapters/db/in-memory/index.cjs +46 -0
- package/dist/adapters/db/in-memory/index.cjs.map +1 -0
- package/dist/adapters/db/in-memory/index.d.cts +12 -0
- package/dist/adapters/db/in-memory/index.d.ts +12 -0
- package/dist/adapters/db/in-memory/index.js +19 -0
- package/dist/adapters/db/in-memory/index.js.map +1 -0
- package/dist/adapters/db/index.cjs +80 -0
- package/dist/adapters/db/index.cjs.map +1 -0
- package/dist/adapters/db/index.d.cts +4 -0
- package/dist/adapters/db/index.d.ts +4 -0
- package/dist/adapters/db/index.js +52 -0
- package/dist/adapters/db/index.js.map +1 -0
- package/dist/adapters/db/mongo/index.cjs +62 -0
- package/dist/adapters/db/mongo/index.cjs.map +1 -0
- package/dist/adapters/db/mongo/index.d.cts +22 -0
- package/dist/adapters/db/mongo/index.d.ts +22 -0
- package/dist/adapters/db/mongo/index.js +35 -0
- package/dist/adapters/db/mongo/index.js.map +1 -0
- package/dist/adapters/index.cjs +651 -0
- package/dist/adapters/index.cjs.map +1 -0
- package/dist/adapters/index.d.cts +26 -0
- package/dist/adapters/index.d.ts +26 -0
- package/dist/adapters/index.js +609 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/adapters/kernel/console/index.cjs +46 -0
- package/dist/adapters/kernel/console/index.cjs.map +1 -0
- package/dist/adapters/kernel/console/index.d.cts +10 -0
- package/dist/adapters/kernel/console/index.d.ts +10 -0
- package/dist/adapters/kernel/console/index.js +19 -0
- package/dist/adapters/kernel/console/index.js.map +1 -0
- package/dist/adapters/kernel/index.cjs +46 -0
- package/dist/adapters/kernel/index.cjs.map +1 -0
- package/dist/adapters/kernel/index.d.cts +2 -0
- package/dist/adapters/kernel/index.d.ts +2 -0
- package/dist/adapters/kernel/index.js +19 -0
- package/dist/adapters/kernel/index.js.map +1 -0
- package/dist/adapters/pubsub/amqp/index.cjs +724 -0
- package/dist/adapters/pubsub/amqp/index.cjs.map +1 -0
- package/dist/adapters/pubsub/amqp/index.d.cts +42 -0
- package/dist/adapters/pubsub/amqp/index.d.ts +42 -0
- package/dist/adapters/pubsub/amqp/index.js +691 -0
- package/dist/adapters/pubsub/amqp/index.js.map +1 -0
- package/dist/adapters/pubsub/in-memory/index.cjs +79 -0
- package/dist/adapters/pubsub/in-memory/index.cjs.map +1 -0
- package/dist/adapters/pubsub/in-memory/index.d.cts +21 -0
- package/dist/adapters/pubsub/in-memory/index.d.ts +21 -0
- package/dist/adapters/pubsub/in-memory/index.js +51 -0
- package/dist/adapters/pubsub/in-memory/index.js.map +1 -0
- package/dist/adapters/pubsub/index.cjs +492 -0
- package/dist/adapters/pubsub/index.cjs.map +1 -0
- package/dist/adapters/pubsub/index.d.cts +11 -0
- package/dist/adapters/pubsub/index.d.ts +11 -0
- package/dist/adapters/pubsub/index.js +456 -0
- package/dist/adapters/pubsub/index.js.map +1 -0
- package/dist/adapters/ui/express/index.cjs +126 -0
- package/dist/adapters/ui/express/index.cjs.map +1 -0
- package/dist/adapters/ui/express/index.d.cts +49 -0
- package/dist/adapters/ui/express/index.d.ts +49 -0
- package/dist/adapters/ui/express/index.js +98 -0
- package/dist/adapters/ui/express/index.js.map +1 -0
- package/dist/adapters/ui/index.cjs +489 -0
- package/dist/adapters/ui/index.cjs.map +1 -0
- package/dist/adapters/ui/index.d.cts +14 -0
- package/dist/adapters/ui/index.d.ts +14 -0
- package/dist/adapters/ui/index.js +455 -0
- package/dist/adapters/ui/index.js.map +1 -0
- package/dist/adapters/ui/routes/index.cjs +393 -0
- package/dist/adapters/ui/routes/index.cjs.map +1 -0
- package/dist/adapters/ui/routes/index.d.cts +5 -0
- package/dist/adapters/ui/routes/index.d.ts +5 -0
- package/dist/adapters/ui/routes/index.js +361 -0
- package/dist/adapters/ui/routes/index.js.map +1 -0
- package/dist/contracts/db/index.cjs +19 -0
- package/dist/contracts/db/index.cjs.map +1 -0
- package/dist/contracts/db/index.d.cts +7 -0
- package/dist/contracts/db/index.d.ts +7 -0
- package/dist/contracts/db/index.js +1 -0
- package/dist/contracts/db/index.js.map +1 -0
- package/dist/contracts/index.cjs +49 -0
- package/dist/contracts/index.cjs.map +1 -0
- package/dist/contracts/index.d.cts +11 -0
- package/dist/contracts/index.d.ts +11 -0
- package/dist/contracts/index.js +22 -0
- package/dist/contracts/index.js.map +1 -0
- package/dist/contracts/kernel/index.cjs +19 -0
- package/dist/contracts/kernel/index.cjs.map +1 -0
- package/dist/contracts/kernel/index.d.cts +9 -0
- package/dist/contracts/kernel/index.d.ts +9 -0
- package/dist/contracts/kernel/index.js +1 -0
- package/dist/contracts/kernel/index.js.map +1 -0
- package/dist/contracts/pubsub/index.cjs +19 -0
- package/dist/contracts/pubsub/index.cjs.map +1 -0
- package/dist/contracts/pubsub/index.d.cts +26 -0
- package/dist/contracts/pubsub/index.d.ts +26 -0
- package/dist/contracts/pubsub/index.js +1 -0
- package/dist/contracts/pubsub/index.js.map +1 -0
- package/dist/contracts/ui/index.cjs +49 -0
- package/dist/contracts/ui/index.cjs.map +1 -0
- package/dist/contracts/ui/index.d.cts +29 -0
- package/dist/contracts/ui/index.d.ts +29 -0
- package/dist/contracts/ui/index.js +22 -0
- package/dist/contracts/ui/index.js.map +1 -0
- package/dist/domain/index.cjs +121 -0
- package/dist/domain/index.cjs.map +1 -0
- package/dist/domain/index.d.cts +26 -0
- package/dist/domain/index.d.ts +26 -0
- package/dist/domain/index.js +89 -0
- package/dist/domain/index.js.map +1 -0
- package/dist/errors/index.cjs +58 -0
- package/dist/errors/index.cjs.map +1 -0
- package/dist/errors/index.d.cts +13 -0
- package/dist/errors/index.d.ts +13 -0
- package/dist/errors/index.js +29 -0
- package/dist/errors/index.js.map +1 -0
- package/dist/index.cjs +442 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +19 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +405 -0
- package/dist/index.js.map +1 -0
- package/dist/infrastructure/dependency-injection/index.cjs +169 -0
- package/dist/infrastructure/dependency-injection/index.cjs.map +1 -0
- package/dist/infrastructure/dependency-injection/index.d.cts +30 -0
- package/dist/infrastructure/dependency-injection/index.d.ts +30 -0
- package/dist/infrastructure/dependency-injection/index.js +137 -0
- package/dist/infrastructure/dependency-injection/index.js.map +1 -0
- package/dist/infrastructure/express/index.cjs +126 -0
- package/dist/infrastructure/express/index.cjs.map +1 -0
- package/dist/infrastructure/express/index.d.cts +14 -0
- package/dist/infrastructure/express/index.d.ts +14 -0
- package/dist/infrastructure/express/index.js +98 -0
- package/dist/infrastructure/express/index.js.map +1 -0
- package/dist/infrastructure/lifecycle/index.cjs +19 -0
- package/dist/infrastructure/lifecycle/index.cjs.map +1 -0
- package/dist/infrastructure/lifecycle/index.d.cts +9 -0
- package/dist/infrastructure/lifecycle/index.d.ts +9 -0
- package/dist/infrastructure/lifecycle/index.js +1 -0
- package/dist/infrastructure/lifecycle/index.js.map +1 -0
- package/dist/infrastructure/logs/index.cjs +144 -0
- package/dist/infrastructure/logs/index.cjs.map +1 -0
- package/dist/infrastructure/logs/index.d.cts +30 -0
- package/dist/infrastructure/logs/index.d.ts +30 -0
- package/dist/infrastructure/logs/index.js +107 -0
- package/dist/infrastructure/logs/index.js.map +1 -0
- package/dist/infrastructure/scheduler/index.cjs +461 -0
- package/dist/infrastructure/scheduler/index.cjs.map +1 -0
- package/dist/infrastructure/scheduler/index.d.cts +17 -0
- package/dist/infrastructure/scheduler/index.d.ts +17 -0
- package/dist/infrastructure/scheduler/index.js +426 -0
- package/dist/infrastructure/scheduler/index.js.map +1 -0
- package/dist/infrastructure/websocket/index.cjs +131 -0
- package/dist/infrastructure/websocket/index.cjs.map +1 -0
- package/dist/infrastructure/websocket/index.d.cts +56 -0
- package/dist/infrastructure/websocket/index.d.ts +56 -0
- package/dist/infrastructure/websocket/index.js +103 -0
- package/dist/infrastructure/websocket/index.js.map +1 -0
- package/package.json +248 -0
|
@@ -0,0 +1,691 @@
|
|
|
1
|
+
// src/adapters/pubsub/amqp/AmqpMessageBusAdapter.ts
|
|
2
|
+
import amqplib from "amqplib";
|
|
3
|
+
import { randomUUID } from "crypto";
|
|
4
|
+
|
|
5
|
+
// src/Kernel.ts
|
|
6
|
+
import path2 from "path";
|
|
7
|
+
|
|
8
|
+
// src/adapters/kernel/console/ConsoleKernelLogger.ts
|
|
9
|
+
var ConsoleKernelLogger = class {
|
|
10
|
+
debug(message) {
|
|
11
|
+
console.debug(message);
|
|
12
|
+
}
|
|
13
|
+
error(message) {
|
|
14
|
+
console.error(message);
|
|
15
|
+
}
|
|
16
|
+
info(message) {
|
|
17
|
+
console.info(message);
|
|
18
|
+
}
|
|
19
|
+
warn(message) {
|
|
20
|
+
console.warn(message);
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
// src/infrastructure/dependency-injection/DependencyInjection.ts
|
|
25
|
+
import fs from "fs-extra";
|
|
26
|
+
import {
|
|
27
|
+
Autowire,
|
|
28
|
+
ContainerBuilder,
|
|
29
|
+
ServiceFile,
|
|
30
|
+
YamlFileLoader
|
|
31
|
+
} from "node-dependency-injection";
|
|
32
|
+
import path from "path";
|
|
33
|
+
var DependencyInjection = class _DependencyInjection {
|
|
34
|
+
constructor(options = {
|
|
35
|
+
containerBuild: process.env.CONTAINER_BUILD === "true",
|
|
36
|
+
servicesYamlPath: path.resolve(
|
|
37
|
+
process.cwd(),
|
|
38
|
+
"config",
|
|
39
|
+
"container",
|
|
40
|
+
"services.yaml"
|
|
41
|
+
),
|
|
42
|
+
sourceDirectory: path.resolve(process.cwd(), "src")
|
|
43
|
+
}) {
|
|
44
|
+
this.options = options;
|
|
45
|
+
this.container = new ContainerBuilder(false, this.options.sourceDirectory);
|
|
46
|
+
}
|
|
47
|
+
options;
|
|
48
|
+
static configuredInstance;
|
|
49
|
+
autowire;
|
|
50
|
+
loader;
|
|
51
|
+
container;
|
|
52
|
+
static configure(options) {
|
|
53
|
+
_DependencyInjection.configuredInstance = new _DependencyInjection(options);
|
|
54
|
+
return _DependencyInjection.configuredInstance;
|
|
55
|
+
}
|
|
56
|
+
static get instance() {
|
|
57
|
+
if (!_DependencyInjection.configuredInstance) {
|
|
58
|
+
throw new Error("DependencyInjection has not been configured.");
|
|
59
|
+
}
|
|
60
|
+
return _DependencyInjection.configuredInstance;
|
|
61
|
+
}
|
|
62
|
+
get definitions() {
|
|
63
|
+
const container = this.container;
|
|
64
|
+
return container._definitions || /* @__PURE__ */ new Map();
|
|
65
|
+
}
|
|
66
|
+
get aliases() {
|
|
67
|
+
const container = this.container;
|
|
68
|
+
return container._alias || /* @__PURE__ */ new Map();
|
|
69
|
+
}
|
|
70
|
+
async ensureFolderExists(filePath) {
|
|
71
|
+
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
72
|
+
}
|
|
73
|
+
getServiceClassName(serviceName) {
|
|
74
|
+
return typeof serviceName === "function" ? serviceName.name : void 0;
|
|
75
|
+
}
|
|
76
|
+
parentMatchesService(parentId, serviceClassName) {
|
|
77
|
+
if (!parentId) {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
const parentName = Buffer.from(parentId, "base64").toString("utf8");
|
|
81
|
+
return parentName.endsWith(`__${serviceClassName}__${serviceClassName}`);
|
|
82
|
+
}
|
|
83
|
+
serviceIdMatchesService(serviceId, serviceClassName) {
|
|
84
|
+
const serviceName = Buffer.from(serviceId, "base64").toString("utf8");
|
|
85
|
+
return serviceName.endsWith(`__${serviceClassName}__${serviceClassName}`);
|
|
86
|
+
}
|
|
87
|
+
findConcreteChildServiceId(serviceName) {
|
|
88
|
+
const serviceClassName = this.getServiceClassName(serviceName);
|
|
89
|
+
if (!serviceClassName) {
|
|
90
|
+
return void 0;
|
|
91
|
+
}
|
|
92
|
+
const matches = [...this.definitions.entries()].filter(([, definition]) => definition._abstract !== true).filter(
|
|
93
|
+
([, definition]) => this.parentMatchesService(definition._parent, serviceClassName)
|
|
94
|
+
).map(([id]) => id);
|
|
95
|
+
return matches[matches.length - 1];
|
|
96
|
+
}
|
|
97
|
+
findRegisteredServiceId(serviceName) {
|
|
98
|
+
const serviceClassName = this.getServiceClassName(serviceName);
|
|
99
|
+
if (!serviceClassName) {
|
|
100
|
+
return void 0;
|
|
101
|
+
}
|
|
102
|
+
const matches = [...this.definitions.keys()].filter(
|
|
103
|
+
(id) => this.serviceIdMatchesService(id, serviceClassName)
|
|
104
|
+
);
|
|
105
|
+
return matches[matches.length - 1];
|
|
106
|
+
}
|
|
107
|
+
findAliasServiceId(serviceName) {
|
|
108
|
+
const serviceClassName = this.getServiceClassName(serviceName);
|
|
109
|
+
if (!serviceClassName) {
|
|
110
|
+
return void 0;
|
|
111
|
+
}
|
|
112
|
+
const matches = [...this.aliases.keys()].filter(
|
|
113
|
+
(id) => this.serviceIdMatchesService(id, serviceClassName)
|
|
114
|
+
);
|
|
115
|
+
return matches[matches.length - 1];
|
|
116
|
+
}
|
|
117
|
+
registerParentAliases() {
|
|
118
|
+
for (const [id, definition] of this.definitions.entries()) {
|
|
119
|
+
if (definition._abstract === true || !definition._parent) {
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
this.container.setAlias(definition._parent, id);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
async compile() {
|
|
126
|
+
if (this.options.containerBuild) {
|
|
127
|
+
await this.ensureFolderExists(this.options.servicesYamlPath);
|
|
128
|
+
this.autowire = new Autowire(this.container);
|
|
129
|
+
this.autowire.serviceFile = new ServiceFile(
|
|
130
|
+
this.options.servicesYamlPath,
|
|
131
|
+
false
|
|
132
|
+
);
|
|
133
|
+
await this.autowire.process();
|
|
134
|
+
} else {
|
|
135
|
+
this.loader = new YamlFileLoader(this.container);
|
|
136
|
+
await this.loader.load(this.options.servicesYamlPath);
|
|
137
|
+
}
|
|
138
|
+
this.registerParentAliases();
|
|
139
|
+
await this.container.compile();
|
|
140
|
+
}
|
|
141
|
+
getService(serviceName) {
|
|
142
|
+
const childServiceId = this.findConcreteChildServiceId(serviceName);
|
|
143
|
+
if (childServiceId) {
|
|
144
|
+
return this.container.get(childServiceId);
|
|
145
|
+
}
|
|
146
|
+
const aliasServiceId = this.findAliasServiceId(serviceName);
|
|
147
|
+
if (aliasServiceId) {
|
|
148
|
+
return this.container.get(aliasServiceId);
|
|
149
|
+
}
|
|
150
|
+
const registeredServiceId = this.findRegisteredServiceId(serviceName);
|
|
151
|
+
if (registeredServiceId) {
|
|
152
|
+
return this.container.get(registeredServiceId);
|
|
153
|
+
}
|
|
154
|
+
return this.container.get(serviceName);
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
// src/Kernel.ts
|
|
159
|
+
var Kernel = class _Kernel {
|
|
160
|
+
constructor(options = {}) {
|
|
161
|
+
this.options = options;
|
|
162
|
+
this.loggerInstance = options.logger ?? new ConsoleKernelLogger();
|
|
163
|
+
this.dependencyInjectionInstance = options.di;
|
|
164
|
+
_Kernel.state.activeKernel = this;
|
|
165
|
+
}
|
|
166
|
+
options;
|
|
167
|
+
static stateKey = /* @__PURE__ */ Symbol.for(
|
|
168
|
+
"@haskou/ddd-kernel/kernel-state"
|
|
169
|
+
);
|
|
170
|
+
consumerMiddlewares = [];
|
|
171
|
+
consumersList = [];
|
|
172
|
+
loggerInstance;
|
|
173
|
+
routesList = [];
|
|
174
|
+
schedulersList = [];
|
|
175
|
+
shutdownHooks = [];
|
|
176
|
+
dependencyInjectionInstance;
|
|
177
|
+
static get state() {
|
|
178
|
+
const stateContainer = globalThis;
|
|
179
|
+
stateContainer[_Kernel.stateKey] = stateContainer[_Kernel.stateKey] ?? {};
|
|
180
|
+
return stateContainer[_Kernel.stateKey];
|
|
181
|
+
}
|
|
182
|
+
static get configDirectory() {
|
|
183
|
+
return path2.resolve(_Kernel.rootDirectory, "config");
|
|
184
|
+
}
|
|
185
|
+
static get consumers() {
|
|
186
|
+
return _Kernel.getActiveKernel().consumers;
|
|
187
|
+
}
|
|
188
|
+
static get consumerMiddleware() {
|
|
189
|
+
return _Kernel.getActiveKernel().consumerMiddleware;
|
|
190
|
+
}
|
|
191
|
+
static get di() {
|
|
192
|
+
return _Kernel.getActiveKernel().di;
|
|
193
|
+
}
|
|
194
|
+
static get logger() {
|
|
195
|
+
return _Kernel.getActiveKernel().logger;
|
|
196
|
+
}
|
|
197
|
+
static get rootDirectory() {
|
|
198
|
+
return process.cwd();
|
|
199
|
+
}
|
|
200
|
+
static get routes() {
|
|
201
|
+
return _Kernel.getActiveKernel().routes;
|
|
202
|
+
}
|
|
203
|
+
static get schedulers() {
|
|
204
|
+
return _Kernel.getActiveKernel().schedulers;
|
|
205
|
+
}
|
|
206
|
+
static get sourceDirectory() {
|
|
207
|
+
return path2.resolve(_Kernel.rootDirectory, "src");
|
|
208
|
+
}
|
|
209
|
+
static getActiveKernel() {
|
|
210
|
+
if (!_Kernel.state.activeKernel) {
|
|
211
|
+
_Kernel.state.activeKernel = new _Kernel();
|
|
212
|
+
}
|
|
213
|
+
return _Kernel.state.activeKernel;
|
|
214
|
+
}
|
|
215
|
+
async closeCandidate(candidate) {
|
|
216
|
+
if (candidate.shutdown) {
|
|
217
|
+
await candidate.shutdown();
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
if (candidate.close) {
|
|
221
|
+
await candidate.close();
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
if (candidate.stop) {
|
|
225
|
+
await candidate.stop();
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
if (candidate.flush) {
|
|
229
|
+
await candidate.flush();
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
getConsumerFromClass(ClassDefinition) {
|
|
233
|
+
return this.di.getService(ClassDefinition);
|
|
234
|
+
}
|
|
235
|
+
getInitializerFromClass(ClassDefinition) {
|
|
236
|
+
return this.di.getService(ClassDefinition);
|
|
237
|
+
}
|
|
238
|
+
getRuntimeFromClass(ClassDefinition) {
|
|
239
|
+
return this.di.getService(ClassDefinition);
|
|
240
|
+
}
|
|
241
|
+
getSchedulerFromClass(ClassDefinition) {
|
|
242
|
+
return this.di.getService(ClassDefinition);
|
|
243
|
+
}
|
|
244
|
+
get consumers() {
|
|
245
|
+
return this.consumersList;
|
|
246
|
+
}
|
|
247
|
+
get consumerMiddleware() {
|
|
248
|
+
return this.consumerMiddlewares;
|
|
249
|
+
}
|
|
250
|
+
get di() {
|
|
251
|
+
if (!this.dependencyInjectionInstance) {
|
|
252
|
+
throw new Error("Kernel dependency injection has not been initialized.");
|
|
253
|
+
}
|
|
254
|
+
return this.dependencyInjectionInstance;
|
|
255
|
+
}
|
|
256
|
+
get logger() {
|
|
257
|
+
return this.loggerInstance;
|
|
258
|
+
}
|
|
259
|
+
get routes() {
|
|
260
|
+
return this.routesList;
|
|
261
|
+
}
|
|
262
|
+
get schedulers() {
|
|
263
|
+
return this.schedulersList;
|
|
264
|
+
}
|
|
265
|
+
async dependencyInjection() {
|
|
266
|
+
this.dependencyInjectionInstance = this.dependencyInjectionInstance ?? DependencyInjection.configure({
|
|
267
|
+
containerBuild: process.env.CONTAINER_BUILD === "true",
|
|
268
|
+
servicesYamlPath: this.options.servicesYamlPath ?? path2.resolve(_Kernel.configDirectory, "container", "services.yaml"),
|
|
269
|
+
sourceDirectory: this.options.sourceDirectory ?? _Kernel.sourceDirectory
|
|
270
|
+
});
|
|
271
|
+
await this.dependencyInjectionInstance.compile();
|
|
272
|
+
}
|
|
273
|
+
getRoutes() {
|
|
274
|
+
return this.routes;
|
|
275
|
+
}
|
|
276
|
+
registerConsumerMiddleware(...middlewares) {
|
|
277
|
+
this.consumerMiddlewares.push(...middlewares);
|
|
278
|
+
}
|
|
279
|
+
registerConsumers(...ClassDefinitions) {
|
|
280
|
+
for (const ClassDefinition of ClassDefinitions) {
|
|
281
|
+
this.consumersList.push(this.getConsumerFromClass(ClassDefinition));
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
registerConsumerInstances(...consumers) {
|
|
285
|
+
this.consumersList.push(...consumers);
|
|
286
|
+
}
|
|
287
|
+
registerRoutes(...ClassDefinitions) {
|
|
288
|
+
this.routesList.push(...ClassDefinitions);
|
|
289
|
+
}
|
|
290
|
+
registerSchedulers(...ClassDefinitions) {
|
|
291
|
+
for (const ClassDefinition of ClassDefinitions) {
|
|
292
|
+
this.schedulersList.push(this.getSchedulerFromClass(ClassDefinition));
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
registerSchedulerInstances(...schedulers) {
|
|
296
|
+
this.schedulersList.push(...schedulers);
|
|
297
|
+
}
|
|
298
|
+
registerShutdownHook(hook) {
|
|
299
|
+
this.shutdownHooks.push(hook);
|
|
300
|
+
}
|
|
301
|
+
removeConsumers() {
|
|
302
|
+
this.consumersList.length = 0;
|
|
303
|
+
}
|
|
304
|
+
removeRoutes() {
|
|
305
|
+
this.routesList.length = 0;
|
|
306
|
+
}
|
|
307
|
+
removeSchedulers() {
|
|
308
|
+
this.schedulersList.length = 0;
|
|
309
|
+
}
|
|
310
|
+
async runConsumers() {
|
|
311
|
+
for (const consumer of this.consumersList) {
|
|
312
|
+
await consumer.init();
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
async runInitializers(...ClassDefinitions) {
|
|
316
|
+
for (const ClassDefinition of ClassDefinitions) {
|
|
317
|
+
await this.getInitializerFromClass(ClassDefinition).ensure();
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
async runRuntimes(...ClassDefinitions) {
|
|
321
|
+
for (const ClassDefinition of ClassDefinitions) {
|
|
322
|
+
const runtime = this.getRuntimeFromClass(ClassDefinition);
|
|
323
|
+
await runtime.run();
|
|
324
|
+
this.registerShutdownHook(
|
|
325
|
+
() => this.closeCandidate(runtime)
|
|
326
|
+
);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
async runSchedulerNowAndSchedule(ClassDefinition) {
|
|
330
|
+
const scheduler = this.getSchedulerFromClass(ClassDefinition);
|
|
331
|
+
await scheduler.runOnce();
|
|
332
|
+
await scheduler.init();
|
|
333
|
+
this.schedulersList.push(scheduler);
|
|
334
|
+
}
|
|
335
|
+
async runSchedulers() {
|
|
336
|
+
for (const scheduler of this.schedulersList) {
|
|
337
|
+
await scheduler.init();
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
async shutdown() {
|
|
341
|
+
for (const consumer of [...this.consumersList].reverse()) {
|
|
342
|
+
await this.closeCandidate(consumer);
|
|
343
|
+
}
|
|
344
|
+
for (const scheduler of [...this.schedulersList].reverse()) {
|
|
345
|
+
await this.closeCandidate(scheduler);
|
|
346
|
+
}
|
|
347
|
+
for (const hook of [...this.shutdownHooks].reverse()) {
|
|
348
|
+
await hook();
|
|
349
|
+
}
|
|
350
|
+
await this.closeCandidate(this.loggerInstance);
|
|
351
|
+
}
|
|
352
|
+
};
|
|
353
|
+
|
|
354
|
+
// src/adapters/pubsub/amqp/InvalidDomainEventError.ts
|
|
355
|
+
var InvalidDomainEventError = class extends Error {
|
|
356
|
+
constructor(message) {
|
|
357
|
+
super(`Invalid domain event: ${message}`);
|
|
358
|
+
this.name = "InvalidDomainEventError";
|
|
359
|
+
}
|
|
360
|
+
};
|
|
361
|
+
|
|
362
|
+
// src/adapters/pubsub/amqp/NoFailedMessagesError.ts
|
|
363
|
+
var NoFailedMessagesError = class extends Error {
|
|
364
|
+
constructor(queueName) {
|
|
365
|
+
super(`No failed messages found in "${queueName}".`);
|
|
366
|
+
this.name = "NoFailedMessagesError";
|
|
367
|
+
}
|
|
368
|
+
};
|
|
369
|
+
|
|
370
|
+
// src/adapters/pubsub/amqp/AmqpMessageBusAdapter.ts
|
|
371
|
+
var AmqpMessageBusAdapter = class {
|
|
372
|
+
constructor(options = {}) {
|
|
373
|
+
this.options = options;
|
|
374
|
+
this.exchange = options.exchange ?? options.serviceName ?? process.env.SERVICE_NAME ?? "";
|
|
375
|
+
}
|
|
376
|
+
options;
|
|
377
|
+
channelInstance;
|
|
378
|
+
connection;
|
|
379
|
+
delayConsumers = [];
|
|
380
|
+
exchange;
|
|
381
|
+
get dsn() {
|
|
382
|
+
return this.options.dsn ?? process.env.TRANSPORT_DSN ?? "";
|
|
383
|
+
}
|
|
384
|
+
get logger() {
|
|
385
|
+
return this.options.logger ?? Kernel.logger;
|
|
386
|
+
}
|
|
387
|
+
get maxRetries() {
|
|
388
|
+
const configuredRetries = this.options.maxRetries ?? process.env.TRANSPORT_MAX_RETRIES;
|
|
389
|
+
if (configuredRetries === void 0) {
|
|
390
|
+
return void 0;
|
|
391
|
+
}
|
|
392
|
+
return Number(configuredRetries);
|
|
393
|
+
}
|
|
394
|
+
get retryDelayInMilliseconds() {
|
|
395
|
+
const retryDelayFromEnv = Number(process.env.TRANSPORT_RETRY_DELAY);
|
|
396
|
+
return this.options.retryDelayInMilliseconds ?? (Number.isFinite(retryDelayFromEnv) ? retryDelayFromEnv : 1e3);
|
|
397
|
+
}
|
|
398
|
+
instanceDomainEvent(DomainEventInstance, message) {
|
|
399
|
+
if (!message) {
|
|
400
|
+
throw new InvalidDomainEventError(JSON.stringify(message));
|
|
401
|
+
}
|
|
402
|
+
return new DomainEventInstance(
|
|
403
|
+
message.aggregate_id,
|
|
404
|
+
message.attributes,
|
|
405
|
+
message.event_id,
|
|
406
|
+
message.occurred_on ? new Date(message.occurred_on) : /* @__PURE__ */ new Date(),
|
|
407
|
+
message.correlation_id,
|
|
408
|
+
message.causation_id
|
|
409
|
+
);
|
|
410
|
+
}
|
|
411
|
+
async handle(msg, context) {
|
|
412
|
+
const message = JSON.parse(msg.content.toString());
|
|
413
|
+
this.logger?.info(
|
|
414
|
+
`AMQP message bus (${context.queueName}) handling message: ${JSON.stringify(message)}`
|
|
415
|
+
);
|
|
416
|
+
try {
|
|
417
|
+
const domainEvent = this.instanceDomainEvent(
|
|
418
|
+
context.DomainEventInstance,
|
|
419
|
+
message
|
|
420
|
+
);
|
|
421
|
+
await context.handler(domainEvent);
|
|
422
|
+
} catch (error) {
|
|
423
|
+
await this.handleError(msg, message, context, error);
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
async handleError(msg, message, context, error) {
|
|
427
|
+
this.logger?.error(error instanceof Error ? error.message : String(error));
|
|
428
|
+
const headers = msg.properties.headers ?? {};
|
|
429
|
+
const retryCount = Number(headers.retries ?? 0);
|
|
430
|
+
if (this.maxRetries !== void 0 && retryCount <= this.maxRetries) {
|
|
431
|
+
await this.retry(message, headers, context);
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
434
|
+
await this.sendToDlx(message, context, String(error));
|
|
435
|
+
}
|
|
436
|
+
async retry(message, headers, context) {
|
|
437
|
+
const retry = Number(headers.retries || 1);
|
|
438
|
+
const delayTimeInMs = retry * this.retryDelayInMilliseconds;
|
|
439
|
+
const delayedQueueName = `${context.queueName}_delayed_${delayTimeInMs}`;
|
|
440
|
+
const delayedRoutingKey = `${context.queueName}_${context.bindingKey}_delayed_${delayTimeInMs}`;
|
|
441
|
+
const consumerTag = `${delayedQueueName}_${randomUUID()}`;
|
|
442
|
+
this.logger?.info(`Retry # ${retry}`);
|
|
443
|
+
if (!this.delayConsumers.includes(delayedQueueName)) {
|
|
444
|
+
await this.registerDelayedConsumer(
|
|
445
|
+
delayedQueueName,
|
|
446
|
+
delayedRoutingKey,
|
|
447
|
+
delayTimeInMs,
|
|
448
|
+
consumerTag,
|
|
449
|
+
context
|
|
450
|
+
);
|
|
451
|
+
}
|
|
452
|
+
try {
|
|
453
|
+
const domainEvent = this.instanceDomainEvent(
|
|
454
|
+
context.DomainEventInstance,
|
|
455
|
+
message
|
|
456
|
+
);
|
|
457
|
+
context.channel.publish(
|
|
458
|
+
this.exchange,
|
|
459
|
+
delayedRoutingKey,
|
|
460
|
+
Buffer.from(JSON.stringify(message)),
|
|
461
|
+
this.opts(domainEvent, retry)
|
|
462
|
+
);
|
|
463
|
+
} catch (error) {
|
|
464
|
+
await context.channel.cancel(consumerTag);
|
|
465
|
+
this.logger?.error(
|
|
466
|
+
error instanceof Error ? error.message : String(error)
|
|
467
|
+
);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
async registerDelayedConsumer(delayedQueueName, delayedRoutingKey, delayTimeInMs, consumerTag, context) {
|
|
471
|
+
await context.channel.assertExchange(this.exchange, "topic", {
|
|
472
|
+
durable: true
|
|
473
|
+
});
|
|
474
|
+
await context.channel.assertQueue(delayedQueueName, {
|
|
475
|
+
autoDelete: true,
|
|
476
|
+
deadLetterExchange: this.exchange,
|
|
477
|
+
deadLetterRoutingKey: delayedRoutingKey,
|
|
478
|
+
durable: false,
|
|
479
|
+
messageTtl: delayTimeInMs
|
|
480
|
+
});
|
|
481
|
+
await context.channel.bindQueue(
|
|
482
|
+
delayedQueueName,
|
|
483
|
+
this.exchange,
|
|
484
|
+
delayedRoutingKey
|
|
485
|
+
);
|
|
486
|
+
this.delayConsumers.push(delayedQueueName);
|
|
487
|
+
await context.channel.consume(
|
|
488
|
+
delayedQueueName,
|
|
489
|
+
async (msg) => {
|
|
490
|
+
if (!msg) {
|
|
491
|
+
return;
|
|
492
|
+
}
|
|
493
|
+
await new Promise((resolve) => setTimeout(resolve, delayTimeInMs));
|
|
494
|
+
await this.handle(msg, context);
|
|
495
|
+
this.removeDelayConsumer(delayedQueueName);
|
|
496
|
+
await context.channel.cancel(consumerTag);
|
|
497
|
+
},
|
|
498
|
+
{ consumerTag, noAck: true }
|
|
499
|
+
);
|
|
500
|
+
}
|
|
501
|
+
removeDelayConsumer(delayedQueueName) {
|
|
502
|
+
const index = this.delayConsumers.indexOf(delayedQueueName);
|
|
503
|
+
if (index !== -1) {
|
|
504
|
+
this.delayConsumers.splice(index, 1);
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
async sendToDlx(message, context, error) {
|
|
508
|
+
await context.channel.assertExchange(this.exchange, "topic");
|
|
509
|
+
const dlxQueueName = `${context.queueName}_dlx`;
|
|
510
|
+
const dlxRoutingKey = `${context.queueName}_${context.bindingKey}_dlx`;
|
|
511
|
+
await context.channel.assertQueue(dlxQueueName, {
|
|
512
|
+
deadLetterExchange: this.exchange,
|
|
513
|
+
deadLetterRoutingKey: dlxRoutingKey,
|
|
514
|
+
durable: true
|
|
515
|
+
});
|
|
516
|
+
await context.channel.bindQueue(dlxQueueName, this.exchange, dlxRoutingKey);
|
|
517
|
+
try {
|
|
518
|
+
const domainEvent = this.instanceDomainEvent(
|
|
519
|
+
context.DomainEventInstance,
|
|
520
|
+
message
|
|
521
|
+
);
|
|
522
|
+
context.channel.publish(
|
|
523
|
+
this.exchange,
|
|
524
|
+
dlxRoutingKey,
|
|
525
|
+
Buffer.from(JSON.stringify(message)),
|
|
526
|
+
this.opts(domainEvent, message.retries, error)
|
|
527
|
+
);
|
|
528
|
+
} catch (publishError) {
|
|
529
|
+
this.logger?.error(
|
|
530
|
+
publishError instanceof Error ? publishError.message : String(publishError)
|
|
531
|
+
);
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
async connect() {
|
|
535
|
+
this.connection = await amqplib.connect(this.getConnectionDsn());
|
|
536
|
+
this.channelInstance = await this.connection.createChannel();
|
|
537
|
+
this.channelInstance.on("close", async () => {
|
|
538
|
+
this.logger?.error("AMQP message bus event close");
|
|
539
|
+
await this.reconnect();
|
|
540
|
+
}).on("error", async (error) => {
|
|
541
|
+
this.logger?.error(`AMQP message bus event error: ${error.message}`);
|
|
542
|
+
await this.reconnect();
|
|
543
|
+
});
|
|
544
|
+
}
|
|
545
|
+
async reconnect() {
|
|
546
|
+
await this.connect();
|
|
547
|
+
for (const consumer of Kernel.consumers) {
|
|
548
|
+
await consumer.init();
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
async channel(forceNew = false) {
|
|
552
|
+
if (forceNew) {
|
|
553
|
+
const connection = await amqplib.connect(this.getConnectionDsn());
|
|
554
|
+
return connection.createChannel();
|
|
555
|
+
}
|
|
556
|
+
if (!this.channelInstance) {
|
|
557
|
+
await this.connect();
|
|
558
|
+
}
|
|
559
|
+
if (!this.channelInstance) {
|
|
560
|
+
throw new Error("AMQP channel could not be created.");
|
|
561
|
+
}
|
|
562
|
+
await this.channelInstance.assertExchange(this.exchange, "topic", {
|
|
563
|
+
durable: true
|
|
564
|
+
});
|
|
565
|
+
return this.channelInstance;
|
|
566
|
+
}
|
|
567
|
+
getConnectionDsn() {
|
|
568
|
+
const separator = this.dsn.includes("?") ? "&" : "?";
|
|
569
|
+
return `${this.dsn}${separator}heartbeat=60`;
|
|
570
|
+
}
|
|
571
|
+
opts(event, retries, error) {
|
|
572
|
+
return {
|
|
573
|
+
appId: this.options.serviceName ?? process.env.SERVICE_NAME,
|
|
574
|
+
contentEncoding: "utf-8",
|
|
575
|
+
contentType: "application/json",
|
|
576
|
+
deliveryMode: 2,
|
|
577
|
+
headers: {
|
|
578
|
+
error,
|
|
579
|
+
retries: retries ? retries + 1 : 0
|
|
580
|
+
},
|
|
581
|
+
messageId: event.eventId,
|
|
582
|
+
priority: 0,
|
|
583
|
+
timestamp: event.occurredOn.getTime(),
|
|
584
|
+
type: event.eventName()
|
|
585
|
+
};
|
|
586
|
+
}
|
|
587
|
+
getMessagesToRetry(requestedMessagesToRetry, availableMessages) {
|
|
588
|
+
return requestedMessagesToRetry ?? availableMessages;
|
|
589
|
+
}
|
|
590
|
+
async retryDlxMessage(msg, DomainEventInstance, handler, channel) {
|
|
591
|
+
const content = msg.content.toString();
|
|
592
|
+
this.logger?.info(`Retrying message from DLX ${content}`);
|
|
593
|
+
try {
|
|
594
|
+
const message = JSON.parse(content);
|
|
595
|
+
const domainEvent = this.instanceDomainEvent(
|
|
596
|
+
DomainEventInstance,
|
|
597
|
+
message
|
|
598
|
+
);
|
|
599
|
+
await handler(domainEvent);
|
|
600
|
+
this.logger?.info(`${content} successfully handled.`);
|
|
601
|
+
channel.ack(msg);
|
|
602
|
+
} catch (error) {
|
|
603
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
604
|
+
this.logger?.error(`${content} error with ${errorMessage}.`);
|
|
605
|
+
channel.nack(msg);
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
async areQueuesBound() {
|
|
609
|
+
if (Kernel.consumers.length === 0) {
|
|
610
|
+
return false;
|
|
611
|
+
}
|
|
612
|
+
const channel = await this.channel();
|
|
613
|
+
for (const consumer of Kernel.consumers) {
|
|
614
|
+
const queueChecker = await channel.checkQueue(consumer.queueName);
|
|
615
|
+
if (queueChecker.consumerCount !== 0) {
|
|
616
|
+
return false;
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
return true;
|
|
620
|
+
}
|
|
621
|
+
async consumeDlx(queueName, DomainEventInstance, handler, messagesToRetry) {
|
|
622
|
+
const dlxQueueName = `${queueName}_dlx`;
|
|
623
|
+
const channel = await this.channel(true);
|
|
624
|
+
channel.on("error", () => {
|
|
625
|
+
this.logger?.error("AMQP message bus event error");
|
|
626
|
+
});
|
|
627
|
+
const queue = await channel.checkQueue(dlxQueueName);
|
|
628
|
+
const messagesToRetryCount = this.getMessagesToRetry(
|
|
629
|
+
messagesToRetry,
|
|
630
|
+
queue.messageCount
|
|
631
|
+
);
|
|
632
|
+
this.logger?.info(
|
|
633
|
+
`Retrying ${messagesToRetryCount} messages from DLX ${dlxQueueName}`
|
|
634
|
+
);
|
|
635
|
+
if (messagesToRetryCount > 0 && queue.messageCount !== 0) {
|
|
636
|
+
for (let index = 0; index < messagesToRetryCount; index++) {
|
|
637
|
+
const msg = await channel.get(dlxQueueName);
|
|
638
|
+
if (msg === false) {
|
|
639
|
+
continue;
|
|
640
|
+
}
|
|
641
|
+
await this.retryDlxMessage(msg, DomainEventInstance, handler, channel);
|
|
642
|
+
}
|
|
643
|
+
return;
|
|
644
|
+
}
|
|
645
|
+
throw new NoFailedMessagesError(dlxQueueName);
|
|
646
|
+
}
|
|
647
|
+
async consume(queueName, bindingKey, DomainEventInstance, exchange, handler) {
|
|
648
|
+
const channel = await this.channel();
|
|
649
|
+
await channel.assertQueue(queueName, { durable: true });
|
|
650
|
+
await channel.assertExchange(exchange, "topic");
|
|
651
|
+
await channel.prefetch(1);
|
|
652
|
+
await channel.bindQueue(queueName, exchange, bindingKey);
|
|
653
|
+
const context = {
|
|
654
|
+
bindingKey,
|
|
655
|
+
channel,
|
|
656
|
+
DomainEventInstance,
|
|
657
|
+
handler,
|
|
658
|
+
queueName
|
|
659
|
+
};
|
|
660
|
+
await channel.consume(queueName, async (msg) => {
|
|
661
|
+
if (!msg) {
|
|
662
|
+
return;
|
|
663
|
+
}
|
|
664
|
+
await this.handle(msg, context);
|
|
665
|
+
channel.ack(msg);
|
|
666
|
+
});
|
|
667
|
+
}
|
|
668
|
+
async publish(domainEvents) {
|
|
669
|
+
const channel = await this.channel();
|
|
670
|
+
for (const event of domainEvents) {
|
|
671
|
+
channel.publish(
|
|
672
|
+
this.exchange,
|
|
673
|
+
event.eventName(),
|
|
674
|
+
Buffer.from(event.decode()),
|
|
675
|
+
this.opts(event)
|
|
676
|
+
);
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
async close() {
|
|
680
|
+
await this.channelInstance?.close();
|
|
681
|
+
await this.connection?.close();
|
|
682
|
+
this.channelInstance = void 0;
|
|
683
|
+
this.connection = void 0;
|
|
684
|
+
}
|
|
685
|
+
};
|
|
686
|
+
export {
|
|
687
|
+
InvalidDomainEventError,
|
|
688
|
+
NoFailedMessagesError,
|
|
689
|
+
AmqpMessageBusAdapter as default
|
|
690
|
+
};
|
|
691
|
+
//# sourceMappingURL=index.js.map
|