@ti-engine/core 1.7.1 → 1.7.2

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 +383 -377
  2. package/LICENSE.md +321 -321
  3. package/README.md +597 -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,213 +1,213 @@
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 _ = require( "lodash" );
10
- const tools = require( "#tools" );
11
- const config = require( "#config" );
12
- const logger = require( "#logger" );
13
- const exceptions = require( "#exceptions" );
14
- const cache = require( "#cache" );
15
-
16
- const traceRoot = {
17
- trace: []
18
- };
19
- const UNKNOWN_TOKEN = "UNKNOWN";
20
-
21
- /**
22
- * Enum for listing message types.
23
- *
24
- * @readonly
25
- * @enum {number}
26
- * @typedef {number} TiMessageType
27
- */
28
- let messageTypeEnum = tools.enum( {
29
- MESSAGE_REQUEST: [ 1000, "REQUEST", "" ],
30
- MESSAGE_RESPONSE: [ 1001, "RESPONSE", "" ]
31
- } );
32
- module.exports.messageType = messageTypeEnum;
33
-
34
- /**
35
- * Enum for listing dispatch events.
36
- *
37
- * @readonly
38
- * @enum {number}
39
- * @typedef {number} TiDispatchEvent
40
- */
41
- let dispatchEventEnum = tools.enum( {
42
- DELIVERED: [ 1100, "DELIVERED", "When message delivery is confirmed." ],
43
- FAILED: [ 1101, "FAILED", "When message delivery has failed." ],
44
- RECEIVED: [ 1102, "RECEIVED", "When message was received." ],
45
- SENT: [ 1103, "SENT", "When message was sent." ]
46
- } );
47
- module.exports.dispatchEvent = dispatchEventEnum;
48
-
49
- /**
50
- * Enum for listing message states.
51
- *
52
- * @readonly
53
- * @enum {number}
54
- * @typedef {number} TiMessageState
55
- */
56
- let messageStateEnum = tools.enum( {
57
- PENDING: [ 1200, "PENDING", "" ],
58
- PROCESSED: [ 1201, "PROCESSED", "" ]
59
- } );
60
- module.exports.messageState = messageStateEnum;
61
-
62
- /**
63
- * Used for recording message trace entries.
64
- *
65
- * @class MessageTracer
66
- * @singleton
67
- * @public
68
- */
69
- class MessageTracer {
70
-
71
- static #instance = null;
72
-
73
- /**
74
- * @constructor
75
- * @return {MessageTracer}
76
- */
77
- constructor() {
78
- if ( !MessageTracer.#instance ) {
79
- MessageTracer.#instance = this;
80
- }
81
- return MessageTracer.#instance;
82
- }
83
-
84
- /* Public interface */
85
-
86
- /**
87
- * Used to initialize the message tracer.
88
- *
89
- * @method
90
- * @returns {Promise}
91
- * @public
92
- */
93
- initialize() {
94
- return new Promise( ( resolve, reject ) => {
95
- cache.instance.setJSON( config.getSetting( config.setting.MESSAGE_EXCHANGE_TRACE_REPOSITORY ), traceRoot, "$", 1 ).then( () => {
96
- resolve();
97
- } ).catch( ( error ) => {
98
- // If JSON is unsupported in Redis server, the trace will use a Set later, so we can resolve the promise:
99
- if ( error.code === exceptions.exceptionCode.E_GEN_FEATURE_UNSUPPORTED ) {
100
- resolve();
101
- } else {
102
- reject( exceptions.raise( error ) );
103
- }
104
- } );
105
- } );
106
- }
107
-
108
- /**
109
- * Used to create a trace entry for the provided {@link Message} and parameters.
110
- * <br/>
111
- * NOTE: By default, all trace events are stored in the memory cache for further processing and analysis. The
112
- * location is configured in the MESSAGE_EXCHANGE_TRACE_REPOSITORY setting.
113
- * <br/>
114
- * NOTE: Trace events are logged with severity level NOTICE or ERROR for failed dispatches. They still might be
115
- * filtered out if the minimum log level setting is set too high.
116
- *
117
- * @method
118
- * @param {Message} message The message to trace.
119
- * @param {TiMessageType} messageType The type of the message.
120
- * @param {TiDispatchEvent} dispatchEvent The event in the dispatch system that triggered the trace entry.
121
- * @param {TiMessageState} messageState The state of the message processing.
122
- * @public
123
- */
124
- recordTraceEntry( message, messageType, dispatchEvent, messageState ) {
125
- // Depending on whether the message comes as a request or response, the from and to addresses will be opposite:
126
- let source = message.source.route + "." + message.source.instanceID;
127
- let destination = message.destination.route + ( ( message.destination.instanceID != null ) ? "." + message.destination.instanceID : "" );
128
- let messageSnapshot = MessageTracer.#obscureSensitiveData( message );
129
- delete messageSnapshot.chainID;
130
- delete messageSnapshot.messageID;
131
- let currentDate = new Date();
132
-
133
- /** @type TiTraceEntry */
134
- let traceEntry = {
135
- chainID: message.chainID,
136
- dispatchEvent: dispatchEventEnum.name( dispatchEvent, UNKNOWN_TOKEN ),
137
- fromAddress: ( messageType === messageTypeEnum.MESSAGE_REQUEST ) ? source : destination,
138
- messageID: message.messageID,
139
- messageSnapshot: messageSnapshot,
140
- messageState: messageStateEnum.name( messageState, UNKNOWN_TOKEN ),
141
- messageType: messageTypeEnum.name( messageType, UNKNOWN_TOKEN ),
142
- toAddress: ( messageType === messageTypeEnum.MESSAGE_REQUEST ) ? destination : source,
143
- traceTimestamp: currentDate.getTime(),
144
- traceID: tools.getUUID()
145
- };
146
-
147
- // Only write the trace in the general log if this is enabled:
148
- if ( config.getSetting( config.setting.MESSAGE_EXCHANGE_TRACE_LOG_ENABLED ) === true ) {
149
- MessageTracer.#createLogEntry( traceEntry, ( dispatchEvent === dispatchEventEnum.FAILED ) ? logger.logSeverity.ERROR : logger.logSeverity.NOTICE );
150
- }
151
-
152
- // Add the trace entry to the repository in the memory cache:
153
- cache.instance.arrayAppendJSON( config.getSetting( config.setting.MESSAGE_EXCHANGE_TRACE_REPOSITORY ), traceEntry, "$.trace" ).then( () => {
154
- // This will refresh the expiration time for the trace repository on each new record:
155
- let expiration = config.getSetting( config.setting.MESSAGE_EXCHANGE_TRACE_EXPIRATION_TIME );
156
- return ( expiration > 0 ) ? cache.instance.expireValue( config.getSetting( config.setting.MESSAGE_EXCHANGE_TRACE_REPOSITORY ), expiration ) : expiration;
157
- } ).catch( ( error ) => {
158
- // If JSON is unsupported in Redis server, then try to store the trace entry in a Set:
159
- if ( error.code === exceptions.exceptionCode.E_GEN_FEATURE_UNSUPPORTED ) {
160
- cache.instance.addToSet( config.getSetting( config.setting.MESSAGE_EXCHANGE_TRACE_REPOSITORY ), traceEntry ).catch( ( error ) => {
161
- logger.log( `Failed to add message trace entry to the trace repository. While this will not prevent the application from running, it might still be a sign of a more serious problem!`, logger.logSeverity.WARNING, error );
162
- } );
163
- } else {
164
- logger.log( `Failed to add message trace entry to the trace repository. While this will not prevent the application from running, it might still be a sign of a more serious problem!`, logger.logSeverity.WARNING, error );
165
- }
166
- } );
167
- }
168
-
169
- /* Private interface */
170
-
171
- /**
172
- * Used to create a log entry from the trace entry.
173
- *
174
- * @method
175
- * @param {TiTraceEntry} traceEntry The trace entry to log.
176
- * @param {TiLogSeverity} severity The log entry severity.
177
- * @private
178
- */
179
- static #createLogEntry( traceEntry, severity ) {
180
- logger.log( MessageTracer.#formatLogEntry( traceEntry ), severity, traceEntry );
181
- }
182
-
183
- /**
184
- * Used to format trace entry into log-suitable string.
185
- *
186
- * @method
187
- * @param {TiTraceEntry} traceEntry
188
- * @return {string} Prepared trace info.
189
- * @private
190
- */
191
- static #formatLogEntry( traceEntry ) {
192
- return `Message(${ traceEntry.chainID || traceEntry.messageID }) Trace: '${ traceEntry.messageType } ${ traceEntry.dispatchEvent } ${ traceEntry.messageState }' From: '${ traceEntry.fromAddress }' To: '${ traceEntry.toAddress }'`;
193
- }
194
-
195
- /**
196
- * Used to obscure sensitive data in the message, remove the payload, and return a snapshot.
197
- *
198
- * @method
199
- * @param {Message} message
200
- * @returns {Message}
201
- * @private
202
- */
203
- static #obscureSensitiveData( message ) {
204
- /** @type Message */
205
- let messageSnapshot = tools.parseJSON( _.replace( tools.stringifyJSON( message ), /("\w*?pin\w*?"|"\w*?pass\w*?"|"\w*?otp\w*?"):"(.*?)"/gmi, "\"SENSITIVE_PROPERTY\":\"OBSCURED_BY_SYSTEM\"" ) );
206
- delete messageSnapshot.payload;
207
- return messageSnapshot;
208
- }
209
-
210
- }
211
-
212
- const instance = new MessageTracer();
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 _ = require( "lodash" );
10
+ const tools = require( "#tools" );
11
+ const config = require( "#config" );
12
+ const logger = require( "#logger" );
13
+ const exceptions = require( "#exceptions" );
14
+ const cache = require( "#cache" );
15
+
16
+ const traceRoot = {
17
+ trace: []
18
+ };
19
+ const UNKNOWN_TOKEN = "UNKNOWN";
20
+
21
+ /**
22
+ * Enum for listing message types.
23
+ *
24
+ * @readonly
25
+ * @enum {number}
26
+ * @typedef {number} TiMessageType
27
+ */
28
+ let messageTypeEnum = tools.enum( {
29
+ MESSAGE_REQUEST: [ 1000, "REQUEST", "" ],
30
+ MESSAGE_RESPONSE: [ 1001, "RESPONSE", "" ]
31
+ } );
32
+ module.exports.messageType = messageTypeEnum;
33
+
34
+ /**
35
+ * Enum for listing dispatch events.
36
+ *
37
+ * @readonly
38
+ * @enum {number}
39
+ * @typedef {number} TiDispatchEvent
40
+ */
41
+ let dispatchEventEnum = tools.enum( {
42
+ DELIVERED: [ 1100, "DELIVERED", "When message delivery is confirmed." ],
43
+ FAILED: [ 1101, "FAILED", "When message delivery has failed." ],
44
+ RECEIVED: [ 1102, "RECEIVED", "When message was received." ],
45
+ SENT: [ 1103, "SENT", "When message was sent." ]
46
+ } );
47
+ module.exports.dispatchEvent = dispatchEventEnum;
48
+
49
+ /**
50
+ * Enum for listing message states.
51
+ *
52
+ * @readonly
53
+ * @enum {number}
54
+ * @typedef {number} TiMessageState
55
+ */
56
+ let messageStateEnum = tools.enum( {
57
+ PENDING: [ 1200, "PENDING", "" ],
58
+ PROCESSED: [ 1201, "PROCESSED", "" ]
59
+ } );
60
+ module.exports.messageState = messageStateEnum;
61
+
62
+ /**
63
+ * Used for recording message trace entries.
64
+ *
65
+ * @class MessageTracer
66
+ * @singleton
67
+ * @public
68
+ */
69
+ class MessageTracer {
70
+
71
+ static #instance = null;
72
+
73
+ /**
74
+ * @constructor
75
+ * @return {MessageTracer}
76
+ */
77
+ constructor() {
78
+ if ( !MessageTracer.#instance ) {
79
+ MessageTracer.#instance = this;
80
+ }
81
+ return MessageTracer.#instance;
82
+ }
83
+
84
+ /* Public interface */
85
+
86
+ /**
87
+ * Used to initialize the message tracer.
88
+ *
89
+ * @method
90
+ * @returns {Promise}
91
+ * @public
92
+ */
93
+ initialize() {
94
+ return new Promise( ( resolve, reject ) => {
95
+ cache.instance.setJSON( config.getSetting( config.setting.MESSAGE_EXCHANGE_TRACE_REPOSITORY ), traceRoot, "$", 1 ).then( () => {
96
+ resolve();
97
+ } ).catch( ( error ) => {
98
+ // If JSON is unsupported in Redis server, the trace will use a Set later, so we can resolve the promise:
99
+ if ( error.code === exceptions.exceptionCode.E_GEN_FEATURE_UNSUPPORTED ) {
100
+ resolve();
101
+ } else {
102
+ reject( exceptions.raise( error ) );
103
+ }
104
+ } );
105
+ } );
106
+ }
107
+
108
+ /**
109
+ * Used to create a trace entry for the provided {@link Message} and parameters.
110
+ * <br/>
111
+ * NOTE: By default, all trace events are stored in the memory cache for further processing and analysis. The
112
+ * location is configured in the MESSAGE_EXCHANGE_TRACE_REPOSITORY setting.
113
+ * <br/>
114
+ * NOTE: Trace events are logged with severity level NOTICE or ERROR for failed dispatches. They still might be
115
+ * filtered out if the minimum log level setting is set too high.
116
+ *
117
+ * @method
118
+ * @param {Message} message The message to trace.
119
+ * @param {TiMessageType} messageType The type of the message.
120
+ * @param {TiDispatchEvent} dispatchEvent The event in the dispatch system that triggered the trace entry.
121
+ * @param {TiMessageState} messageState The state of the message processing.
122
+ * @public
123
+ */
124
+ recordTraceEntry( message, messageType, dispatchEvent, messageState ) {
125
+ // Depending on whether the message comes as a request or response, the from and to addresses will be opposite:
126
+ let source = message.source.route + "." + message.source.instanceID;
127
+ let destination = message.destination.route + ( ( message.destination.instanceID != null ) ? "." + message.destination.instanceID : "" );
128
+ let messageSnapshot = MessageTracer.#obscureSensitiveData( message );
129
+ delete messageSnapshot.chainID;
130
+ delete messageSnapshot.messageID;
131
+ let currentDate = new Date();
132
+
133
+ /** @type TiTraceEntry */
134
+ let traceEntry = {
135
+ chainID: message.chainID,
136
+ dispatchEvent: dispatchEventEnum.name( dispatchEvent, UNKNOWN_TOKEN ),
137
+ fromAddress: ( messageType === messageTypeEnum.MESSAGE_REQUEST ) ? source : destination,
138
+ messageID: message.messageID,
139
+ messageSnapshot: messageSnapshot,
140
+ messageState: messageStateEnum.name( messageState, UNKNOWN_TOKEN ),
141
+ messageType: messageTypeEnum.name( messageType, UNKNOWN_TOKEN ),
142
+ toAddress: ( messageType === messageTypeEnum.MESSAGE_REQUEST ) ? destination : source,
143
+ traceTimestamp: currentDate.getTime(),
144
+ traceID: tools.getUUID()
145
+ };
146
+
147
+ // Only write the trace in the general log if this is enabled:
148
+ if ( config.getSetting( config.setting.MESSAGE_EXCHANGE_TRACE_LOG_ENABLED ) === true ) {
149
+ MessageTracer.#createLogEntry( traceEntry, ( dispatchEvent === dispatchEventEnum.FAILED ) ? logger.logSeverity.ERROR : logger.logSeverity.NOTICE );
150
+ }
151
+
152
+ // Add the trace entry to the repository in the memory cache:
153
+ cache.instance.arrayAppendJSON( config.getSetting( config.setting.MESSAGE_EXCHANGE_TRACE_REPOSITORY ), traceEntry, "$.trace" ).then( () => {
154
+ // This will refresh the expiration time for the trace repository on each new record:
155
+ let expiration = config.getSetting( config.setting.MESSAGE_EXCHANGE_TRACE_EXPIRATION_TIME );
156
+ return ( expiration > 0 ) ? cache.instance.expireValue( config.getSetting( config.setting.MESSAGE_EXCHANGE_TRACE_REPOSITORY ), expiration ) : expiration;
157
+ } ).catch( ( error ) => {
158
+ // If JSON is unsupported in Redis server, then try to store the trace entry in a Set:
159
+ if ( error.code === exceptions.exceptionCode.E_GEN_FEATURE_UNSUPPORTED ) {
160
+ cache.instance.addToSet( config.getSetting( config.setting.MESSAGE_EXCHANGE_TRACE_REPOSITORY ), traceEntry ).catch( ( error ) => {
161
+ logger.log( `Failed to add message trace entry to the trace repository. While this will not prevent the application from running, it might still be a sign of a more serious problem!`, logger.logSeverity.WARNING, error );
162
+ } );
163
+ } else {
164
+ logger.log( `Failed to add message trace entry to the trace repository. While this will not prevent the application from running, it might still be a sign of a more serious problem!`, logger.logSeverity.WARNING, error );
165
+ }
166
+ } );
167
+ }
168
+
169
+ /* Private interface */
170
+
171
+ /**
172
+ * Used to create a log entry from the trace entry.
173
+ *
174
+ * @method
175
+ * @param {TiTraceEntry} traceEntry The trace entry to log.
176
+ * @param {TiLogSeverity} severity The log entry severity.
177
+ * @private
178
+ */
179
+ static #createLogEntry( traceEntry, severity ) {
180
+ logger.log( MessageTracer.#formatLogEntry( traceEntry ), severity, traceEntry );
181
+ }
182
+
183
+ /**
184
+ * Used to format trace entry into log-suitable string.
185
+ *
186
+ * @method
187
+ * @param {TiTraceEntry} traceEntry
188
+ * @return {string} Prepared trace info.
189
+ * @private
190
+ */
191
+ static #formatLogEntry( traceEntry ) {
192
+ return `Message(${ traceEntry.chainID || traceEntry.messageID }) Trace: '${ traceEntry.messageType } ${ traceEntry.dispatchEvent } ${ traceEntry.messageState }' From: '${ traceEntry.fromAddress }' To: '${ traceEntry.toAddress }'`;
193
+ }
194
+
195
+ /**
196
+ * Used to obscure sensitive data in the message, remove the payload, and return a snapshot.
197
+ *
198
+ * @method
199
+ * @param {Message} message
200
+ * @returns {Message}
201
+ * @private
202
+ */
203
+ static #obscureSensitiveData( message ) {
204
+ /** @type Message */
205
+ let messageSnapshot = tools.parseJSON( _.replace( tools.stringifyJSON( message ), /("\w*?pin\w*?"|"\w*?pass\w*?"|"\w*?otp\w*?"):"(.*?)"/gmi, "\"SENSITIVE_PROPERTY\":\"OBSCURED_BY_SYSTEM\"" ) );
206
+ delete messageSnapshot.payload;
207
+ return messageSnapshot;
208
+ }
209
+
210
+ }
211
+
212
+ const instance = new MessageTracer();
213
213
  module.exports.instance = Object.freeze( instance );