@63klabs/cache-data 1.3.5 → 1.3.7

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.
@@ -11,7 +11,7 @@
11
11
  * via the APIRequest class.
12
12
  *
13
13
  * The class itself is not exposed, instead various functions can be used
14
- * to access the class. For exmaple, getDataDirectFromURI(connection, data)
14
+ * to access the class. For exmaple, get(connection, data)
15
15
  *
16
16
  * The connection parameter is used to pass connection information to the
17
17
  * API (host, path, query, etc).
@@ -21,7 +21,7 @@
21
21
  *
22
22
  * @example
23
23
  * // access function that utilizes the class
24
- * const getDataDirectFromURI = async (connection, data = null) => {
24
+ * const get = async (connection, data = null) => {
25
25
  * return (new Endpoint(connection).get());
26
26
  * };
27
27
  */
@@ -33,19 +33,20 @@
33
33
  */
34
34
 
35
35
  /**
36
- * @typedef ConnectionObject
37
- * @property {Object} connection
38
- * @property {string} connection.method
39
- * @property {string} connection.uri
40
- * @property {string} connection.protocol http or https
41
- * @property {string} connection.host
42
- * @property {string} connection.path
43
- * @property {string} connection.body
44
- * @property {object} connection.parameters
45
- * @property {object} connection.headers
46
- * @property {object} connection.options
47
- * @property {number} connection.options.timeout
48
- * @property {string} connection.note
36
+ * Connection configuration object for making endpoint requests.
37
+ *
38
+ * @typedef {Object} ConnectionObject
39
+ * @property {string} [method="GET"] - HTTP method (GET, POST, PUT, DELETE, etc.)
40
+ * @property {string} [uri] - Complete URI including protocol, host, and path (alternative to separate protocol/host/path)
41
+ * @property {string} [protocol="https"] - Protocol to use (http or https)
42
+ * @property {string} [host] - Hostname or IP address of the endpoint
43
+ * @property {string} [path] - Path portion of the URL
44
+ * @property {string|null} [body=null] - Request body for POST/PUT requests
45
+ * @property {Object.<string, string|number|boolean>|null} [parameters=null] - Query string parameters as key-value pairs
46
+ * @property {Object.<string, string>|null} [headers=null] - HTTP headers as key-value pairs
47
+ * @property {Object} [options] - Additional request options
48
+ * @property {number} [options.timeout] - Request timeout in milliseconds
49
+ * @property {string} [note="Get data from endpoint"] - Descriptive note for logging purposes
49
50
  */
50
51
 
51
52
  /*
@@ -57,27 +58,112 @@
57
58
  const tools = require("./tools/index.js");
58
59
 
59
60
  /**
61
+ * Makes a GET request to a remote endpoint with the specified connection configuration.
62
+ *
63
+ * This function provides a simple interface for making HTTP requests to external APIs
64
+ * or services. It supports both URI-based and component-based (protocol/host/path)
65
+ * connection specifications. Query parameters can be provided either in the connection
66
+ * object or as a separate query parameter, which will be merged together.
67
+ *
68
+ * The response body is automatically parsed as JSON if possible, otherwise returned as text.
69
+ *
70
+ * @param {ConnectionObject} connection - Connection configuration object specifying the endpoint details
71
+ * @param {Object} [query={}] - Additional query data to merge with connection parameters. If query.parameters is provided, it will be merged with connection.parameters
72
+ * @returns {Promise<{success: boolean, statusCode: number, body: Object|string|null, headers: Object}>} Response object containing success status, HTTP status code, parsed body, and response headers
73
+ * @throws {Error} Throws an error if the request fails due to network issues or invalid configuration
60
74
  *
61
- * @param {ConnectionObject} connection An object with details about the connection (method, uri, host, etc)
62
- * @param {Object} query Additional data to perform a query for the request.
63
- * @returns {Object} The response
64
75
  * @example
65
- const { endpoint } = require("@63klabs/cache-data");
66
- const data = await endpoint.get({host: "api.example.com", path: "data"}, { parameters: {q: "Chicago" }});
76
+ * // Using separate host and path
77
+ * const { endpoint } = require("@63klabs/cache-data");
78
+ * const response = await endpoint.get(
79
+ * { host: "api.example.com", path: "/data" },
80
+ * { parameters: { q: "Chicago" } }
81
+ * );
82
+ * console.log(response.body);
83
+ *
84
+ * @example
85
+ * // Using complete URI
86
+ * const { endpoint } = require("@63klabs/cache-data");
87
+ * const response = await endpoint.get(
88
+ * { uri: "https://api.example.com/data" },
89
+ * { parameters: { q: "Chicago" } }
90
+ * );
91
+ * console.log(response.body);
92
+ *
93
+ * @example
94
+ * // With custom headers and timeout
95
+ * const { endpoint } = require("@63klabs/cache-data");
96
+ * const response = await endpoint.get({
97
+ * host: "api.example.com",
98
+ * path: "/secure/data",
99
+ * headers: { "Authorization": "Bearer token123" },
100
+ * options: { timeout: 5000 }
101
+ * });
102
+ *
103
+ * @example
104
+ * // POST request with body
105
+ * const { endpoint } = require("@63klabs/cache-data");
106
+ * const response = await endpoint.get({
107
+ * method: "POST",
108
+ * uri: "https://api.example.com/submit",
109
+ * body: JSON.stringify({ name: "John", age: 30 }),
110
+ * headers: { "Content-Type": "application/json" }
111
+ * });
67
112
  */
68
- const get = async (connection, query = null) => {
113
+ const get = async (connection, query = {}) => {
114
+ if (query === null) { query = {} };
69
115
  return (new Endpoint(connection, query).get());
70
116
  };
71
117
 
72
118
  /**
73
- * A bare bones request to an endpoint. Can be used as a template to
74
- * create more elaborate requests.
119
+ * Endpoint request class for making HTTP requests to remote APIs.
120
+ *
121
+ * This class provides a bare-bones implementation for making API requests and can be used
122
+ * as a template to create more elaborate request handlers with custom logic for data
123
+ * manipulation before/after requests. The class handles connection configuration, parameter
124
+ * merging, and automatic JSON parsing of responses.
125
+ *
126
+ * The class is typically not instantiated directly but accessed through convenience functions
127
+ * like `endpoint.get()`. However, it can be extended to add custom pre/post-processing logic.
128
+ *
129
+ * @example
130
+ * // Direct instantiation (advanced usage)
131
+ * const endpoint = new Endpoint({ host: "api.example.com", path: "/data" });
132
+ * const response = await endpoint.get();
133
+ *
134
+ * @example
135
+ * // Extending for custom logic
136
+ * class CustomEndpoint extends Endpoint {
137
+ * async get() {
138
+ * // Custom pre-processing
139
+ * const response = await super.get();
140
+ * // Custom post-processing
141
+ * return response;
142
+ * }
143
+ * }
75
144
  */
76
145
  class Endpoint {
77
146
 
78
147
  /**
148
+ * Creates a new Endpoint instance with the specified connection configuration.
149
+ *
150
+ * The constructor initializes the request configuration by merging connection settings
151
+ * with query parameters. If query.parameters are provided, they are merged with
152
+ * connection.parameters. All connection properties are set with appropriate defaults.
79
153
  *
80
- * @param {ConnectionObject} connection An object with connection data
154
+ * @param {ConnectionObject} connection - Connection configuration object with endpoint details
155
+ * @param {Object} [query={}] - Additional query data to merge with connection. If query.parameters is provided, it will be merged with connection.parameters
156
+ *
157
+ * @example
158
+ * // Basic constructor usage
159
+ * const endpoint = new Endpoint({ host: "api.example.com", path: "/users" });
160
+ *
161
+ * @example
162
+ * // With query parameters
163
+ * const endpoint = new Endpoint(
164
+ * { host: "api.example.com", path: "/search" },
165
+ * { parameters: { q: "javascript", limit: 10 } }
166
+ * );
81
167
  */
82
168
  constructor(connection, query = {}) {
83
169
 
@@ -109,12 +195,22 @@ class Endpoint {
109
195
  };
110
196
 
111
197
  /**
112
- * Takes the connection object, checks for the key provided and if the key
113
- * exists it returns its value. Otherwise it returns the default value.
114
- * @param {ConnectionObject} connection The connection object to check for the existence of a key
115
- * @param {string} key The key to check for and return the value from connection
116
- * @param {*} defaultValue The value to use if the key is not found in the connection object
117
- * @returns {*} Either the value of the key if found in the connection object, or the default value
198
+ * Sets a request setting from the connection object or uses a default value.
199
+ *
200
+ * This internal helper method checks if a key exists in the connection object.
201
+ * If the key exists, it returns the value; otherwise, it sets the key to the
202
+ * default value and returns that default. This ensures all request settings
203
+ * have valid values.
204
+ *
205
+ * @param {ConnectionObject} connection - The connection object to check for the key
206
+ * @param {string} key - The property key to check for and retrieve
207
+ * @param {*} defaultValue - The default value to use if the key is not found
208
+ * @returns {*} The value from the connection object if found, otherwise the default value
209
+ *
210
+ * @example
211
+ * // Internal usage within constructor
212
+ * this.request.method = this._setRequestSetting(connection, "method", "GET");
213
+ * // If connection.method exists, uses that value; otherwise uses "GET"
118
214
  */
119
215
  _setRequestSetting(connection, key, defaultValue) {
120
216
  if (!(key in connection)) {
@@ -125,18 +221,36 @@ class Endpoint {
125
221
  };
126
222
 
127
223
  /**
128
- * This is the function used by the accessor method after the constructor
129
- * is called.
224
+ * Executes the HTTP request and returns the response.
130
225
  *
131
- * As a template, it can be modified to perform additional checks,
132
- * operations, etc before or after sending the call.
226
+ * This method sends the configured request to the remote endpoint using the APIRequest
227
+ * class. It automatically attempts to parse the response body as JSON. If the body is
228
+ * not valid JSON, it is kept as text. The method caches the response so subsequent
229
+ * calls return the same result without making additional requests.
230
+ *
231
+ * This method can be overridden in subclasses to add custom pre-processing (before the
232
+ * request) or post-processing (after the response) logic.
233
+ *
234
+ * @returns {Promise<{success: boolean, statusCode: number, body: Object|string|null, headers: Object}>} Response object with success status, HTTP status code, parsed body, and headers
235
+ * @throws {Error} Throws an error if the request fails due to network issues, timeout, or invalid configuration
236
+ *
237
+ * @example
238
+ * // Basic usage through the get() function
239
+ * const endpoint = new Endpoint({ host: "api.example.com", path: "/data" });
240
+ * const response = await endpoint.get();
241
+ * if (response.success) {
242
+ * console.log(response.body);
243
+ * }
133
244
  *
134
245
  * @example
135
- * // access function that utilizes the class
136
- * const getDataDirectFromURI = async (connection, data = null) => {
137
- * return (new Endpoint(connection).get());
138
- * };
139
- * @returns {object} Response data from the completed request
246
+ * // Handling errors
247
+ * try {
248
+ * const endpoint = new Endpoint({ uri: "https://api.example.com/data" });
249
+ * const response = await endpoint.get();
250
+ * console.log(response.statusCode, response.body);
251
+ * } catch (error) {
252
+ * console.error("Request failed:", error.message);
253
+ * }
140
254
  */
141
255
  async get() {
142
256
 
@@ -173,8 +287,18 @@ class Endpoint {
173
287
  }
174
288
 
175
289
  /**
176
- * An internal function that actually makes the call to APIRequest class
177
- * @returns {object} Response data from the completed request
290
+ * Internal method that makes the actual HTTP request using the APIRequest class.
291
+ *
292
+ * This method creates an APIRequest instance with the configured request settings
293
+ * and sends the request. It handles errors by logging them and returning a formatted
294
+ * error response. This method is called internally by the get() method.
295
+ *
296
+ * @returns {Promise<{success: boolean, statusCode: number, body: Object|string|null, headers: Object}>} Response object from the APIRequest
297
+ * @throws {Error} Throws an error if the APIRequest instantiation or send operation fails
298
+ *
299
+ * @example
300
+ * // Internal usage (not typically called directly)
301
+ * const response = await this._call();
178
302
  */
179
303
  async _call() {
180
304
 
@@ -1,9 +1,20 @@
1
+ /**
2
+ * Can a value be considered true?
3
+ * "true", "1", 1, and true are all true
4
+ * @param {boolean|number|string|null|undefined} value
5
+ * @returns {boolean} Whether or not the value could be considered true
6
+ */
1
7
  const isTrue = (value) => {
2
8
  return (value !== null && typeof value !== 'undefined' &&
3
9
  (value === true || value === 1 || value === "1" ||
4
10
  (typeof value === 'string' && value.toLowerCase() === "true")));
5
11
  };
6
12
 
13
+ /**
14
+ * Should we enable X-Ray?
15
+ * Checks for either CacheData_AWSXRayOn or CACHE_DATA_AWS_X_RAY_ON in process.env
16
+ * @returns {boolean} True if X-Ray is enabled
17
+ */
7
18
  const USE_XRAY = isTrue(process.env?.CacheData_AWSXRayOn) || isTrue(process.env?.CACHE_DATA_AWS_X_RAY_ON);
8
19
 
9
20
  let AWSXRay = null;
@@ -88,12 +99,19 @@ class AWS {
88
99
  static #nodeVer = [];
89
100
  static #aws_region = null;
90
101
 
102
+ /**
103
+ * @private
104
+ * @returns {boolean} True if X-Ray is enabled and initialized
105
+ */
91
106
  static get #XRayOn() {
92
107
  return initializeXRay() !== null;
93
108
  }
94
109
 
95
110
  constructor() {}
96
111
 
112
+ /**
113
+ * @returns {number[]} Node version in array [major, minor, patch]
114
+ */
97
115
  static get nodeVersionArray() {
98
116
  if (this.#nodeVer.length === 0) {
99
117
  // split this.NODE_VER into an array of integers
@@ -102,6 +120,12 @@ class AWS {
102
120
  return this.#nodeVer;
103
121
  };
104
122
 
123
+ /**
124
+ * Returns the AWS Region from process.env.AWS_REGION.
125
+ * If not set, it will return 'us-east-1' and issue a warning.
126
+ *
127
+ * @returns {string} AWS Region
128
+ */
105
129
  static get region() {
106
130
  if (this.#aws_region === null) {
107
131
 
@@ -122,17 +146,59 @@ class AWS {
122
146
  return this.#aws_region;
123
147
  }
124
148
 
149
+ /**
150
+ * @returns {string} Node.js version ex: '24.12.0'
151
+ */
125
152
  static get NODE_VER() { return ( ("versions" in process && "node" in process.versions) ? process.versions.node : "0.0.0"); }
153
+
154
+ /**
155
+ * @returns {number} Node.js major version ex: 24
156
+ */
126
157
  static get NODE_VER_MAJOR() { return ( this.nodeVersionArray[0] ); }
158
+
159
+ /**
160
+ * @returns {number} Node.js minor version ex: 12
161
+ */
127
162
  static get NODE_VER_MINOR() { return ( this.nodeVersionArray[1] ); }
163
+
164
+ /**
165
+ * @returns {number} Node.js patch version ex: 0
166
+ */
128
167
  static get NODE_VER_PATCH() { return ( this.nodeVersionArray[2] ); }
168
+
169
+ /**
170
+ * @returns {string} Node.js major.minor version ex: '24.12'
171
+ */
129
172
  static get NODE_VER_MAJOR_MINOR() { return (this.nodeVersionArray[0] + "." + this.nodeVersionArray[1]); }
173
+
174
+ /**
175
+ * @returns {number[]} Node.js version array [major, minor, patch]
176
+ */
130
177
  static get NODE_VER_ARRAY() { return (this.nodeVersionArray); }
178
+
179
+ /**
180
+ * @returns {'V2'|'V3'} AWS SDK Version being used based on Node.js version. V2 for Node.js < 18, V3 for Node.js >= 18
181
+ */
131
182
  static get SDK_VER() { return ((this.NODE_VER_MAJOR < 18) ? "V2" : "V3"); }
183
+
184
+ /**
185
+ * @returns {string} AWS Region
186
+ */
132
187
  static get REGION() { return ( this.region ); }
188
+
189
+ /**
190
+ * @returns {boolean} True if using AWS SDK v2
191
+ */
133
192
  static get SDK_V2() { return (this.SDK_VER === "V2"); }
193
+
194
+ /**
195
+ * @returns {boolean} True if using AWS SDK v3
196
+ */
134
197
  static get SDK_V3() { return (this.SDK_VER === "V3"); }
135
198
 
199
+ /**
200
+ * @returns {object} Information about the current Node.js and AWS environment
201
+ */
136
202
  static get INFO() {
137
203
  return ( {
138
204
  NODE_VER: this.NODE_VER,
@@ -149,6 +215,10 @@ class AWS {
149
215
  });
150
216
  }
151
217
 
218
+ /**
219
+ * @private
220
+ * @returns {object} SDK v3 objects
221
+ */
152
222
  static #SDK = (
153
223
  function(){
154
224
 
@@ -209,6 +279,9 @@ class AWS {
209
279
  }
210
280
  )();
211
281
 
282
+ /**
283
+ * @returns {object} DynamoDB Document Client and functions for put, get, scan, delete, update
284
+ */
212
285
  static get dynamo() {
213
286
  return {
214
287
  client: this.#SDK.dynamo.client,
@@ -221,6 +294,9 @@ class AWS {
221
294
  };
222
295
  }
223
296
 
297
+ /**
298
+ * @returns {object} S3 Client and functions for put, get
299
+ */
224
300
  static get s3() {
225
301
  return {
226
302
  client: this.#SDK.s3.client,
@@ -230,6 +306,9 @@ class AWS {
230
306
  };
231
307
  }
232
308
 
309
+ /**
310
+ * @returns {object} SSM Client and functions for getByName, getByPath
311
+ */
233
312
  static get ssm() {
234
313
  return {
235
314
  client: this.#SDK.ssm.client,
@@ -239,6 +318,9 @@ class AWS {
239
318
  };
240
319
  }
241
320
 
321
+ /**
322
+ * @returns {object} AWS X-Ray SDK
323
+ */
242
324
  static get XRay() {
243
325
  return AWSXRay;
244
326
  }
@@ -18,11 +18,24 @@ const Timer = require('./Timer.class');
18
18
  * https://aws.amazon.com/blogs/compute/using-the-aws-parameter-and-secrets-lambda-extension-to-cache-parameters-and-secrets/
19
19
  *************************************************************************** */
20
20
 
21
- /* ****************************************************************************
22
-
23
-
21
+ /**
22
+ * @class CachedParameterSecrets - Container class for CachedParameterSecret objects
23
+ * @example
24
+ * // Create parameters and secrets
25
+ * const dbPassword = new CachedSSMParameter('/myapp/db/password');
26
+ * const apiKey = new CachedSecret('myapp-api-key');
27
+ *
28
+ * // Prime all parameters and secrets before use
29
+ * await CachedParameterSecrets.prime();
30
+ *
31
+ * // Get all names
32
+ * const names = CachedParameterSecrets.getNames();
33
+ * console.log(names); // ['/myapp/db/password', 'myapp-api-key']
34
+ *
35
+ * // Get specific parameter
36
+ * const param = CachedParameterSecrets.get('/myapp/db/password');
37
+ * const value = await param.getValue();
24
38
  */
25
-
26
39
  class CachedParameterSecrets {
27
40
  /**
28
41
  * @typedef {Array<CachedParameterSecret>}
@@ -30,14 +43,14 @@ class CachedParameterSecrets {
30
43
  static #cachedParameterSecrets = [];
31
44
 
32
45
  /**
33
- * @param {CachedParameterSecret} The CachedParameterSecret object to add
46
+ * @param {CachedParameterSecret} cachedParameterSecretObject - The CachedParameterSecret object to add
34
47
  */
35
48
  static add (cachedParameterSecretObject) {
36
49
  CachedParameterSecrets.#cachedParameterSecrets.push(cachedParameterSecretObject);
37
50
  }
38
51
 
39
52
  /**
40
- * @param {string} The Parameter name or Secret Id to locate
53
+ * @param {string} name - The Parameter name or Secret Id to locate
41
54
  * @returns {CachedParameterSecret}
42
55
  */
43
56
  static get (name) {
@@ -58,6 +71,9 @@ class CachedParameterSecrets {
58
71
  return objects;
59
72
  };
60
73
 
74
+ /**
75
+ * @returns {object: Array<object>} An object containing an array of CachedParameterSecret.toObject()
76
+ */
61
77
  static toObject() {
62
78
  // return an object of cachedParameterSecret.toObject()
63
79
  return {objects: CachedParameterSecrets.toArray()};
@@ -124,7 +140,7 @@ class CachedParameterSecrets {
124
140
  }
125
141
 
126
142
  /**
127
- * Parent class to extend CachedSSMParameter and CachedSecret classes.
143
+ * @class CachedParameterSecret - Base class for CachedSSMParameter and CachedSecret
128
144
  * Accesses data through Systems Manager Parameter Store and Secrets Manager Lambda Extension
129
145
  * Since the Lambda Extension runs a localhost via http, it handles it's own http request. Also,
130
146
  * since the lambda extension needs time to boot during a cold start, it is not available during
@@ -355,6 +371,7 @@ class CachedParameterSecret {
355
371
  * The value comes back decrypted.
356
372
  * It will return the current, cached copy which may have expired.
357
373
  * @returns {string} The value of the Secret or Parameter
374
+ * @throws {Error} If the secret is null or .get(), .getValue(), or .refresh() has not been called first
358
375
  */
359
376
  sync_getValue() {
360
377
  if (this.isValid()) {
@@ -455,7 +472,40 @@ class CachedParameterSecret {
455
472
 
456
473
  }
457
474
 
475
+ /**
476
+ * CachedSSMParameter extends CachedParameterSecret and is used to retrieve parameters from AWS Systems Manager Parameter Store
477
+ * @extends CachedParameterSecret
478
+ * @example
479
+ * // Create a cached SSM parameter
480
+ * const dbPassword = new CachedSSMParameter('/myapp/db/password');
481
+ *
482
+ * // Get the parameter value (async)
483
+ * const password = await dbPassword.getValue();
484
+ * console.log(password); // 'my-secret-password'
485
+ *
486
+ * @example
487
+ * // Use with synchronous functions after priming
488
+ * const apiKey = new CachedSSMParameter('/myapp/api/key');
489
+ *
490
+ * async function init() {
491
+ * // Prime the parameter in the background
492
+ * apiKey.prime();
493
+ *
494
+ * // Do other initialization work...
495
+ *
496
+ * // Ensure parameter is loaded before sync usage
497
+ * await apiKey.prime();
498
+ *
499
+ * // Now safe to use in sync functions
500
+ * const key = apiKey.sync_getValue();
501
+ * return key;
502
+ * }
503
+ */
458
504
  class CachedSSMParameter extends CachedParameterSecret {
505
+ /**
506
+ * Returns the URL path for the AWS Parameters and Secrets Lambda Extension to retrieve this SSM parameter
507
+ * @returns {string} The URL path with encoded parameter name
508
+ */
459
509
  getPath() {
460
510
  const uriEncodedSecret = encodeURIComponent(this.name);
461
511
  return `/systemsmanager/parameters/get/?name=${uriEncodedSecret}&withDecryption=true`;
@@ -469,8 +519,49 @@ class CachedSSMParameter extends CachedParameterSecret {
469
519
  }
470
520
  }
471
521
 
522
+ /**
523
+ * CachedSecret extends CachedParameterSecret and is used to retrieve secrets from AWS Secrets Manager
524
+ * @extends CachedParameterSecret
525
+ * @example
526
+ * // Create a cached secret
527
+ * const dbCredentials = new CachedSecret('myapp-database-credentials');
528
+ *
529
+ * // Get the secret value (async)
530
+ * const credentials = await dbCredentials.getValue();
531
+ * console.log(credentials); // '{"username":"admin","password":"secret"}'
532
+ *
533
+ * @example
534
+ * // Use with JSON secrets
535
+ * const apiSecret = new CachedSecret('myapp-api-secret');
536
+ *
537
+ * async function connectToAPI() {
538
+ * const secretString = await apiSecret.getValue();
539
+ * const secretData = JSON.parse(secretString);
540
+ *
541
+ * return {
542
+ * apiKey: secretData.apiKey,
543
+ * apiSecret: secretData.apiSecret
544
+ * };
545
+ * }
546
+ *
547
+ * @example
548
+ * // Prime multiple secrets at once
549
+ * const secret1 = new CachedSecret('secret-1');
550
+ * const secret2 = new CachedSecret('secret-2');
551
+ *
552
+ * // Prime all secrets in parallel
553
+ * await CachedParameterSecrets.prime();
554
+ *
555
+ * // Now all secrets are loaded and cached
556
+ * const value1 = secret1.sync_getValue();
557
+ * const value2 = secret2.sync_getValue();
558
+ */
472
559
  class CachedSecret extends CachedParameterSecret {
473
560
 
561
+ /**
562
+ * Returns the URL path for the AWS Parameters and Secrets Lambda Extension to retrieve this secret
563
+ * @returns {string} The URL path with encoded secret ID
564
+ */
474
565
  getPath() {
475
566
  const uriEncodedSecret = encodeURIComponent(this.name);
476
567
  return `/secretsmanager/get?secretId=${uriEncodedSecret}&withDecryption=true`;
@@ -1,11 +1,48 @@
1
1
  const RequestInfo = require('./RequestInfo.class');
2
2
  const Timer = require('./Timer.class');
3
3
  const DebugAndLog = require('./DebugAndLog.class');
4
+ const { safeClone } = require('./utils');
4
5
 
5
6
 
6
7
  /**
7
8
  * Extends RequestInfo
8
9
  * Can be used to create a custom ClientRequest object
10
+ * @example
11
+ * // Initialize ClientRequest with validations
12
+ * ClientRequest.init({
13
+ * validations: {
14
+ * referrers: ['example.com', 'myapp.com'],
15
+ * parameters: {
16
+ * queryStringParameters: {
17
+ * limit: (value) => !isNaN(value) && value > 0 && value <= 100,
18
+ * page: (value) => !isNaN(value) && value > 0
19
+ * },
20
+ * pathParameters: {
21
+ * id: (value) => /^[a-zA-Z0-9-]+$/.test(value)
22
+ * }
23
+ * }
24
+ * }
25
+ * });
26
+ *
27
+ * @example
28
+ * // Use in Lambda handler
29
+ * exports.handler = async (event, context) => {
30
+ * const clientRequest = new ClientRequest(event, context);
31
+ *
32
+ * if (!clientRequest.isValid()) {
33
+ * return { statusCode: 400, body: 'Invalid request' };
34
+ * }
35
+ *
36
+ * const userId = clientRequest.getPathAt(1);
37
+ * const queryParams = clientRequest.getQueryStringParameters();
38
+ *
39
+ * // Add logging for monitoring
40
+ * clientRequest.addPathLog(`users/${userId}`);
41
+ * clientRequest.addQueryLog(`limit=${queryParams.limit}`);
42
+ *
43
+ * // Process request...
44
+ * return { statusCode: 200, body: 'Success' };
45
+ * };
9
46
  */
10
47
  class ClientRequest extends RequestInfo {
11
48
 
@@ -22,7 +59,7 @@ class ClientRequest extends RequestInfo {
22
59
  /* What and who of the request */
23
60
  #event = null;
24
61
  #context = null;
25
- #authorizations = JSON.parse(JSON.stringify(ClientRequest.#unauthenticatedAuthorizations));
62
+ #authorizations = safeClone(ClientRequest.#unauthenticatedAuthorizations);
26
63
  #roles = [];
27
64
 
28
65
  /* The request data */
@@ -83,8 +120,8 @@ class ClientRequest extends RequestInfo {
83
120
  * This is used to initialize the ClientRequest class for all requests.
84
121
  * Add ClientRequest.init(options) to the Config.init process or at the
85
122
  * top of the main index.js file outside of the handler.
86
- * @param {Array<string>} options.validations.referrers An array of accepted referrers. String matching goes from right to left, so ['example.com'] will allow example.com and subdomain.example.com
87
- * @param {object} options.validations.parameters An object containing functions for validating request parameters (path, querystring, headers, cookies, etc).
123
+ * @param {object} options - Configuration options with validations property containing referrers and parameters
124
+ * @throws {Error} If options is not an object
88
125
  */
89
126
  static init(options) {
90
127
  if (typeof options === 'object') {
@@ -193,12 +230,8 @@ class ClientRequest extends RequestInfo {
193
230
  }
194
231
 
195
232
 
196
- /**
197
- * Utility function for getPathArray and getResourceArray
198
- * @param {array<string>} arr array to slice
199
- * @param {number} n number of elements to return
200
- * @returns {array<string>} array of elements
201
- */
233
+ // Utility function for getPathArray and getResourceArray
234
+ // Returns array slice based on n parameter
202
235
  #getArray(arr, n = 0) {
203
236
  if (n === 0 || arr.length <= n || (n < 0 && arr.length <= (n*-1))) {
204
237
  return arr;
@@ -352,7 +385,7 @@ class ClientRequest extends RequestInfo {
352
385
  if (this.isAuthenticated()) {
353
386
  return this.#authorizations;
354
387
  } else {
355
- return JSON.parse(JSON.stringify(ClientRequest.#unauthenticatedAuthorizations));
388
+ return safeClone(ClientRequest.#unauthenticatedAuthorizations);
356
389
  }
357
390
  };
358
391