@opra/rabbitmq 1.21.0 → 1.22.1

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.
@@ -2,6 +2,8 @@ import { merge } from '@jsopen/objects';
2
2
  import { MQ_CONTROLLER_METADATA, } from '@opra/common';
3
3
  import { RMQ_OPERATION_METADATA, RMQ_OPERATION_METADATA_RESOLVER, } from './constants.js';
4
4
  export class ConfigBuilder {
5
+ document;
6
+ config;
5
7
  constructor(document, config) {
6
8
  this.document = document;
7
9
  this.config = config;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opra/rabbitmq",
3
- "version": "1.21.0",
3
+ "version": "1.22.1",
4
4
  "description": "Opra RabbitMQ adapter",
5
5
  "author": "Panates",
6
6
  "license": "MIT",
@@ -8,50 +8,34 @@
8
8
  "@jsopen/objects": "^2.0.2",
9
9
  "@browsery/type-is": "^2.0.1",
10
10
  "content-type": "^1.0.5",
11
- "iconv-lite": "^0.7.0",
12
- "node-events-async": "^1.2.0",
11
+ "iconv-lite": "^0.7.1",
12
+ "node-events-async": "^1.5.0",
13
13
  "tslib": "^2.8.1",
14
14
  "valgen": "^5.18.2"
15
15
  },
16
16
  "peerDependencies": {
17
- "@opra/common": "^1.21.0",
18
- "@opra/core": "^1.21.0",
17
+ "@opra/common": "^1.22.1",
18
+ "@opra/core": "^1.22.1",
19
19
  "rabbitmq-client": ">=5.0.0 <6"
20
20
  },
21
21
  "type": "module",
22
+ "module": "./index.js",
23
+ "types": "./index.d.ts",
22
24
  "exports": {
23
25
  ".": {
24
- "import": {
25
- "types": "./types/index.d.ts",
26
- "default": "./esm/index.js"
27
- },
28
- "require": {
29
- "types": "./types/index.d.cts",
30
- "default": "./cjs/index.js"
31
- },
32
- "default": "./esm/index.js"
26
+ "types": "./index.d.ts",
27
+ "default": "./index.js"
33
28
  },
34
29
  "./package.json": "./package.json"
35
30
  },
36
- "main": "./cjs/index.js",
37
- "module": "./esm/index.js",
38
- "types": "./types/index.d.ts",
31
+ "engines": {
32
+ "node": ">=20.0"
33
+ },
39
34
  "repository": {
40
35
  "type": "git",
41
36
  "url": "git+https://github.com/panates/opra.git",
42
37
  "directory": "packages/rabbitmq"
43
38
  },
44
- "engines": {
45
- "node": ">=16.0",
46
- "npm": ">=7.0.0"
47
- },
48
- "files": [
49
- "cjs/",
50
- "esm/",
51
- "types/",
52
- "LICENSE",
53
- "README.md"
54
- ],
55
39
  "keywords": [
56
40
  "opra",
57
41
  "rabbitmq",
@@ -21,6 +21,15 @@ const noOp = () => undefined;
21
21
  * @class RabbitmqAdapter
22
22
  */
23
23
  export class RabbitmqAdapter extends PlatformAdapter {
24
+ static PlatformName = 'rabbitmq';
25
+ _config;
26
+ _controllerInstances = new Map();
27
+ _connection;
28
+ _consumers = [];
29
+ _status = 'idle';
30
+ transform = 'mq';
31
+ platform = RabbitmqAdapter.PlatformName;
32
+ interceptors;
24
33
  /**
25
34
  *
26
35
  * @param document
@@ -29,11 +38,6 @@ export class RabbitmqAdapter extends PlatformAdapter {
29
38
  */
30
39
  constructor(document, config) {
31
40
  super(config);
32
- this._controllerInstances = new Map();
33
- this._consumers = [];
34
- this._status = 'idle';
35
- this.transform = 'mq';
36
- this.platform = RabbitmqAdapter.PlatformName;
37
41
  this._document = document;
38
42
  this._config = config;
39
43
  if (!(this.document.api instanceof MQApi &&
@@ -299,4 +303,3 @@ export class RabbitmqAdapter extends PlatformAdapter {
299
303
  return wrappedErrors;
300
304
  }
301
305
  }
302
- RabbitmqAdapter.PlatformName = 'rabbitmq';
@@ -4,6 +4,12 @@ import { ExecutionContext } from '@opra/core';
4
4
  * It extends the ExecutionContext and implements the AsyncEventEmitter.
5
5
  */
6
6
  export class RabbitmqContext extends ExecutionContext {
7
+ queue;
8
+ consumer;
9
+ message;
10
+ content;
11
+ headers;
12
+ reply;
7
13
  /**
8
14
  * Constructor
9
15
  * @param init the context options
@@ -1,19 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- require("@opra/core");
4
- const common_1 = require("@opra/common");
5
- const constants_js_1 = require("../constants.js");
6
- /** Implementation **/
7
- common_1.classes.MQOperationDecoratorFactory.augment((decorator, decoratorChain) => {
8
- decorator.RabbitMQ = (config) => {
9
- decoratorChain.push((_, target, propertyKey) => {
10
- if (typeof config === 'function') {
11
- Reflect.defineMetadata(constants_js_1.RMQ_OPERATION_METADATA_RESOLVER, config, target, propertyKey);
12
- }
13
- else {
14
- Reflect.defineMetadata(constants_js_1.RMQ_OPERATION_METADATA, { ...config }, target, propertyKey);
15
- }
16
- });
17
- return decorator;
18
- };
19
- });
@@ -1,88 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ConfigBuilder = void 0;
4
- const objects_1 = require("@jsopen/objects");
5
- const common_1 = require("@opra/common");
6
- const constants_js_1 = require("./constants.js");
7
- class ConfigBuilder {
8
- constructor(document, config) {
9
- this.document = document;
10
- this.config = config;
11
- }
12
- async build() {
13
- this.controllerInstances = new Map();
14
- this.handlerArgs = [];
15
- this._prepareConnectionOptions();
16
- /** Initialize consumers */
17
- for (const controller of this.document.getMqApi().controllers.values()) {
18
- let instance = controller.instance;
19
- if (!instance && controller.ctor)
20
- instance = new controller.ctor();
21
- if (!instance)
22
- continue;
23
- this.controllerInstances.set(controller, instance);
24
- /** Build HandlerData array */
25
- for (const operation of controller.operations.values()) {
26
- const consumerConfig = await this._getConsumerConfig(controller, instance, operation);
27
- if (!consumerConfig)
28
- continue;
29
- const args = {
30
- // consumer: null as any,
31
- controller,
32
- instance,
33
- operation,
34
- consumerConfig,
35
- // handler: null as any,
36
- topics: (Array.isArray(operation.channel)
37
- ? operation.channel
38
- : [operation.channel]).map(String),
39
- };
40
- this.handlerArgs.push(args);
41
- }
42
- }
43
- }
44
- _prepareConnectionOptions() {
45
- this.connectionOptions = {};
46
- if (Array.isArray(this.config.connection))
47
- this.connectionOptions.urls = this.config.connection;
48
- else if (typeof this.config.connection === 'object') {
49
- (0, objects_1.merge)(this.connectionOptions, this.config.connection, { deep: true });
50
- }
51
- else
52
- this.connectionOptions.urls = [
53
- this.config.connection || 'amqp://guest:guest@localhost:5672',
54
- ];
55
- }
56
- /**
57
- *
58
- * @param controller
59
- * @param instance
60
- * @param operation
61
- * @protected
62
- */
63
- async _getConsumerConfig(controller, instance, operation) {
64
- if (typeof instance[operation.name] !== 'function')
65
- return;
66
- const proto = controller.ctor?.prototype || Object.getPrototypeOf(instance);
67
- if (Reflect.hasMetadata(common_1.MQ_CONTROLLER_METADATA, proto, operation.name))
68
- return;
69
- const operationConfig = {};
70
- if (this.config.defaults) {
71
- if (this.config.defaults.consumer) {
72
- (0, objects_1.merge)(operationConfig, this.config.defaults.consumer, { deep: true });
73
- }
74
- }
75
- let metadata = Reflect.getMetadata(constants_js_1.RMQ_OPERATION_METADATA, proto, operation.name);
76
- if (!metadata) {
77
- const configResolver = Reflect.getMetadata(constants_js_1.RMQ_OPERATION_METADATA_RESOLVER, proto, operation.name);
78
- if (configResolver) {
79
- metadata = await configResolver();
80
- }
81
- }
82
- if (metadata && typeof metadata === 'object') {
83
- (0, objects_1.merge)(operationConfig, metadata, { deep: true });
84
- }
85
- return operationConfig;
86
- }
87
- }
88
- exports.ConfigBuilder = ConfigBuilder;
package/cjs/constants.js DELETED
@@ -1,6 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.RMQ_OPERATION_METADATA_RESOLVER = exports.RMQ_OPERATION_METADATA = exports.RMQ_DEFAULT_GROUP = void 0;
4
- exports.RMQ_DEFAULT_GROUP = 'default';
5
- exports.RMQ_OPERATION_METADATA = 'RMQ_OPERATION_METADATA';
6
- exports.RMQ_OPERATION_METADATA_RESOLVER = 'RMQ_OPERATION_METADATA_RESOLVER';
package/cjs/index.js DELETED
@@ -1,7 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const tslib_1 = require("tslib");
4
- require("./augmentation/opra-common.augmentation.js");
5
- tslib_1.__exportStar(require("./constants.js"), exports);
6
- tslib_1.__exportStar(require("./rabbitmq-adapter.js"), exports);
7
- tslib_1.__exportStar(require("./rabbitmq-context.js"), exports);
package/cjs/package.json DELETED
@@ -1,3 +0,0 @@
1
- {
2
- "type": "commonjs"
3
- }
@@ -1,307 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.RabbitmqAdapter = void 0;
4
- const tslib_1 = require("tslib");
5
- const node_zlib_1 = tslib_1.__importDefault(require("node:zlib"));
6
- const type_is_1 = tslib_1.__importDefault(require("@browsery/type-is"));
7
- const common_1 = require("@opra/common");
8
- const core_1 = require("@opra/core");
9
- const content_type_1 = require("content-type");
10
- const iconv_lite_1 = tslib_1.__importDefault(require("iconv-lite"));
11
- const rabbit = tslib_1.__importStar(require("rabbitmq-client"));
12
- const util_1 = require("util");
13
- const valgen_1 = require("valgen");
14
- const config_builder_js_1 = require("./config-builder.js");
15
- const rabbitmq_context_js_1 = require("./rabbitmq-context.js");
16
- const gunzipAsync = (0, util_1.promisify)(node_zlib_1.default.gunzip);
17
- const deflateAsync = (0, util_1.promisify)(node_zlib_1.default.deflate);
18
- const inflateAsync = (0, util_1.promisify)(node_zlib_1.default.inflate);
19
- const brotliAsync = (0, util_1.promisify)(node_zlib_1.default.brotliCompress);
20
- const globalErrorTypes = ['unhandledRejection', 'uncaughtException'];
21
- const signalTraps = ['SIGTERM', 'SIGINT', 'SIGUSR2'];
22
- const noOp = () => undefined;
23
- /**
24
- *
25
- * @class RabbitmqAdapter
26
- */
27
- class RabbitmqAdapter extends core_1.PlatformAdapter {
28
- /**
29
- *
30
- * @param document
31
- * @param config
32
- * @constructor
33
- */
34
- constructor(document, config) {
35
- super(config);
36
- this._controllerInstances = new Map();
37
- this._consumers = [];
38
- this._status = 'idle';
39
- this.transform = 'mq';
40
- this.platform = RabbitmqAdapter.PlatformName;
41
- this._document = document;
42
- this._config = config;
43
- if (!(this.document.api instanceof common_1.MQApi &&
44
- this.document.api.platform === RabbitmqAdapter.PlatformName)) {
45
- throw new TypeError(`The document doesn't expose a RabbitMQ Api`);
46
- }
47
- this.interceptors = [...(config.interceptors || [])];
48
- globalErrorTypes.forEach(type => {
49
- process.on(type, e => {
50
- this._emitError(e);
51
- return this.close();
52
- });
53
- });
54
- signalTraps.forEach(type => {
55
- process.once(type, () => this.close());
56
- });
57
- }
58
- get api() {
59
- return this.document.getMqApi();
60
- }
61
- get connection() {
62
- return this._connection;
63
- }
64
- get scope() {
65
- return this._config.scope;
66
- }
67
- get status() {
68
- return this._status;
69
- }
70
- /**
71
- * Starts the service
72
- */
73
- async start() {
74
- if (this.status !== 'idle')
75
- return;
76
- this._status = 'starting';
77
- // const handlerArgs: HandlerArguments[] = [];
78
- const configBuilder = new config_builder_js_1.ConfigBuilder(this.document, this._config);
79
- await configBuilder.build();
80
- this._connection = new rabbit.Connection(configBuilder.connectionOptions);
81
- try {
82
- /** Establish connection */
83
- await this._connection.onConnect().catch(e => {
84
- e.message = `RabbitMQ connection error. ${e.message}`;
85
- throw e;
86
- });
87
- this.logger?.info?.(`Connected RabbitMQ at ${configBuilder.connectionOptions.urls}`);
88
- /** Subscribe to queues */
89
- const promises = [];
90
- for (const args of configBuilder.handlerArgs) {
91
- for (const queue of args.topics) {
92
- const handler = this._createHandler(args);
93
- promises.push(new Promise((resolve, reject) => {
94
- const consumer = this._connection.createConsumer({
95
- ...args.consumerConfig,
96
- queueOptions: {
97
- ...args.consumerConfig.queueOptions,
98
- durable: args.consumerConfig.queueOptions?.durable ?? true,
99
- },
100
- queue,
101
- }, async (msg, reply) => {
102
- await this.emitAsync('message', msg, queue).catch(noOp);
103
- try {
104
- await handler(consumer, queue, msg, reply);
105
- }
106
- catch (e) {
107
- this._emitError(e);
108
- }
109
- });
110
- this._consumers.push(consumer);
111
- consumer.on('ready', () => {
112
- this.logger?.info?.(`Subscribed to topic "${queue}"`);
113
- resolve();
114
- });
115
- consumer.on('error', (err) => {
116
- err.message = `Consumer error (${queue})". ${err.message}`;
117
- err.queue = queue;
118
- reject(err);
119
- });
120
- }));
121
- }
122
- }
123
- await Promise.all(promises);
124
- this._status = 'started';
125
- }
126
- catch (err) {
127
- this._emitError(err);
128
- await this.close();
129
- }
130
- }
131
- /**
132
- * Closes all connections and stops the service
133
- */
134
- async close() {
135
- if (this._consumers.length)
136
- await Promise.allSettled(this._consumers.map(consumer => consumer.close()));
137
- await this._connection?.close();
138
- this._connection = undefined;
139
- this._consumers = [];
140
- this._controllerInstances.clear();
141
- this._status = 'idle';
142
- }
143
- getControllerInstance(controllerPath) {
144
- const controller = this.api.findController(controllerPath);
145
- return controller && this._controllerInstances.get(controller);
146
- }
147
- /**
148
- *
149
- * @param args
150
- * @protected
151
- */
152
- _createHandler(args) {
153
- const { controller, instance, operation } = args;
154
- /** Prepare parsers */
155
- const decodePayload = operation.generateCodec('decode', {
156
- scope: this.scope,
157
- ignoreReadonlyFields: true,
158
- });
159
- operation.headers.forEach(header => {
160
- let decode = this[core_1.kAssetCache].get(header, 'decode');
161
- if (!decode) {
162
- decode = header.generateCodec('decode', {
163
- scope: this.scope,
164
- ignoreReadonlyFields: true,
165
- });
166
- this[core_1.kAssetCache].set(header, 'decode', decode);
167
- }
168
- });
169
- return async (consumer, queue, message, _reply) => {
170
- if (!message)
171
- return;
172
- const operationHandler = instance[operation.name];
173
- let replyCalled = false;
174
- const reply = async (body, envelope) => {
175
- if (replyCalled)
176
- return;
177
- replyCalled = true;
178
- return _reply(body, envelope);
179
- };
180
- const headers = {};
181
- /** Create context */
182
- const context = new rabbitmq_context_js_1.RabbitmqContext({
183
- __adapter: this,
184
- __contDef: controller,
185
- __controller: instance,
186
- __oprDef: operation,
187
- __handler: operationHandler,
188
- queue,
189
- consumer,
190
- message,
191
- content: undefined,
192
- headers,
193
- reply,
194
- });
195
- try {
196
- /** Parse and decode `payload` */
197
- let content = await this._parseContent(message);
198
- if (content && decodePayload) {
199
- if (Buffer.isBuffer(content))
200
- content = content.toString('utf-8');
201
- content = decodePayload(content);
202
- }
203
- // message.properties.
204
- /** Parse and decode `headers` */
205
- if (message.headers) {
206
- for (const [k, v] of Object.entries(message.headers)) {
207
- const header = operation.findHeader(k);
208
- const decode = this[core_1.kAssetCache].get(header, 'decode') || valgen_1.vg.isAny();
209
- headers[k] = decode(Buffer.isBuffer(v) ? v.toString() : v);
210
- }
211
- }
212
- context.content = content;
213
- }
214
- catch (e) {
215
- this._emitError(e, context);
216
- return;
217
- }
218
- await this.emitAsync('execute', context).catch(noOp);
219
- try {
220
- /** Call operation handler */
221
- const result = await operationHandler.call(instance, context);
222
- if (result !== undefined)
223
- await reply(result);
224
- await this.emitAsync('finish', context, result).catch(noOp);
225
- }
226
- catch (e) {
227
- this._emitError(e, context);
228
- }
229
- };
230
- }
231
- async _parseContent(msg) {
232
- if (!Buffer.isBuffer(msg.body))
233
- return msg.body;
234
- if (!msg.body?.length)
235
- return;
236
- let content = msg.body;
237
- if (msg.contentEncoding) {
238
- switch (msg.contentEncoding) {
239
- case 'gzip':
240
- case 'x-gzip': {
241
- content = await gunzipAsync(content);
242
- break;
243
- }
244
- case 'deflate':
245
- case 'x-deflate': {
246
- content = await deflateAsync(content);
247
- break;
248
- }
249
- case 'inflate':
250
- case 'x-inflate': {
251
- content = await inflateAsync(content);
252
- break;
253
- }
254
- case 'br': {
255
- content = await brotliAsync(content);
256
- break;
257
- }
258
- case 'base64': {
259
- content = content.toString('base64');
260
- break;
261
- }
262
- }
263
- }
264
- const mediaType = msg.contentType
265
- ? (0, content_type_1.parse)(msg.contentType || '')
266
- : undefined;
267
- if (mediaType && type_is_1.default.is(mediaType.type, ['json', 'xml', 'txt'])) {
268
- const charset = (mediaType.parameters.charset || '').toLowerCase() || 'utf-8';
269
- content = iconv_lite_1.default.decode(content, charset);
270
- if (type_is_1.default.is(mediaType.type, ['json']))
271
- return JSON.parse(content);
272
- }
273
- return content;
274
- }
275
- _emitError(error, context) {
276
- Promise.resolve()
277
- .then(async () => {
278
- const logger = this.logger;
279
- if (context) {
280
- if (!context.errors.length)
281
- context.errors.push(error);
282
- context.errors = this._wrapExceptions(context.errors);
283
- if (context.listenerCount('error')) {
284
- await context
285
- .emitAsync('error', context.errors[0], context)
286
- .catch(noOp);
287
- }
288
- if (logger?.error) {
289
- context.errors.forEach(err => logger.error(err));
290
- }
291
- }
292
- else
293
- logger?.error(error);
294
- if (this.listenerCount('error'))
295
- this._emitError(error, context);
296
- })
297
- .catch(noOp);
298
- }
299
- _wrapExceptions(exceptions) {
300
- const wrappedErrors = exceptions.map(e => e instanceof common_1.OpraException ? e : new common_1.OpraException(e));
301
- if (!wrappedErrors.length)
302
- wrappedErrors.push(new common_1.OpraException('Internal Server Error'));
303
- return wrappedErrors;
304
- }
305
- }
306
- exports.RabbitmqAdapter = RabbitmqAdapter;
307
- RabbitmqAdapter.PlatformName = 'rabbitmq';
@@ -1,39 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.RabbitmqContext = void 0;
4
- const core_1 = require("@opra/core");
5
- /**
6
- * RabbitmqContext class provides the context for handling RabbitMQ messages.
7
- * It extends the ExecutionContext and implements the AsyncEventEmitter.
8
- */
9
- class RabbitmqContext extends core_1.ExecutionContext {
10
- /**
11
- * Constructor
12
- * @param init the context options
13
- */
14
- constructor(init) {
15
- super({
16
- ...init,
17
- __docNode: init.__oprDef?.node ||
18
- init.__contDef?.node ||
19
- init.__adapter.document.node,
20
- transport: 'mq',
21
- platform: 'rabbitmq',
22
- });
23
- if (init.__contDef)
24
- this.__contDef = init.__contDef;
25
- if (init.__oprDef)
26
- this.__oprDef = init.__oprDef;
27
- if (init.__controller)
28
- this.__controller = init.__controller;
29
- if (init.__handler)
30
- this.__handler = init.__handler;
31
- this.consumer = init.consumer;
32
- this.queue = init.queue;
33
- this.message = init.message;
34
- this.headers = init.headers || {};
35
- this.content = init.content;
36
- this.reply = init.reply;
37
- }
38
- }
39
- exports.RabbitmqContext = RabbitmqContext;
package/esm/package.json DELETED
@@ -1,3 +0,0 @@
1
- {
2
- "type": "module"
3
- }
package/types/index.d.cts DELETED
@@ -1,4 +0,0 @@
1
- import './augmentation/opra-common.augmentation.js';
2
- export * from './constants.js';
3
- export * from './rabbitmq-adapter.js';
4
- export * from './rabbitmq-context.js';
File without changes
File without changes
File without changes
File without changes