@ti-engine/core 1.7.1 → 1.8.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 (33) hide show
  1. package/CHANGELOG.md +393 -377
  2. package/LICENSE.md +321 -321
  3. package/README.md +599 -597
  4. package/bin/localization/labels.json +122 -122
  5. package/bin/settings.json +41 -41
  6. package/bin/start-instance.js +164 -164
  7. package/components/auditing.js +191 -191
  8. package/components/connection-observer.js +72 -72
  9. package/components/definitions.types.js +248 -248
  10. package/components/exchange/default/default-message-exchange.js +136 -136
  11. package/components/exchange/default/default-message-receiver.js +101 -101
  12. package/components/exchange/default/default-message-sender.js +100 -100
  13. package/components/exchange/message-dispatcher.js +168 -168
  14. package/components/exchange/message-exchange.js +449 -449
  15. package/components/exchange/message-handler.js +235 -235
  16. package/components/exchange/message-memory-cache.js +190 -190
  17. package/components/exchange/message-observer.js +126 -126
  18. package/components/exchange/message-receiver.js +181 -181
  19. package/components/exchange/message-sender.js +143 -143
  20. package/components/exchange/message-tracer.js +212 -212
  21. package/components/service-caller.js +370 -370
  22. package/components/service-consumer.js +131 -131
  23. package/components/service-executor.js +278 -278
  24. package/components/service-instance.js +316 -316
  25. package/components/service-provider.js +251 -251
  26. package/integrations/redis-integration.js +591 -591
  27. package/package.json +89 -89
  28. package/utils/cache.js +772 -772
  29. package/utils/config.js +103 -103
  30. package/utils/exceptions.js +368 -368
  31. package/utils/localization.js +298 -298
  32. package/utils/logger.js +82 -82
  33. package/utils/tools.js +632 -632
