@middy/util 7.2.1 → 7.2.3
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/index.d.ts +3 -0
- package/index.js +58 -10
- package/package.json +2 -2
package/index.d.ts
CHANGED
|
@@ -21,6 +21,7 @@ export interface Options<Client, ClientOptions> {
|
|
|
21
21
|
cacheKey?: string;
|
|
22
22
|
cacheExpiry?: number;
|
|
23
23
|
cacheKeyExpiry?: Record<string, number>;
|
|
24
|
+
cacheMaxSize?: number;
|
|
24
25
|
setToContext?: boolean;
|
|
25
26
|
}
|
|
26
27
|
|
|
@@ -157,6 +158,8 @@ declare function jsonSafeStringify(
|
|
|
157
158
|
space?: string | number,
|
|
158
159
|
): string | unknown;
|
|
159
160
|
|
|
161
|
+
declare const jsonContentTypePattern: RegExp;
|
|
162
|
+
|
|
160
163
|
declare function decodeBody(event: {
|
|
161
164
|
body?: string | null;
|
|
162
165
|
isBase64Encoded?: boolean;
|
package/index.js
CHANGED
|
@@ -51,7 +51,7 @@ const safeGet = (obj, key) =>
|
|
|
51
51
|
|
|
52
52
|
// Internal Context
|
|
53
53
|
export const getInternal = async (variables, request) => {
|
|
54
|
-
if (!variables || !request) return
|
|
54
|
+
if (!variables || !request) return Object.create(null);
|
|
55
55
|
let keys = [];
|
|
56
56
|
let values = [];
|
|
57
57
|
if (variables === true) {
|
|
@@ -86,7 +86,7 @@ export const getInternal = async (variables, request) => {
|
|
|
86
86
|
syncResults[i] = value;
|
|
87
87
|
}
|
|
88
88
|
if (allSync) {
|
|
89
|
-
const obj =
|
|
89
|
+
const obj = Object.create(null);
|
|
90
90
|
for (let i = 0; i < keys.length; i++) {
|
|
91
91
|
obj[sanitizeKey(keys[i])] = syncResults[i];
|
|
92
92
|
}
|
|
@@ -110,7 +110,7 @@ export const getInternal = async (variables, request) => {
|
|
|
110
110
|
// ensure promise has resolved by the time it's needed
|
|
111
111
|
// If one of the promises throws it will bubble up to @middy/core
|
|
112
112
|
values = await Promise.allSettled(promises);
|
|
113
|
-
const obj =
|
|
113
|
+
const obj = Object.create(null);
|
|
114
114
|
let errors;
|
|
115
115
|
for (let i = 0; i < keys.length; i++) {
|
|
116
116
|
if (values[i].status === "rejected") {
|
|
@@ -140,13 +140,30 @@ export const sanitizeKey = (key) => {
|
|
|
140
140
|
|
|
141
141
|
// fetch Cache
|
|
142
142
|
const cache = Object.create(null); // key: { value:{fetchKey:Promise}, expiry }
|
|
143
|
+
const defaultCacheMaxSize = 128;
|
|
144
|
+
|
|
145
|
+
const validateCacheExpiry = (cacheExpiry) => {
|
|
146
|
+
if (
|
|
147
|
+
typeof cacheExpiry === "number" &&
|
|
148
|
+
cacheExpiry < -1 &&
|
|
149
|
+
!Number.isNaN(cacheExpiry)
|
|
150
|
+
) {
|
|
151
|
+
throw new Error(
|
|
152
|
+
`Invalid cacheExpiry value: ${cacheExpiry}. Must be -1 (infinite), 0 (disabled), or a positive number (ms duration or unix timestamp)`,
|
|
153
|
+
{ cause: { package: "@middy/util" } },
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
|
|
143
158
|
export const processCache = (
|
|
144
159
|
options,
|
|
145
160
|
middlewareFetch = () => undefined,
|
|
146
161
|
middlewareFetchRequest = {},
|
|
147
162
|
) => {
|
|
148
|
-
let { cacheKey, cacheKeyExpiry, cacheExpiry } = options;
|
|
163
|
+
let { cacheKey, cacheKeyExpiry, cacheExpiry, cacheMaxSize } = options;
|
|
164
|
+
cacheMaxSize ??= defaultCacheMaxSize;
|
|
149
165
|
cacheExpiry = cacheKeyExpiry?.[cacheKey] ?? cacheExpiry;
|
|
166
|
+
validateCacheExpiry(cacheExpiry);
|
|
150
167
|
const now = Date.now();
|
|
151
168
|
if (cacheExpiry) {
|
|
152
169
|
const cached = getCache(cacheKey);
|
|
@@ -164,10 +181,15 @@ export const processCache = (
|
|
|
164
181
|
}
|
|
165
182
|
}
|
|
166
183
|
const value = middlewareFetch(middlewareFetchRequest);
|
|
167
|
-
//
|
|
184
|
+
// cacheExpiry semantics:
|
|
185
|
+
// >86400000 (24h): treated as unix timestamp (ms)
|
|
186
|
+
// >0 && <=86400000: treated as duration (ms) from now
|
|
187
|
+
// -1: infinite cache (never expires)
|
|
188
|
+
// 0/undefined/null: no caching
|
|
168
189
|
const expiry = cacheExpiry > 86400000 ? cacheExpiry : now + cacheExpiry;
|
|
169
190
|
const duration = cacheExpiry > 86400000 ? cacheExpiry - now : cacheExpiry;
|
|
170
191
|
if (cacheExpiry) {
|
|
192
|
+
clearTimeout(cache[cacheKey]?.refresh);
|
|
171
193
|
const refresh =
|
|
172
194
|
duration > 0
|
|
173
195
|
? setTimeout(
|
|
@@ -177,6 +199,7 @@ export const processCache = (
|
|
|
177
199
|
)
|
|
178
200
|
: undefined;
|
|
179
201
|
cache[cacheKey] = { value, expiry, refresh };
|
|
202
|
+
evictCache(cacheMaxSize);
|
|
180
203
|
}
|
|
181
204
|
return { value, expiry };
|
|
182
205
|
};
|
|
@@ -195,10 +218,29 @@ export const getCache = (key) => {
|
|
|
195
218
|
|
|
196
219
|
// Used to remove parts of a cache
|
|
197
220
|
export const modifyCache = (cacheKey, value) => {
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
221
|
+
const entry = cache[cacheKey];
|
|
222
|
+
if (!entry) return;
|
|
223
|
+
clearTimeout(entry.refresh);
|
|
224
|
+
entry.value = value;
|
|
225
|
+
entry.modified = true;
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
const evictCache = (maxSize) => {
|
|
229
|
+
const cacheKeys = Object.keys(cache);
|
|
230
|
+
if (cacheKeys.length <= maxSize) return;
|
|
231
|
+
let oldestKey = null;
|
|
232
|
+
let oldestExpiry = Infinity;
|
|
233
|
+
for (const key of cacheKeys) {
|
|
234
|
+
const entry = cache[key];
|
|
235
|
+
if (entry && entry.expiry < oldestExpiry) {
|
|
236
|
+
oldestExpiry = entry.expiry;
|
|
237
|
+
oldestKey = key;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
if (oldestKey) {
|
|
241
|
+
clearTimeout(cache[oldestKey]?.refresh);
|
|
242
|
+
cache[oldestKey] = undefined;
|
|
243
|
+
}
|
|
202
244
|
};
|
|
203
245
|
|
|
204
246
|
export const clearCache = (inputKeys = null) => {
|
|
@@ -269,6 +311,9 @@ export const jsonSafeStringify = (value, replacer, space) => {
|
|
|
269
311
|
}
|
|
270
312
|
};
|
|
271
313
|
|
|
314
|
+
export const jsonContentTypePattern =
|
|
315
|
+
/^application\/([a-z0-9.+-]+\+)?json(;|$)/i;
|
|
316
|
+
|
|
272
317
|
export const decodeBody = (event) => {
|
|
273
318
|
const { body, isBase64Encoded } = event;
|
|
274
319
|
if (typeof body === "undefined" || body === null) return body;
|
|
@@ -304,7 +349,10 @@ export class HttpError extends Error {
|
|
|
304
349
|
message ??= httpErrorCodes[code];
|
|
305
350
|
super(message, options);
|
|
306
351
|
|
|
307
|
-
const name = httpErrorCodes[code]
|
|
352
|
+
const name = (httpErrorCodes[code] ?? "Unknown").replace(
|
|
353
|
+
createErrorRegexp,
|
|
354
|
+
"",
|
|
355
|
+
);
|
|
308
356
|
this.name = !name.endsWith("Error") ? `${name}Error` : name;
|
|
309
357
|
|
|
310
358
|
this.status = this.statusCode = code; // setting `status` for backwards compatibility w/ `http-errors`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@middy/util",
|
|
3
|
-
"version": "7.2.
|
|
3
|
+
"version": "7.2.3",
|
|
4
4
|
"description": "🛵 The stylish Node.js middleware engine for AWS Lambda (util package)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
},
|
|
61
61
|
"devDependencies": {
|
|
62
62
|
"@aws-sdk/client-ssm": "^3.0.0",
|
|
63
|
-
"@middy/core": "7.2.
|
|
63
|
+
"@middy/core": "7.2.3",
|
|
64
64
|
"@types/aws-lambda": "^8.0.0",
|
|
65
65
|
"@types/node": "^22.0.0",
|
|
66
66
|
"aws-xray-sdk": "^3.3.3"
|