@ti-engine/core 1.13.0 → 1.14.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/CHANGELOG.md CHANGED
@@ -2,6 +2,14 @@
2
2
 
3
3
  This document contains the list of changes made to the framework. The format is based on the [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) specification.
4
4
 
5
+ ## Version 1.14.0
6
+
7
+ * feat(cache): select the backend through the new `memoryCache.provider` setting (`TI_MEMORY_CACHE_PROVIDER`). `"redis"` selects the built-in provider and remains the default; any other value is a module path resolved against the working directory and must export a `CacheProvider` subclass. Without this the provider contract added in 1.13.0 could not actually be pointed at anything else.
8
+ * feat(service-instance): make the message exchange optional through `messageExchange.enabled` (`TI_MESSAGE_EXCHANGE_ENABLED`), defaulting to `true`. When off, neither the exchange nor the dispatcher is initialized or shut down — and the exchange is the only remaining user of the cache primitives a non-Redis backend cannot reasonably provide (`blockingCommand`, pub/sub).
9
+ * feat(service-instance): make health reporting optional through `serviceConfig.healthCheckEnabled`, defaulting to `true`. The default interval is a six-field cron, so this writes once per second for the life of the instance; that is what an orchestrator watching a mesh wants and pure cost for a single instance with nothing reading the key.
10
+ * fix(cache): validate the configured provider class before constructing it. `typeof === "function"` accepts an arrow function, which is not a constructor — `new` on one threw a raw `TypeError` instead of the documented `E_GEN_INVALID_ARGUMENT_TYPE` — and asking `instanceof` after construction meant an unrelated class had its constructor run, executing arbitrary code from a misconfigured path, before anything rejected it. Both now fail the same declared way, without constructing anything.
11
+ * test(cache): cover backend selection and, with a stub backend that reproduces the Redis client's notify-then-resolve ordering, the rollback on a refused start — the 1.13.0 fix that shipped untested because it needed a live server. Verified to fail without the rollback. Core 53 to 60 tests.
12
+
5
13
  ## Version 1.13.0
6
14
 
7
15
  * feat(cache): introduce `CacheProvider`, an abstract backend contract, and move every Redis-specific detail behind it into `RedisCacheProvider`. `CommonMemoryCache` keeps its public API and now owns only the operational state, the connection observation and one guard shared by all twenty-one data methods.
package/bin/settings.json CHANGED
@@ -17,6 +17,7 @@
17
17
  },
18
18
  "memoryCache": {
19
19
  "authKey": null,
20
+ "provider": "redis",
20
21
  "redisDB": 0,
21
22
  "redisHost": "127.0.0.1",
22
23
  "redisPort": 6379,
@@ -24,6 +25,7 @@
24
25
  "user": "default"
25
26
  },
