@allthings/cloud-toolkit 5.1.0 → 5.2.0-beta.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.
@@ -0,0 +1,690 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var querystring = require('querystring');
6
+ var zlib = require('zlib');
7
+ var crypto = require('crypto');
8
+
9
+ function _interopNamespaceDefault(e) {
10
+ var n = Object.create(null);
11
+ if (e) {
12
+ Object.keys(e).forEach(function (k) {
13
+ if (k !== 'default') {
14
+ var d = Object.getOwnPropertyDescriptor(e, k);
15
+ Object.defineProperty(n, k, d.get ? d : {
16
+ enumerable: true,
17
+ get: function () { return e[k]; }
18
+ });
19
+ }
20
+ });
21
+ }
22
+ n.default = e;
23
+ return Object.freeze(n);
24
+ }
25
+
26
+ var querystring__namespace = /*#__PURE__*/_interopNamespaceDefault(querystring);
27
+ var zlib__namespace = /*#__PURE__*/_interopNamespaceDefault(zlib);
28
+ var crypto__namespace = /*#__PURE__*/_interopNamespaceDefault(crypto);
29
+
30
+ const defaultErrorHandler = (request, response, error) => {
31
+ const { requestId } = request.requestContext;
32
+ const errorName = error.name && typeof error.name === 'string'
33
+ ? error.name.replace(/([A-Z])([A-Z])([a-z])|([a-z])([A-Z])/g, '$1$4 $2$3$5')
34
+ : 'Error';
35
+ const errorMessage = error.message || '';
36
+ const statusCode = error.statusCode ? ` ${error.statusCode}` : '';
37
+ return error.name === 'ClientError' ||
38
+ (error.statusCode >= 400 && error.statusCode < 500)
39
+ ? response.respondTo({
40
+ html: `<html><body><strong>${errorName}${statusCode}</strong>: ${errorMessage}<br/>Request ID: ${requestId}</body></html>`,
41
+ json: { error: error.name, message: errorMessage, requestId },
42
+ }, error.statusCode || 400)
43
+ : response.respondTo({
44
+ html: `<html><body>An internal server error occurred.<br/>Request ID: ${requestId}</body></html>`,
45
+ json: {
46
+ error: 'Internal server error',
47
+ message: 'An internal server error occurred',
48
+ requestId,
49
+ },
50
+ }, 500);
51
+ };
52
+
53
+ class ClientError extends Error {
54
+ name = 'ClientError';
55
+ statusCode = 400;
56
+ constructor(message, name, statusCode = 400) {
57
+ super(String(message));
58
+ this.name = name || this.name;
59
+ this.statusCode = statusCode;
60
+ }
61
+ }
62
+
63
+ class ServerError extends Error {
64
+ name = 'ServerError';
65
+ statusCode = 500;
66
+ constructor(message, name, statusCode = 500) {
67
+ super(String(message));
68
+ this.name = name || this.name;
69
+ this.statusCode = statusCode;
70
+ }
71
+ }
72
+
73
+ const applyMiddleware = async (middlewareList, initialData, ...arguments_) => middlewareList.reduce(async (applied, middleware) => middleware(await applied, ...arguments_), Promise.resolve(initialData));
74
+
75
+ const decodeBase64 = (encoded) => {
76
+ const bodyBuffer = Buffer.from(encoded, 'base64');
77
+ return bodyBuffer.toString('utf8');
78
+ };
79
+ const decodeBase64Body = (request) => request.isBase64Encoded && typeof request.body === 'string'
80
+ ? {
81
+ ...request,
82
+ body: decodeBase64(request.body),
83
+ }
84
+ : request;
85
+
86
+ const parseCookiePair = (pair) => {
87
+ const indexOfEqualCharacter = pair.indexOf('=');
88
+ const looksLikeKeyValue = indexOfEqualCharacter !== -1;
89
+ const key = looksLikeKeyValue &&
90
+ pair.slice(0, Math.max(0, indexOfEqualCharacter)).trim();
91
+ const value = looksLikeKeyValue &&
92
+ pair.substr(indexOfEqualCharacter + 1, pair.length).trim();
93
+ const cleanValue = value && '"' === value[0] ? value.slice(1, -1) : value;
94
+ return key ? { [key]: cleanValue ? decodeURIComponent(cleanValue) : '' } : {};
95
+ };
96
+ const parseCookieHeader = (cookieHeader) => {
97
+ const pairs = cookieHeader.split(/; */);
98
+ return pairs.reduce((parsedCookies, pair) => ({ ...parsedCookies, ...parseCookiePair(pair) }), {});
99
+ };
100
+ const cookies = (request) => ({
101
+ ...request,
102
+ cookies: parseCookieHeader(request.headers?.cookie || ''),
103
+ });
104
+
105
+ const setHostname = (request) => ({
106
+ ...request,
107
+ hostname: request.headers?.host || undefined,
108
+ });
109
+
110
+ const parseJson = (stringified) => {
111
+ try {
112
+ return JSON.parse(stringified);
113
+ }
114
+ catch {
115
+ return {};
116
+ }
117
+ };
118
+ const parseJsonBody = (request) => {
119
+ const { headers, body } = request;
120
+ return headers['content-type']?.split(';').shift() === 'application/json' &&
121
+ typeof body === 'string'
122
+ ? { ...request, body: parseJson(body) }
123
+ : request;
124
+ };
125
+
126
+ const state = new Map([['invocationCount', 0]]);
127
+ const meta = (request) => {
128
+ const invocationCount = Number(state.get('invocationCount'));
129
+ return {
130
+ ...request,
131
+ meta: {
132
+ ...request.meta,
133
+ coldStart: !invocationCount,
134
+ invocationCount: state
135
+ .set('invocationCount', invocationCount + 1)
136
+ .get('invocationCount'),
137
+ },
138
+ };
139
+ };
140
+
141
+ const normalizeHeaders = ({ headers = {}, ...request }) => ({
142
+ ...request,
143
+ headers: Object.keys(headers).reduce((normalizedHeaders, key) => {
144
+ const normalizedHeaderKey = key.toLowerCase();
145
+ return request.provider === 'aws' &&
146
+ Reflect.has(normalizedHeaders, normalizedHeaderKey) &&
147
+ normalizedHeaderKey === 'content-type' &&
148
+ headers[key] === 'application/json'
149
+ ? normalizedHeaders
150
+ : {
151
+ ...normalizedHeaders,
152
+ [normalizedHeaderKey]: headers[key],
153
+ };
154
+ }, {}),
155
+ });
156
+
157
+ const normalizeProgrammingModel = (request) => ({
158
+ ...request,
159
+ method: request.httpMethod ??
160
+ request.requestContext?.http?.method ??
161
+ '',
162
+ path: request.path ??
163
+ request.requestContext?.http?.path ??
164
+ '',
165
+ provider: 'aws',
166
+ query: request.queryStringParameters || {},
167
+ source: request.requestContext ? 'api-gateway' : request.source,
168
+ });
169
+
170
+ const addTimestamp = (request) => ({
171
+ ...request,
172
+ timestamp: Date.now(),
173
+ });
174
+
175
+ const parseUrlEncodedBody = (request) => {
176
+ const { headers, body } = request;
177
+ return headers['content-type']?.split(';').shift() ===
178
+ 'application/x-www-form-urlencoded' && typeof body === 'string'
179
+ ? { ...request, body: querystring__namespace.parse(body) }
180
+ : request;
181
+ };
182
+
183
+ var makeRequest = async (event, context, options = {}) => applyMiddleware([
184
+ addTimestamp,
185
+ normalizeProgrammingModel,
186
+ normalizeHeaders,
187
+ decodeBase64Body,
188
+ cookies,
189
+ parseUrlEncodedBody,
190
+ parseJsonBody,
191
+ setHostname,
192
+ meta,
193
+ ...(options.requestMiddleware || []),
194
+ ], Object.freeze({
195
+ ...event,
196
+ context,
197
+ }), options);
198
+
199
+ const setHeader = (response, responseData, key, value) => {
200
+ Object.assign(responseData.headers, { [key]: value });
201
+ return response;
202
+ };
203
+
204
+ const makeResponseObject = (responseData, body = '', statusCode = 200, { headers = {}, ...options } = {}, contentType) => Object.freeze({
205
+ ...responseData,
206
+ body: body || '',
207
+ headers: {
208
+ ...responseData?.headers,
209
+ 'content-type': contentType,
210
+ ...headers,
211
+ },
212
+ statusCode,
213
+ ...options,
214
+ });
215
+
216
+ const html = (responseData, _, body, statusCode, options) => makeResponseObject(responseData, body, statusCode, options, 'text/html');
217
+
218
+ const stringifyIfNotStringifiedJson = (data) => {
219
+ const maybeTrimmedDataString = typeof data === 'string' ? data.trim() : data;
220
+ const isStringifiedJson = typeof data === 'string' &&
221
+ ['{', '"'].includes(maybeTrimmedDataString[0]) &&
222
+ ['}', '"'].includes(maybeTrimmedDataString.at(-1));
223
+ return isStringifiedJson ? maybeTrimmedDataString : JSON.stringify(data);
224
+ };
225
+ const json = (responseData, _, body, statusCode, options) => makeResponseObject(responseData, stringifyIfNotStringifiedJson(body), statusCode, options, 'application/json');
226
+
227
+ const COMPRESSIBLE = [
228
+ 'application/activity+json',
229
+ 'application/alto-costmap+json',
230
+ 'application/alto-costmapfilter+json',
231
+ 'application/alto-directory+json',
232
+ 'application/alto-endpointcost+json',
233
+ 'application/alto-endpointcostparams+json',
234
+ 'application/alto-endpointprop+json',
235
+ 'application/alto-endpointpropparams+json',
236
+ 'application/alto-error+json',
237
+ 'application/alto-networkmap+json',
238
+ 'application/alto-networkmapfilter+json',
239
+ 'application/atom+xml',
240
+ 'application/calendar+json',
241
+ 'application/coap-group+json',
242
+ 'application/csvm+json',
243
+ 'application/dart',
244
+ 'application/dicom+json',
245
+ 'application/ecmascript',
246
+ 'application/fhir+json',
247
+ 'application/fido.trusted-apps+json',
248
+ 'application/geo+json',
249
+ 'application/javascript',
250
+ 'application/jf2feed+json',
251
+ 'application/jose+json',
252
+ 'application/jrd+json',
253
+ 'application/json',
254
+ 'application/json-patch+json',
255
+ 'application/jsonml+json',
256
+ 'application/jwk+json',
257
+ 'application/jwk-set+json',
258
+ 'application/ld+json',
259
+ 'application/manifest+json',
260
+ 'application/merge-patch+json',
261
+ 'application/mud+json',
262
+ 'application/postscript',
263
+ 'application/ppsp-tracker+json',
264
+ 'application/problem+json',
265
+ 'application/raml+yaml',
266
+ 'application/rdap+json',
267
+ 'application/rdf+xml',
268
+ 'application/reputon+json',
269
+ 'application/rss+xml',
270
+ 'application/rtf',
271
+ 'application/scim+json',
272
+ 'application/soap+xml',
273
+ 'application/tar',
274
+ 'application/vcard+json',
275
+ 'application/vnd.amadeus+json',
276
+ 'application/vnd.api+json',
277
+ 'application/vnd.apothekende.reservation+json',
278
+ 'application/vnd.avalon+json',
279
+ 'application/vnd.bbf.usp.msg+json',
280
+ 'application/vnd.bekitzur-stech+json',
281
+ 'application/vnd.capasystems-pg+json',
282
+ 'application/vnd.collection+json',
283
+ 'application/vnd.collection.doc+json',
284
+ 'application/vnd.collection.next+json',
285
+ 'application/vnd.coreos.ignition+json',
286
+ 'application/vnd.dart',
287
+ 'application/vnd.datapackage+json',
288
+ 'application/vnd.dataresource+json',
289
+ 'application/vnd.document+json',
290
+ 'application/vnd.drive+json',
291
+ 'application/vnd.geo+json',
292
+ 'application/vnd.google-earth.kml+xml',
293
+ 'application/vnd.hal+json',
294
+ 'application/vnd.hc+json',
295
+ 'application/vnd.heroku+json',
296
+ 'application/vnd.hyper+json',
297
+ 'application/vnd.hyper-item+json',
298
+ 'application/vnd.hyperdrive+json',
299
+ 'application/vnd.ims.lis.v2.result+json',
300
+ 'application/vnd.ims.lti.v2.toolconsumerprofile+json',
301
+ 'application/vnd.ims.lti.v2.toolproxy+json',
302
+ 'application/vnd.ims.lti.v2.toolproxy.id+json',
303
+ 'application/vnd.ims.lti.v2.toolsettings+json',
304
+ 'application/vnd.ims.lti.v2.toolsettings.simple+json',
305
+ 'application/vnd.las.las+json',
306
+ 'application/vnd.mason+json',
307
+ 'application/vnd.micro+json',
308
+ 'application/vnd.miele+json',
309
+ 'application/vnd.mozilla.xul+xml',
310
+ 'application/vnd.ms-fontobject',
311
+ 'application/vnd.ms-opentype',
312
+ 'application/vnd.nearst.inv+json',
313
+ 'application/vnd.oftn.l10n+json',
314
+ 'application/vnd.oma.lwm2m+json',
315
+ 'application/vnd.oracle.resource+json',
316
+ 'application/vnd.pagerduty+json',
317
+ 'application/vnd.restful+json',
318
+ 'application/vnd.siren+json',
319
+ 'application/vnd.sun.wadl+xml',
320
+ 'application/vnd.tableschema+json',
321
+ 'application/vnd.vel+json',
322
+ 'application/vnd.xacml+json',
323
+ 'application/voucher-cms+json',
324
+ 'application/wasm',
325
+ 'application/webpush-options+json',
326
+ 'application/x-httpd-php',
327
+ 'application/x-javascript',
328
+ 'application/x-ns-proxy-autoconfig',
329
+ 'application/x-sh',
330
+ 'application/x-tar',
331
+ 'application/x-virtualbox-hdd',
332
+ 'application/x-virtualbox-ova',
333
+ 'application/x-virtualbox-ovf',
334
+ 'application/x-virtualbox-vbox',
335
+ 'application/x-virtualbox-vdi',
336
+ 'application/x-virtualbox-vhd',
337
+ 'application/x-virtualbox-vmdk',
338
+ 'application/x-web-app-manifest+json',
339
+ 'application/x-www-form-urlencoded',
340
+ 'application/xhtml+xml',
341
+ 'application/xml',
342
+ 'application/xml-dtd',
343
+ 'application/xop+xml',
344
+ 'application/yang-data+json',
345
+ 'application/yang-patch+json',
346
+ 'font/otf',
347
+ 'image/bmp',
348
+ 'image/svg+xml',
349
+ 'image/vnd.adobe.photoshop',
350
+ 'image/x-icon',
351
+ 'image/x-ms-bmp',
352
+ 'message/imdn+xml',
353
+ 'message/rfc822',
354
+ 'model/gltf+json',
355
+ 'model/gltf-binary',
356
+ 'model/x3d+xml',
357
+ 'text/cache-manifest',
358
+ 'text/calender',
359
+ 'text/cmd',
360
+ 'text/css',
361
+ 'text/csv',
362
+ 'text/html',
363
+ 'text/javascript',
364
+ 'text/jsx',
365
+ 'text/markdown',
366
+ 'text/n3',
367
+ 'text/plain',
368
+ 'text/richtext',
369
+ 'text/rtf',
370
+ 'text/tab-separated-values',
371
+ 'text/uri-list',
372
+ 'text/vcard',
373
+ 'text/vtt',
374
+ 'text/x-gwt-rpc',
375
+ 'text/x-jquery-tmpl',
376
+ 'text/x-markdown',
377
+ 'text/x-org',
378
+ 'text/x-processing',
379
+ 'text/x-suse-ymp',
380
+ 'text/xml',
381
+ 'x-shader/x-fragment',
382
+ 'x-shader/x-vertex',
383
+ ];
384
+ const ZLIB_DEFAULT_OPTIONS = {
385
+ level: 5,
386
+ };
387
+ const acceptEncoding = (accepts, encodings) => accepts.find((accept) => encodings.some((encoding) => accept === encoding));
388
+ const getBestAcceptEncoding = (request) => {
389
+ const { headers } = request;
390
+ const acceptEncodings = (headers['accept-encoding'] || '').split(/, */);
391
+ const encoding = acceptEncoding(acceptEncodings, [
392
+ 'gzip',
393
+ 'deflate',
394
+ 'identity',
395
+ ]);
396
+ return encoding === 'deflate' && acceptEncoding(acceptEncodings, ['gzip'])
397
+ ? acceptEncoding(acceptEncodings, ['gzip', 'identity'])
398
+ : encoding;
399
+ };
400
+ const compressResponse = (response, encoding) => {
401
+ const { body, headers, ...rest } = response;
402
+ const compressed = encoding === 'gzip'
403
+ ? zlib__namespace.gzipSync(body, ZLIB_DEFAULT_OPTIONS)
404
+ : zlib__namespace.deflateSync(body, ZLIB_DEFAULT_OPTIONS);
405
+ return {
406
+ ...rest,
407
+ body: compressed.toString('base64'),
408
+ headers: {
409
+ ...headers,
410
+ 'content-encoding': encoding,
411
+ 'content-length': compressed.byteLength,
412
+ },
413
+ isBase64Encoded: true,
414
+ };
415
+ };
416
+ const compress = (response, request) => {
417
+ const { body, headers } = response;
418
+ const encoding = getBestAcceptEncoding(request);
419
+ const weShouldCompress = COMPRESSIBLE.includes(headers['content-type']) &&
420
+ encoding !== undefined &&
421
+ encoding !== 'identity' &&
422
+ body &&
423
+ body.length > 256;
424
+ return weShouldCompress && encoding
425
+ ? compressResponse(response, encoding)
426
+ : response;
427
+ };
428
+
429
+ const contentLengthHeader = (response) => {
430
+ const { body, headers, ...rest } = response;
431
+ return {
432
+ ...rest,
433
+ body,
434
+ headers: {
435
+ ...headers,
436
+ 'content-length': headers['content-length'] || Buffer.byteLength(body),
437
+ },
438
+ };
439
+ };
440
+
441
+ const DEFAULT_POLICIES = {
442
+ 'default-src': "'self' https: 'unsafe-inline'",
443
+ 'img-src': '* data: blob:',
444
+ };
445
+ const cspHeaders = (response, _, options) => {
446
+ const { headers = {}, ...rest } = response;
447
+ const cspPolicies = {
448
+ ...DEFAULT_POLICIES,
449
+ ...options.cspPolicies,
450
+ };
451
+ const cspPolicy = Object.keys(cspPolicies)
452
+ .map((policy) => `${policy} ${cspPolicies[policy]}`)
453
+ .join(';');
454
+ return {
455
+ ...rest,
456
+ headers: {
457
+ ...headers,
458
+ 'content-security-policy': cspPolicy,
459
+ 'referrer-policy': 'strict-origin-when-cross-origin',
460
+ 'x-content-type-options': 'nosniff',
461
+ 'x-frame-options': {
462
+ "'none'": 'DENY',
463
+ "'self'": 'SAMEORIGIN',
464
+ }[cspPolicies['frame-ancestors']] || undefined,
465
+ 'x-xss-protection': '1; mode=block',
466
+ },
467
+ };
468
+ };
469
+
470
+ const enforcedHeaders = (response, _, options) => ({
471
+ ...response,
472
+ headers: {
473
+ ...response.headers,
474
+ ...(options.headers ?? {}),
475
+ },
476
+ });
477
+
478
+ const EMPTY_ENTITY_TAG = '"0-2jmj7l5rSw0yVb/vlWAYkK/YBwk"';
479
+ const entitytag = (entity) => {
480
+ const hash = crypto__namespace
481
+ .createHash('sha1')
482
+ .update(entity, 'utf8')
483
+ .digest('base64')
484
+ .slice(0, 27);
485
+ const length = typeof entity === 'string'
486
+ ? Buffer.byteLength(entity, 'utf8')
487
+ : entity.length;
488
+ return `"${length.toString(16)}-${hash}"`;
489
+ };
490
+ const etag = (entity) => entity.length === 0 ? EMPTY_ENTITY_TAG : entitytag(entity);
491
+ const etagHeader = (response) => ({
492
+ ...response,
493
+ headers: {
494
+ ...response.headers,
495
+ etag: etag(response.body),
496
+ },
497
+ });
498
+
499
+ const aws = (request) => {
500
+ const { headers: requestHeaders, requestContext, context } = request;
501
+ return {
502
+ apig: {
503
+ requestId: requestContext.requestId,
504
+ resourcePath: requestContext.resourcePath ??
505
+ '',
506
+ },
507
+ awsAccountId: requestContext.accountId,
508
+ cloudfront: {
509
+ country: requestHeaders['cloudfront-viewer-country'],
510
+ desktop: requestHeaders['cloudfront-is-desktop-viewer'] === 'true',
511
+ mobile: requestHeaders['cloudfront-is-mobile-viewer'] === 'true',
512
+ smarttv: requestHeaders['cloudfront-is-smarttv-viewer'] === 'true',
513
+ tablet: requestHeaders['cloudfront-is-tablet-viewer'] === 'true',
514
+ },
515
+ deploymentStage: requestContext.stage,
516
+ lambda: {
517
+ functionName: context.functionName,
518
+ memoryLimit: Number(context.memoryLimitInMB),
519
+ remainingExecutionTime: context.getRemainingTimeInMillis?.(),
520
+ requestId: context.awsRequestId,
521
+ },
522
+ sourceIp: requestContext.identity
523
+ ?.sourceIp ??
524
+ requestContext?.http?.sourceIp,
525
+ };
526
+ };
527
+ const logger = (request, response) => {
528
+ const { source, headers: requestHeaders, meta, requestContext, provider, hostname, body, timestamp, path, query, } = request;
529
+ const error = response.statusCode >= 500 && response.statusCode < 600;
530
+ const logEntry = {
531
+ dateTime: new Date().toISOString(),
532
+ eventSource: source,
533
+ hostname,
534
+ httpMethod: requestContext.httpMethod,
535
+ httpProtocol: requestHeaders.via?.split(' ')[0],
536
+ httpReferrer: requestHeaders.referer,
537
+ httpUserAgent: requestHeaders['user-agent'],
538
+ requestContentLength: (body && body.length > 0) || 0,
539
+ requestTime: Date.now() - timestamp,
540
+ requestUri: path +
541
+ (query && Object.keys(query).length > 0
542
+ ? `?${querystring__namespace.stringify(query)}`
543
+ : ''),
544
+ responseContentLength: response.headers['content-length'],
545
+ statusCode: response.statusCode,
546
+ xForwardedHost: requestHeaders['x-forwarded-host'],
547
+ ...meta,
548
+ ...(provider === 'aws' && aws(request)),
549
+ };
550
+ return ((error ? process.stderr : process.stdout).write(JSON.stringify(logEntry)) &&
551
+ logEntry);
552
+ };
553
+ const logResponse = (response, request, options) => (options.enableLogger &&
554
+ (typeof options.logger === 'function'
555
+ ? options.logger(request, response)
556
+ : logger(request, response)) &&
557
+ response) ||
558
+ response;
559
+
560
+ const redirect = (responseData, _, location, statusCode = 302, options = {}) => makeResponseObject(responseData, '', statusCode, {
561
+ ...options,
562
+ headers: {
563
+ ...options.headers,
564
+ location,
565
+ },
566
+ });
567
+
568
+ const getBestMatchedFormat = (formats, accept) => {
569
+ const fallback = formats.default || 'html';
570
+ const acceptFormats = accept &&
571
+ typeof accept === 'string' &&
572
+ accept
573
+ .split(';')[0]
574
+ .split(',')
575
+ .map((type) => {
576
+ const mimeParts = type.split('/');
577
+ return mimeParts.at(-1);
578
+ });
579
+ return ((acceptFormats &&
580
+ acceptFormats.find((type) => Reflect.has(formats, type))) ||
581
+ fallback);
582
+ };
583
+ const getBestMatchedResponseHelper = (format) => {
584
+ switch (format) {
585
+ case 'html':
586
+ return html;
587
+ case 'json':
588
+ return json;
589
+ }
590
+ throw new TypeError(`"${format}" is not a valid Alagarr respondTo() format.`);
591
+ };
592
+ const getBestMatchedFormatBody = (formats, format) => {
593
+ switch (format) {
594
+ case 'html':
595
+ return formats.html;
596
+ case 'json':
597
+ return formats.json;
598
+ }
599
+ throw new TypeError(`"${format}" is not a valid Alagarr respondTo() format.`);
600
+ };
601
+ const respondTo = (responseData, request, formats, statusCode, options) => {
602
+ const { headers: { accept }, } = request;
603
+ const format = getBestMatchedFormat(formats, accept);
604
+ const responseHelper = getBestMatchedResponseHelper(format);
605
+ const body = getBestMatchedFormatBody(formats, format);
606
+ return responseHelper(responseData, request, body, statusCode, options);
607
+ };
608
+
609
+ const text = (responseData, _, body, statusCode, options) => makeResponseObject(responseData, body, statusCode, options, 'text/plain');
610
+
611
+ const middlewareMap = {
612
+ enableCompression: compress,
613
+ enableContentLength: contentLengthHeader,
614
+ enableCspHeaders: cspHeaders,
615
+ enableETagHeader: etagHeader,
616
+ enableEnforcedHeaders: enforcedHeaders,
617
+ };
618
+ var makeResponse = async (request, callback, options) => {
619
+ const responseData = makeResponseObject();
620
+ const responseWithHelpers = [
621
+ text,
622
+ html,
623
+ json,
624
+ redirect,
625
+ respondTo,
626
+ ].reduce((methods, method) => ({
627
+ ...methods,
628
+ [method.name]: async (...arguments_) => callback(null, await applyMiddleware(Object.keys(middlewareMap).reduce((middlewareList, middleware) => options[middleware]
629
+ ? [...middlewareList, middlewareMap[middleware]]
630
+ : middlewareList, [
631
+ ...(options.responseMiddleware || []),
632
+ ...(options.enableLogger ? [logResponse] : []),
633
+ ]), method(responseData, request, ...arguments_), request, options)),
634
+ }), { raw: callback });
635
+ const response = [setHeader].reduce((responseMethods, method) => ({
636
+ ...responseMethods,
637
+ [method.name]: (...arguments_) => method(response, responseData, ...arguments_),
638
+ }), responseWithHelpers);
639
+ return response;
640
+ };
641
+
642
+ const DEFAULT_OPTIONS = {
643
+ cspPolicies: [],
644
+ enableCompression: true,
645
+ enableContentLength: true,
646
+ enableCspHeaders: true,
647
+ enableETagHeader: true,
648
+ enableEnforcedHeaders: true,
649
+ enableLogger: true,
650
+ enableStrictTransportSecurity: true,
651
+ errorHandler: defaultErrorHandler,
652
+ requestMiddleware: [],
653
+ responseMiddleware: [],
654
+ };
655
+ const noopHandler = (_, _1) => {
656
+ throw new ServerError('Misconfiguration in Alagarr setup. No handler function was provided.');
657
+ };
658
+ const alagarr = (handler = noopHandler, options = DEFAULT_OPTIONS) => (event, context) => new Promise((resolve, reject) => {
659
+ const settleResponse = (error, result) => error
660
+ ? reject(error instanceof Error ? error : new Error(String(error)))
661
+ : resolve(result);
662
+ const runHandler = async () => {
663
+ const mergedOptions = {
664
+ ...DEFAULT_OPTIONS,
665
+ ...options,
666
+ };
667
+ const request = await makeRequest(event, context, mergedOptions);
668
+ const response = await makeResponse(request, settleResponse, mergedOptions);
669
+ try {
670
+ return await handler(request, response, context);
671
+ }
672
+ catch (handlerError) {
673
+ const errorHandler = typeof mergedOptions.errorHandler === 'function'
674
+ ? mergedOptions.errorHandler
675
+ : defaultErrorHandler;
676
+ try {
677
+ return await errorHandler(request, response, handlerError);
678
+ }
679
+ catch (error) {
680
+ console.error('There was an error in the error handler provided to Alagaar', error, errorHandler.toString());
681
+ return defaultErrorHandler(request, response, error);
682
+ }
683
+ }
684
+ };
685
+ runHandler().then(resolve, reject);
686
+ });
687
+
688
+ exports.ClientError = ClientError;
689
+ exports.ServerError = ServerError;
690
+ exports.default = alagarr;