@middy/util 2.5.2 → 2.5.6
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 +1 -2
- package/index.js +142 -164
- package/package.json +3 -3
package/index.d.ts
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import middy from '@middy/core'
|
|
2
|
-
import { captureAWSClient } from 'aws-xray-sdk'
|
|
3
2
|
|
|
4
3
|
interface Options<Client, ClientOptions> {
|
|
5
4
|
AwsClient?: new() => Client
|
|
6
5
|
awsClientOptions?: Partial<ClientOptions>
|
|
7
6
|
awsClientAssumeRole?: string
|
|
8
|
-
awsClientCapture?:
|
|
7
|
+
awsClientCapture?: (service: Client) => Client
|
|
9
8
|
fetchData?: { [key: string]: string }
|
|
10
9
|
disablePrefetch?: boolean
|
|
11
10
|
cacheKey?: string
|
package/index.js
CHANGED
|
@@ -1,14 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
const {
|
|
4
|
-
Agent
|
|
5
|
-
} = require('https'); // const { NodeHttpHandler } = require('@aws-sdk/node-http-handler') // aws-sdk v3
|
|
6
|
-
|
|
1
|
+
const { Agent } = require('https')
|
|
2
|
+
// const { NodeHttpHandler } = require('@aws-sdk/node-http-handler') // aws-sdk v3
|
|
7
3
|
|
|
8
4
|
const awsClientDefaultOptions = {
|
|
9
5
|
// AWS SDK v3
|
|
10
6
|
// Docs: https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/enforcing-tls.html
|
|
11
|
-
|
|
12
7
|
/* requestHandler: new NodeHttpHandler({
|
|
13
8
|
httpsAgent: new Agent(
|
|
14
9
|
{
|
|
@@ -22,242 +17,225 @@ const awsClientDefaultOptions = {
|
|
|
22
17
|
secureProtocol: 'TLSv1_2_method'
|
|
23
18
|
})
|
|
24
19
|
}
|
|
25
|
-
}
|
|
20
|
+
}
|
|
26
21
|
|
|
27
|
-
const createPrefetchClient = options => {
|
|
28
|
-
const awsClientOptions = {
|
|
22
|
+
const createPrefetchClient = (options) => {
|
|
23
|
+
const awsClientOptions = {
|
|
24
|
+
...awsClientDefaultOptions,
|
|
29
25
|
...options.awsClientOptions
|
|
30
|
-
}
|
|
31
|
-
const client = new options.AwsClient(awsClientOptions)
|
|
26
|
+
}
|
|
27
|
+
const client = new options.AwsClient(awsClientOptions)
|
|
32
28
|
|
|
29
|
+
// AWS XRay
|
|
33
30
|
if (options.awsClientCapture) {
|
|
34
|
-
return options.awsClientCapture(client)
|
|
31
|
+
return options.awsClientCapture(client)
|
|
35
32
|
}
|
|
36
33
|
|
|
37
|
-
return client
|
|
38
|
-
}
|
|
34
|
+
return client
|
|
35
|
+
}
|
|
39
36
|
|
|
40
37
|
const createClient = async (options, request) => {
|
|
41
|
-
let awsClientCredentials = {}
|
|
38
|
+
let awsClientCredentials = {}
|
|
42
39
|
|
|
40
|
+
// Role Credentials
|
|
43
41
|
if (options.awsClientAssumeRole) {
|
|
44
|
-
if (!request) throw new Error('Request required when assuming role')
|
|
45
|
-
awsClientCredentials = await getInternal(
|
|
46
|
-
credentials: options.awsClientAssumeRole
|
|
47
|
-
|
|
42
|
+
if (!request) throw new Error('Request required when assuming role')
|
|
43
|
+
awsClientCredentials = await getInternal(
|
|
44
|
+
{ credentials: options.awsClientAssumeRole },
|
|
45
|
+
request
|
|
46
|
+
)
|
|
48
47
|
}
|
|
49
48
|
|
|
50
|
-
awsClientCredentials = {
|
|
49
|
+
awsClientCredentials = {
|
|
50
|
+
...awsClientCredentials,
|
|
51
51
|
...options.awsClientOptions
|
|
52
|
-
}
|
|
53
|
-
return createPrefetchClient({ ...options,
|
|
54
|
-
awsClientOptions: awsClientCredentials
|
|
55
|
-
});
|
|
56
|
-
};
|
|
52
|
+
}
|
|
57
53
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
54
|
+
return createPrefetchClient({
|
|
55
|
+
...options,
|
|
56
|
+
awsClientOptions: awsClientCredentials
|
|
57
|
+
})
|
|
58
|
+
}
|
|
61
59
|
|
|
60
|
+
const canPrefetch = (options) => {
|
|
61
|
+
return !options?.awsClientAssumeRole && !options?.disablePrefetch
|
|
62
|
+
}
|
|
62
63
|
|
|
64
|
+
// Internal Context
|
|
63
65
|
const getInternal = async (variables, request) => {
|
|
64
|
-
if (!variables || !request) return {}
|
|
65
|
-
let keys = []
|
|
66
|
-
let values = []
|
|
67
|
-
|
|
66
|
+
if (!variables || !request) return {}
|
|
67
|
+
let keys = []
|
|
68
|
+
let values = []
|
|
68
69
|
if (variables === true) {
|
|
69
|
-
keys = values = Object.keys(request.internal)
|
|
70
|
+
keys = values = Object.keys(request.internal)
|
|
70
71
|
} else if (typeof variables === 'string') {
|
|
71
|
-
keys = values = [variables]
|
|
72
|
+
keys = values = [variables]
|
|
72
73
|
} else if (Array.isArray(variables)) {
|
|
73
|
-
keys = values = variables
|
|
74
|
+
keys = values = variables
|
|
74
75
|
} else if (typeof variables === 'object') {
|
|
75
|
-
keys = Object.keys(variables)
|
|
76
|
-
values = Object.values(variables)
|
|
76
|
+
keys = Object.keys(variables)
|
|
77
|
+
values = Object.values(variables)
|
|
77
78
|
}
|
|
78
|
-
|
|
79
|
-
const promises = [];
|
|
80
|
-
|
|
79
|
+
const promises = []
|
|
81
80
|
for (const internalKey of values) {
|
|
82
|
-
var _valuePromise;
|
|
83
|
-
|
|
84
81
|
// 'internal.key.sub_value' -> { [key]: internal.key.sub_value }
|
|
85
|
-
const pathOptionKey = internalKey.split('.')
|
|
86
|
-
const rootOptionKey = pathOptionKey.shift()
|
|
87
|
-
let valuePromise = request.internal[rootOptionKey]
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
valuePromise = Promise.resolve(valuePromise);
|
|
82
|
+
const pathOptionKey = internalKey.split('.')
|
|
83
|
+
const rootOptionKey = pathOptionKey.shift()
|
|
84
|
+
let valuePromise = request.internal[rootOptionKey]
|
|
85
|
+
if (typeof valuePromise?.then !== 'function') {
|
|
86
|
+
valuePromise = Promise.resolve(valuePromise)
|
|
91
87
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
88
|
+
promises.push(
|
|
89
|
+
valuePromise.then((value) =>
|
|
90
|
+
pathOptionKey.reduce((p, c) => p?.[c], value)
|
|
91
|
+
)
|
|
92
|
+
)
|
|
93
|
+
}
|
|
94
|
+
// ensure promise has resolved by the time it's needed
|
|
95
95
|
// If one of the promises throws it will bubble up to @middy/core
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
if (errors.length) throw new Error(JSON.stringify(errors))
|
|
101
|
-
return keys.reduce(
|
|
102
|
-
[sanitizeKey(key)]: values[index].value
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
const sanitizeKeyPrefixLeadingNumber = /^([0-9])
|
|
107
|
-
const sanitizeKeyRemoveDisallowedChar = /[^a-zA-Z0-9]+/g
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
96
|
+
values = await Promise.allSettled(promises)
|
|
97
|
+
const errors = values
|
|
98
|
+
.filter((res) => res.status === 'rejected')
|
|
99
|
+
.map((res) => res.reason.message)
|
|
100
|
+
if (errors.length) throw new Error(JSON.stringify(errors))
|
|
101
|
+
return keys.reduce(
|
|
102
|
+
(obj, key, index) => ({ ...obj, [sanitizeKey(key)]: values[index].value }),
|
|
103
|
+
{}
|
|
104
|
+
)
|
|
105
|
+
}
|
|
106
|
+
const sanitizeKeyPrefixLeadingNumber = /^([0-9])/
|
|
107
|
+
const sanitizeKeyRemoveDisallowedChar = /[^a-zA-Z0-9]+/g
|
|
108
|
+
const sanitizeKey = (key) => {
|
|
109
|
+
return key
|
|
110
|
+
.replace(sanitizeKeyPrefixLeadingNumber, '_$1')
|
|
111
|
+
.replace(sanitizeKeyRemoveDisallowedChar, '_')
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// fetch Cache
|
|
115
|
+
const cache = {} // key: { value:{fetchKey:Promise}, expiry }
|
|
116
116
|
const processCache = (options, fetch = () => undefined, request) => {
|
|
117
|
-
const {
|
|
118
|
-
cacheExpiry,
|
|
119
|
-
cacheKey
|
|
120
|
-
} = options;
|
|
121
|
-
|
|
117
|
+
const { cacheExpiry, cacheKey } = options
|
|
122
118
|
if (cacheExpiry) {
|
|
123
|
-
const cached = getCache(cacheKey)
|
|
124
|
-
const unexpired = cached && (cacheExpiry < 0 || cached.expiry > Date.now())
|
|
119
|
+
const cached = getCache(cacheKey)
|
|
120
|
+
const unexpired = cached && (cacheExpiry < 0 || cached.expiry > Date.now())
|
|
125
121
|
|
|
126
122
|
if (unexpired && cached.modified) {
|
|
127
|
-
const value = fetch(request, cached.value)
|
|
123
|
+
const value = fetch(request, cached.value)
|
|
128
124
|
cache[cacheKey] = {
|
|
129
|
-
value: { ...cached.value,
|
|
130
|
-
...value
|
|
131
|
-
},
|
|
125
|
+
value: { ...cached.value, ...value },
|
|
132
126
|
expiry: cached.expiry
|
|
133
|
-
}
|
|
134
|
-
return cache[cacheKey]
|
|
127
|
+
}
|
|
128
|
+
return cache[cacheKey]
|
|
135
129
|
}
|
|
136
|
-
|
|
137
130
|
if (unexpired) {
|
|
138
|
-
return { ...cached,
|
|
139
|
-
cache: true
|
|
140
|
-
};
|
|
131
|
+
return { ...cached, cache: true }
|
|
141
132
|
}
|
|
142
133
|
}
|
|
134
|
+
const value = fetch(request)
|
|
143
135
|
|
|
144
|
-
const
|
|
145
|
-
const expiry = Date.now() + cacheExpiry;
|
|
146
|
-
|
|
136
|
+
const expiry = Date.now() + cacheExpiry
|
|
147
137
|
if (cacheExpiry) {
|
|
148
|
-
cache[cacheKey] = {
|
|
149
|
-
value,
|
|
150
|
-
expiry
|
|
151
|
-
};
|
|
138
|
+
cache[cacheKey] = { value, expiry }
|
|
152
139
|
}
|
|
140
|
+
return { value, expiry }
|
|
141
|
+
}
|
|
153
142
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
};
|
|
158
|
-
};
|
|
159
|
-
|
|
160
|
-
const getCache = key => {
|
|
161
|
-
return cache[key];
|
|
162
|
-
}; // Used to remove parts of a cache
|
|
163
|
-
|
|
143
|
+
const getCache = (key) => {
|
|
144
|
+
return cache[key]
|
|
145
|
+
}
|
|
164
146
|
|
|
147
|
+
// Used to remove parts of a cache
|
|
165
148
|
const modifyCache = (cacheKey, value) => {
|
|
166
|
-
if (!cache[cacheKey]) return
|
|
167
|
-
cache[cacheKey] = { ...cache[cacheKey],
|
|
168
|
-
|
|
169
|
-
modified: true
|
|
170
|
-
};
|
|
171
|
-
};
|
|
149
|
+
if (!cache[cacheKey]) return
|
|
150
|
+
cache[cacheKey] = { ...cache[cacheKey], value, modified: true }
|
|
151
|
+
}
|
|
172
152
|
|
|
173
153
|
const clearCache = (keys = null) => {
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
keys = (_keys = keys) !== null && _keys !== void 0 ? _keys : Object.keys(cache);
|
|
177
|
-
if (!Array.isArray(keys)) keys = [keys];
|
|
178
|
-
|
|
154
|
+
keys = keys ?? Object.keys(cache)
|
|
155
|
+
if (!Array.isArray(keys)) keys = [keys]
|
|
179
156
|
for (const cacheKey of keys) {
|
|
180
|
-
cache[cacheKey] = undefined
|
|
157
|
+
cache[cacheKey] = undefined
|
|
181
158
|
}
|
|
182
|
-
}
|
|
159
|
+
}
|
|
183
160
|
|
|
184
161
|
const jsonSafeParse = (string, reviver) => {
|
|
185
|
-
if (typeof string !== 'string') return string
|
|
186
|
-
const firstChar = string[0]
|
|
187
|
-
if (firstChar !== '{' && firstChar !== '[' && firstChar !== '"') return string
|
|
188
|
-
|
|
162
|
+
if (typeof string !== 'string') return string
|
|
163
|
+
const firstChar = string[0]
|
|
164
|
+
if (firstChar !== '{' && firstChar !== '[' && firstChar !== '"') return string
|
|
189
165
|
try {
|
|
190
|
-
return JSON.parse(string, reviver)
|
|
166
|
+
return JSON.parse(string, reviver)
|
|
191
167
|
} catch (e) {}
|
|
192
168
|
|
|
193
|
-
return string
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
const normalizeHttpResponse = response => {
|
|
197
|
-
var _response$headers, _response;
|
|
169
|
+
return string
|
|
170
|
+
}
|
|
198
171
|
|
|
172
|
+
const normalizeHttpResponse = (response) => {
|
|
199
173
|
// May require updating to catch other types
|
|
200
174
|
if (response === undefined) {
|
|
201
|
-
response = {}
|
|
202
|
-
} else if (
|
|
203
|
-
response
|
|
204
|
-
|
|
205
|
-
|
|
175
|
+
response = {}
|
|
176
|
+
} else if (
|
|
177
|
+
Array.isArray(response) ||
|
|
178
|
+
typeof response !== 'object' ||
|
|
179
|
+
response === null
|
|
180
|
+
) {
|
|
181
|
+
response = { body: response }
|
|
206
182
|
}
|
|
183
|
+
response.headers = response?.headers ?? {}
|
|
184
|
+
return response
|
|
185
|
+
}
|
|
207
186
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
const statuses = require('./codes.json');
|
|
214
|
-
|
|
215
|
-
const {
|
|
216
|
-
inherits
|
|
217
|
-
} = require('util');
|
|
218
|
-
|
|
219
|
-
const createErrorRegexp = /[^a-zA-Z]/g;
|
|
187
|
+
// smaller version of `http-errors`
|
|
188
|
+
const statuses = require('./codes.json')
|
|
189
|
+
const { inherits } = require('util')
|
|
220
190
|
|
|
191
|
+
const createErrorRegexp = /[^a-zA-Z]/g
|
|
221
192
|
const createError = (code, message, properties = {}) => {
|
|
222
|
-
const name = statuses[code].replace(createErrorRegexp, '')
|
|
223
|
-
const className = name.substr(-5) !== 'Error' ? name + 'Error' : name
|
|
193
|
+
const name = statuses[code].replace(createErrorRegexp, '')
|
|
194
|
+
const className = name.substr(-5) !== 'Error' ? name + 'Error' : name
|
|
224
195
|
|
|
225
|
-
function HttpError(message) {
|
|
196
|
+
function HttpError (message) {
|
|
226
197
|
// create the error object
|
|
227
|
-
const msg = message
|
|
228
|
-
const err = new Error(msg)
|
|
198
|
+
const msg = message ?? statuses[code]
|
|
199
|
+
const err = new Error(msg)
|
|
229
200
|
|
|
230
|
-
|
|
201
|
+
// capture a stack trace to the construction point
|
|
202
|
+
Error.captureStackTrace(err, HttpError)
|
|
231
203
|
|
|
232
|
-
|
|
204
|
+
// adjust the [[Prototype]]
|
|
205
|
+
Object.setPrototypeOf(err, HttpError.prototype)
|
|
233
206
|
|
|
207
|
+
// redefine the error message
|
|
234
208
|
Object.defineProperty(err, 'message', {
|
|
235
209
|
enumerable: true,
|
|
236
210
|
configurable: true,
|
|
237
211
|
value: msg,
|
|
238
212
|
writable: true
|
|
239
|
-
})
|
|
213
|
+
})
|
|
240
214
|
|
|
215
|
+
// redefine the error name
|
|
241
216
|
Object.defineProperty(err, 'name', {
|
|
242
217
|
enumerable: false,
|
|
243
218
|
configurable: true,
|
|
244
219
|
value: className,
|
|
245
220
|
writable: true
|
|
246
|
-
})
|
|
247
|
-
|
|
221
|
+
})
|
|
222
|
+
|
|
223
|
+
return err
|
|
248
224
|
}
|
|
249
225
|
|
|
250
|
-
inherits(HttpError, Error)
|
|
251
|
-
const desc = Object.getOwnPropertyDescriptor(HttpError, 'name')
|
|
252
|
-
desc.value = className
|
|
253
|
-
Object.defineProperty(HttpError, 'name', desc)
|
|
226
|
+
inherits(HttpError, Error)
|
|
227
|
+
const desc = Object.getOwnPropertyDescriptor(HttpError, 'name')
|
|
228
|
+
desc.value = className
|
|
229
|
+
Object.defineProperty(HttpError, 'name', desc)
|
|
230
|
+
|
|
254
231
|
Object.assign(HttpError.prototype, {
|
|
255
232
|
status: code,
|
|
256
233
|
statusCode: code,
|
|
257
234
|
expose: code < 500
|
|
258
|
-
}, properties)
|
|
259
|
-
|
|
260
|
-
|
|
235
|
+
}, properties)
|
|
236
|
+
|
|
237
|
+
return new HttpError(message)
|
|
238
|
+
}
|
|
261
239
|
|
|
262
240
|
module.exports = {
|
|
263
241
|
createPrefetchClient,
|
|
@@ -272,4 +250,4 @@ module.exports = {
|
|
|
272
250
|
jsonSafeParse,
|
|
273
251
|
normalizeHttpResponse,
|
|
274
252
|
createError
|
|
275
|
-
}
|
|
253
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@middy/util",
|
|
3
|
-
"version": "2.5.
|
|
3
|
+
"version": "2.5.6",
|
|
4
4
|
"description": "🛵 The stylish Node.js middleware engine for AWS Lambda (util package)",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"engines": {
|
|
@@ -44,12 +44,12 @@
|
|
|
44
44
|
"url": "https://github.com/middyjs/middy/issues"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
|
-
"@middy/core": "^2.5.
|
|
47
|
+
"@middy/core": "^2.5.6",
|
|
48
48
|
"@types/aws-lambda": "^8.10.76",
|
|
49
49
|
"@types/node": "^16.0.0",
|
|
50
50
|
"aws-sdk": "^2.939.0",
|
|
51
51
|
"aws-xray-sdk": "^3.3.3"
|
|
52
52
|
},
|
|
53
53
|
"homepage": "https://github.com/middyjs/middy#readme",
|
|
54
|
-
"gitHead": "
|
|
54
|
+
"gitHead": "0c789f55b4adf691f977b0d9904d1a805bb3bb2b"
|
|
55
55
|
}
|