@middy/util 7.2.3 → 7.3.1
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 +85 -0
- package/index.js +265 -19
- package/package.json +2 -5
package/index.d.ts
CHANGED
|
@@ -184,3 +184,88 @@ declare function lambdaContext(
|
|
|
184
184
|
): unknown;
|
|
185
185
|
|
|
186
186
|
declare const httpErrorCodes: Record<number, string>;
|
|
187
|
+
|
|
188
|
+
export type JsonSchemaType =
|
|
189
|
+
| "string"
|
|
190
|
+
| "number"
|
|
191
|
+
| "integer"
|
|
192
|
+
| "boolean"
|
|
193
|
+
| "object"
|
|
194
|
+
| "array";
|
|
195
|
+
|
|
196
|
+
export type StringRule = {
|
|
197
|
+
type: "string";
|
|
198
|
+
pattern?: string;
|
|
199
|
+
minLength?: number;
|
|
200
|
+
maxLength?: number;
|
|
201
|
+
enum?: readonly string[];
|
|
202
|
+
examples?: readonly string[];
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
export type NumberRule = {
|
|
206
|
+
type: "number" | "integer";
|
|
207
|
+
minimum?: number;
|
|
208
|
+
maximum?: number;
|
|
209
|
+
enum?: readonly number[];
|
|
210
|
+
examples?: readonly number[];
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
export type BooleanRule = {
|
|
214
|
+
type: "boolean";
|
|
215
|
+
enum?: readonly boolean[];
|
|
216
|
+
examples?: readonly boolean[];
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
export type ArrayRule = {
|
|
220
|
+
type: "array";
|
|
221
|
+
items?: OptionSchemaRule;
|
|
222
|
+
examples?: readonly unknown[];
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
export type ObjectRule = {
|
|
226
|
+
type: "object";
|
|
227
|
+
required?: readonly string[];
|
|
228
|
+
properties?: { [key: string]: OptionSchemaRule };
|
|
229
|
+
additionalProperties?: boolean | OptionSchemaRule;
|
|
230
|
+
examples?: readonly object[];
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
export type EnumRule = {
|
|
234
|
+
enum: readonly unknown[];
|
|
235
|
+
type?: JsonSchemaType;
|
|
236
|
+
examples?: readonly unknown[];
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
export type ConstRule = {
|
|
240
|
+
const: unknown;
|
|
241
|
+
examples?: readonly unknown[];
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
export type InstanceofRule = {
|
|
245
|
+
instanceof: string;
|
|
246
|
+
examples?: readonly unknown[];
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
export type OneOfRule = {
|
|
250
|
+
oneOf: readonly OptionSchemaRule[];
|
|
251
|
+
examples?: readonly unknown[];
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
export type OptionSchemaRule =
|
|
255
|
+
| StringRule
|
|
256
|
+
| NumberRule
|
|
257
|
+
| BooleanRule
|
|
258
|
+
| ArrayRule
|
|
259
|
+
| ObjectRule
|
|
260
|
+
| EnumRule
|
|
261
|
+
| ConstRule
|
|
262
|
+
| InstanceofRule
|
|
263
|
+
| OneOfRule;
|
|
264
|
+
|
|
265
|
+
export type OptionSchema = ObjectRule;
|
|
266
|
+
|
|
267
|
+
export declare function validateOptions(
|
|
268
|
+
packageName: string,
|
|
269
|
+
schema: OptionSchema,
|
|
270
|
+
options?: Record<string, unknown>,
|
|
271
|
+
): void;
|
package/index.js
CHANGED
|
@@ -1,5 +1,248 @@
|
|
|
1
1
|
// Copyright 2017 - 2026 will Farrell, Luciano Mammino, and Middy contributors.
|
|
2
2
|
// SPDX-License-Identifier: MIT
|
|
3
|
+
|
|
4
|
+
// Option validation helper.
|
|
5
|
+
// Schema values:
|
|
6
|
+
// 'string' | 'number' | 'integer' | 'boolean' | 'function' | 'object' | 'array'
|
|
7
|
+
// Trailing '?' marks the field as optional (may be undefined).
|
|
8
|
+
// (value) => boolean predicate — only called when value is not undefined
|
|
9
|
+
// (i.e. predicates treat the field as optional by design).
|
|
10
|
+
// { type: 'array' | 'array?', items: <itemSchema> }
|
|
11
|
+
// `items` is applied to each array element. It can be a type string,
|
|
12
|
+
// a predicate function, or a plain object treated as a per-element
|
|
13
|
+
// object schema (validated recursively with the same rules).
|
|
14
|
+
// { type: '<type>' | '<type>?', minimum?, maximum?, minLength?, maxLength?, pattern? }
|
|
15
|
+
// Numeric: `minimum`/`maximum` (number/integer).
|
|
16
|
+
// String: `minLength`/`maxLength` (string length), `pattern` (regex source).
|
|
17
|
+
// { type: 'object' | 'object?', properties?: {...}, additionalProperties?: <rule> }
|
|
18
|
+
// `properties` validates known keys with the flat-schema form.
|
|
19
|
+
// `additionalProperties` validates every other key's value against the
|
|
20
|
+
// given rule (string, predicate, or nested object schema). Without it,
|
|
21
|
+
// unknown keys throw.
|
|
22
|
+
// { enum: [...values], type?: '<type>' | '<type>?' }
|
|
23
|
+
// Value must strict-equal one of the listed values. Optional by default;
|
|
24
|
+
// combine with `type` to require a specific type and/or presence.
|
|
25
|
+
// Keys in `options` (or nested objects) that are not in `schema` throw,
|
|
26
|
+
// catching typos.
|
|
27
|
+
const validateOptionsTypeCheckers = {
|
|
28
|
+
string: (v) => typeof v === "string",
|
|
29
|
+
number: (v) => typeof v === "number" && !Number.isNaN(v),
|
|
30
|
+
integer: (v) => Number.isInteger(v),
|
|
31
|
+
boolean: (v) => typeof v === "boolean",
|
|
32
|
+
function: (v) => typeof v === "function",
|
|
33
|
+
object: (v) => v !== null && typeof v === "object" && !Array.isArray(v),
|
|
34
|
+
array: (v) => Array.isArray(v),
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const isPlainObject = (v) =>
|
|
38
|
+
v !== null && typeof v === "object" && !Array.isArray(v);
|
|
39
|
+
|
|
40
|
+
const checkSchemaObject = (schema, options, path, fail) => {
|
|
41
|
+
if (!isPlainObject(options)) {
|
|
42
|
+
fail(
|
|
43
|
+
path ? `Option '${path}' must be object` : "options must be an object",
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
for (const key of Object.keys(options)) {
|
|
47
|
+
if (!Object.hasOwn(schema, key)) {
|
|
48
|
+
fail(`Unknown option '${path ? `${path}.${key}` : key}'`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
for (const key of Object.keys(schema)) {
|
|
52
|
+
const childPath = path ? `${path}.${key}` : key;
|
|
53
|
+
checkRule(schema[key], options[key], childPath, fail);
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
// Returns true if type check passed (and value is defined), false if the
|
|
58
|
+
// caller should stop validating (value was undefined and optional).
|
|
59
|
+
const checkTypeSpec = (rawType, value, path, fail) => {
|
|
60
|
+
const optional = rawType.endsWith("?");
|
|
61
|
+
const type = optional ? rawType.slice(0, -1) : rawType;
|
|
62
|
+
const checker = validateOptionsTypeCheckers[type];
|
|
63
|
+
if (!checker) fail(`Unknown schema type '${type}' for option '${path}'`);
|
|
64
|
+
if (value === undefined) {
|
|
65
|
+
if (!optional) fail(`Missing required option '${path}' (${type})`);
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
if (!checker(value)) fail(`Option '${path}' must be ${type}`);
|
|
69
|
+
return true;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
// Plain object with no rule-marker key (`type`, `enum`, `oneOf`, `const`,
|
|
73
|
+
// `instanceof`) is a flat object schema; anything else is a rule. Used when
|
|
74
|
+
// dispatching `items` and `additionalProperties`.
|
|
75
|
+
const checkNestedRule = (rule, value, path, fail) => {
|
|
76
|
+
if (
|
|
77
|
+
isPlainObject(rule) &&
|
|
78
|
+
typeof rule.type !== "string" &&
|
|
79
|
+
!Array.isArray(rule.enum) &&
|
|
80
|
+
!Array.isArray(rule.oneOf) &&
|
|
81
|
+
!Object.hasOwn(rule, "const") &&
|
|
82
|
+
typeof rule.instanceof !== "string"
|
|
83
|
+
) {
|
|
84
|
+
checkSchemaObject(rule, value, path, fail);
|
|
85
|
+
} else {
|
|
86
|
+
checkRule(rule, value, path, fail);
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const childPathOf = (path, key) => (path ? `${path}.${key}` : key);
|
|
91
|
+
|
|
92
|
+
const resolveInstance = (name) => {
|
|
93
|
+
const ctor = globalThis[name];
|
|
94
|
+
if (typeof ctor !== "function") {
|
|
95
|
+
throw new Error(`Unknown 'instanceof' class '${name}'`);
|
|
96
|
+
}
|
|
97
|
+
return ctor;
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
const checkRule = (rule, value, path, fail) => {
|
|
101
|
+
if (typeof rule === "function") {
|
|
102
|
+
if (value !== undefined && !rule(value)) {
|
|
103
|
+
fail(`Invalid option '${path}'`);
|
|
104
|
+
}
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
if (typeof rule === "string") {
|
|
108
|
+
checkTypeSpec(rule, value, path, fail);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
if (isPlainObject(rule) && Object.hasOwn(rule, "const")) {
|
|
112
|
+
if (value === undefined) return;
|
|
113
|
+
if (value !== rule.const) {
|
|
114
|
+
fail(`Option '${path}' must equal ${JSON.stringify(rule.const)}`);
|
|
115
|
+
}
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
if (isPlainObject(rule) && Array.isArray(rule.oneOf)) {
|
|
119
|
+
if (value === undefined) return;
|
|
120
|
+
let matches = 0;
|
|
121
|
+
for (const sub of rule.oneOf) {
|
|
122
|
+
try {
|
|
123
|
+
checkRule(sub, value, path, (msg) => {
|
|
124
|
+
throw new TypeError(msg);
|
|
125
|
+
});
|
|
126
|
+
matches++;
|
|
127
|
+
} catch {}
|
|
128
|
+
}
|
|
129
|
+
if (matches !== 1) {
|
|
130
|
+
fail(`Option '${path}' must match exactly one schema in oneOf`);
|
|
131
|
+
}
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
if (isPlainObject(rule) && typeof rule.instanceof === "string") {
|
|
135
|
+
if (value === undefined) return;
|
|
136
|
+
const ctor = resolveInstance(rule.instanceof);
|
|
137
|
+
if (!(value instanceof ctor)) {
|
|
138
|
+
fail(`Option '${path}' must be instanceof ${rule.instanceof}`);
|
|
139
|
+
}
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
if (isPlainObject(rule) && Array.isArray(rule.enum)) {
|
|
143
|
+
if (typeof rule.type === "string") {
|
|
144
|
+
if (!checkTypeSpec(rule.type, value, path, fail)) return;
|
|
145
|
+
} else if (value === undefined) {
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
if (!rule.enum.includes(value)) {
|
|
149
|
+
fail(`Option '${path}' must be one of ${JSON.stringify(rule.enum)}`);
|
|
150
|
+
}
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
if (isPlainObject(rule) && typeof rule.type === "string") {
|
|
154
|
+
const {
|
|
155
|
+
type: rawType,
|
|
156
|
+
items,
|
|
157
|
+
properties,
|
|
158
|
+
required,
|
|
159
|
+
additionalProperties,
|
|
160
|
+
minimum,
|
|
161
|
+
maximum,
|
|
162
|
+
pattern,
|
|
163
|
+
minLength,
|
|
164
|
+
maxLength,
|
|
165
|
+
} = rule;
|
|
166
|
+
if (!checkTypeSpec(rawType, value, path, fail)) return;
|
|
167
|
+
const type = rawType.endsWith("?") ? rawType.slice(0, -1) : rawType;
|
|
168
|
+
if (minimum !== undefined && value < minimum) {
|
|
169
|
+
fail(`Option '${path}' must be >= ${minimum}`);
|
|
170
|
+
}
|
|
171
|
+
if (maximum !== undefined && value > maximum) {
|
|
172
|
+
fail(`Option '${path}' must be <= ${maximum}`);
|
|
173
|
+
}
|
|
174
|
+
if (pattern !== undefined && !new RegExp(pattern).test(value)) {
|
|
175
|
+
fail(`Option '${path}' must match pattern ${pattern}`);
|
|
176
|
+
}
|
|
177
|
+
if (minLength !== undefined && value.length < minLength) {
|
|
178
|
+
fail(`Option '${path}' must have length >= ${minLength}`);
|
|
179
|
+
}
|
|
180
|
+
if (maxLength !== undefined && value.length > maxLength) {
|
|
181
|
+
fail(`Option '${path}' must have length <= ${maxLength}`);
|
|
182
|
+
}
|
|
183
|
+
if (type === "array" && items !== undefined) {
|
|
184
|
+
for (let i = 0; i < value.length; i++) {
|
|
185
|
+
checkNestedRule(items, value[i], `${path}[${i}]`, fail);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
if (type === "object" && Array.isArray(required)) {
|
|
189
|
+
for (const key of required) {
|
|
190
|
+
if (value[key] === undefined) {
|
|
191
|
+
fail(`Missing required option '${childPathOf(path, key)}'`);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
if (
|
|
196
|
+
type === "object" &&
|
|
197
|
+
(properties || additionalProperties !== undefined)
|
|
198
|
+
) {
|
|
199
|
+
for (const key of Object.keys(value)) {
|
|
200
|
+
if (properties && Object.hasOwn(properties, key)) continue;
|
|
201
|
+
if (
|
|
202
|
+
additionalProperties === undefined ||
|
|
203
|
+
additionalProperties === false
|
|
204
|
+
) {
|
|
205
|
+
fail(`Unknown option '${childPathOf(path, key)}'`);
|
|
206
|
+
}
|
|
207
|
+
if (additionalProperties === true) continue;
|
|
208
|
+
checkNestedRule(
|
|
209
|
+
additionalProperties,
|
|
210
|
+
value[key],
|
|
211
|
+
childPathOf(path, key),
|
|
212
|
+
fail,
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
if (properties) {
|
|
216
|
+
for (const key of Object.keys(properties)) {
|
|
217
|
+
if (value[key] === undefined) continue;
|
|
218
|
+
checkRule(properties[key], value[key], childPathOf(path, key), fail);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
fail(`Invalid schema for option '${path}'`);
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
const isJsonSchemaForm = (schema) =>
|
|
228
|
+
isPlainObject(schema) &&
|
|
229
|
+
schema.type === "object" &&
|
|
230
|
+
(Object.hasOwn(schema, "properties") ||
|
|
231
|
+
Object.hasOwn(schema, "required") ||
|
|
232
|
+
Object.hasOwn(schema, "additionalProperties"));
|
|
233
|
+
|
|
234
|
+
export const validateOptions = (packageName, schema, options = {}) => {
|
|
235
|
+
const fail = (message) => {
|
|
236
|
+
throw new TypeError(message, { cause: { package: packageName } });
|
|
237
|
+
};
|
|
238
|
+
if (isJsonSchemaForm(schema)) {
|
|
239
|
+
checkRule(schema, options, "", fail);
|
|
240
|
+
} else {
|
|
241
|
+
checkSchemaObject(schema, options, "", fail);
|
|
242
|
+
}
|
|
243
|
+
return options;
|
|
244
|
+
};
|
|
245
|
+
|
|
3
246
|
export const createPrefetchClient = (options) => {
|
|
4
247
|
const { awsClientOptions } = options;
|
|
5
248
|
const client = new options.AwsClient(awsClientOptions);
|
|
@@ -139,7 +382,10 @@ export const sanitizeKey = (key) => {
|
|
|
139
382
|
};
|
|
140
383
|
|
|
141
384
|
// fetch Cache
|
|
142
|
-
|
|
385
|
+
// Map keyed by cacheKey; value shape: { value:{fetchKey:Promise}, expiry, refresh?, modified? }
|
|
386
|
+
// Map chosen over plain object so deletion is O(1), frees the key slot, and
|
|
387
|
+
// avoids the `delete` operator (biome's performance/noDelete rule).
|
|
388
|
+
const cache = new Map();
|
|
143
389
|
const defaultCacheMaxSize = 128;
|
|
144
390
|
|
|
145
391
|
const validateCacheExpiry = (cacheExpiry) => {
|
|
@@ -173,8 +419,9 @@ export const processCache = (
|
|
|
173
419
|
if (cached.modified) {
|
|
174
420
|
const value = middlewareFetch(middlewareFetchRequest, cached.value);
|
|
175
421
|
Object.assign(cached.value, value);
|
|
176
|
-
|
|
177
|
-
|
|
422
|
+
const entry = { value: cached.value, expiry: cached.expiry };
|
|
423
|
+
cache.set(cacheKey, entry);
|
|
424
|
+
return entry;
|
|
178
425
|
}
|
|
179
426
|
cached.cache = true;
|
|
180
427
|
return cached;
|
|
@@ -189,16 +436,18 @@ export const processCache = (
|
|
|
189
436
|
const expiry = cacheExpiry > 86400000 ? cacheExpiry : now + cacheExpiry;
|
|
190
437
|
const duration = cacheExpiry > 86400000 ? cacheExpiry - now : cacheExpiry;
|
|
191
438
|
if (cacheExpiry) {
|
|
192
|
-
clearTimeout(cache
|
|
439
|
+
clearTimeout(cache.get(cacheKey)?.refresh);
|
|
440
|
+
// .unref() so a pending refresh timer does not keep the Lambda event
|
|
441
|
+
// loop alive (relevant under `callbackWaitsForEmptyEventLoop: false`).
|
|
193
442
|
const refresh =
|
|
194
443
|
duration > 0
|
|
195
444
|
? setTimeout(
|
|
196
445
|
() =>
|
|
197
446
|
processCache(options, middlewareFetch, middlewareFetchRequest),
|
|
198
447
|
duration,
|
|
199
|
-
)
|
|
448
|
+
).unref()
|
|
200
449
|
: undefined;
|
|
201
|
-
cache
|
|
450
|
+
cache.set(cacheKey, { value, expiry, refresh });
|
|
202
451
|
evictCache(cacheMaxSize);
|
|
203
452
|
}
|
|
204
453
|
return { value, expiry };
|
|
@@ -212,13 +461,12 @@ export const catchInvalidSignatureException = (e, client, command) => {
|
|
|
212
461
|
};
|
|
213
462
|
|
|
214
463
|
export const getCache = (key) => {
|
|
215
|
-
|
|
216
|
-
return cache[key];
|
|
464
|
+
return cache.get(key) ?? {};
|
|
217
465
|
};
|
|
218
466
|
|
|
219
467
|
// Used to remove parts of a cache
|
|
220
468
|
export const modifyCache = (cacheKey, value) => {
|
|
221
|
-
const entry = cache
|
|
469
|
+
const entry = cache.get(cacheKey);
|
|
222
470
|
if (!entry) return;
|
|
223
471
|
clearTimeout(entry.refresh);
|
|
224
472
|
entry.value = value;
|
|
@@ -226,32 +474,30 @@ export const modifyCache = (cacheKey, value) => {
|
|
|
226
474
|
};
|
|
227
475
|
|
|
228
476
|
const evictCache = (maxSize) => {
|
|
229
|
-
|
|
230
|
-
if (cacheKeys.length <= maxSize) return;
|
|
477
|
+
if (cache.size <= maxSize) return;
|
|
231
478
|
let oldestKey = null;
|
|
232
479
|
let oldestExpiry = Infinity;
|
|
233
|
-
for (const key of
|
|
234
|
-
const entry = cache[key];
|
|
480
|
+
for (const [key, entry] of cache) {
|
|
235
481
|
if (entry && entry.expiry < oldestExpiry) {
|
|
236
482
|
oldestExpiry = entry.expiry;
|
|
237
483
|
oldestKey = key;
|
|
238
484
|
}
|
|
239
485
|
}
|
|
240
|
-
if (oldestKey) {
|
|
241
|
-
clearTimeout(cache
|
|
242
|
-
cache
|
|
486
|
+
if (oldestKey !== null) {
|
|
487
|
+
clearTimeout(cache.get(oldestKey)?.refresh);
|
|
488
|
+
cache.delete(oldestKey);
|
|
243
489
|
}
|
|
244
490
|
};
|
|
245
491
|
|
|
246
492
|
export const clearCache = (inputKeys = null) => {
|
|
247
493
|
let keys = inputKeys;
|
|
248
|
-
keys ??=
|
|
494
|
+
keys ??= [...cache.keys()];
|
|
249
495
|
if (!Array.isArray(keys)) {
|
|
250
496
|
keys = [keys];
|
|
251
497
|
}
|
|
252
498
|
for (const cacheKey of keys) {
|
|
253
|
-
clearTimeout(cache
|
|
254
|
-
cache
|
|
499
|
+
clearTimeout(cache.get(cacheKey)?.refresh);
|
|
500
|
+
cache.delete(cacheKey);
|
|
255
501
|
}
|
|
256
502
|
};
|
|
257
503
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@middy/util",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.3.1",
|
|
4
4
|
"description": "🛵 The stylish Node.js middleware engine for AWS Lambda (util package)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -17,9 +17,6 @@
|
|
|
17
17
|
"import": {
|
|
18
18
|
"types": "./index.d.ts",
|
|
19
19
|
"default": "./index.js"
|
|
20
|
-
},
|
|
21
|
-
"require": {
|
|
22
|
-
"default": "./index.js"
|
|
23
20
|
}
|
|
24
21
|
}
|
|
25
22
|
},
|
|
@@ -60,7 +57,7 @@
|
|
|
60
57
|
},
|
|
61
58
|
"devDependencies": {
|
|
62
59
|
"@aws-sdk/client-ssm": "^3.0.0",
|
|
63
|
-
"@middy/core": "7.
|
|
60
|
+
"@middy/core": "7.3.1",
|
|
64
61
|
"@types/aws-lambda": "^8.0.0",
|
|
65
62
|
"@types/node": "^22.0.0",
|
|
66
63
|
"aws-xray-sdk": "^3.3.3"
|