@middy/util 7.1.2 → 7.1.4

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.
Files changed (4) hide show
  1. package/README.md +18 -5
  2. package/index.d.ts +20 -19
  3. package/index.js +54 -24
  4. package/package.json +3 -4
package/README.md CHANGED
@@ -30,10 +30,23 @@
30
30
  <p>You can read the documentation at: <a href="https://middy.js.org/docs/intro/utilities">https://middy.js.org/docs/intro/utilities</a></p>
31
31
  </div>
32
32
 
33
- ## License
33
+ ## Install
34
+
35
+ ```bash
36
+ npm install --save @middy/util
37
+ ```
38
+
39
+
40
+ ## Documentation and examples
41
+
42
+ For documentation and examples, refer to the main [Middy monorepo on GitHub](https://github.com/middyjs/middy) or [Middy official website](https://middy.js.org/docs/intro/utilities).
34
43
 
35
- Licensed under [MIT License](LICENSE). Copyright (c) 2017-2026 [will Farrell](https://github.com/willfarrell), [Luciano Mammino](https://github.com/lmammino), and [Middy contributors](https://github.com/middyjs/middy/graphs/contributors).
36
44
 
37
- <a href="https://app.fossa.io/projects/git%2Bgithub.com%2Fmiddyjs%2Fmiddy?ref=badge_large">
38
- <img src="https://app.fossa.io/api/projects/git%2Bgithub.com%2Fmiddyjs%2Fmiddy.svg?type=large" alt="FOSSA Status" style="max-width:100%;">
39
- </a>
45
+ ## Contributing
46
+
47
+ Everyone is very welcome to contribute to this repository. Feel free to [raise issues](https://github.com/middyjs/middy/issues) or to [submit Pull Requests](https://github.com/middyjs/middy/pulls).
48
+
49
+
50
+ ## License
51
+
52
+ Licensed under [MIT License](LICENSE). Copyright (c) 2017-2026 [will Farrell](https://github.com/willfarrell), [Luciano Mammino](https://github.com/lmammino), and [Middy contributors](https://github.com/middyjs/middy/graphs/contributors).
package/index.d.ts CHANGED
@@ -12,7 +12,7 @@ import type {
12
12
  } from "./type-utils.d.ts";
13
13
 
14
14
  export interface Options<Client, ClientOptions> {
15
- AwsClient?: new (...[config]: [any] | any) => Client;
15
+ AwsClient?: new (config: ClientOptions) => Client;
16
16
  awsClientOptions?: Partial<ClientOptions>;
17
17
  awsClientAssumeRole?: string;
18
18
  awsClientCapture?: (service: Client) => Client;
@@ -20,6 +20,7 @@ export interface Options<Client, ClientOptions> {
20
20
  disablePrefetch?: boolean;
21
21
  cacheKey?: string;
22
22
  cacheExpiry?: number;
23
+ cacheKeyExpiry?: Record<string, number>;
23
24
  setToContext?: boolean;
24
25
  }
25
26
 
@@ -27,8 +28,8 @@ export declare class HttpError extends Error {
27
28
  status: number;
28
29
  statusCode: number;
29
30
  expose: boolean;
30
- [key: string]: any;
31
- [key: number]: any;
31
+ [key: string]: unknown;
32
+ [key: number]: unknown;
32
33
  }
33
34
 
34
35
  declare function createPrefetchClient<Client, ClientOptions>(
@@ -118,43 +119,43 @@ declare function sanitizeKey<T extends string>(key: T): SanitizeKey<T>;
118
119
 
119
120
  declare function processCache<Client, ClientOptions>(
120
121
  options: Options<Client, ClientOptions>,
121
- fetch: (request: middy.Request, cachedValues: any) => any,
122
+ fetch: (request: middy.Request, cachedValues: unknown) => unknown,
122
123
  request?: middy.Request,
123
- ): { value: any; expiry: number };
124
+ ): { value: unknown; expiry: number };
124
125
 
125
- declare function getCache(keys: string): any;
126
+ declare function getCache(keys: string): unknown;
126
127
 
127
128
  declare function clearCache(keys?: string | string[] | null): void;
128
129
 
129
130
  declare function jsonSafeParse(
130
131
  string: string,
131
- reviver?: (key: string, value: any) => any,
132
- ): any;
132
+ reviver?: (key: string, value: unknown) => unknown,
133
+ ): unknown;
133
134
 
134
135
  declare function normalizeHttpResponse(
135
- request: any,
136
- fallbackResponse?: any,
137
- ): any;
136
+ request: middy.Request,
137
+ fallbackResponse?: Record<string, unknown>,
138
+ ): Record<string, unknown>;
138
139
 
139
140
  declare function createError(
140
141
  code: number,
141
142
  message: string,
142
- properties?: Record<string, any>,
143
+ properties?: Record<string, unknown>,
143
144
  ): HttpError;
144
145
 
145
- declare function modifyCache(cacheKey: string, value: any): void;
146
+ declare function modifyCache(cacheKey: string, value: unknown): void;
146
147
 
147
148
  declare function catchInvalidSignatureException<Client, Command>(
148
149
  e: Error & { __type?: string },
149
150
  client: Client,
150
151
  command: Command,
151
- ): Promise<any>;
152
+ ): Promise<unknown>;
152
153
 
153
154
  declare function jsonSafeStringify(
154
- value: any,
155
- replacer?: (key: string, value: any) => any,
155
+ value: unknown,
156
+ replacer?: (key: string, value: unknown) => unknown,
156
157
  space?: string | number,
157
- ): string | any;
158
+ ): string | unknown;
158
159
 
159
160
  declare function decodeBody(event: {
160
161
  body?: string | null;
@@ -171,12 +172,12 @@ declare function executionContext(
171
172
  request: middy.Request,
172
173
  key: string,
173
174
  context: LambdaContext,
174
- ): any;
175
+ ): unknown;
175
176
 
176
177
  declare function lambdaContext(
177
178
  request: middy.Request,
178
179
  key: string,
179
180
  context: LambdaContext,
180
- ): any;
181
+ ): unknown;
181
182
 
182
183
  declare const httpErrorCodes: Record<number, string>;
package/index.js CHANGED
@@ -46,6 +46,9 @@ export const canPrefetch = (options = {}) => {
46
46
  return !options.awsClientAssumeRole && !options.disablePrefetch;
47
47
  };
48
48
 
49
+ const safeGet = (obj, key) =>
50
+ obj != null && Object.hasOwn(obj, key) ? obj[key] : undefined;
51
+
49
52
  // Internal Context
50
53
  export const getInternal = async (variables, request) => {
51
54
  if (!variables || !request) return {};
@@ -61,6 +64,36 @@ export const getInternal = async (variables, request) => {
61
64
  keys = Object.keys(variables);
62
65
  values = Object.values(variables);
63
66
  }
67
+ // Fast synchronous path: when all internal values are already resolved
68
+ // (warm/cached invocations), skip all Promise machinery entirely
69
+ let allSync = true;
70
+ const syncResults = new Array(values.length);
71
+ for (let i = 0; i < values.length; i++) {
72
+ const internalKey = values[i];
73
+ const dotIndex = internalKey.indexOf(".");
74
+ const rootKey =
75
+ dotIndex === -1 ? internalKey : internalKey.substring(0, dotIndex);
76
+ let value = request.internal[rootKey];
77
+ if (isPromise(value)) {
78
+ allSync = false;
79
+ break;
80
+ }
81
+ if (dotIndex !== -1) {
82
+ for (const part of internalKey.substring(dotIndex + 1).split(".")) {
83
+ value = safeGet(value, part);
84
+ }
85
+ }
86
+ syncResults[i] = value;
87
+ }
88
+ if (allSync) {
89
+ const obj = {};
90
+ for (let i = 0; i < keys.length; i++) {
91
+ obj[sanitizeKey(keys[i])] = syncResults[i];
92
+ }
93
+ return obj;
94
+ }
95
+
96
+ // Async fallback: for cold/first invocations with pending promises
64
97
  const promises = [];
65
98
  for (const internalKey of values) {
66
99
  // 'internal.key.sub_value' -> { [key]: internal.key.sub_value }
@@ -71,26 +104,27 @@ export const getInternal = async (variables, request) => {
71
104
  valuePromise = Promise.resolve(valuePromise);
72
105
  }
73
106
  promises.push(
74
- valuePromise.then((value) =>
75
- pathOptionKey.reduce((p, c) => p?.[c], value),
76
- ),
107
+ valuePromise.then((value) => pathOptionKey.reduce(safeGet, value)),
77
108
  );
78
109
  }
79
110
  // ensure promise has resolved by the time it's needed
80
111
  // If one of the promises throws it will bubble up to @middy/core
81
112
  values = await Promise.allSettled(promises);
82
- const errors = values
83
- .filter((res) => res.status === "rejected")
84
- .map((res) => res.reason);
85
- if (errors.length) {
113
+ const obj = {};
114
+ let errors;
115
+ for (let i = 0; i < keys.length; i++) {
116
+ if (values[i].status === "rejected") {
117
+ errors ??= [];
118
+ errors.push(values[i].reason);
119
+ } else {
120
+ obj[sanitizeKey(keys[i])] = values[i].value;
121
+ }
122
+ }
123
+ if (errors) {
86
124
  throw new Error("Failed to resolve internal values", {
87
125
  cause: { package: "@middy/util", data: errors },
88
126
  });
89
127
  }
90
- const obj = {};
91
- for (let i = keys.length; i--; ) {
92
- obj[sanitizeKey(keys[i])] = values[i].value;
93
- }
94
128
  return obj;
95
129
  };
96
130
 
@@ -125,7 +159,8 @@ export const processCache = (
125
159
  cache[cacheKey] = { value: cached.value, expiry: cached.expiry };
126
160
  return cache[cacheKey];
127
161
  }
128
- return { ...cached, cache: true };
162
+ cached.cache = true;
163
+ return cached;
129
164
  }
130
165
  }