@@ -1,236 +1,236 @@
1
- /*
2
- * The ti-engine is an open source, free to use—both for personal and commercial projects—framework for the creation of microservice-based solutions using node.js.
3
- * Copyright © 2021-2026 Boris Kostadinov <kostadinov.boris@gmail.com>
4
- * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
5
- * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
6
- * You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
7
- */
8
-
9
- const ConnectionObserver = require( "#connection-observer" );
10
- const _ = require( "lodash" );
11
- const crypto = require( "node:crypto" );
12
- const exceptions = require( "#exceptions" );
13
- const logger = require( "#logger" );
14
- const tools = require( "#tools" );
15
- const config = require( "#config" );
16
-
17
- const OLD_DEFAULT_HASH_KEY = "23e7bdc7-a793-41f9-856e-6760332f0c73";
18
- let keyWarningEmitted = false;
19
-
20
- /**
21
- * An abstract class that defines a basic message handler behavior.
22
- * <br/>
23
- * NOTE: This class and its children are designed to be used internally by classes extending the {@link MessageObserver} class.
24
- *
25
- * @class MessageHandler
26
- * @extends ConnectionObserver
27
- * @abstract
28
- * @public
29
- */
30
- class MessageHandler extends ConnectionObserver {
31
-
32
- #isAvailable = false;
33
- #connectionIdentifier;
34
- #messageObservers = [];
35
-
36
- /**
37
- * @constructor
38
- * @param {string} identifier An identifier for this message handler. Should be unique in the context of the message exchange.
39
- * @throws {TiException.E_GEN_ABSTRACT_CLASS_INIT} If this class is instantiated directly.
40
- */
41
- constructor( identifier ) {
42
- super();
43
-
44
- // make sure this abstract class cannot be instantiated:
45
- if ( new.target === MessageHandler ) {
46
- throw exceptions.raise( exceptions.exceptionCode.E_GEN_ABSTRACT_CLASS_INIT, { name: this.constructor.name } );
47
- }
48
-
49
- this.#connectionIdentifier = identifier;
50
- }
51
-
52
- /* Public interface */
53
-
54
- /**
55
- * Indicates whether the message handler is currently available.
56
- *
57
- * @property
58
- * @returns {boolean}
59
- * @public
60
- */
61
- get isAvailable() {
62
- return this.#isAvailable;
63
- }
64
-
65
- /**
66
- * Used to set the isAvailable flag.
67
- * <br/>
68
- * NOTE: For use by implementing classes only!
69
- *
70
- * @property
71
- * @param {boolean} value
72
- * @public
73
- */
74
- set isAvailable( value ) {
75
- this.#isAvailable = value;
76
- }
77
-
78
- /**
79
- * Returns the connection identifier.
80
- *
81
- * @property
82
- * @returns {string}
83
- * @public
84
- */
85
- get connectionIdentifier() {
86
- return this.#connectionIdentifier;
87
- }
88
-
89
- /**
90
- * Used to initialize and enable the communication capabilities of the handler.
91
- * <br/>
92
- * NOTE: Override this to add functionality.
93
- *
94
- * @method
95
- * @returns {Promise}
96
- * @abstract
97
- * @public
98
- */
99
- enable() {
100
- return Promise.reject( exceptions.raise( exceptions.exceptionCode.E_GEN_ABSTRACT_METHOD_CALL, { name: this.constructor.name + "." + this.enable.name } ) );
101
- }
102
-
103
- /**
104
- * Used to shut down and disable the communication behavior of the handler.
105
- * <br/>
106
- * NOTE: Override this to add functionality.
107
- *
108
- * @method
109
- * @returns {Promise}
110
- * @abstract
111
- * @public
112
- */
113
- disable() {
114
- return Promise.reject( exceptions.raise( exceptions.exceptionCode.E_GEN_ABSTRACT_METHOD_CALL, { name: this.constructor.name + "." + this.disable.name } ) );
115
- }
116
-
117
- /**
118
- * Used to create a security hash from the message.
119
- *
120
- * @method
121
- * @param {Message} message
122
- * @returns {string}
123
- * @public
124
- */
125
- createMessageHash( message ) {
126
- const rawKey = config.getSetting( config.setting.MESSAGE_EXCHANGE_SECURITY_HASH_KEY );
127
- let key = rawKey == null ? "" : String( rawKey );
128
- if ( keyWarningEmitted === false ) {
129
- keyWarningEmitted = true;
130
- if ( !key || key === OLD_DEFAULT_HASH_KEY ) {
131
- logger.log( "Message-exchange security hash is enabled but no private key is set ('securityHashKey' is missing or the published default). Set TI_MESSAGE_EXCHANGE_SECURITY_HASH_KEY to a private value, otherwise tamper protection is ineffective.", logger.logSeverity.WARNING );
132
- }
133
- }
134
- let transformed = tools.decomposeJSON( tools.decycle( message ) );
135
- let hmac = crypto.createHmac( "sha256", Buffer.from( key, "utf8" ) );
136
- hmac.update( Buffer.from( transformed ) );
137
- return hmac.digest( "hex" );
138
- }
139
-
140
- /**
141
- * Used to register a new {@link MessageObserver} for events related to the messages passing through this handler.
142
- *
143
- * @method
144
- * @param {MessageObserver} messageObserver The {@link MessageObserver} that will be notified of any changes.
145
- * @public
146
- */
147
- addMessageObserver( messageObserver ) {
148
- const MessageObserver = require( "#message-observer" );
149
- if ( messageObserver instanceof MessageObserver ) {
150
- this.#messageObservers.push( messageObserver );
151
- this.#messageObservers = _.orderBy( this.#messageObservers, [ "priority" ], [ "desc" ] );
152
- } else {
153
- logger.log( `Attempting to add '${ messageObserver.constructor.name }' as message observer but it's not a child-class of 'MessageObserver'!`, logger.logSeverity.WARNING );
154
- }
155
- }
156
-
157
- /**
158
- * An event-triggered method that will notify any observers about a new message for handling.
159
- * <br/>
160
- * NOTE: Each observer will be notified in the order of their priority via their {@link MessageObserver.onMessage} method. Additionally, the message will be
161
- * passed through each observer in the order of their priority. If the observer returns a modified message, it will be used instead of the original message!
162
- *
163
- * @method
164
- * @param {Message} message
165
- * @public
166
- */
167
- notifyMessageObservers( message ) {
168
- let modifiedMessage = message;
169
- _.forEach( this.#messageObservers, ( messageObserver ) => {
170
- modifiedMessage = messageObserver.onMessage( this.#connectionIdentifier, modifiedMessage );
171
- } );
172
- }
173
-
174
- /**
175
- * An event-triggered method that will notify any observers about the primary connection recovered state.
176
- * <br/>
177
- * NOTE: You can override this to add custom functionality but make sure to also call the base method
178
- * using: super.onConnectionRecovered( identifier )
179
- *
180
- * @method
181
- * @param {string} identifier The identifier of the observed connection.
182
- * @override
183
- * @private
184
- */
185
- onConnectionRecovered( identifier ) {
186
- if ( this.#isAvailable === false && identifier === this.#connectionIdentifier ) {
187
- this.#isAvailable = true;
188
- _.forEach( this.#messageObservers, ( messageObserver ) => {
189
- messageObserver.onConnectionRecovered( this.#connectionIdentifier );
190
- } );
191
- }
192
- }
193
-
194
- /**
195
- * An event-triggered method that will notify any observers about the primary connection disrupted state.
196
- * <br/>
197
- * NOTE: You can override this to add custom functionality but make sure to also call the base method
198
- * using: super.onConnectionDisrupted( identifier )
199
- *
200
- * @method
201
- * @param {string} identifier The identifier of the observed connection.
202
- * @override
203
- * @private
204
- */
205
- onConnectionDisrupted( identifier ) {
206
- if ( this.#isAvailable === true && identifier === this.#connectionIdentifier ) {
207
- this.#isAvailable = false;
208
- _.forEach( this.#messageObservers, ( messageObserver ) => {
209
- messageObserver.onConnectionDisrupted( this.#connectionIdentifier );
210
- } );
211
- }
212
- }
213
-
214
- /**
215
- * An event-triggered method that will notify any observers about the primary connection having been lost.
216
- * <br/>
217
- * NOTE: You can override this to add custom functionality but make sure to also call the base method
218
- * using: super.onConnectionLost( identifier )
219
- *
220
- * @method
221
- * @param {string} identifier The identifier of the observed connection.
222
- * @override
223
- * @private
224
- */
225
- onConnectionLost( identifier ) {
226
- if ( identifier === this.#connectionIdentifier ) {
227
- this.#isAvailable = false;
228
- _.forEach( this.#messageObservers, ( messageObserver ) => {
229
- messageObserver.onConnectionLost( this.#connectionIdentifier );
230
- } );
231
- }
232
- }
233
-
234
- }
235
-
1
+ /*
2
+ * The ti-engine is an open source, free to use—both for personal and commercial projects—framework for the creation of microservice-based solutions using node.js.
3
+ * Copyright © 2021-2026 Boris Kostadinov <kostadinov.boris@gmail.com>
4
+ * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
5
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
6
+ * You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
7
+ */
8
+
9
+ const ConnectionObserver = require( "#connection-observer" );
10
+ const _ = require( "lodash" );
11
+ const crypto = require( "node:crypto" );
12
+ const exceptions = require( "#exceptions" );
13
+ const logger = require( "#logger" );
14
+ const tools = require( "#tools" );
15
+ const config = require( "#config" );
16
+
17
+ const OLD_DEFAULT_HASH_KEY = "23e7bdc7-a793-41f9-856e-6760332f0c73";
18
+ let keyWarningEmitted = false;
19
+
20
+ /**
21
+ * An abstract class that defines a basic message handler behavior.
22
+ * <br/>
23
+ * NOTE: This class and its children are designed to be used internally by classes extending the {@link MessageObserver} class.
24
+ *
25
+ * @class MessageHandler
26
+ * @extends ConnectionObserver
27
+ * @abstract
28
+ * @public
29
+ */
30
+ class MessageHandler extends ConnectionObserver {
31
+
32
+ #isAvailable = false;
33
+ #connectionIdentifier;
34
+ #messageObservers = [];
35
+
36
+ /**
37
+ * @constructor
38
+ * @param {string} identifier An identifier for this message handler. Should be unique in the context of the message exchange.
39
+ * @throws {TiException.E_GEN_ABSTRACT_CLASS_INIT} If this class is instantiated directly.
40
+ */
41
+ constructor( identifier ) {
42
+ super();
43
+
44
+ // make sure this abstract class cannot be instantiated:
45
+ if ( new.target === MessageHandler ) {
46
+ throw exceptions.raise( exceptions.exceptionCode.E_GEN_ABSTRACT_CLASS_INIT, { name: this.constructor.name } );
47
+ }
48
+
49
+ this.#connectionIdentifier = identifier;
50
+ }
51
+
52
+ /* Public interface */
53
+
54
+ /**
55
+ * Indicates whether the message handler is currently available.
56
+ *
57
+ * @property
58
+ * @returns {boolean}
59
+ * @public
60
+ */
61
+ get isAvailable() {
62
+ return this.#isAvailable;
63
+ }
64
+
65
+ /**
66
+ * Used to set the isAvailable flag.
67
+ * <br/>
68
+ * NOTE: For use by implementing classes only!
69
+ *
70
+ * @property
71
+ * @param {boolean} value
72
+ * @public
73
+ */
74
+ set isAvailable( value ) {
75
+ this.#isAvailable = value;
76
+ }
77
+
78
+ /**
79
+ * Returns the connection identifier.
80
+ *
81
+ * @property
82
+ * @returns {string}
83
+ * @public
84
+ */
85
+ get connectionIdentifier() {
86
+ return this.#connectionIdentifier;
87
+ }
88
+
89
+ /**
90
+ * Used to initialize and enable the communication capabilities of the handler.
91
+ * <br/>
92
+ * NOTE: Override this to add functionality.
93
+ *
94
+ * @method
95
+ * @returns {Promise}
96
+ * @abstract
97
+ * @public
98
+ */
99
+ enable() {
100
+ return Promise.reject( exceptions.raise( exceptions.exceptionCode.E_GEN_ABSTRACT_METHOD_CALL, { name: this.constructor.name + "." + this.enable.name } ) );
101
+ }
102
+
103
+ /**
104
+ * Used to shut down and disable the communication behavior of the handler.
105
+ * <br/>
106
+ * NOTE: Override this to add functionality.
107
+ *
108
+ * @method
109
+ * @returns {Promise}
110
+ * @abstract
111
+ * @public
112
+ */
113
+ disable() {
114
+ return Promise.reject( exceptions.raise( exceptions.exceptionCode.E_GEN_ABSTRACT_METHOD_CALL, { name: this.constructor.name + "." + this.disable.name } ) );
115
+ }
116
+
117
+ /**
118
+ * Used to create a security hash from the message.
119
+ *
120
+ * @method
121
+ * @param {Message} message
122
+ * @returns {string}
123
+ * @public
124
+ */
125
+ createMessageHash( message ) {
126
+ const rawKey = config.getSetting( config.setting.MESSAGE_EXCHANGE_SECURITY_HASH_KEY );
127
+ let key = rawKey == null ? "" : String( rawKey );
128
+ if ( keyWarningEmitted === false ) {
129
+ keyWarningEmitted = true;
130
+ if ( !key || key === OLD_DEFAULT_HASH_KEY ) {
131
+ logger.log( "Message-exchange security hash is enabled but no private key is set ('securityHashKey' is missing or the published default). Set TI_MESSAGE_EXCHANGE_SECURITY_HASH_KEY to a private value, otherwise tamper protection is ineffective.", logger.logSeverity.WARNING );
132
+ }
133
+ }
134
+ let transformed = tools.decomposeJSON( tools.decycle( message ) );
135
+ let hmac = crypto.createHmac( "sha256", Buffer.from( key, "utf8" ) );
136
+ hmac.update( Buffer.from( transformed ) );
137
+ return hmac.digest( "hex" );
138
+ }
139
+
140
+ /**
141
+ * Used to register a new {@link MessageObserver} for events related to the messages passing through this handler.
142
+ *
143
+ * @method
144
+ * @param {MessageObserver} messageObserver The {@link MessageObserver} that will be notified of any changes.
145
+ * @public
146
+ */
147
+ addMessageObserver( messageObserver ) {
148
+ const MessageObserver = require( "#message-observer" );
149
+ if ( messageObserver instanceof MessageObserver ) {
150
+ this.#messageObservers.push( messageObserver );
151
+ this.#messageObservers = _.orderBy( this.#messageObservers, [ "priority" ], [ "desc" ] );
152
+ } else {
153
+ logger.log( `Attempting to add '${ messageObserver.constructor.name }' as message observer but it's not a child-class of 'MessageObserver'!`, logger.logSeverity.WARNING );
154
+ }
155
+ }
156
+
157
+ /**
158
+ * An event-triggered method that will notify any observers about a new message for handling.
159
+ * <br/>
160
+ * NOTE: Each observer will be notified in the order of their priority via their {@link MessageObserver.onMessage} method. Additionally, the message will be
161
+ * passed through each observer in the order of their priority. If the observer returns a modified message, it will be used instead of the original message!
162
+ *
163
+ * @method
164
+ * @param {Message} message
165
+ * @public
166
+ */
167
+ notifyMessageObservers( message ) {
168
+ let modifiedMessage = message;
169
+ _.forEach( this.#messageObservers, ( messageObserver ) => {
170
+ modifiedMessage = messageObserver.onMessage( this.#connectionIdentifier, modifiedMessage );
171
+ } );
172
+ }
173
+
174
+ /**
175
+ * An event-triggered method that will notify any observers about the primary connection recovered state.
176
+ * <br/>
177
+ * NOTE: You can override this to add custom functionality but make sure to also call the base method
178
+ * using: super.onConnectionRecovered( identifier )
179
+ *
180
+ * @method
181
+ * @param {string} identifier The identifier of the observed connection.
182
+ * @override
183
+ * @private
184
+ */
185
+ onConnectionRecovered( identifier ) {
186
+ if ( this.#isAvailable === false && identifier === this.#connectionIdentifier ) {
187
+ this.#isAvailable = true;
188
+ _.forEach( this.#messageObservers, ( messageObserver ) => {
189
+ messageObserver.onConnectionRecovered( this.#connectionIdentifier );
190
+ } );
191
+ }
192
+ }
193
+
194
+ /**
195
+ * An event-triggered method that will notify any observers about the primary connection disrupted state.
196
+ * <br/>
197
+ * NOTE: You can override this to add custom functionality but make sure to also call the base method
198
+ * using: super.onConnectionDisrupted( identifier )
199
+ *
200
+ * @method
201
+ * @param {string} identifier The identifier of the observed connection.
202
+ * @override
203
+ * @private
204
+ */
205
+ onConnectionDisrupted( identifier ) {
206
+ if ( this.#isAvailable === true && identifier === this.#connectionIdentifier ) {
207
+ this.#isAvailable = false;
208
+ _.forEach( this.#messageObservers, ( messageObserver ) => {
209
+ messageObserver.onConnectionDisrupted( this.#connectionIdentifier );
210
+ } );
211
+ }
212
+ }
213
+
214
+ /**
215
+ * An event-triggered method that will notify any observers about the primary connection having been lost.
216
+ * <br/>
217
+ * NOTE: You can override this to add custom functionality but make sure to also call the base method
218
+ * using: super.onConnectionLost( identifier )
219
+ *
220
+ * @method
221
+ * @param {string} identifier The identifier of the observed connection.
222
+ * @override
223
+ * @private
224
+ */
225
+ onConnectionLost( identifier ) {
226
+ if ( identifier === this.#connectionIdentifier ) {
227
+ this.#isAvailable = false;
228
+ _.forEach( this.#messageObservers, ( messageObserver ) => {
229
+ messageObserver.onConnectionLost( this.#connectionIdentifier );
230
+ } );
231
+ }
232
+ }
233
+
234
+ }
235
+
236
236
  module.exports = MessageHandler;