@middy/util 3.0.0-alpha.1 → 3.0.0-alpha.2
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.js +235 -0
- package/package.json +4 -3
package/index.js
ADDED
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import { Agent } from 'https';
|
|
2
|
+
import statuses from './codes.js';
|
|
3
|
+
import { inherits } from 'util';
|
|
4
|
+
export const awsClientDefaultOptions = {
|
|
5
|
+
httpOptions: {
|
|
6
|
+
agent: new Agent({
|
|
7
|
+
secureProtocol: 'TLSv1_2_method'
|
|
8
|
+
})
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
export const createPrefetchClient = options => {
|
|
12
|
+
const awsClientOptions = { ...awsClientDefaultOptions,
|
|
13
|
+
...options.awsClientOptions
|
|
14
|
+
};
|
|
15
|
+
const client = new options.AwsClient(awsClientOptions);
|
|
16
|
+
|
|
17
|
+
if (options.awsClientCapture && options.disablePrefetch) {
|
|
18
|
+
return options.awsClientCapture(client);
|
|
19
|
+
} else if (options.awsClientCapture) {
|
|
20
|
+
console.warn('Unable to apply X-Ray outside of handler invocation scope.');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return client;
|
|
24
|
+
};
|
|
25
|
+
export const createClient = async (options, request) => {
|
|
26
|
+
let awsClientCredentials = {};
|
|
27
|
+
|
|
28
|
+
if (options.awsClientAssumeRole) {
|
|
29
|
+
if (!request) throw new Error('Request required when assuming role');
|
|
30
|
+
awsClientCredentials = await getInternal({
|
|
31
|
+
credentials: options.awsClientAssumeRole
|
|
32
|
+
}, request);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
awsClientCredentials = { ...awsClientCredentials,
|
|
36
|
+
...options.awsClientOptions
|
|
37
|
+
};
|
|
38
|
+
return createPrefetchClient({ ...options,
|
|
39
|
+
awsClientOptions: awsClientCredentials
|
|
40
|
+
});
|
|
41
|
+
};
|
|
42
|
+
export const canPrefetch = (options = {}) => {
|
|
43
|
+
return !options.awsClientAssumeRole && !options.disablePrefetch;
|
|
44
|
+
};
|
|
45
|
+
export const getInternal = async (variables, request) => {
|
|
46
|
+
if (!variables || !request) return {};
|
|
47
|
+
let keys = [];
|
|
48
|
+
let values = [];
|
|
49
|
+
|
|
50
|
+
if (variables === true) {
|
|
51
|
+
keys = values = Object.keys(request.internal);
|
|
52
|
+
} else if (typeof variables === 'string') {
|
|
53
|
+
keys = values = [variables];
|
|
54
|
+
} else if (Array.isArray(variables)) {
|
|
55
|
+
keys = values = variables;
|
|
56
|
+
} else if (typeof variables === 'object') {
|
|
57
|
+
keys = Object.keys(variables);
|
|
58
|
+
values = Object.values(variables);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const promises = [];
|
|
62
|
+
|
|
63
|
+
for (const internalKey of values) {
|
|
64
|
+
const pathOptionKey = internalKey.split('.');
|
|
65
|
+
const rootOptionKey = pathOptionKey.shift();
|
|
66
|
+
let valuePromise = request.internal[rootOptionKey];
|
|
67
|
+
|
|
68
|
+
if (typeof valuePromise.then !== 'function') {
|
|
69
|
+
valuePromise = Promise.resolve(valuePromise);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
promises.push(valuePromise.then(value => pathOptionKey.reduce((p, c) => p === null || p === void 0 ? void 0 : p[c], value)));
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
values = await Promise.allSettled(promises);
|
|
76
|
+
const errors = values.filter(res => res.status === 'rejected').map(res => res.reason);
|
|
77
|
+
|
|
78
|
+
if (errors.length) {
|
|
79
|
+
const error = new Error('Failed to resolve internal values');
|
|
80
|
+
error.cause = errors;
|
|
81
|
+
throw error;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return keys.reduce((obj, key, index) => ({ ...obj,
|
|
85
|
+
[sanitizeKey(key)]: values[index].value
|
|
86
|
+
}), {});
|
|
87
|
+
};
|
|
88
|
+
const sanitizeKeyPrefixLeadingNumber = /^([0-9])/;
|
|
89
|
+
const sanitizeKeyRemoveDisallowedChar = /[^a-zA-Z0-9]+/g;
|
|
90
|
+
export const sanitizeKey = key => {
|
|
91
|
+
return key.replace(sanitizeKeyPrefixLeadingNumber, '_$1').replace(sanitizeKeyRemoveDisallowedChar, '_');
|
|
92
|
+
};
|
|
93
|
+
const cache = {};
|
|
94
|
+
export const processCache = (options, fetch = () => undefined, request) => {
|
|
95
|
+
const {
|
|
96
|
+
cacheExpiry,
|
|
97
|
+
cacheKey
|
|
98
|
+
} = options;
|
|
99
|
+
|
|
100
|
+
if (cacheExpiry) {
|
|
101
|
+
const cached = getCache(cacheKey);
|
|
102
|
+
const unexpired = cached.expiry && (cacheExpiry < 0 || cached.expiry > Date.now());
|
|
103
|
+
|
|
104
|
+
if (unexpired && cached.modified) {
|
|
105
|
+
const value = fetch(request, cached.value);
|
|
106
|
+
cache[cacheKey] = {
|
|
107
|
+
value: { ...cached.value,
|
|
108
|
+
...value
|
|
109
|
+
},
|
|
110
|
+
expiry: cached.expiry
|
|
111
|
+
};
|
|
112
|
+
return cache[cacheKey];
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (unexpired) {
|
|
116
|
+
return { ...cached,
|
|
117
|
+
cache: true
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const value = fetch(request);
|
|
123
|
+
const expiry = Date.now() + cacheExpiry;
|
|
124
|
+
|
|
125
|
+
if (cacheExpiry) {
|
|
126
|
+
cache[cacheKey] = {
|
|
127
|
+
value,
|
|
128
|
+
expiry
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return {
|
|
133
|
+
value,
|
|
134
|
+
expiry
|
|
135
|
+
};
|
|
136
|
+
};
|
|
137
|
+
export const getCache = key => {
|
|
138
|
+
if (!cache[key]) return {};
|
|
139
|
+
return cache[key];
|
|
140
|
+
};
|
|
141
|
+
export const modifyCache = (cacheKey, value) => {
|
|
142
|
+
if (!cache[cacheKey]) return;
|
|
143
|
+
cache[cacheKey] = { ...cache[cacheKey],
|
|
144
|
+
value,
|
|
145
|
+
modified: true
|
|
146
|
+
};
|
|
147
|
+
};
|
|
148
|
+
export const clearCache = (keys = null) => {
|
|
149
|
+
keys = keys ?? Object.keys(cache);
|
|
150
|
+
if (!Array.isArray(keys)) keys = [keys];
|
|
151
|
+
|
|
152
|
+
for (const cacheKey of keys) {
|
|
153
|
+
cache[cacheKey] = undefined;
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
export const jsonSafeParse = (string, reviver) => {
|
|
157
|
+
if (typeof string !== 'string') return string;
|
|
158
|
+
const firstChar = string[0];
|
|
159
|
+
if (firstChar !== '{' && firstChar !== '[' && firstChar !== '"') return string;
|
|
160
|
+
|
|
161
|
+
try {
|
|
162
|
+
return JSON.parse(string, reviver);
|
|
163
|
+
} catch (e) {}
|
|
164
|
+
|
|
165
|
+
return string;
|
|
166
|
+
};
|
|
167
|
+
export const normalizeHttpResponse = request => {
|
|
168
|
+
var _response, _response2, _response3, _response4;
|
|
169
|
+
|
|
170
|
+
let {
|
|
171
|
+
response
|
|
172
|
+
} = request;
|
|
173
|
+
|
|
174
|
+
if (response === undefined) {
|
|
175
|
+
response = {};
|
|
176
|
+
} else if (((_response = response) === null || _response === void 0 ? void 0 : _response.statusCode) === undefined && ((_response2 = response) === null || _response2 === void 0 ? void 0 : _response2.body) === undefined && ((_response3 = response) === null || _response3 === void 0 ? void 0 : _response3.headers) === undefined) {
|
|
177
|
+
response = {
|
|
178
|
+
body: response
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
(_response4 = response).headers ?? (_response4.headers = {});
|
|
183
|
+
request.response = response;
|
|
184
|
+
return response;
|
|
185
|
+
};
|
|
186
|
+
const createErrorRegexp = /[^a-zA-Z]/g;
|
|
187
|
+
export const createError = (code, message, properties = {}) => {
|
|
188
|
+
const name = statuses[code].replace(createErrorRegexp, '');
|
|
189
|
+
const className = name.substr(-5) !== 'Error' ? name + 'Error' : name;
|
|
190
|
+
|
|
191
|
+
function HttpError(message) {
|
|
192
|
+
const msg = message ?? statuses[code];
|
|
193
|
+
const err = new Error(msg);
|
|
194
|
+
Error.captureStackTrace(err, HttpError);
|
|
195
|
+
Object.setPrototypeOf(err, HttpError.prototype);
|
|
196
|
+
Object.defineProperty(err, 'message', {
|
|
197
|
+
enumerable: true,
|
|
198
|
+
configurable: true,
|
|
199
|
+
value: msg,
|
|
200
|
+
writable: true
|
|
201
|
+
});
|
|
202
|
+
Object.defineProperty(err, 'name', {
|
|
203
|
+
enumerable: false,
|
|
204
|
+
configurable: true,
|
|
205
|
+
value: className,
|
|
206
|
+
writable: true
|
|
207
|
+
});
|
|
208
|
+
return err;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
inherits(HttpError, Error);
|
|
212
|
+
const desc = Object.getOwnPropertyDescriptor(HttpError, 'name');
|
|
213
|
+
desc.value = className;
|
|
214
|
+
Object.defineProperty(HttpError, 'name', desc);
|
|
215
|
+
Object.assign(HttpError.prototype, {
|
|
216
|
+
status: code,
|
|
217
|
+
statusCode: code,
|
|
218
|
+
expose: code < 500
|
|
219
|
+
}, properties);
|
|
220
|
+
return new HttpError(message);
|
|
221
|
+
};
|
|
222
|
+
export default {
|
|
223
|
+
createPrefetchClient,
|
|
224
|
+
createClient,
|
|
225
|
+
canPrefetch,
|
|
226
|
+
getInternal,
|
|
227
|
+
sanitizeKey,
|
|
228
|
+
processCache,
|
|
229
|
+
getCache,
|
|
230
|
+
modifyCache,
|
|
231
|
+
clearCache,
|
|
232
|
+
jsonSafeParse,
|
|
233
|
+
normalizeHttpResponse,
|
|
234
|
+
createError
|
|
235
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@middy/util",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.2",
|
|
4
4
|
"description": "🛵 The stylish Node.js middleware engine for AWS Lambda (util package)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
"exports": "./index.js",
|
|
14
14
|
"types": "index.d.ts",
|
|
15
15
|
"files": [
|
|
16
|
+
"index.js",
|
|
16
17
|
"index.d.ts",
|
|
17
18
|
"codes.js"
|
|
18
19
|
],
|
|
@@ -44,12 +45,12 @@
|
|
|
44
45
|
"url": "https://github.com/middyjs/middy/issues"
|
|
45
46
|
},
|
|
46
47
|
"devDependencies": {
|
|
47
|
-
"@middy/core": "^3.0.0-alpha.
|
|
48
|
+
"@middy/core": "^3.0.0-alpha.2",
|
|
48
49
|
"@types/aws-lambda": "^8.10.76",
|
|
49
50
|
"@types/node": "^17.0.0",
|
|
50
51
|
"aws-sdk": "^2.939.0",
|
|
51
52
|
"aws-xray-sdk": "^3.3.3"
|
|
52
53
|
},
|
|
53
54
|
"homepage": "https://github.com/middyjs/middy#readme",
|
|
54
|
-
"gitHead": "
|
|
55
|
+
"gitHead": "de30419273ecbff08f367f47c7e320ec981cf145"
|
|
55
56
|
}
|