131
166
  const value = middlewareFetch(middlewareFetchRequest);
@@ -161,8 +196,9 @@ export const getCache = (key) => {
161
196
  // Used to remove parts of a cache
162
197
  export const modifyCache = (cacheKey, value) => {
163
198
  if (!cache[cacheKey]) return;
164
- clearTimeout(cache[cacheKey]?.refresh);
165
- cache[cacheKey] = { ...cache[cacheKey], value, modified: true };
199
+ clearTimeout(cache[cacheKey].refresh);
200
+ cache[cacheKey].value = value;
201
+ cache[cacheKey].modified = true;
166
202
  };
167
203
 
168
204
  export const clearCache = (inputKeys = null) => {
@@ -192,18 +228,12 @@ export const lambdaContextKeys = [
192
228
  "callbackWaitsForEmptyEventLoop",
193
229
  ];
194
230
 
195
- export const executionContextKeys = [
196
- //'requestId',
197
- "tenantId",
198
- ];
231
+ export const executionContextKeys = ["tenantId"];
199
232
 
200
233
  export const isExecutionModeDurable = (context) => {
201
234
  // using `context instanceof DurableContextImpl` would be better
202
235
  // but would require an extra dependency
203
- if (context.constructor.name === "DurableContextImpl") {
204
- return true;
205
- }
206
- return false;
236
+ return context.constructor.name === "DurableContextImpl";
207
237
  };
208
238
 
209
239
  export const executionContext = (request, key, context) => {
@@ -241,7 +271,7 @@ export const jsonSafeStringify = (value, replacer, space) => {
241
271
 
242
272
  export const decodeBody = (event) => {
243
273
  const { body, isBase64Encoded } = event;
244
- if (body === undefined || body === null) return body;
274
+ if (typeof body === "undefined" || body === null) return body;
245
275
  return isBase64Encoded ? Buffer.from(body, "base64").toString() : body;
246
276
  };
247
277
 
@@ -286,7 +316,7 @@ export const createError = (code, message, properties = {}) => {
286
316
  return new HttpError(code, message, properties);
287
317
  };
288
318
 
289
- const httpErrorCodes = {
319
+ export const httpErrorCodes = {
290
320
  100: "Continue",
291
321
  101: "Switching Protocols",
292
322
  102: "Processing",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@middy/util",
3
- "version": "7.1.2",
3
+ "version": "7.1.4",
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.1.2",
63
+ "@middy/core": "7.1.4",
64
64
  "@types/aws-lambda": "^8.0.0",
65
65
  "@types/node": "^22.0.0",
66
66
  "aws-xray-sdk": "^3.3.3"
@@ -69,6 +69,5 @@
69
69
  "funding": {
70
70
  "type": "github",
71
71
  "url": "https://github.com/sponsors/willfarrell"
72
- },
73
- "gitHead": "7a6c0fbb8ab71d6a2171e678697de9f237568431"
72
+ }
74
73
  }