@salesforce/mrt-utilities 0.1.0 → 0.1.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/dist/cjs/data-store/index.d.ts +56 -0
- package/dist/cjs/data-store/index.js +131 -0
- package/dist/cjs/data-store/index.js.map +1 -0
- package/dist/cjs/index.js +22 -4
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/metrics/index.js +17 -1
- package/dist/cjs/metrics/index.js.map +1 -1
- package/dist/cjs/metrics/metrics-sender.js +14 -9
- package/dist/cjs/metrics/metrics-sender.js.map +1 -1
- package/dist/cjs/middleware/data-store.d.ts +3 -54
- package/dist/cjs/middleware/data-store.js +19 -116
- package/dist/cjs/middleware/data-store.js.map +1 -1
- package/dist/cjs/middleware/express.d.ts +2 -0
- package/dist/cjs/middleware/express.js +23 -0
- package/dist/cjs/middleware/express.js.map +1 -0
- package/dist/cjs/middleware/index.d.ts +2 -3
- package/dist/cjs/middleware/index.js +18 -2
- package/dist/cjs/middleware/index.js.map +1 -1
- package/dist/cjs/middleware/middleware.js +94 -44
- package/dist/cjs/middleware/middleware.js.map +1 -1
- package/dist/cjs/streaming/create-lambda-adapter.js +66 -76
- package/dist/cjs/streaming/create-lambda-adapter.js.map +1 -1
- package/dist/cjs/streaming/index.js +7 -1
- package/dist/cjs/streaming/index.js.map +1 -1
- package/dist/cjs/utils/configure-proxying.js +16 -10
- package/dist/cjs/utils/configure-proxying.js.map +1 -1
- package/dist/cjs/utils/ssr-proxying.js +43 -32
- package/dist/cjs/utils/ssr-proxying.js.map +1 -1
- package/dist/cjs/utils/utils.js +7 -2
- package/dist/cjs/utils/utils.js.map +1 -1
- package/dist/esm/data-store/index.d.ts +56 -0
- package/dist/esm/data-store/index.js +124 -0
- package/dist/esm/data-store/index.js.map +1 -0
- package/dist/esm/middleware/data-store.d.ts +3 -54
- package/dist/esm/middleware/data-store.js +3 -116
- package/dist/esm/middleware/data-store.js.map +1 -1
- package/dist/esm/middleware/express.d.ts +2 -0
- package/dist/esm/middleware/express.js +8 -0
- package/dist/esm/middleware/express.js.map +1 -0
- package/dist/esm/middleware/index.d.ts +2 -3
- package/dist/esm/middleware/index.js +2 -3
- package/dist/esm/middleware/index.js.map +1 -1
- package/dist/esm/middleware/middleware.js +15 -10
- package/dist/esm/middleware/middleware.js.map +1 -1
- package/dist/esm/streaming/create-lambda-adapter.js +39 -57
- package/dist/esm/streaming/create-lambda-adapter.js.map +1 -1
- package/package.json +26 -3
|
@@ -1,8 +1,47 @@
|
|
|
1
|
+
"use strict";
|
|
1
2
|
/*
|
|
2
3
|
* Copyright (c) 2025, Salesforce, Inc.
|
|
3
4
|
* SPDX-License-Identifier: Apache-2
|
|
4
5
|
* For full license text, see the license.txt file in the repo root or http://www.apache.org/licenses/LICENSE-2.0
|
|
5
6
|
*/
|
|
7
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
8
|
+
if (k2 === undefined) k2 = k;
|
|
9
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
10
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
11
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
12
|
+
}
|
|
13
|
+
Object.defineProperty(o, k2, desc);
|
|
14
|
+
}) : (function(o, m, k, k2) {
|
|
15
|
+
if (k2 === undefined) k2 = k;
|
|
16
|
+
o[k2] = m[k];
|
|
17
|
+
}));
|
|
18
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
19
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
20
|
+
}) : function(o, v) {
|
|
21
|
+
o["default"] = v;
|
|
22
|
+
});
|
|
23
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
24
|
+
var ownKeys = function(o) {
|
|
25
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
26
|
+
var ar = [];
|
|
27
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
28
|
+
return ar;
|
|
29
|
+
};
|
|
30
|
+
return ownKeys(o);
|
|
31
|
+
};
|
|
32
|
+
return function (mod) {
|
|
33
|
+
if (mod && mod.__esModule) return mod;
|
|
34
|
+
var result = {};
|
|
35
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
36
|
+
__setModuleDefault(result, mod);
|
|
37
|
+
return result;
|
|
38
|
+
};
|
|
39
|
+
})();
|
|
40
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
41
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
42
|
+
};
|
|
43
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
44
|
+
exports.createMRTCleanUpMiddleware = exports.createMRTCommonMiddleware = exports.createMRTStaticAssetServingMiddleware = exports.setLocalAssetHeaders = exports.createMRTProxyMiddlewares = exports.createMRTRequestProcessorMiddleware = exports.X_MOBIFY_REQUEST_PROCESSOR_LOCAL = exports.X_MOBIFY_QUERYSTRING = exports.X_MOBIFY_REQUEST_CLASS = void 0;
|
|
6
45
|
/**
|
|
7
46
|
* @fileoverview MRT (Managed Runtime) Middleware for Express.js applications.
|
|
8
47
|
*
|
|
@@ -13,13 +52,13 @@
|
|
|
13
52
|
* @author Salesforce Commerce Cloud
|
|
14
53
|
* @version 0.0.1
|
|
15
54
|
*/
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
55
|
+
const ssr_proxying_js_1 = require("../utils/ssr-proxying.js");
|
|
56
|
+
const configure_proxying_js_1 = require("../utils/configure-proxying.js");
|
|
57
|
+
const express_1 = __importDefault(require("express"));
|
|
58
|
+
const fs_1 = __importDefault(require("fs"));
|
|
59
|
+
const path_1 = __importDefault(require("path"));
|
|
60
|
+
const mime_types_1 = __importDefault(require("mime-types"));
|
|
61
|
+
const qs_1 = __importDefault(require("qs"));
|
|
23
62
|
const MOBIFY_PATH = '/mobify';
|
|
24
63
|
const PROXY_PATH_BASE = `${MOBIFY_PATH}/proxy`;
|
|
25
64
|
const CACHING_PATH_BASE = `${MOBIFY_PATH}/caching`;
|
|
@@ -33,9 +72,9 @@ const X_HEADERS_TO_REMOVE_ORIGIN = [
|
|
|
33
72
|
'x-mobify-access-key',
|
|
34
73
|
'x-sfdc-access-control',
|
|
35
74
|
];
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
75
|
+
exports.X_MOBIFY_REQUEST_CLASS = 'x-mobify-request-class';
|
|
76
|
+
exports.X_MOBIFY_QUERYSTRING = 'x-mobify-querystring';
|
|
77
|
+
exports.X_MOBIFY_REQUEST_PROCESSOR_LOCAL = 'x-mobify-rp-local';
|
|
39
78
|
const CONTENT_TYPE = 'content-type';
|
|
40
79
|
const NO_CACHE = 'max-age=0, nocache, nostore, must-revalidate';
|
|
41
80
|
/**
|
|
@@ -56,8 +95,8 @@ const _isBundleOrProxyPath = (url) => {
|
|
|
56
95
|
* @private
|
|
57
96
|
*/
|
|
58
97
|
const _getRequestProcessor = async (requestProcessorPath) => {
|
|
59
|
-
if (requestProcessorPath &&
|
|
60
|
-
const module = await
|
|
98
|
+
if (requestProcessorPath && fs_1.default.existsSync(requestProcessorPath)) {
|
|
99
|
+
const module = await Promise.resolve(`${requestProcessorPath}`).then(s => __importStar(require(s)));
|
|
61
100
|
return module;
|
|
62
101
|
}
|
|
63
102
|
return null;
|
|
@@ -94,7 +133,7 @@ const getRequestProcessorParameters = () => {
|
|
|
94
133
|
const updatePathAndQueryString = (req, updatedPath, updatedQuerystring) => {
|
|
95
134
|
let newQuery = {};
|
|
96
135
|
if (updatedQuerystring) {
|
|
97
|
-
newQuery =
|
|
136
|
+
newQuery = qs_1.default.parse(updatedQuerystring);
|
|
98
137
|
req.originalUrl = `${updatedPath}?${updatedQuerystring}`;
|
|
99
138
|
}
|
|
100
139
|
else {
|
|
@@ -125,7 +164,7 @@ const cleanUpHeaders = (req, cleanupLocalRequestProcessorHeader = false) => {
|
|
|
125
164
|
// we don't want to remove the X_MOBIFY_REQUEST_PROCESSOR_LOCAL header
|
|
126
165
|
// because we need to not overwrite it in the cleanup middleware
|
|
127
166
|
if (cleanupLocalRequestProcessorHeader) {
|
|
128
|
-
delete req.headers[X_MOBIFY_REQUEST_PROCESSOR_LOCAL];
|
|
167
|
+
delete req.headers[exports.X_MOBIFY_REQUEST_PROCESSOR_LOCAL];
|
|
129
168
|
}
|
|
130
169
|
X_HEADERS_TO_REMOVE_ORIGIN.forEach((key) => {
|
|
131
170
|
delete req.headers[key];
|
|
@@ -152,11 +191,11 @@ const getMobifyQueryString = (req, originalQuerystring) => {
|
|
|
152
191
|
// querystring. This header is used in production, not in local dev,
|
|
153
192
|
// but we always handle it here to allow for testing.
|
|
154
193
|
let updatedQuerystring = originalQuerystring;
|
|
155
|
-
const xQueryString = req.headers[X_MOBIFY_QUERYSTRING];
|
|
194
|
+
const xQueryString = req.headers[exports.X_MOBIFY_QUERYSTRING];
|
|
156
195
|
if (xQueryString && xQueryString !== '') {
|
|
157
196
|
updatedQuerystring = xQueryString;
|
|
158
197
|
}
|
|
159
|
-
delete req.headers[X_MOBIFY_QUERYSTRING];
|
|
198
|
+
delete req.headers[exports.X_MOBIFY_QUERYSTRING];
|
|
160
199
|
return updatedQuerystring;
|
|
161
200
|
};
|
|
162
201
|
/**
|
|
@@ -183,7 +222,7 @@ const getMobifyQueryString = (req, originalQuerystring) => {
|
|
|
183
222
|
* app.use(middleware);
|
|
184
223
|
* ```
|
|
185
224
|
*/
|
|
186
|
-
|
|
225
|
+
const createMRTRequestProcessorMiddleware = (requestProcessorPath, proxyConfigs) => {
|
|
187
226
|
const processIncomingRequest = async (req, res) => {
|
|
188
227
|
// If the request is for a proxy or bundle path, do nothing
|
|
189
228
|
if (_isBundleOrProxyPath(req.originalUrl)) {
|
|
@@ -201,14 +240,14 @@ export const createMRTRequestProcessorMiddleware = (requestProcessorPath, proxyC
|
|
|
201
240
|
// runs only in the local development server, we intentionally do
|
|
202
241
|
// not swallow errors - we want them to happen and show up on the
|
|
203
242
|
// console because that's how developers can test the processor.
|
|
204
|
-
const headers = new Headers(req.headers, 'http');
|
|
243
|
+
const headers = new ssr_proxying_js_1.Headers(req.headers, 'http');
|
|
205
244
|
const { appHostname, deployTarget, environment } = getRequestProcessorParameters();
|
|
206
245
|
const processed = requestProcessor.processRequest({
|
|
207
246
|
headers,
|
|
208
247
|
path: req.path,
|
|
209
248
|
querystring: updatedQuerystring,
|
|
210
|
-
getRequestClass: () => headers.getHeader(X_MOBIFY_REQUEST_CLASS),
|
|
211
|
-
setRequestClass: (value) => headers.setHeader(X_MOBIFY_REQUEST_CLASS, value),
|
|
249
|
+
getRequestClass: () => headers.getHeader(exports.X_MOBIFY_REQUEST_CLASS),
|
|
250
|
+
setRequestClass: (value) => headers.setHeader(exports.X_MOBIFY_REQUEST_CLASS, value),
|
|
212
251
|
// This matches the set of parameters passed in the
|
|
213
252
|
// Lambda@Edge context.
|
|
214
253
|
parameters: {
|
|
@@ -236,10 +275,10 @@ export const createMRTRequestProcessorMiddleware = (requestProcessorPath, proxyC
|
|
|
236
275
|
// Get the request class and store it for general use. We
|
|
237
276
|
// must do this AFTER the request-processor, because that's
|
|
238
277
|
// what may set the request class.
|
|
239
|
-
res.locals.requestClass = req.headers[X_MOBIFY_REQUEST_CLASS];
|
|
240
|
-
req.headers[X_MOBIFY_REQUEST_PROCESSOR_LOCAL] = 'true'; // Mark the request as processed by the request processor
|
|
278
|
+
res.locals.requestClass = req.headers[exports.X_MOBIFY_REQUEST_CLASS];
|
|
279
|
+
req.headers[exports.X_MOBIFY_REQUEST_PROCESSOR_LOCAL] = 'true'; // Mark the request as processed by the request processor
|
|
241
280
|
};
|
|
242
|
-
const ssrRequestProcessorMiddleware =
|
|
281
|
+
const ssrRequestProcessorMiddleware = (req, res, next) => {
|
|
243
282
|
// If the path is /, we enforce that the only methods
|
|
244
283
|
// allowed are GET, HEAD or OPTIONS. This is a restriction
|
|
245
284
|
// imposed by API Gateway: we enforce it here so that the
|
|
@@ -248,18 +287,24 @@ export const createMRTRequestProcessorMiddleware = (requestProcessorPath, proxyC
|
|
|
248
287
|
res.sendStatus(405);
|
|
249
288
|
return;
|
|
250
289
|
}
|
|
251
|
-
// Apply custom query parameter parsing
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
290
|
+
// Apply custom query parameter parsing and forward errors via next()
|
|
291
|
+
processIncomingRequest(req, res)
|
|
292
|
+
.then(() => {
|
|
293
|
+
// Strip out API Gateway headers from the incoming request. We
|
|
294
|
+
// do that now so that the rest of the code don't have to deal
|
|
295
|
+
// with these headers, which can be large and may be accidentally
|
|
296
|
+
// forwarded to other servers.
|
|
297
|
+
cleanUpHeaders(req, false);
|
|
298
|
+
// Hand off to the next middleware
|
|
299
|
+
next();
|
|
300
|
+
})
|
|
301
|
+
.catch((err) => {
|
|
302
|
+
next(err);
|
|
303
|
+
});
|
|
260
304
|
};
|
|
261
305
|
return ssrRequestProcessorMiddleware;
|
|
262
306
|
};
|
|
307
|
+
exports.createMRTRequestProcessorMiddleware = createMRTRequestProcessorMiddleware;
|
|
263
308
|
/**
|
|
264
309
|
* Creates proxy middleware functions for the specified proxy configurations.
|
|
265
310
|
*
|
|
@@ -286,12 +331,12 @@ export const createMRTRequestProcessorMiddleware = (requestProcessorPath, proxyC
|
|
|
286
331
|
* });
|
|
287
332
|
* ```
|
|
288
333
|
*/
|
|
289
|
-
|
|
334
|
+
const createMRTProxyMiddlewares = (proxyConfigs, appProtocol = 'http', includeCaching = false, createProxyFn) => {
|
|
290
335
|
if (!proxyConfigs) {
|
|
291
336
|
return [];
|
|
292
337
|
}
|
|
293
338
|
const { appHostname } = getRequestProcessorParameters();
|
|
294
|
-
const proxies = configureProxying(proxyConfigs, appHostname, appProtocol, createProxyFn);
|
|
339
|
+
const proxies = (0, configure_proxying_js_1.configureProxying)(proxyConfigs, appHostname, appProtocol, createProxyFn);
|
|
295
340
|
const middlewares = [];
|
|
296
341
|
proxies.forEach((proxy) => {
|
|
297
342
|
const proxyPath = `${PROXY_PATH_BASE}/${proxy.path}`;
|
|
@@ -303,6 +348,7 @@ export const createMRTProxyMiddlewares = (proxyConfigs, appProtocol = 'http', in
|
|
|
303
348
|
});
|
|
304
349
|
return middlewares;
|
|
305
350
|
};
|
|
351
|
+
exports.createMRTProxyMiddlewares = createMRTProxyMiddlewares;
|
|
306
352
|
/**
|
|
307
353
|
* Sets appropriate HTTP headers for local asset files.
|
|
308
354
|
*
|
|
@@ -320,14 +366,14 @@ export const createMRTProxyMiddlewares = (proxyConfigs, appProtocol = 'http', in
|
|
|
320
366
|
* }));
|
|
321
367
|
* ```
|
|
322
368
|
*/
|
|
323
|
-
|
|
324
|
-
const base =
|
|
325
|
-
const contentType =
|
|
369
|
+
const setLocalAssetHeaders = (res, assetPath) => {
|
|
370
|
+
const base = path_1.default.basename(assetPath);
|
|
371
|
+
const contentType = mime_types_1.default.lookup(base) || 'application/octet-stream';
|
|
326
372
|
res.set(CONTENT_TYPE, contentType);
|
|
327
373
|
// Stat the file and return the last-modified Date
|
|
328
374
|
// in RFC1123 format. Also use that value as the ETag
|
|
329
375
|
// and Last-Modified
|
|
330
|
-
const mtime =
|
|
376
|
+
const mtime = fs_1.default.statSync(assetPath).mtime;
|
|
331
377
|
const mtimeRFC1123 = mtime.toUTCString();
|
|
332
378
|
res.set('date', mtimeRFC1123);
|
|
333
379
|
res.set('last-modified', mtimeRFC1123);
|
|
@@ -335,6 +381,7 @@ export const setLocalAssetHeaders = (res, assetPath) => {
|
|
|
335
381
|
// We don't cache local bundle assets
|
|
336
382
|
res.set('cache-control', NO_CACHE);
|
|
337
383
|
};
|
|
384
|
+
exports.setLocalAssetHeaders = setLocalAssetHeaders;
|
|
338
385
|
/**
|
|
339
386
|
* Creates an Express static middleware configured for MRT asset serving.
|
|
340
387
|
*
|
|
@@ -350,13 +397,14 @@ export const setLocalAssetHeaders = (res, assetPath) => {
|
|
|
350
397
|
* app.use('/static', staticMiddleware);
|
|
351
398
|
* ```
|
|
352
399
|
*/
|
|
353
|
-
|
|
354
|
-
return
|
|
400
|
+
const createMRTStaticAssetServingMiddleware = (staticAssetDir) => {
|
|
401
|
+
return express_1.default.static(staticAssetDir, {
|
|
355
402
|
dotfiles: 'deny',
|
|
356
|
-
setHeaders: setLocalAssetHeaders,
|
|
403
|
+
setHeaders: exports.setLocalAssetHeaders,
|
|
357
404
|
fallthrough: false,
|
|
358
405
|
});
|
|
359
406
|
};
|
|
407
|
+
exports.createMRTStaticAssetServingMiddleware = createMRTStaticAssetServingMiddleware;
|
|
360
408
|
/**
|
|
361
409
|
* Creates a common middleware function that sets the host header based on environment variables.
|
|
362
410
|
*
|
|
@@ -371,12 +419,13 @@ export const createMRTStaticAssetServingMiddleware = (staticAssetDir) => {
|
|
|
371
419
|
* app.use(middleware);
|
|
372
420
|
* ```
|
|
373
421
|
*/
|
|
374
|
-
|
|
422
|
+
const createMRTCommonMiddleware = () => {
|
|
375
423
|
return (req, res, next) => {
|
|
376
424
|
req.headers.host = process.env.EXTERNAL_DOMAIN_NAME || 'localhost:2401';
|
|
377
425
|
next();
|
|
378
426
|
};
|
|
379
427
|
};
|
|
428
|
+
exports.createMRTCommonMiddleware = createMRTCommonMiddleware;
|
|
380
429
|
/**
|
|
381
430
|
* Creates a cleanup middleware function that removes internal headers and cleans up request state.
|
|
382
431
|
*
|
|
@@ -396,9 +445,9 @@ export const createMRTCommonMiddleware = () => {
|
|
|
396
445
|
* app.use(cleanupMiddleware);
|
|
397
446
|
* ```
|
|
398
447
|
*/
|
|
399
|
-
|
|
448
|
+
const createMRTCleanUpMiddleware = () => {
|
|
400
449
|
return (req, res, next) => {
|
|
401
|
-
if (!req.headers[X_MOBIFY_REQUEST_PROCESSOR_LOCAL]) {
|
|
450
|
+
if (!req.headers[exports.X_MOBIFY_REQUEST_PROCESSOR_LOCAL]) {
|
|
402
451
|
const originalQuerystring = req.originalUrl.split('?')[1] || undefined;
|
|
403
452
|
const updatedQuerystring = getMobifyQueryString(req, originalQuerystring);
|
|
404
453
|
const updatedPath = req.originalUrl.split('?')[0];
|
|
@@ -408,4 +457,5 @@ export const createMRTCleanUpMiddleware = () => {
|
|
|
408
457
|
next();
|
|
409
458
|
};
|
|
410
459
|
};
|
|
460
|
+
exports.createMRTCleanUpMiddleware = createMRTCleanUpMiddleware;
|
|
411
461
|
//# sourceMappingURL=middleware.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"middleware.js","sourceRoot":"","sources":["../../../src/middleware/middleware.ts"],"names":[],"mappings":"AAAA;;;;GAIG
|
|
1
|
+
{"version":3,"file":"middleware.js","sourceRoot":"","sources":["../../../src/middleware/middleware.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,14 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
1
2
|
/*
|
|
2
3
|
* Copyright (c) 2025, Salesforce, Inc.
|
|
3
4
|
* SPDX-License-Identifier: Apache-2
|
|
4
5
|
* For full license text, see the license.txt file in the repo root or http://www.apache.org/licenses/LICENSE-2.0
|
|
5
6
|
*/
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
8
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
9
|
+
};
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.createStreamingLambdaAdapter = createStreamingLambdaAdapter;
|
|
12
|
+
exports.createExpressRequest = createExpressRequest;
|
|
13
|
+
exports.createExpressResponse = createExpressResponse;
|
|
14
|
+
const http_1 = require("http");
|
|
15
|
+
const promises_1 = require("node:stream/promises");
|
|
16
|
+
const node_zlib_1 = __importDefault(require("node:zlib"));
|
|
17
|
+
const negotiator_1 = __importDefault(require("negotiator"));
|
|
18
|
+
const compressible_1 = __importDefault(require("compressible"));
|
|
19
|
+
const serverless_adapter_1 = require("@h4ad/serverless-adapter");
|
|
12
20
|
/**
|
|
13
21
|
* Header keys to copy from the request to the response.
|
|
14
22
|
* These headers are typically used for tracing, correlation, or other request/response matching purposes.
|
|
@@ -18,8 +26,8 @@ const REQUEST_HEADERS_TO_COPY = ['x-correlation-id'];
|
|
|
18
26
|
let createZstdCompress;
|
|
19
27
|
try {
|
|
20
28
|
// Try to import createZstdCompress - it may not exist in older Node.js versions
|
|
21
|
-
if (typeof
|
|
22
|
-
createZstdCompress =
|
|
29
|
+
if (typeof node_zlib_1.default.createZstdCompress === 'function') {
|
|
30
|
+
createZstdCompress = node_zlib_1.default.createZstdCompress;
|
|
23
31
|
}
|
|
24
32
|
}
|
|
25
33
|
catch {
|
|
@@ -34,7 +42,7 @@ catch {
|
|
|
34
42
|
* @param compressionConfig - Optional compression configuration
|
|
35
43
|
* @returns Lambda handler function
|
|
36
44
|
*/
|
|
37
|
-
|
|
45
|
+
function createStreamingLambdaAdapter(app, responseStream, compressionConfig = { enabled: true }) {
|
|
38
46
|
const handler = async (event, context) => {
|
|
39
47
|
try {
|
|
40
48
|
await streamResponse(event, responseStream, context, app, compressionConfig);
|
|
@@ -44,7 +52,7 @@ export function createStreamingLambdaAdapter(app, responseStream, compressionCon
|
|
|
44
52
|
const isStreamOpen = responseStream && responseStream.writable && !responseStream.destroyed && !responseStream.writableEnded;
|
|
45
53
|
if (isStreamOpen && typeof responseStream.write === 'function') {
|
|
46
54
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
47
|
-
responseStream.write(`
|
|
55
|
+
responseStream.write(`Internal Server Error: ${errorMessage}`);
|
|
48
56
|
}
|
|
49
57
|
else {
|
|
50
58
|
console.error('[error handler] Cannot write error - stream is closed');
|
|
@@ -81,34 +89,17 @@ async function streamResponse(event, responseStream, context, app, compressionCo
|
|
|
81
89
|
reject(err);
|
|
82
90
|
}
|
|
83
91
|
};
|
|
92
|
+
// See events defined for hhtp.serverResponse interface here:
|
|
93
|
+
// https://nodejs.org/docs/latest-v24.x/api/http.html#class-httpserverresponse
|
|
84
94
|
// Handle response finish
|
|
85
|
-
expressResponse.once('finish',
|
|
86
|
-
|
|
87
|
-
|
|
95
|
+
expressResponse.once('finish', resolveOnce);
|
|
96
|
+
// handle response close, either after a 'finish' or if underlying
|
|
97
|
+
// connection is closed early
|
|
98
|
+
expressResponse.once('close', resolveOnce);
|
|
88
99
|
// Handle response errors
|
|
89
|
-
expressResponse.once('error',
|
|
90
|
-
rejectOnce(err);
|
|
91
|
-
});
|
|
100
|
+
expressResponse.once('error', rejectOnce);
|
|
92
101
|
try {
|
|
93
|
-
app(expressRequest, expressResponse
|
|
94
|
-
if (err) {
|
|
95
|
-
console.error('Express app error:', err);
|
|
96
|
-
rejectOnce(err);
|
|
97
|
-
}
|
|
98
|
-
else {
|
|
99
|
-
// If response has finished, resolveOnce will be called by the finish event
|
|
100
|
-
// Otherwise, resolve after a short delay to allow async operations
|
|
101
|
-
if (expressResponse.finished) {
|
|
102
|
-
resolveOnce();
|
|
103
|
-
}
|
|
104
|
-
else {
|
|
105
|
-
// Wait a bit for the response to finish
|
|
106
|
-
setTimeout(() => {
|
|
107
|
-
resolveOnce();
|
|
108
|
-
}, 10);
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
});
|
|
102
|
+
app(expressRequest, expressResponse);
|
|
112
103
|
}
|
|
113
104
|
catch (error) {
|
|
114
105
|
console.error('Error in streamResponse:', error);
|
|
@@ -162,7 +153,7 @@ const getPathFromEvent = (event) => {
|
|
|
162
153
|
* Creates a proper IncomingMessage-like object with stream properties
|
|
163
154
|
*/
|
|
164
155
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
165
|
-
|
|
156
|
+
function createExpressRequest(event, context) {
|
|
166
157
|
const { httpMethod, headers, multiValueHeaders, body, isBase64Encoded, requestContext } = event;
|
|
167
158
|
const remoteAddress = requestContext?.identity?.sourceIp ?? undefined;
|
|
168
159
|
const bodyEncoding = isBase64Encoded ? 'base64' : 'utf-8';
|
|
@@ -184,7 +175,7 @@ export function createExpressRequest(event, context) {
|
|
|
184
175
|
continue;
|
|
185
176
|
normalizedHeaders[multiValueHeaderKey] = value.join(',');
|
|
186
177
|
}
|
|
187
|
-
const request = new ServerlessRequest({
|
|
178
|
+
const request = new serverless_adapter_1.ServerlessRequest({
|
|
188
179
|
method: httpMethod,
|
|
189
180
|
url: getPathFromEvent(event),
|
|
190
181
|
headers: normalizedHeaders,
|
|
@@ -223,7 +214,7 @@ function isCompressible(contentType) {
|
|
|
223
214
|
if (!contentType) {
|
|
224
215
|
return false;
|
|
225
216
|
}
|
|
226
|
-
return !!
|
|
217
|
+
return !!(0, compressible_1.default)(contentType);
|
|
227
218
|
}
|
|
228
219
|
const isNullOrUndefined = (value) => value == null;
|
|
229
220
|
/**
|
|
@@ -246,7 +237,7 @@ function getBestEncoding(acceptEncoding, compressionConfig) {
|
|
|
246
237
|
if (!acceptEncoding) {
|
|
247
238
|
return null;
|
|
248
239
|
}
|
|
249
|
-
const negotiator = new
|
|
240
|
+
const negotiator = new negotiator_1.default({ headers: { 'accept-encoding': acceptEncoding } });
|
|
250
241
|
// Build available encodings list based on what's supported
|
|
251
242
|
// Order of preference: br (brotli), zstd (if available), gzip, deflate
|
|
252
243
|
const availableEncodings = ['br', 'gzip', 'deflate'];
|
|
@@ -268,16 +259,16 @@ function createCompressionStream(encoding, compressionConfig) {
|
|
|
268
259
|
const options = compressionConfig?.options || undefined;
|
|
269
260
|
switch (encoding) {
|
|
270
261
|
case 'br':
|
|
271
|
-
return
|
|
262
|
+
return node_zlib_1.default.createBrotliCompress(options);
|
|
272
263
|
case 'zstd':
|
|
273
264
|
if (!createZstdCompress) {
|
|
274
265
|
throw new Error('zstd compression is not available in this Node.js version (requires v22.15.0+)');
|
|
275
266
|
}
|
|
276
267
|
return createZstdCompress(options);
|
|
277
268
|
case 'gzip':
|
|
278
|
-
return
|
|
269
|
+
return node_zlib_1.default.createGzip(options);
|
|
279
270
|
case 'deflate':
|
|
280
|
-
return
|
|
271
|
+
return node_zlib_1.default.createDeflate(options);
|
|
281
272
|
default:
|
|
282
273
|
throw new Error(`Unsupported encoding: ${encoding}`);
|
|
283
274
|
}
|
|
@@ -303,7 +294,7 @@ function createCompressionStream(encoding, compressionConfig) {
|
|
|
303
294
|
* @param request - Optional Express request object (used to check Accept-Encoding header)
|
|
304
295
|
* @returns Express-compatible response object
|
|
305
296
|
*/
|
|
306
|
-
|
|
297
|
+
function createExpressResponse(responseStream, event, context, request, compressionConfig) {
|
|
307
298
|
const method = event.httpMethod;
|
|
308
299
|
let statusCode = 200;
|
|
309
300
|
let statusMessage = undefined;
|
|
@@ -410,6 +401,7 @@ export function createExpressResponse(responseStream, event, context, request, c
|
|
|
410
401
|
}
|
|
411
402
|
if (!isStreamOpen()) {
|
|
412
403
|
console.error('Cannot initialize response - stream is closed');
|
|
404
|
+
emitCloseOnce(response);
|
|
413
405
|
return;
|
|
414
406
|
}
|
|
415
407
|
// Collect all current headers from the response
|
|
@@ -432,7 +424,7 @@ export function createExpressResponse(responseStream, event, context, request, c
|
|
|
432
424
|
// Create HttpResponseStream with metadata
|
|
433
425
|
// This writes the HTTP status and headers to the stream
|
|
434
426
|
const metadata = {
|
|
435
|
-
statusCode,
|
|
427
|
+
statusCode: response.statusCode || statusCode,
|
|
436
428
|
headers,
|
|
437
429
|
};
|
|
438
430
|
const cookies = metadata.headers['set-cookie'];
|
|
@@ -442,6 +434,14 @@ export function createExpressResponse(responseStream, event, context, request, c
|
|
|
442
434
|
}
|
|
443
435
|
metadata.headers = convertHeaders(metadata.headers);
|
|
444
436
|
httpResponseStream = awslambda.HttpResponseStream.from(responseStream, metadata);
|
|
437
|
+
// Bind 'finish' and 'close' handlers to emit the same events on the response object
|
|
438
|
+
httpResponseStream.on('finish', () => {
|
|
439
|
+
response.finished = true;
|
|
440
|
+
response.emit('finish');
|
|
441
|
+
});
|
|
442
|
+
httpResponseStream.on('close', () => {
|
|
443
|
+
response.emit('close');
|
|
444
|
+
});
|
|
445
445
|
// Set up compression stream if compression is enabled
|
|
446
446
|
// The compression stream pipes to httpResponseStream, which pipes to responseStream
|
|
447
447
|
// 'identity' means no encoding, so we should not initialize compression for it
|
|
@@ -490,7 +490,7 @@ export function createExpressResponse(responseStream, event, context, request, c
|
|
|
490
490
|
return false;
|
|
491
491
|
}
|
|
492
492
|
// @ts-expect-error - Pipeline expects Readable, but compression streams are Transform streams
|
|
493
|
-
await pipeline(sourceStream, destination);
|
|
493
|
+
await (0, promises_1.pipeline)(sourceStream, destination);
|
|
494
494
|
return true;
|
|
495
495
|
}
|
|
496
496
|
catch (error) {
|
|
@@ -499,7 +499,7 @@ export function createExpressResponse(responseStream, event, context, request, c
|
|
|
499
499
|
}
|
|
500
500
|
};
|
|
501
501
|
// @ts-expect-error - ServerResponse constructor expects IncomingMessage, but we're creating a minimal mock
|
|
502
|
-
const res = new ServerResponse({
|
|
502
|
+
const res = new http_1.ServerResponse({
|
|
503
503
|
method,
|
|
504
504
|
});
|
|
505
505
|
// Override statusMessage property to track custom status messages
|
|
@@ -529,7 +529,7 @@ export function createExpressResponse(responseStream, event, context, request, c
|
|
|
529
529
|
headerObj = reasonPhrase;
|
|
530
530
|
reasonPhrase = undefined;
|
|
531
531
|
}
|
|
532
|
-
statusCode = code || statusCode;
|
|
532
|
+
statusCode = code || this.statusCode || statusCode;
|
|
533
533
|
this.statusCode = statusCode;
|
|
534
534
|
// Set statusMessage if provided
|
|
535
535
|
if (reasonPhrase) {
|
|
@@ -552,6 +552,7 @@ export function createExpressResponse(responseStream, event, context, request, c
|
|
|
552
552
|
res.write = function (chunk) {
|
|
553
553
|
if (!isStreamOpen()) {
|
|
554
554
|
console.error(`Cannot write - stream is closed`);
|
|
555
|
+
emitCloseOnce(this);
|
|
555
556
|
return false;
|
|
556
557
|
}
|
|
557
558
|
initializeResponse(this);
|
|
@@ -575,6 +576,14 @@ export function createExpressResponse(responseStream, event, context, request, c
|
|
|
575
576
|
httpResponseStream.flush();
|
|
576
577
|
}
|
|
577
578
|
};
|
|
579
|
+
let emittedClose = false;
|
|
580
|
+
const emitCloseOnce = (response) => {
|
|
581
|
+
if (emittedClose) {
|
|
582
|
+
return;
|
|
583
|
+
}
|
|
584
|
+
response.emit('close');
|
|
585
|
+
emittedClose = true;
|
|
586
|
+
};
|
|
578
587
|
/**
|
|
579
588
|
* Ends the appropriate stream(s) and emits the finish event
|
|
580
589
|
* If compression is enabled, ends the compression stream which will automatically
|
|
@@ -583,50 +592,30 @@ export function createExpressResponse(responseStream, event, context, request, c
|
|
|
583
592
|
* @param response - The Express response object to emit finish event on
|
|
584
593
|
*/
|
|
585
594
|
const endStream = (response) => {
|
|
586
|
-
|
|
595
|
+
const stream = (shouldCompress && compressionStream) || (httpResponseStream?.writable && httpResponseStream);
|
|
596
|
+
if (stream) {
|
|
587
597
|
try {
|
|
588
|
-
// Flush compression stream to ensure all buffered data is written
|
|
589
598
|
_flush();
|
|
590
|
-
|
|
591
|
-
// due to the pipe with { end: true } option
|
|
592
|
-
compressionStream.end(() => {
|
|
593
|
-
response.finished = true;
|
|
594
|
-
response.emit('finish');
|
|
595
|
-
});
|
|
599
|
+
stream.end();
|
|
596
600
|
}
|
|
597
601
|
catch (error) {
|
|
598
|
-
console.error(`Error ending
|
|
599
|
-
// Still emit finish even if there was an error
|
|
602
|
+
console.error(`Error ending stream:`, error);
|
|
600
603
|
response.finished = true;
|
|
601
|
-
response
|
|
602
|
-
}
|
|
603
|
-
}
|
|
604
|
-
else if (httpResponseStream && httpResponseStream.writable) {
|
|
605
|
-
// No compression, end httpResponseStream directly
|
|
606
|
-
try {
|
|
607
|
-
_flush();
|
|
608
|
-
httpResponseStream.end(() => {
|
|
609
|
-
response.finished = true;
|
|
610
|
-
response.emit('finish');
|
|
611
|
-
});
|
|
612
|
-
}
|
|
613
|
-
catch (error) {
|
|
614
|
-
console.error(`Error ending httpResponseStream:`, error);
|
|
615
|
-
response.finished = true;
|
|
616
|
-
response.emit('finish');
|
|
604
|
+
emitCloseOnce(response);
|
|
617
605
|
}
|
|
618
606
|
}
|
|
619
607
|
else {
|
|
620
608
|
console.error(`Cannot call end() - stream is closed`);
|
|
621
609
|
// Still emit finish to prevent hanging
|
|
622
610
|
response.finished = true;
|
|
623
|
-
response
|
|
611
|
+
emitCloseOnce(response);
|
|
624
612
|
}
|
|
625
613
|
};
|
|
626
614
|
// @ts-expect-error - Type signature doesn't match ServerResponse.end exactly, but our implementation is compatible
|
|
627
615
|
res.end = function (chunk) {
|
|
628
616
|
if (!isStreamOpen()) {
|
|
629
617
|
console.error(`Cannot end - stream is already closed`);
|
|
618
|
+
emitCloseOnce(this);
|
|
630
619
|
return this;
|
|
631
620
|
}
|
|
632
621
|
initializeResponse(this);
|
|
@@ -654,9 +643,8 @@ export function createExpressResponse(responseStream, event, context, request, c
|
|
|
654
643
|
// Add Express-specific methods that aren't in ServerResponse
|
|
655
644
|
res.status = function (code, message) {
|
|
656
645
|
this.statusCode = code;
|
|
657
|
-
statusCode = code;
|
|
658
646
|
if (message !== undefined) {
|
|
659
|
-
statusMessage = message;
|
|
647
|
+
this.statusMessage = message;
|
|
660
648
|
}
|
|
661
649
|
return this;
|
|
662
650
|
};
|
|
@@ -700,6 +688,7 @@ export function createExpressResponse(responseStream, event, context, request, c
|
|
|
700
688
|
if (!responseStarted) {
|
|
701
689
|
if (!isStreamOpen()) {
|
|
702
690
|
console.error('[res.flushHeaders] Cannot flush headers - stream is closed');
|
|
691
|
+
emitCloseOnce(this);
|
|
703
692
|
return this;
|
|
704
693
|
}
|
|
705
694
|
// Collect all current headers and send them
|
|
@@ -739,6 +728,7 @@ export function createExpressResponse(responseStream, event, context, request, c
|
|
|
739
728
|
res.flush = function () {
|
|
740
729
|
if (!isStreamOpen()) {
|
|
741
730
|
console.error(`Cannot flush - stream is closed`);
|
|
731
|
+
emitCloseOnce(this);
|
|
742
732
|
return this;
|
|
743
733
|
}
|
|
744
734
|
initializeResponse(this);
|