26
27
  "messageExchange": {
28
+ "enabled": true,
27
29
  "messageQueuePrefix": "ti:messages:",
28
30
  "messageStore": "ti:messages:store",
29
31
  "securityHashEnabled": true,
@@ -35,6 +37,7 @@
35
37
  "serviceConfig": {
36
38
  "executionTimeout": 180000,
37
39
  "healthCheckAddress": "ti:services:registry:health:",
40
+ "healthCheckEnabled": true,
38
41
  "healthCheckInterval": "*/1 * * * * *",
39
42
  "healthCheckTimeout": 3,
40
43
  "serviceRegistryAddress": "ti:services:registry:catalog:"
@@ -86,6 +86,23 @@ class ServiceInstance {
86
86
  return ServiceInstance.#instanceID;
87
87
  }
88
88
 
89
+ /**
90
+ * Property returning whether the message exchange is enabled for this instance.
91
+ * <br/>
92
+ * NOTE: When this is off the instance neither initializes nor shuts down the message dispatcher, and therefore
93
+ * touches none of the primitives the exchange needs from the cache backend - blocking reads and pub/sub, which are
94
+ * the only parts of the cache surface a non-Redis backend cannot reasonably provide. A single-instance deployment
95
+ * with no mesh to talk to is the case this exists for. Defaults to enabled, so an existing deployment is
96
+ * unaffected.
97
+ *
98
+ * @property
99
+ * @returns {boolean}
100
+ * @public
101
+ */
102
+ static get isMessageExchangeEnabled() {
103
+ return config.getSetting( config.setting.MESSAGE_EXCHANGE_ENABLED, true ) !== false;
104
+ }
105
+
89
106
  /**
90
107
  * Property returning the current service domain name.
91
108
  *
@@ -160,6 +177,11 @@ class ServiceInstance {
160
177
  onStart() {
161
178
  return new Promise( ( resolve, reject ) => {
162
179
  cache.instance.initialize().then( () => {
180
+ if ( ServiceInstance.isMessageExchangeEnabled === false ) {
181
+ logger.log( `Message exchange is disabled for instance '${ ServiceInstance.instanceID }' - it will neither send nor receive service messages.`, logger.logSeverity.NOTICE );
182
+ return undefined;
183
+ }
184
+
163
185
  const DefaultMessageExchange = require( "#default-message-exchange" );
164
186
  const ServiceProvider = require( "#service-provider" );
165
187
  const ServiceConsumer = require( "#service-consumer" );
@@ -214,6 +236,13 @@ class ServiceInstance {
214
236
  */
215
237
  onStop() {
216
238
  return new Promise( ( resolve, reject ) => {
239
+ // Symmetrical with onStart: a dispatcher that was never initialized has nothing to shut down, and asking
240
+ // it to would fail a stop that has nothing wrong with it.
241
+ if ( ServiceInstance.isMessageExchangeEnabled === false ) {
242
+ resolve();
243
+ return;
244
+ }
245
+
217
246
  messageDispatcher.instance.shutDown().then( () => {
218
247
  resolve();
219
248
  } ).catch( ( error ) => {
@@ -274,9 +303,17 @@ class ServiceInstance {
274
303
  #postStart() {
275
304
  return new Promise( ( resolve ) => {
276
305
  // Schedule regular health check:
277
- this.#reportHealthyJob = schedule.scheduleJob( config.getSetting( config.setting.SERVICE_HEALTH_CHECK_INTERVAL ), () => {
278
- this.reportHealthy();
279
- } );
306
+ // The default interval is a six-field cron, so this writes once per second for as long as the instance
307
+ // lives. That is what an orchestrator watching a mesh of instances wants, and pure cost anywhere else -
308
+ // a single instance with nothing reading the key, or a host that already knows whether the process is
309
+ // running, pays a write per second for nothing.
310
+ if ( config.getSetting( config.setting.SERVICE_HEALTH_CHECK_ENABLED, true ) === false ) {
311
+ logger.log( `Health check reporting is disabled for instance '${ ServiceInstance.instanceID }'.`, logger.logSeverity.NOTICE );
312
+ } else {
313
+ this.#reportHealthyJob = schedule.scheduleJob( config.getSetting( config.setting.SERVICE_HEALTH_CHECK_INTERVAL ), () => {
314
+ this.reportHealthy();
315
+ } );
316
+ }
280
317
 
281
318
  logger.log( `Instance '${ ServiceInstance.instanceID }' started successfully.`, logger.logSeverity.NOTICE, {
282
319
  nodeVersion: process.version,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ti-engine/core",
3
- "version": "1.13.0",
3
+ "version": "1.14.0",
4
4
  "description": "Microservice framework for Node.js: a Redis-backed message exchange with end-to-end call tracing, retries and tamper-evident message envelopes.",
5
5
  "keywords": [
6
6
  "microservices",
@@ -28,6 +28,20 @@ declare class ServiceInstance {
28
28
  * @public
29
29
  */
30
30
  static get instanceID(): string;
31
+ /**
32
+ * Property returning whether the message exchange is enabled for this instance.
33
+ * <br/>
34
+ * NOTE: When this is off the instance neither initializes nor shuts down the message dispatcher, and therefore
35
+ * touches none of the primitives the exchange needs from the cache backend - blocking reads and pub/sub, which are
36
+ * the only parts of the cache surface a non-Redis backend cannot reasonably provide. A single-instance deployment
37
+ * with no mesh to talk to is the case this exists for. Defaults to enabled, so an existing deployment is
38
+ * unaffected.
39
+ *
40
+ * @property
41
+ * @returns {boolean}
42
+ * @public
43
+ */
44
+ static get isMessageExchangeEnabled(): boolean;
31
45
  /**
32
46
  * Property returning the current service domain name.
33
47
  *
@@ -4,15 +4,51 @@ export declare var decodeCommandValue: typeof import("#redis-cache-provider").de
4
4
  export declare var mapCommandValues: typeof import("#redis-cache-provider").mapCommandValues;
5
5
  export { cacheCapability };
6
6
  export { findMissingCapabilities };
7
+ export { createConfiguredProvider };
8
+ export { isCacheProviderClass };
9
+ import CacheProvider = require("#cache-provider");
7
10
  import ConnectionObserver = require("#connection-observer");
8
11
  import RedisCacheProvider = require("#redis-cache-provider");
9
12
  import { cacheCapability } from "#cache-capability";
13
+ /**
14
+ * Determines whether a value is a class extending {@link CacheProvider}, without constructing it.
15
+ * <br/>
16
+ * NOTE: A `typeof === "function"` test is not enough, and constructing first to ask `instanceof` afterwards is worse.
17
+ * An arrow function passes the `typeof` test but is not a constructor, so `new` on it throws a raw TypeError instead
18
+ * of the documented exception; and an unrelated class would have its constructor RUN - arbitrary code from a
19
+ * misconfigured path - before anything rejected it. Walking the prototype chain answers the question without
20
+ * executing anything.
21
+ *
22
+ * @method
23
+ * @param {*} candidate The value exported by the configured provider module.
24
+ * @returns {boolean}
25
+ * @public
26
+ */
27
+ declare function isCacheProviderClass(candidate: any): boolean;
28
+ /**
29
+ * Creates the cache backend named by the 'memoryCache.provider' setting.
30
+ * <br/>
31
+ * NOTE: The built-in name "redis" selects {@link RedisCacheProvider}. Any other value is treated as a module path
32
+ * resolved against the process working directory, much as 'TI_INSTANCE_CLASS' already is, and must export a class
33
+ * extending {@link CacheProvider}. Resolution uses `path.resolve` rather than `path.join` so that an absolute path is
34
+ * taken as given instead of being appended to the working directory.
35
+ * <br/>
36
+ * NOTE: This runs while the singleton is being constructed, which is to say at require time. A bad provider name
37
+ * therefore fails the process immediately rather than at the first cache call - which is the point: a deployment
38
+ * pointed at a backend that does not exist should not reach the code that assumes one.
39
+ *
40
+ * @method
41
+ * @param {string} connectionIdentifier The identifier under which the backend's connection is observed.
42
+ * @returns {CacheProvider}
43
+ * @throws {TiException.E_GEN_INVALID_ARGUMENT_TYPE} If the configured module does not export a {@link CacheProvider}.
44
+ * @public
45
+ */
46
+ declare function createConfiguredProvider(connectionIdentifier: string): CacheProvider;
10
47
  /**
11
48
  * Determines which of the required capabilities a backend does not provide.
12
49
  * <br/>
13
- * NOTE: This lives outside the class, and is exported, for the same reason the Redis decoders are: the cache singleton
14
- * builds its own backend in its constructor, so the reconciliation cannot be driven without a live server. This is the
15
- * pure half of it, and it is the half that decides whether an instance starts.
50
+ * NOTE: This lives outside the class, and is exported, for the same reason the Redis decoders are: it is the pure half
51
+ * of the reconciliation, and the half that decides whether an instance starts.
16
52
  *
17
53
  * @method
18
54
  * @param {string[]} [required] Capabilities the application declared it needs.
@@ -18,6 +18,7 @@ declare const settingsEnum: import("../components/definitions.types").TiEnumOf<{
18
18
  LOCALIZATION_LABELS_PATH: string[];
19
19
  LOCALIZATION_LANGUAGE: string[];
20
20
  MEMORY_CACHE_AUTH_KEY: string[];
21
+ MEMORY_CACHE_PROVIDER: string[];
21
22
  MEMORY_CACHE_REDIS_DB: string[];
22
23
  MEMORY_CACHE_REDIS_HOST: string[];
23
24
  MEMORY_CACHE_REDIS_PORT: string[];
@@ -25,6 +26,7 @@ declare const settingsEnum: import("../components/definitions.types").TiEnumOf<{
25
26
  MEMORY_CACHE_RETRY_MAX_ATTEMPTS: string[];
26
27
  MEMORY_CACHE_RETRY_MAX_INTERVAL: string[];
27
28
  MEMORY_CACHE_USER: string[];
29
+ MESSAGE_EXCHANGE_ENABLED: string[];
28
30
  MESSAGE_EXCHANGE_QUEUE_PREFIX: string[];
29
31
  MESSAGE_EXCHANGE_MESSAGE_STORE: string[];
30
32
  MESSAGE_EXCHANGE_SECURITY_HASH_ENABLED: string[];
@@ -34,6 +36,7 @@ declare const settingsEnum: import("../components/definitions.types").TiEnumOf<{
34
36
  MESSAGE_EXCHANGE_TRACE_REPOSITORY: string[];
35
37
  SERVICE_EXECUTION_TIMEOUT: string[];
36
38
  SERVICE_HEALTH_CHECK_ADDRESS: string[];
39
+ SERVICE_HEALTH_CHECK_ENABLED: string[];
37
40
  SERVICE_HEALTH_CHECK_INTERVAL: string[];
38
41
  SERVICE_HEALTH_CHECK_TIMEOUT: string[];
39
42
  SERVICE_REGISTRY_ADDRESS: string[];
package/utils/cache.js CHANGED
@@ -15,19 +15,81 @@
15
15
  * limitations under the License.
16
16
  */
17
17
 
18
+ const CacheProvider = require( "#cache-provider" );
18
19
  const ConnectionObserver = require( "#connection-observer" );
19
20
  const RedisCacheProvider = require( "#redis-cache-provider" );
20
21
  const _ = require( "lodash" );
21
22
  const config = require( "#config" );
22
23
  const exceptions = require( "#exceptions" );
24
+ const path = require( "path" );
23
25
  const { cacheCapability } = require( "#cache-capability" );
24
26
 
27
+ /**
28
+ * Determines whether a value is a class extending {@link CacheProvider}, without constructing it.
29
+ * <br/>
30
+ * NOTE: A `typeof === "function"` test is not enough, and constructing first to ask `instanceof` afterwards is worse.
31
+ * An arrow function passes the `typeof` test but is not a constructor, so `new` on it throws a raw TypeError instead
32
+ * of the documented exception; and an unrelated class would have its constructor RUN - arbitrary code from a
33
+ * misconfigured path - before anything rejected it. Walking the prototype chain answers the question without
34
+ * executing anything.
35
+ *
36
+ * @method
37
+ * @param {*} candidate The value exported by the configured provider module.
38
+ * @returns {boolean}
39
+ * @public
40
+ */
41
+ function isCacheProviderClass( candidate ) {
42
+ return typeof candidate === "function" && candidate.prototype instanceof CacheProvider;
43
+ }
44
+
45
+ /**
46
+ * Creates the cache backend named by the 'memoryCache.provider' setting.
47
+ * <br/>
48
+ * NOTE: The built-in name "redis" selects {@link RedisCacheProvider}. Any other value is treated as a module path
49
+ * resolved against the process working directory, much as 'TI_INSTANCE_CLASS' already is, and must export a class
50
+ * extending {@link CacheProvider}. Resolution uses `path.resolve` rather than `path.join` so that an absolute path is
51
+ * taken as given instead of being appended to the working directory.
52
+ * <br/>
53
+ * NOTE: This runs while the singleton is being constructed, which is to say at require time. A bad provider name
54
+ * therefore fails the process immediately rather than at the first cache call - which is the point: a deployment
55
+ * pointed at a backend that does not exist should not reach the code that assumes one.
56
+ *
57
+ * @method
58
+ * @param {string} connectionIdentifier The identifier under which the backend's connection is observed.
59
+ * @returns {CacheProvider}
60
+ * @throws {TiException.E_GEN_INVALID_ARGUMENT_TYPE} If the configured module does not export a {@link CacheProvider}.
61
+ * @public
62
+ */
63
+ function createConfiguredProvider( connectionIdentifier ) {
64
+ let selected = config.getSetting( config.setting.MEMORY_CACHE_PROVIDER, "redis" );
65
+
66
+ if ( selected === "redis" ) {
67
+ return new RedisCacheProvider( connectionIdentifier );
68
+ }
69
+
70
+ let ProviderClass;
71
+ try {
72
+ ProviderClass = require( path.resolve( process.cwd(), selected ) );
73
+ } catch ( error ) {
74
+ throw exceptions.raise( exceptions.exceptionCode.E_GEN_INVALID_ARGUMENT_TYPE, {
75
+ details: `Could not load the cache provider configured as '${ selected }': ${ error.message }`
76
+ } );
77
+ }
78
+
79
+ if ( isCacheProviderClass( ProviderClass ) === false ) {
80
+ throw exceptions.raise( exceptions.exceptionCode.E_GEN_INVALID_ARGUMENT_TYPE, {
81
+ details: `The cache provider configured as '${ selected }' does not export a class extending CacheProvider.`
82
+ } );
83
+ }
84
+
85
+ return new ProviderClass( connectionIdentifier );
86
+ }
87
+
25
88
  /**
26
89
  * Determines which of the required capabilities a backend does not provide.
27
90
  * <br/>
28
- * NOTE: This lives outside the class, and is exported, for the same reason the Redis decoders are: the cache singleton
29
- * builds its own backend in its constructor, so the reconciliation cannot be driven without a live server. This is the
30
- * pure half of it, and it is the half that decides whether an instance starts.
91
+ * NOTE: This lives outside the class, and is exported, for the same reason the Redis decoders are: it is the pure half
92
+ * of the reconciliation, and the half that decides whether an instance starts.
31
93
  *
32
94
  * @method
33
95
  * @param {string[]} [required] Capabilities the application declared it needs.
@@ -68,7 +130,7 @@ class CommonMemoryCache extends ConnectionObserver {
68
130
  super();
69
131
 
70
132
  if ( !CommonMemoryCache.#instance ) {
71
- this.#provider = new RedisCacheProvider( this.#connectionIdentifier );
133
+ this.#provider = createConfiguredProvider( this.#connectionIdentifier );
72
134
  this.#provider.addConnectionObserver( this );
73
135
 
74
136
  CommonMemoryCache.#instance = this;
@@ -564,5 +626,7 @@ module.exports.mapCommandValues = RedisCacheProvider.mapCommandValues;
564
626
  // Re-exported so a consumer can name a capability without reaching past this module's exports map.
565
627
  module.exports.cacheCapability = cacheCapability;
566
628
 
567
- // Exported for testing, per the note on the function itself.
629
+ // Exported for testing, per the notes on the functions themselves.
568
630
  module.exports.findMissingCapabilities = findMissingCapabilities;
631
+ module.exports.createConfiguredProvider = createConfiguredProvider;
632
+ module.exports.isCacheProviderClass = isCacheProviderClass;
package/utils/config.js CHANGED
@@ -35,6 +35,7 @@ const settingsEnum = tools.enum( {
35
35
  LOCALIZATION_LABELS_PATH: [ "localization.labelsPath", "labelsPath", "" ],
36
36
  LOCALIZATION_LANGUAGE: [ "localization.language", "language", "" ],
37
37
  MEMORY_CACHE_AUTH_KEY: [ "memoryCache.authKey", "authKey", "" ],
38
+ MEMORY_CACHE_PROVIDER: [ "memoryCache.provider", "provider", "" ],
38
39
  MEMORY_CACHE_REDIS_DB: [ "memoryCache.redisDB", "redisDB", "" ],
39
40
  MEMORY_CACHE_REDIS_HOST: [ "memoryCache.redisHost", "redisHost", "" ],
40
41
  MEMORY_CACHE_REDIS_PORT: [ "memoryCache.redisPort", "redisPort", "" ],
@@ -42,6 +43,7 @@ const settingsEnum = tools.enum( {
42
43
  MEMORY_CACHE_RETRY_MAX_ATTEMPTS: [ "memoryCache.retryMaxAttempts", "retryMaxAttempts", "" ],
43
44
  MEMORY_CACHE_RETRY_MAX_INTERVAL: [ "memoryCache.retryMaxInterval", "retryMaxInterval", "" ],
44
45
  MEMORY_CACHE_USER: [ "memoryCache.user", "user", "" ],
46
+ MESSAGE_EXCHANGE_ENABLED: [ "messageExchange.enabled", "enabled", "" ],
45
47
  MESSAGE_EXCHANGE_QUEUE_PREFIX: [ "messageExchange.messageQueuePrefix", "messageQueuePrefix", "" ],
46
48
  MESSAGE_EXCHANGE_MESSAGE_STORE: [ "messageExchange.messageStore", "messageStore", "" ],
47
49
  MESSAGE_EXCHANGE_SECURITY_HASH_ENABLED: [ "messageExchange.securityHashEnabled", "securityHashEnabled", "" ],
@@ -51,6 +53,7 @@ const settingsEnum = tools.enum( {
51
53
  MESSAGE_EXCHANGE_TRACE_REPOSITORY: [ "messageExchange.traceRepository", "traceRepository", "" ],
52
54
  SERVICE_EXECUTION_TIMEOUT: [ "serviceConfig.executionTimeout", "executionTimeout", "" ],
53
55
  SERVICE_HEALTH_CHECK_ADDRESS: [ "serviceConfig.healthCheckAddress", "healthCheckAddress", "" ],
56
+ SERVICE_HEALTH_CHECK_ENABLED: [ "serviceConfig.healthCheckEnabled", "healthCheckEnabled", "" ],
54
57
  SERVICE_HEALTH_CHECK_INTERVAL: [ "serviceConfig.healthCheckInterval", "healthCheckInterval", "" ],
55
58
  SERVICE_HEALTH_CHECK_TIMEOUT: [ "serviceConfig.healthCheckTimeout", "healthCheckTimeout", "" ],
56
59
  SERVICE_REGISTRY_ADDRESS: [ "serviceConfig.serviceRegistryAddress", "serviceRegistryAddress", "" ],
@@ -76,6 +79,7 @@ if ( settings.localization ) {
76
79
  }
77
80
  if ( settings.memoryCache ) {
78
81
  settings.memoryCache.authKey = ( process.env.TI_MEMORY_CACHE_AUTH_KEY !== undefined ) ? process.env.TI_MEMORY_CACHE_AUTH_KEY : settings.memoryCache.authKey;
82
+ settings.memoryCache.provider = ( process.env.TI_MEMORY_CACHE_PROVIDER !== undefined ) ? process.env.TI_MEMORY_CACHE_PROVIDER : settings.memoryCache.provider;
79
83
  settings.memoryCache.redisDB = ( process.env.TI_MEMORY_CACHE_REDIS_DB !== undefined ) ? Number( process.env.TI_MEMORY_CACHE_REDIS_DB ) : settings.memoryCache.redisDB;
80
84
  settings.memoryCache.redisHost = ( process.env.TI_MEMORY_CACHE_REDIS_HOST !== undefined ) ? process.env.TI_MEMORY_CACHE_REDIS_HOST : settings.memoryCache.redisHost;
81
85
  settings.memoryCache.redisPort = ( process.env.TI_MEMORY_CACHE_REDIS_PORT !== undefined ) ? Number( process.env.TI_MEMORY_CACHE_REDIS_PORT ) : settings.memoryCache.redisPort;
@@ -90,6 +94,7 @@ if ( settings.memoryCache ) {
90
94
  settings.memoryCache.user = ( process.env.TI_MEMORY_CACHE_USER !== undefined ) ? process.env.TI_MEMORY_CACHE_USER : settings.memoryCache.user;
91
95
  }
92
96
  if ( settings.messageExchange ) {
97
+ settings.messageExchange.enabled = ( process.env.TI_MESSAGE_EXCHANGE_ENABLED !== undefined ) ? tools.toBool( process.env.TI_MESSAGE_EXCHANGE_ENABLED ) : settings.messageExchange.enabled;
93
98
  settings.messageExchange.securityHashEnabled = ( process.env.TI_MESSAGE_EXCHANGE_SECURITY_HASH_ENABLED !== undefined ) ? tools.toBool( process.env.TI_MESSAGE_EXCHANGE_SECURITY_HASH_ENABLED ) : settings.messageExchange.securityHashEnabled;
94
99
  settings.messageExchange.securityHashKey = ( process.env.TI_MESSAGE_EXCHANGE_SECURITY_HASH_KEY !== undefined ) ? process.env.TI_MESSAGE_EXCHANGE_SECURITY_HASH_KEY : settings.messageExchange.securityHashKey;
95
100
  settings.messageExchange.traceLogEnabled = ( process.env.TI_MESSAGE_EXCHANGE_TRACE_LOG_ENABLED !== undefined ) ? tools.toBool( process.env.TI_MESSAGE_EXCHANGE_TRACE_LOG_ENABLED ) : settings.messageExchange.traceLogEnabled;