@ti-engine/core 1.3.13 → 1.6.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.
@@ -1,6 +1,6 @@
1
1
  /*
2
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-2023 Boris Kostadinov <kostadinov.boris@gmail.com>
3
+ * Copyright © 2021-2026 Boris Kostadinov <kostadinov.boris@gmail.com>
4
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
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
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/>.
@@ -23,7 +23,7 @@ class MessageSender extends MessageHandler {
23
23
  /**
24
24
  * @constructor
25
25
  * @param {string} identifier An identifier for this message handler. Should be unique in the context of the message exchange.
26
- * @throws {Exception.E_GEN_ABSTRACT_CLASS_INIT} If this class is instantiated directly.
26
+ * @throws {TiException.E_GEN_ABSTRACT_CLASS_INIT} If this class is instantiated directly.
27
27
  */
28
28
  constructor( identifier ) {
29
29
  super( identifier );
@@ -1,6 +1,6 @@
1
1
  /*
2
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-2025 Boris Kostadinov <kostadinov.boris@gmail.com>
3
+ * Copyright © 2021-2026 Boris Kostadinov <kostadinov.boris@gmail.com>
4
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
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
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/>.
@@ -13,20 +13,6 @@ const logger = require( "#logger" );
13
13
  const exceptions = require( "#exceptions" );
14
14
  const cache = require( "#cache" );
15
15
 
16
- /**
17
- * @typedef {Object} TiTraceEntry
18
- * @property {string} chainID
19
- * @property {string} dispatchEvent
20
- * @property {string} fromAddress
21
- * @property {string} messageID
22
- * @property {Object} messageSnapshot
23
- * @property {string} messageState
24
- * @property {string} messageType
25
- * @property {string} toAddress
26
- * @property {string} traceID
27
- * @property {number} traceTimestamp
28
- */
29
-
30
16
  const traceRoot = {
31
17
  trace: []
32
18
  };
@@ -37,15 +23,12 @@ const UNKNOWN_TOKEN = "UNKNOWN";
37
23
  *
38
24
  * @readonly
39
25
  * @enum {number}
26
+ * @typedef {number} TiMessageType
40
27
  */
41
28
  let messageTypeEnum = tools.enum( {
42
29
  MESSAGE_REQUEST: [ 1000, "REQUEST", "" ],
43
30
  MESSAGE_RESPONSE: [ 1001, "RESPONSE", "" ]
44
31
  } );
45
-
46
- /**
47
- * @typedef {number} TiMessageType
48
- */
49
32
  module.exports.messageType = messageTypeEnum;
50
33
 
51
34
  /**
@@ -53,6 +36,7 @@ module.exports.messageType = messageTypeEnum;
53
36
  *
54
37
  * @readonly
55
38
  * @enum {number}
39
+ * @typedef {number} TiDispatchEvent
56
40
  */
57
41
  let dispatchEventEnum = tools.enum( {
58
42
  DELIVERED: [ 1100, "DELIVERED", "When message delivery is confirmed." ],
@@ -60,10 +44,6 @@ let dispatchEventEnum = tools.enum( {
60
44
  RECEIVED: [ 1102, "RECEIVED", "When message was received." ],
61
45
  SENT: [ 1103, "SENT", "When message was sent." ]
62
46
  } );
63
-
64
- /**
65
- * @typedef {number} TiDispatchEvent
66
- */
67
47
  module.exports.dispatchEvent = dispatchEventEnum;
68
48
 
69
49
  /**
@@ -71,15 +51,12 @@ module.exports.dispatchEvent = dispatchEventEnum;
71
51
  *
72
52
  * @readonly
73
53
  * @enum {number}
54
+ * @typedef {number} TiMessageState
74
55
  */
75
56
  let messageStateEnum = tools.enum( {
76
57
  PENDING: [ 1200, "PENDING", "" ],
77
58
  PROCESSED: [ 1201, "PROCESSED", "" ]
78
59
  } );
79
-
80
- /**
81
- * @typedef {number} TiMessageState
82
- */
83
60
  module.exports.messageState = messageStateEnum;
84
61
 
85
62
  /**
@@ -1,6 +1,6 @@
1
1
  /*
2
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-2025 Boris Kostadinov <kostadinov.boris@gmail.com>
3
+ * Copyright © 2021-2026 Boris Kostadinov <kostadinov.boris@gmail.com>
4
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
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
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/>.
@@ -14,45 +14,6 @@ const config = require( "#config" );
14
14
  const cache = require( "#cache" );
15
15
  const messageDispatcher = require( "#message-dispatcher" );
16
16
 
17
- /**
18
- * @typedef {Object} ServiceAddress
19
- * @property {string} serviceAlias A valid service alias.
20
- * @property {string} serviceDomainName A valid service domain name.
21
- * @property {number|undefined} serviceVersion Optional service version. If not provided, the latest version will be assumed as a target.
22
- */
23
-
24
- /**
25
- * @typedef {Object} ServiceExecContext
26
- * @property {string|undefined} authToken A valid authentication token that initialized the service call (if applicable).
27
- * @property {ServiceCallPredecessor|undefined} previousServiceCall The previous service call in the execution chain (if such exists).
28
- */
29
-
30
- /**
31
- * @typedef {Message} ServiceCallPredecessor
32
- * @property {string} predecessor The {@link Message.messageID} of the predecessor in the service call tree.
33
- * @property {ServiceAddress} serviceAddress The address of the service that has to process the service call.
34
- * @property {Object|undefined} serviceParams The named params to be provided to the API service.
35
- */
36
-
37
- /**
38
- * @typedef {ServiceCallPredecessor} ServiceCall
39
- * @property {string} authToken A valid authentication token that initialized the service call.
40
- * @property {number} createdOn A unix timestamp taken at creation time of the service call.
41
- * @property {number} executionTime The total execution time of this service call in milliseconds.
42
- * @property {Object|undefined} exception If there was exception during the service call processing, it will be set here. Otherwise, it will be 'undefined'.
43
- * @property {number|undefined} finishedOn A unix timestamp taken at finish time of the service call.
44
- * @property {boolean} isCompleted Flag to indicate if this service call has been completed.
45
- * @property {boolean|undefined} isSuccessful A flag indicating if this service call can be considered successful or not. Will be 'undefined' until the service call is processed.
46
- * @property {string[]} successors The service call IDs of the successors in the service call tree.
47
- */
48
-
49
- /**
50
- * @typedef {Object} ServiceCallResult
51
- * @property {Exception|undefined} exception If there was exception during the service call processing, it will be set here. Otherwise, it will be 'undefined'.
52
- * @property {boolean} isSuccessful A flag indicating if this service call can be considered successful or not.
53
- * @property {Object|string|undefined} payload The payload containing the results from the service call processing. If a string, it is ID of the payload in the memory cache instead.
54
- */
55
-
56
17
  /**
57
18
  * Used to assemble and prepare a new {@link ServiceCall} object.
58
19
  *
@@ -33,7 +33,7 @@ class ServiceConsumer extends ServiceInstance {
33
33
  * @constructor
34
34
  * @param {string} serviceDomainName The service domain name for this service instance.
35
35
  * @param {ServiceConfiguration} [serviceConfig] The JSON configuration for this service.
36
- * @throws {Exception.E_GEN_ABSTRACT_CLASS_INIT} If this class is instantiated directly.
36
+ * @throws {TiException.E_GEN_ABSTRACT_CLASS_INIT} If this class is instantiated directly.
37
37
  */
38
38
  constructor( serviceDomainName, serviceConfig ) {
39
39
  super( serviceDomainName, serviceConfig );
@@ -1,6 +1,6 @@
1
1
  /*
2
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-2023 Boris Kostadinov <kostadinov.boris@gmail.com>
3
+ * Copyright © 2021-2026 Boris Kostadinov <kostadinov.boris@gmail.com>
4
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
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
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/>.
@@ -15,21 +15,6 @@ const config = require( "#config" );
15
15
  const cache = require( "#cache" );
16
16
  const messageDispatcher = require( "#message-dispatcher" );
17
17
 
18
- /**
19
- * @typedef {Object} ServiceDefinition
20
- * @property {string} serviceAlias Service alias.
21
- * @property {string} serviceFile The JS file containing the service itself. This has to be exposed via package.json import structure!
22
- * @property {number} [serviceVersion] Service version.
23
- */
24
-
25
- /**
26
- * @typedef {Object.<string, ServiceInterfaceVersion>} ServiceInterface
27
- */
28
-
29
- /**
30
- * @typedef {Object.<number, ServiceHandlerMethod>} ServiceInterfaceVersion
31
- */
32
-
33
18
  /**
34
19
  * @callback VerifyAccessMethod
35
20
  * @param {string} authToken
@@ -1,6 +1,6 @@
1
1
  /*
2
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-2025 Boris Kostadinov <kostadinov.boris@gmail.com>
3
+ * Copyright © 2021-2026 Boris Kostadinov <kostadinov.boris@gmail.com>
4
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
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
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/>.
@@ -15,11 +15,6 @@ const exceptions = require( "#exceptions" );
15
15
  const cache = require( "#cache" );
16
16
  const messageDispatcher = require( "#message-dispatcher" );
17
17
 
18
- /**
19
- * @typedef {Object} ServiceConfiguration
20
- * @property {ServiceDefinition[]} [services] A list of service definitions to be registered with the {@link ServiceProvider}.
21
- */
22
-
23
18
  /**
24
19
  * Abstract class used to define a Service Instance behavior.
25
20
  * <br/>
@@ -43,8 +38,8 @@ class ServiceInstance {
43
38
  * @constructor
44
39
  * @param {string} serviceDomainName The service domain name for this service instance.
45
40
  * @param {ServiceConfiguration} [serviceConfig={ services: [] }] The JSON configuration for this service.
46
- * @throws {Exception.E_GEN_ABSTRACT_CLASS_INIT} If this class is instantiated directly.
47
- * @throws {Exception.E_GEN_FEATURE_UNSUPPORTED} If multiple instances are started in the same process.
41
+ * @throws {TiException.E_GEN_ABSTRACT_CLASS_INIT} If this class is instantiated directly.
42
+ * @throws {TiException.E_GEN_FEATURE_UNSUPPORTED} If multiple instances are started in the same process.
48
43
  */
49
44
  constructor( serviceDomainName, serviceConfig = { services: [] } ) {
50
45
  // Ensure this abstract class cannot be instantiated:
@@ -143,7 +138,7 @@ class ServiceInstance {
143
138
  * <br/>
144
139
  * NOTE: This method will be invoked automatically.
145
140
  * <br/>
146
- * NOTE: If you need to add more onStart logic you can override this method but make sure to call it in the
141
+ * NOTE: If you need to add more onStart logic, you can override this method but make sure to call it in the
147
142
  * overriding method using: super.onStart()
148
143
  *
149
144
  * @method
@@ -1,6 +1,6 @@
1
1
  /*
2
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-2025 Boris Kostadinov <kostadinov.boris@gmail.com>
3
+ * Copyright © 2021-2026 Boris Kostadinov <kostadinov.boris@gmail.com>
4
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
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
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/>.
@@ -38,7 +38,7 @@ class ServiceProvider extends ServiceConsumer {
38
38
  * @constructor
39
39
  * @param {string} serviceDomainName The service domain name for this service instance.
40
40
  * @param {ServiceConfiguration} [serviceConfig] The JSON configuration for this service.
41
- * @throws {Exception.E_GEN_ABSTRACT_CLASS_INIT} If this class is instantiated directly.
41
+ * @throws {TiException.E_GEN_ABSTRACT_CLASS_INIT} If this class is instantiated directly.
42
42
  */
43
43
  constructor( serviceDomainName, serviceConfig ) {
44
44
  super( serviceDomainName, serviceConfig );
@@ -56,7 +56,7 @@ class ServiceProvider extends ServiceConsumer {
56
56
  * <br/>
57
57
  * NOTE: This method will be invoked automatically.
58
58
  * <br/>
59
- * NOTE: If you need to add more onStart logic you can override this method but make sure to call it in the
59
+ * NOTE: If you need to add more onStart logic, you can override this method but make sure to call it in the
60
60
  * overriding method using: super.onStart()
61
61
  *
62
62
  * @method
@@ -88,7 +88,7 @@ class ServiceProvider extends ServiceConsumer {
88
88
  * <br/>
89
89
  * NOTE: This method will be invoked automatically.
90
90
  * <br/>
91
- * NOTE: If you need to add more onStop logic you can override this method but make sure to call it in the
91
+ * NOTE: If you need to add more onStop logic, you can override this method but make sure to call it in the
92
92
  * overriding method using: super.onStop()
93
93
  *
94
94
  * @method
@@ -110,7 +110,7 @@ class ServiceProvider extends ServiceConsumer {
110
110
  * Used to report health status of the service instance for external monitoring.
111
111
  * This is a scheduled job that will be executed at SERVICE_HEALTH_CHECK_INTERVAL time.
112
112
  * <br/>
113
- * NOTE: By default this method will update a Redis key with an expiration timer. You can override this
113
+ * NOTE: By default, this method will update a Redis key with an expiration timer. You can override this
114
114
  * functionality with something custom like calling an HTTP endpoint.
115
115
  *
116
116
  * @method
@@ -204,13 +204,14 @@ class ServiceProvider extends ServiceConsumer {
204
204
  let promises = [];
205
205
  _.forEach( serviceDefinitions, ( serviceDefinition ) => {
206
206
  // NOTE: we are not going to interrupt the service interface loading if one of the services fails to load or is not found!
207
- // If this happens, a corresponding log entry will be created but the loading process will continue. Therefore, the following
208
- // promise will always resolve (unless a programming error occurs in it, of course).
207
+ // If this happens, a corresponding log entry will be created, but the loading process will continue. Therefore, the following
208
+ // promise will always be resolved (unless a programming error occurs in it, of course).
209
209
  let registrationPromise = ( serviceDefinition, defaultServiceHandler ) => {
210
210
  return new Promise( ( resolve ) => {
211
211
  this.registerService( serviceDefinition, defaultServiceHandler ).then( () => {
212
212
  resolve( true );
213
- } ).catch( () => {
213
+ } ).catch( ( error ) => {
214
+ logger.log( `Service registration failed for '${ serviceDefinition?.serviceAlias || "unknown-service" }'.`, logger.logSeverity.ERROR, error );
214
215
  resolve( false );
215
216
  } );
216
217
  } );
@@ -1,6 +1,6 @@
1
1
  /*
2
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-2025 Boris Kostadinov <kostadinov.boris@gmail.com>
3
+ * Copyright © 2021-2026 Boris Kostadinov <kostadinov.boris@gmail.com>
4
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
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
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/>.
@@ -18,6 +18,7 @@ const _ = require( "lodash" );
18
18
  *
19
19
  * @readonly
20
20
  * @enum {string}
21
+ * @typedef {string} TiRedisCommand
21
22
  */
22
23
  let cacheCommandsEnum = tools.enum( {
23
24
  ADD_TO_SET: [ "sadd", "add to set", "https://redis.io/docs/latest/commands/sadd/" ],
@@ -34,6 +35,8 @@ let cacheCommandsEnum = tools.enum( {
34
35
  IS_SET_MEMBER: [ "sismember", "", "https://redis.io/docs/latest/commands/sismember/" ],
35
36
  JSON_ARRAY_APPEND: [ "json.arrappend", "", "https://redis.io/docs/latest/commands/json.arrappend/" ],
36
37
  JSON_GET: [ "json.get", "", "https://redis.io/docs/latest/commands/json.get/" ],
38
+ JSON_MERGE: [ "json.merge", "", "https://redis.io/docs/latest/commands/json.merge/" ],
39
+ JSON_MGET: [ "json.mget", "", "https://redis.io/docs/latest/commands/json.mget/" ],
37
40
  JSON_SET: [ "json.set", "", "https://redis.io/docs/latest/commands/json.set/" ],
38
41
  KEYS: [ "keys", "(warning: O(N), use SCAN where possible)", "https://redis.io/docs/latest/commands/keys/" ],
39
42
  LIST_PUSH: [ "lpush", "list push", "https://redis.io/docs/latest/commands/lpush/" ],
@@ -43,12 +46,14 @@ let cacheCommandsEnum = tools.enum( {
43
46
  SET_VALUE: [ "set", "set value", "https://redis.io/docs/latest/commands/set/" ],
44
47
  UNION_OF_SETS: [ "sunion", "union of sets", "https://redis.io/docs/latest/commands/sunion/" ]
45
48
  } );
49
+ module.exports.cacheCommands = cacheCommandsEnum;
46
50
 
47
51
  /**
48
52
  * Enum for listing all client statuses.
49
53
  *
50
54
  * @readonly
51
55
  * @enum {number}
56
+ * @typedef {number} TiRedisClientStatus
52
57
  */
53
58
  let clientStatusEnum = tools.enum( {
54
59
  UNINITIALIZED: [ 0, "uninitialized", "Redis client is offline and not yet initialized." ],
@@ -58,31 +63,21 @@ let clientStatusEnum = tools.enum( {
58
63
  SHUTTING_DOWN: [ 4, "shutting down", "Redis client is shutting down." ],
59
64
  DISCONNECTED: [ 5, "disconnected", "Redis client is permanently disconnected from server." ]
60
65
  } );
66
+ module.exports.clientStatus = clientStatusEnum;
61
67
 
62
68
  /**
63
69
  * Enum for listing the Redis key override modes.
64
70
  *
65
71
  * @readonly
66
72
  * @enum {string}
73
+ * @typedef {string} TiRedisOverrideMode
67
74
  */
68
75
  let cacheOverrideModeEnum = tools.enum( {
69
76
  DEFAULT: [ "", "default", "Standard Redis behaviour when setting new key." ],
70
77
  NX: [ "nx", "nx", "Sets the key only if it does not already exist." ],
71
78
  XX: [ "xx", "xx", "Sets the key only if it already exists." ]
72
79
  } );
73
-
74
- /**
75
- * @typedef {string} TiRedisCommand
76
- */
77
- module.exports.cacheCommands = cacheCommandsEnum;
78
- /**
79
- * @typedef {string} TiRedisOverrideMode
80
- */
81
80
  module.exports.cacheOverrideMode = cacheOverrideModeEnum;
82
- /**
83
- * @typedef {number} TiRedisClientStatus
84
- */
85
- module.exports.clientStatus = clientStatusEnum;
86
81
 
87
82
  /**
88
83
  * Used to create a Redis Cache client.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ti-engine/core",
3
- "version": "1.3.13",
3
+ "version": "1.6.0",
4
4
  "description": "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.",
5
5
  "keywords": [
6
6
  "microservices",
@@ -26,6 +26,9 @@
26
26
  "./service-provider": "./components/service-provider.js",
27
27
  "./tools": "./utils/tools.js"
28
28
  },
29
+ "scripts": {
30
+ "test": "node --test"
31
+ },
29
32
  "imports": {
30
33
  "#auditing": "./components/auditing.js",
31
34
  "#cache": "./utils/cache.js",
@@ -35,7 +38,6 @@
35
38
  "#default-message-receiver": "./components/exchange/default/default-message-receiver.js",
36
39
  "#default-message-sender": "./components/exchange/default/default-message-sender.js",
37
40
  "#exceptions": "./utils/exceptions.js",
38
- "#gcloud-integration": "./integrations/gcloud-integration.js",
39
41
  "#labels": "./bin/localization/labels.json",
40
42
  "#localization": "./utils/localization.js",
41
43
  "#logger": "./utils/logger.js",
@@ -57,15 +59,12 @@
57
59
  "#tools": "./utils/tools.js"
58
60
  },
59
61
  "dependencies": {
60
- "@dotenvx/dotenvx": "^1.51.1",
61
- "blake2": "^5.0.0",
62
- "ioredis": "^5.8.2",
63
- "lodash": "^4.17.21",
62
+ "@dotenvx/dotenvx": "^1.73.1",
63
+ "ioredis": "^5.11.1",
64
+ "lodash": "^4.18.1",
64
65
  "node-schedule": "^2.1.1"
65
66
  },
66
67
  "optionalDependencies": {
67
- "@google-cloud/error-reporting": "^3.0.5",
68
- "zeromq": "^6.5.0"
69
68
  },
70
69
  "files": [
71
70
  "bin/",
@@ -75,7 +74,7 @@
75
74
  "package.json",
76
75
  "README.md",
77
76
  "CHANGELOG.md",
78
- "LICENSE"
77
+ "LICENSE.md"
79
78
  ],
80
79
  "repository": {
81
80
  "type": "git",
@@ -87,6 +86,6 @@
87
86
  },
88
87
  "homepage": "https://github.com/Belleal/ti-engine/tree/master/packages/core#readme",
89
88
  "engines": {
90
- "node": ">=18.0.0"
89
+ "node": ">=20.0.0"
91
90
  }
92
- }
91
+ }
package/utils/cache.js CHANGED
@@ -1,6 +1,6 @@
1
1
  /*
2
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-2025 Boris Kostadinov <kostadinov.boris@gmail.com>
3
+ * Copyright © 2021-2026 Boris Kostadinov <kostadinov.boris@gmail.com>
4
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
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
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/>.
@@ -129,7 +129,7 @@ class CommonMemoryCache extends ConnectionObserver {
129
129
  *
130
130
  * @method
131
131
  * @param {string} identifier The identifier of the observed connection.
132
- * @throws {Exception.E_GEN_SYSTEM_CACHE_UNAVAILABLE} If the cache service is no longer available.
132
+ * @throws {TiException.E_GEN_SYSTEM_CACHE_UNAVAILABLE} If the cache service is no longer available.
133
133
  * @override
134
134
  * @public
135
135
  */
@@ -341,12 +341,7 @@ class CommonMemoryCache extends ConnectionObserver {
341
341
  expireValue( key, seconds, name ) {
342
342
  return new Promise( ( resolve, reject ) => {
343
343
  if ( this.#isOperational === true ) {
344
- let commandExpire = [];
345
- if ( name ) {
346
- commandExpire = [ redis.cacheCommands.HASH_EXPIRE, name, seconds, "FIELDS", 1, key ];
347
- } else {
348
- commandExpire = [ redis.cacheCommands.EXPIRE, key, seconds ];
349
- }
344
+ let commandExpire = ( name ) ? [ redis.cacheCommands.HASH_EXPIRE, name, seconds, "FIELDS", 1, key ] : [ redis.cacheCommands.EXPIRE, key, seconds ];
350
345
  this.#redisClient.executeCommands( [ commandExpire ] ).then( () => {
351
346
  resolve( seconds );
352
347
  } ).catch( ( error ) => {
@@ -632,7 +627,7 @@ class CommonMemoryCache extends ConnectionObserver {
632
627
  * @method
633
628
  * @param {string} key
634
629
  * @param {Object} value
635
- * @param {string} [path='&']
630
+ * @param {string|string[]} [path="$"] A dot-separated JSONPath string, or an array of literal key segments (use the array form when key names may contain dots or other special characters).
636
631
  * @param {number} [overrideMode=0] By default this allows full override for existing keys.
637
632
  * Option 1 will set the key only if it doesn't already exist. Option 2 will set it only if it already exists.
638
633
  * @returns {Promise}
@@ -642,7 +637,7 @@ class CommonMemoryCache extends ConnectionObserver {
642
637
  return new Promise( ( resolve, reject ) => {
643
638
  if ( this.#isOperational === true ) {
644
639
  if ( this.#redisClient.isJSONSupported ) {
645
- let commandArguments = [ redis.cacheCommands.JSON_SET, key, path, tools.stringifyJSON( value ) ];
640
+ let commandArguments = [ redis.cacheCommands.JSON_SET, key, this.#normalizeJSONPath( path ), tools.stringifyJSON( value ) ];
646
641
  if ( overrideMode !== 0 ) {
647
642
  commandArguments.push( overrideMode === 1 ? redis.cacheOverrideMode.NX : redis.cacheOverrideMode.XX );
648
643
  }
@@ -652,7 +647,7 @@ class CommonMemoryCache extends ConnectionObserver {
652
647
  reject( error );
653
648
  } );
654
649
  } else {
655
- reject( exceptions.raise( exceptions.exceptionCode.E_GEN_FEATURE_UNSUPPORTED ), { details: "No RedisJSON module installed on server." } );
650
+ reject( exceptions.raise( exceptions.exceptionCode.E_GEN_FEATURE_UNSUPPORTED, { details: "No RedisJSON module installed on server." } ) );
656
651
  }
657
652
  } else {
658
653
  reject( exceptions.raise( exceptions.exceptionCode.E_GEN_SYSTEM_CACHE_UNAVAILABLE ) );
@@ -667,7 +662,7 @@ class CommonMemoryCache extends ConnectionObserver {
667
662
  *
668
663
  * @method
669
664
  * @param {string} key
670
- * @param {string} path
665
+ * @param {string|string[]} [path="$"] A dot-separated JSONPath string, or an array of literal key segments (use the array form when key names may contain dots or other special characters).
671
666
  * @returns {Promise<Object>}
672
667
  * @public
673
668
  */
@@ -675,14 +670,45 @@ class CommonMemoryCache extends ConnectionObserver {
675
670
  return new Promise( ( resolve, reject ) => {
676
671
  if ( this.#isOperational === true ) {
677
672
  if ( this.#redisClient.isJSONSupported ) {
678
- let commandArguments = [ redis.cacheCommands.JSON_GET, key, path ];
673
+ let commandArguments = [ redis.cacheCommands.JSON_GET, key, this.#normalizeJSONPath( path ) ];
679
674
  this.#redisClient.callCommand( commandArguments ).then( ( result ) => {
680
- resolve( tools.parseJSON( result ) );
675
+ resolve( result != null ? tools.parseJSON( String( result ) ) : null );
676
+ } ).catch( ( error ) => {
677
+ reject( error );
678
+ } );
679
+ } else {
680
+ reject( exceptions.raise( exceptions.exceptionCode.E_GEN_FEATURE_UNSUPPORTED, { details: "No RedisJSON module installed on server." } ) );
681
+ }
682
+ } else {
683
+ reject( exceptions.raise( exceptions.exceptionCode.E_GEN_SYSTEM_CACHE_UNAVAILABLE ) );
684
+ }
685
+ } );
686
+ }
687
+
688
+ /**
689
+ * Used to update/edit an existing JSON variable.
690
+ * <br/>
691
+ * NOTE: Requires ReJSON module installed on server to work.
692
+ *
693
+ * @method
694
+ * @param {string} key
695
+ * @param {Object} value
696
+ * @param {string|string[]} [path="$"] A dot-separated JSONPath string, or an array of literal key segments (use the array form when key names may contain dots or other special characters).
697
+ * @returns {Promise}
698
+ * @public
699
+ */
700
+ editJSON( key, value, path = "$" ) {
701
+ return new Promise( ( resolve, reject ) => {
702
+ if ( this.#isOperational === true ) {
703
+ if ( this.#redisClient.isJSONSupported ) {
704
+ let commandArguments = [ redis.cacheCommands.JSON_MERGE, key, this.#normalizeJSONPath( path ), tools.stringifyJSON( value ) ];
705
+ this.#redisClient.callCommand( commandArguments ).then( () => {
706
+ resolve();
681
707
  } ).catch( ( error ) => {
682
708
  reject( error );
683
709
  } );
684
710
  } else {
685
- reject( exceptions.raise( exceptions.exceptionCode.E_GEN_FEATURE_UNSUPPORTED ), { details: "No RedisJSON module installed on server." } );
711
+ reject( exceptions.raise( exceptions.exceptionCode.E_GEN_FEATURE_UNSUPPORTED, { details: "No RedisJSON module installed on server." } ) );
686
712
  }
687
713
  } else {
688
714
  reject( exceptions.raise( exceptions.exceptionCode.E_GEN_SYSTEM_CACHE_UNAVAILABLE ) );
@@ -698,7 +724,7 @@ class CommonMemoryCache extends ConnectionObserver {
698
724
  * @method
699
725
  * @param {string} key
700
726
  * @param {Object} value
701
- * @param {string} path
727
+ * @param {string|string[]} [path="$"] A dot-separated JSONPath string, or an array of literal key segments (use the array form when key names may contain dots or other special characters).
702
728
  * @returns {Promise}
703
729
  * @public
704
730
  */
@@ -706,14 +732,14 @@ class CommonMemoryCache extends ConnectionObserver {
706
732
  return new Promise( ( resolve, reject ) => {
707
733
  if ( this.#isOperational === true ) {
708
734
  if ( this.#redisClient.isJSONSupported ) {
709
- let commandArguments = [ redis.cacheCommands.JSON_ARRAY_APPEND, key, path, tools.stringifyJSON( value ) ];
735
+ let commandArguments = [ redis.cacheCommands.JSON_ARRAY_APPEND, key, this.#normalizeJSONPath( path ), tools.stringifyJSON( value ) ];
710
736
  this.#redisClient.callCommand( commandArguments ).then( () => {
711
737
  resolve();
712
738
  } ).catch( ( error ) => {
713
739
  reject( error );
714
740
  } );
715
741
  } else {
716
- reject( exceptions.raise( exceptions.exceptionCode.E_GEN_FEATURE_UNSUPPORTED ), { details: "No RedisJSON module installed on server." } );
742
+ reject( exceptions.raise( exceptions.exceptionCode.E_GEN_FEATURE_UNSUPPORTED, { details: "No RedisJSON module installed on server." } ) );
717
743
  }
718
744
  } else {
719
745
  reject( exceptions.raise( exceptions.exceptionCode.E_GEN_SYSTEM_CACHE_UNAVAILABLE ) );
@@ -721,6 +747,26 @@ class CommonMemoryCache extends ConnectionObserver {
721
747
  } );
722
748
  }
723
749
 
750
+ /* Private interface */
751
+
752
+ /**
753
+ * Used to normalize a JSON path.
754
+ * <br/>
755
+ * NOTE: If "path" is an array, each element is treated as a literal key name and encoded with bracket notation,
756
+ * which correctly handles key names that contain dots or other JSONPath special characters.
757
+ *
758
+ * @method
759
+ * @param {string|string[]} path
760
+ * @returns {string}
761
+ * @private
762
+ */
763
+ #normalizeJSONPath( path ) {
764
+ if ( Array.isArray( path ) ) {
765
+ return "$" + path.map( ( segment ) => `["${ String( segment ).replace( /\\/g, "\\\\" ).replace( /"/g, '\\"' ) }"]` ).join( "" );
766
+ }
767
+ return ( path.startsWith( "$" ) === false ) ? ( "$." + path ) : path;
768
+ }
769
+
724
770
  }
725
771
 
726
772
  const instance = new CommonMemoryCache();