@63klabs/cache-data 1.3.8 → 1.3.10

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,121 +1,17 @@
1
- contentType = "application/xml";
1
+ const { createGenericResponseModule } = require("./generic.response");
2
2
 
3
- headers = {
4
- "Content-Type": contentType
5
- };
6
-
7
- xml = (body) => {
3
+ const xml = (body) => {
8
4
  return `<?xml version="1.0" encoding="UTF-8" ?>${body}`;
9
- }
10
-
11
- response200 = {
12
- statusCode: 200,
13
- headers: headers,
14
- body: xml("<hello>Success</hello>")
15
- };
16
-
17
- response400 = {
18
- statusCode: 400,
19
- headers: headers,
20
- body: xml("<error>Bad Request</error>")
21
- };
22
-
23
- response401 = {
24
- statusCode: 401,
25
- headers: headers,
26
- body: xml("<error>Unauthorized</error>")
27
- };
28
-
29
- response403 = {
30
- statusCode: 403,
31
- headers: headers,
32
- body: xml("<error>Forbidden</error>")
33
5
  };
34
6
 
35
- response404 = {
36
- statusCode: 404,
37
- headers: headers,
38
- body: xml("<error>Not Found</error>")
39
- };
40
-
41
- response405 = {
42
- statusCode: 405,
43
- headers: headers,
44
- body: xml("<error>Method Not Allowed</error>")
45
- };
46
-
47
- response408 = {
48
- statusCode: 408,
49
- headers: headers,
50
- body: xml("<error>Request Timeout</error>")
51
- };
52
-
53
- response418 = {
54
- statusCode: 418,
55
- headers: headers,
56
- body: xml("<error>418 I'm a teapot</error>")
57
- };
58
-
59
- response427 = {
60
- statusCode: 427,
61
- headers: headers,
62
- body: xml("<error>Too Many Requests</error>")
63
- };
64
-
65
- response500 = {
66
- statusCode: 500,
67
- headers: headers,
68
- body: xml("<error>Internal Server Error</error>")
69
- };
70
-
71
- /**
72
- *
73
- * @param {number|string} statusCode
74
- * @returns {{statusCode: number, headers: object, body: Array|Object|string}}
75
- */
76
- response = function (statusCode) {
77
- // convert to int
78
- statusCode = parseInt(statusCode, 10);
79
-
80
- switch (statusCode) {
81
- case 200:
82
- return this.response200;
83
- case 400:
84
- return this.response400;
85
- case 401:
86
- return this.response401;
87
- case 403:
88
- return this.response403;
89
- case 404:
90
- return this.response404;
91
- case 405:
92
- return this.response405;
93
- case 408:
94
- return this.response408;
95
- case 418:
96
- return this.response418;
97
- case 427:
98
- return this.response427;
99
- case 500:
100
- return this.response500;
101
- default:
102
- return this.response500;
7
+ const xmlBodyFormatter = (statusCode, message) => {
8
+ if (statusCode === 200) {
9
+ return xml("<hello>" + message + "</hello>");
103
10
  }
11
+ const msg = statusCode === 418 ? "418 " + message : message;
12
+ return xml("<error>" + msg + "</error>");
104
13
  };
105
14
 
106
- module.exports = {
107
- contentType,
108
- headers,
109
- xml,
110
- response200,
111
- response400,
112
- response401,
113
- response403,
114
- response404,
115
- response405,
116
- response408,
117
- response418,
118
- response427,
119
- response500,
120
- response
121
- }
15
+ const mod = createGenericResponseModule("application/xml", xmlBodyFormatter);
16
+
17
+ module.exports = { ...mod, xml };
@@ -20,7 +20,7 @@
20
20
 
21
21
  const { nodeVer, nodeVerMajor, nodeVerMinor, nodeVerMajorMinor } = require('./vars');
22
22
  const { AWS, AWSXRay } = require('./AWS.classes');
23
- const APIRequest = require("./APIRequest.class");
23
+ const ApiRequest = require("./ApiRequest.class");
24
24
  const RequestInfo = require("./RequestInfo.class");
25
25
  const ClientRequest = require("./ClientRequest.class");
26
26
  const ResponseDataModel = require("./ResponseDataModel.class");
@@ -34,7 +34,7 @@ const xmlGenericResponse = require('./generic.response.xml');
34
34
  const rssGenericResponse = require('./generic.response.rss');
35
35
  const textGenericResponse = require('./generic.response.text');
36
36
  const { printMsg, sanitize, obfuscate, hashThisData} = require('./utils');
37
- const { CachedParameterSecrets, CachedParameterSecret, CachedSSMParameter, CachedSecret } = require('./CachedParametersSecrets.classes')
37
+ const { CachedParameterSecrets, CachedParameterSecret, CachedSsmParameter, CachedSecret } = require('./CachedParametersSecrets.classes')
38
38
  const { Connections, Connection, ConnectionRequest, ConnectionAuthentication } = require('./Connections.classes')
39
39
 
40
40
  /*
@@ -107,7 +107,11 @@ class AppConfig {
107
107
  static _ssmParameters = null;
108
108
 
109
109
  /**
110
- * Initialize the Config class
110
+ * Initialize the Config class with asynchronous parallel execution.
111
+ *
112
+ * This method returns immediately (synchronously) while initialization operations
113
+ * execute asynchronously in parallel. Use AppConfig.promise() to wait for all
114
+ * initialization to complete before accessing initialized configuration.
111
115
  *
112
116
  * @param {object} options Configuration options
113
117
  * @param {object} options.settings Application settings retrieved by Config.settings()
@@ -124,8 +128,10 @@ class AppConfig {
124
128
  * @param {object} options.responses.rssResponses
125
129
  * @param {object} options.responses.textResponses
126
130
  * @param {object} options.ssmParameters Parameter Store
127
- * @returns {Promise<void>}
131
+ * @param {boolean} [options.debug=false] Enable debug logging
132
+ * @returns {boolean} True if initialization started successfully, false on synchronous error
128
133
  * @example
134
+ * // Initialize configuration (returns immediately)
129
135
  * const { Config } = require("./config");
130
136
  * Config.init({
131
137
  * settings: {
@@ -143,48 +149,100 @@ class AppConfig {
143
149
  * }
144
150
  * }
145
151
  * });
152
+ *
153
+ * // Wait for all initialization to complete
154
+ * await Config.promise();
155
+ *
156
+ * // Now safe to access initialized configuration
157
+ * const settings = Config.settings();
158
+ * const conn = Config.getConn('myConnection');
146
159
  */
147
160
  static init(options = {}) {
148
161
 
149
- try {
162
+ try {
150
163
 
151
- const debug = (options?.debug === true);
152
- if (debug) {
153
- DebugAndLog.debug("Config Init in debug mode");
154
- }
164
+ const debug = (options?.debug === true);
165
+ if (debug) {
166
+ DebugAndLog.debug("Config Init in debug mode");
167
+ }
155
168
 
156
- if (options.settings) {
157
- AppConfig._settings = options.settings;
158
- if (debug) { DebugAndLog.debug("Settings initialized", AppConfig._settings); }
159
- }
169
+ if (options.settings) {
170
+ const settingsPromise = new Promise((resolve) => {
171
+ try {
172
+ AppConfig._settings = options.settings;
173
+ if (debug) {
174
+ DebugAndLog.debug("Settings initialized", AppConfig._settings);
175
+ }
176
+ resolve(true);
177
+ } catch (error) {
178
+ DebugAndLog.error(`Settings initialization failed: ${error.message}`, error.stack);
179
+ resolve(false);
180
+ }
181
+ });
182
+ AppConfig.add(settingsPromise);
183
+ }
160
184
 
161
- if (options.connections) {
162
- AppConfig._connections = new Connections(options.connections);
163
- if (debug) { DebugAndLog.debug("Connections initialized", AppConfig._connections.info()); }
164
- }
185
+ if (options.connections) {
186
+ const connectionsPromise = new Promise((resolve) => {
187
+ try {
188
+ AppConfig._connections = new Connections(options.connections);
189
+ if (debug) {
190
+ DebugAndLog.debug("Connections initialized", AppConfig._connections.info());
191
+ }
192
+ resolve(true);
193
+ } catch (error) {
194
+ DebugAndLog.error(`Connections initialization failed: ${error.message}`, error.stack);
195
+ resolve(false);
196
+ }
197
+ });
198
+ AppConfig.add(connectionsPromise);
199
+ }
165
200
 
166
- if (options.validations) {
167
- ClientRequest.init(options.validations);
168
- if (debug) { DebugAndLog.debug("ClientRequest initialized", ClientRequest.info()); }
169
- }
201
+ if (options.validations) {
202
+ const validationsPromise = new Promise((resolve) => {
203
+ try {
204
+ ClientRequest.init(options.validations);
205
+ if (debug) {
206
+ DebugAndLog.debug("ClientRequest initialized", ClientRequest.info());
207
+ }
208
+ resolve(true);
209
+ } catch (error) {
210
+ DebugAndLog.error(`ClientRequest initialization failed: ${error.message}`, error.stack);
211
+ resolve(false);
212
+ }
213
+ });
214
+ AppConfig.add(validationsPromise);
215
+ }
170
216
 
171
- if (options.responses) {
172
- Response.init(options.responses);
173
- if (debug) { DebugAndLog.debug("Response initialized", Response.info()); }
174
- }
217
+ if (options.responses) {
218
+ const responsesPromise = new Promise((resolve) => {
219
+ try {
220
+ Response.init(options.responses);
221
+ if (debug) {
222
+ DebugAndLog.debug("Response initialized", Response.info());
223
+ }
224
+ resolve(true);
225
+ } catch (error) {
226
+ DebugAndLog.error(`Response initialization failed: ${error.message}`, error.stack);
227
+ resolve(false);
228
+ }
229
+ });
230
+ AppConfig.add(responsesPromise);
231
+ }
175
232
 
176
- if (options.ssmParameters) {
177
- AppConfig._ssmParameters = AppConfig._initParameters(options.ssmParameters);
178
- AppConfig.add(AppConfig._ssmParameters);
179
- }
233
+ if (options.ssmParameters) {
234
+ AppConfig._ssmParameters = AppConfig._initParameters(options.ssmParameters);
235
+ AppConfig.add(AppConfig._ssmParameters);
236
+ }
180
237
 
181
- return true;
238
+ return true;
182
239
 
183
- } catch (error) {
184
- DebugAndLog.error(`Could not initialize Config ${error.message}`, error.stack);
185
- return false;
240
+ } catch (error) {
241
+ DebugAndLog.error(`Could not initialize Config ${error.message}`, error.stack);
242
+ return false;
243
+ }
186
244
  }
187
- };
245
+ ;
188
246
 
189
247
  /**
190
248
  * Add a promise to AppConfig. Use AppConfig.promise() to ensure all are resolved.
@@ -237,7 +295,7 @@ class AppConfig {
237
295
  * const conn = Config.getConn('myConnection');
238
296
  * const cacheObj = await CacheableDataAccess.getData(
239
297
  * cacheProfile,
240
- * endpoint.get
298
+ * endpoint.send
241
299
  * conn
242
300
  * )
243
301
  * */
@@ -264,7 +322,7 @@ class AppConfig {
264
322
  * const { conn, cacheProfile } = Config.getConnCacheProfile('myConnection', 'myCacheProfile');
265
323
  * const cacheObj = await CacheableDataAccess.getData(
266
324
  * cacheProfile,
267
- * endpoint.get
325
+ * endpoint.send
268
326
  * conn
269
327
  * )
270
328
  */
@@ -299,13 +357,13 @@ class AppConfig {
299
357
 
300
358
  /**
301
359
  *
302
- * @returns {Promise} A promise that resolves when the Config class has finished initializing
360
+ * @returns {Promise<array>} A promise that resolves when the Config class has finished initializing
303
361
  */
304
362
  static promise() {
305
363
  if (AppConfig._promise !== null ) { // Backwards compatibility
306
364
  AppConfig._promises.push(AppConfig._promise);
307
365
  }
308
- return Promise.all[AppConfig._promises];
366
+ return Promise.all(AppConfig._promises);
309
367
  };
310
368
 
311
369
 
@@ -405,6 +463,14 @@ class AppConfig {
405
463
  // put the parameter into its group
406
464
  const obj = parameters.find(o => o.path === groupPath);
407
465
  const group = obj.group;
466
+
467
+ // >! Guard against prototype pollution (CWE-471)
468
+ const DANGEROUS_KEYS = ['__proto__', 'constructor', 'prototype'];
469
+ if (DANGEROUS_KEYS.includes(group) || DANGEROUS_KEYS.includes(name)) {
470
+ DebugAndLog.warn(`Skipping dangerous parameter key: group="${group}", name="${name}"`);
471
+ return;
472
+ }
473
+
408
474
  if ( !(group in paramstore)) {
409
475
  paramstore[group] = {};
410
476
  }
@@ -473,8 +539,12 @@ module.exports = {
473
539
  nodeVerMinor,
474
540
  nodeVerMajorMinor,
475
541
  AWS,
542
+ Aws: AWS,
476
543
  AWSXRay,
477
- APIRequest,
544
+ AwsXRay: AWSXRay, // Alias
545
+ ApiRequest,
546
+ /** @deprecated Use ApiRequest instead */
547
+ APIRequest: ApiRequest, // Alias
478
548
  ImmutableObject,
479
549
  Timer,
480
550
  DebugAndLog,
@@ -487,8 +557,11 @@ module.exports = {
487
557
  ResponseDataModel,
488
558
  Response,
489
559
  AppConfig,
560
+ /** @deprecated Use AppConfig instead */
490
561
  _ConfigSuperClass: AppConfig, // Alias
491
- CachedSSMParameter,
562
+ CachedSsmParameter,
563
+ /** @deprecated Use CachedSsmParameter instead */
564
+ CachedSSMParameter: CachedSsmParameter, // Alias
492
565
  CachedSecret,
493
566
  CachedParameterSecret,
494
567
  CachedParameterSecrets,
@@ -0,0 +1,70 @@
1
+ /**
2
+ * ValidationExecutor - Executes validation functions with appropriate interfaces
3
+ *
4
+ * This class provides static methods for executing validation functions with either
5
+ * single-parameter or multi-parameter interfaces. It handles errors gracefully and
6
+ * logs validation failures.
7
+ *
8
+ * The interface is determined by the params array length:
9
+ * - params.length === 1: Pass value directly (single-parameter interface)
10
+ * - params.length > 1: Pass object (multi-parameter interface)
11
+ *
12
+ * @private
13
+ * @class ValidationExecutor
14
+ */
15
+ class ValidationExecutor {
16
+ /**
17
+ * Execute validation function with appropriate interface.
18
+ *
19
+ * Determines whether to pass a single value or an object based on the number
20
+ * of parameters in the params array. Handles validation errors gracefully by
21
+ * catching exceptions and logging them.
22
+ *
23
+ * @param {Function} validateFn - Validation function to execute
24
+ * @param {Array<string>} paramNames - Parameter names to validate
25
+ * @param {Object} paramValues - All parameter values available
26
+ * @returns {boolean} True if validation passes, false if fails or throws
27
+ *
28
+ * @example
29
+ * // Single parameter validation (params.length === 1)
30
+ * const isValid = ValidationExecutor.execute(
31
+ * (value) => value.length > 0,
32
+ * ['id'],
33
+ * { id: '123' }
34
+ * );
35
+ *
36
+ * @example
37
+ * // Multi-parameter validation (params.length > 1)
38
+ * const isValid = ValidationExecutor.execute(
39
+ * ({page, limit}) => page >= 1 && limit >= 1 && limit <= 100,
40
+ * ['page', 'limit'],
41
+ * { page: 1, limit: 10 }
42
+ * );
43
+ */
44
+ static execute(validateFn, paramNames, paramValues) {
45
+ try {
46
+ if (paramNames.length === 1) {
47
+ // Single parameter: pass value directly
48
+ const value = paramValues[paramNames[0]];
49
+ return validateFn(value);
50
+ } else {
51
+ // Multiple parameters: pass object
52
+ const paramObj = {};
53
+ for (const name of paramNames) {
54
+ paramObj[name] = paramValues[name];
55
+ }
56
+ return validateFn(paramObj);
57
+ }
58
+ } catch (error) {
59
+ // Log error and treat as validation failure
60
+ const tools = require("../tools/index.js");
61
+ tools.DebugAndLog.error(
62
+ `Validation function threw error for parameters [${paramNames.join(", ")}]: ${error?.message || "Unknown error"}`,
63
+ error?.stack
64
+ );
65
+ return false;
66
+ }
67
+ }
68
+ }
69
+
70
+ module.exports = ValidationExecutor;