@salesforce/mrt-utilities 0.1.1 → 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 +79 -34
- 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/streaming/create-lambda-adapter.js +39 -57
- package/dist/esm/streaming/create-lambda-adapter.js.map +1 -1
- package/package.json +23 -1
|
@@ -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,8 +275,8 @@ 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
281
|
const ssrRequestProcessorMiddleware = (req, res, next) => {
|
|
243
282
|
// If the path is /, we enforce that the only methods
|
|
@@ -265,6 +304,7 @@ export const createMRTRequestProcessorMiddleware = (requestProcessorPath, proxyC
|
|
|
265
304
|
};
|
|
266
305
|
return ssrRequestProcessorMiddleware;
|
|
267
306
|
};
|
|
307
|
+
exports.createMRTRequestProcessorMiddleware = createMRTRequestProcessorMiddleware;
|
|
268
308
|
/**
|
|
269
309
|
* Creates proxy middleware functions for the specified proxy configurations.
|
|
270
310
|
*
|
|
@@ -291,12 +331,12 @@ export const createMRTRequestProcessorMiddleware = (requestProcessorPath, proxyC
|
|
|
291
331
|
* });
|
|
292
332
|
* ```
|
|
293
333
|
*/
|
|
294
|
-
|
|
334
|
+
const createMRTProxyMiddlewares = (proxyConfigs, appProtocol = 'http', includeCaching = false, createProxyFn) => {
|
|
295
335
|
if (!proxyConfigs) {
|
|
296
336
|
return [];
|
|
297
337
|
}
|
|
298
338
|
const { appHostname } = getRequestProcessorParameters();
|
|
299
|
-
const proxies = configureProxying(proxyConfigs, appHostname, appProtocol, createProxyFn);
|
|
339
|
+
const proxies = (0, configure_proxying_js_1.configureProxying)(proxyConfigs, appHostname, appProtocol, createProxyFn);
|
|
300
340
|
const middlewares = [];
|
|
301
341
|
proxies.forEach((proxy) => {
|
|
302
342
|
const proxyPath = `${PROXY_PATH_BASE}/${proxy.path}`;
|
|
@@ -308,6 +348,7 @@ export const createMRTProxyMiddlewares = (proxyConfigs, appProtocol = 'http', in
|
|
|
308
348
|
});
|
|
309
349
|
return middlewares;
|
|
310
350
|
};
|
|
351
|
+
exports.createMRTProxyMiddlewares = createMRTProxyMiddlewares;
|
|
311
352
|
/**
|
|
312
353
|
* Sets appropriate HTTP headers for local asset files.
|
|
313
354
|
*
|
|
@@ -325,14 +366,14 @@ export const createMRTProxyMiddlewares = (proxyConfigs, appProtocol = 'http', in
|
|
|
325
366
|
* }));
|
|
326
367
|
* ```
|
|
327
368
|
*/
|
|
328
|
-
|
|
329
|
-
const base =
|
|
330
|
-
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';
|
|
331
372
|
res.set(CONTENT_TYPE, contentType);
|
|
332
373
|
// Stat the file and return the last-modified Date
|
|
333
374
|
// in RFC1123 format. Also use that value as the ETag
|
|
334
375
|
// and Last-Modified
|
|
335
|
-
const mtime =
|
|
376
|
+
const mtime = fs_1.default.statSync(assetPath).mtime;
|
|
336
377
|
const mtimeRFC1123 = mtime.toUTCString();
|
|
337
378
|
res.set('date', mtimeRFC1123);
|
|
338
379
|
res.set('last-modified', mtimeRFC1123);
|
|
@@ -340,6 +381,7 @@ export const setLocalAssetHeaders = (res, assetPath) => {
|
|
|
340
381
|
// We don't cache local bundle assets
|
|
341
382
|
res.set('cache-control', NO_CACHE);
|
|
342
383
|
};
|
|
384
|
+
exports.setLocalAssetHeaders = setLocalAssetHeaders;
|
|
343
385
|
/**
|
|
344
386
|
* Creates an Express static middleware configured for MRT asset serving.
|
|
345
387
|
*
|
|
@@ -355,13 +397,14 @@ export const setLocalAssetHeaders = (res, assetPath) => {
|
|
|
355
397
|
* app.use('/static', staticMiddleware);
|
|
356
398
|
* ```
|
|
357
399
|
*/
|
|
358
|
-
|
|
359
|
-
return
|
|
400
|
+
const createMRTStaticAssetServingMiddleware = (staticAssetDir) => {
|
|
401
|
+
return express_1.default.static(staticAssetDir, {
|
|
360
402
|
dotfiles: 'deny',
|
|
361
|
-
setHeaders: setLocalAssetHeaders,
|
|
403
|
+
setHeaders: exports.setLocalAssetHeaders,
|
|
362
404
|
fallthrough: false,
|
|
363
405
|
});
|
|
364
406
|
};
|
|
407
|
+
exports.createMRTStaticAssetServingMiddleware = createMRTStaticAssetServingMiddleware;
|
|
365
408
|
/**
|
|
366
409
|
* Creates a common middleware function that sets the host header based on environment variables.
|
|
367
410
|
*
|
|
@@ -376,12 +419,13 @@ export const createMRTStaticAssetServingMiddleware = (staticAssetDir) => {
|
|
|
376
419
|
* app.use(middleware);
|
|
377
420
|
* ```
|
|
378
421
|
*/
|
|
379
|
-
|
|
422
|
+
const createMRTCommonMiddleware = () => {
|
|
380
423
|
return (req, res, next) => {
|
|
381
424
|
req.headers.host = process.env.EXTERNAL_DOMAIN_NAME || 'localhost:2401';
|
|
382
425
|
next();
|
|
383
426
|
};
|
|
384
427
|
};
|
|
428
|
+
exports.createMRTCommonMiddleware = createMRTCommonMiddleware;
|
|
385
429
|
/**
|
|
386
430
|
* Creates a cleanup middleware function that removes internal headers and cleans up request state.
|
|
387
431
|
*
|
|
@@ -401,9 +445,9 @@ export const createMRTCommonMiddleware = () => {
|
|
|
401
445
|
* app.use(cleanupMiddleware);
|
|
402
446
|
* ```
|
|
403
447
|
*/
|
|
404
|
-
|
|
448
|
+
const createMRTCleanUpMiddleware = () => {
|
|
405
449
|
return (req, res, next) => {
|
|
406
|
-
if (!req.headers[X_MOBIFY_REQUEST_PROCESSOR_LOCAL]) {
|
|
450
|
+
if (!req.headers[exports.X_MOBIFY_REQUEST_PROCESSOR_LOCAL]) {
|
|
407
451
|
const originalQuerystring = req.originalUrl.split('?')[1] || undefined;
|
|
408
452
|
const updatedQuerystring = getMobifyQueryString(req, originalQuerystring);
|
|
409
453
|
const updatedPath = req.originalUrl.split('?')[0];
|
|
@@ -413,4 +457,5 @@ export const createMRTCleanUpMiddleware = () => {
|
|
|
413
457
|
next();
|
|
414
458
|
};
|
|
415
459
|
};
|
|
460
|
+
exports.createMRTCleanUpMiddleware = createMRTCleanUpMiddleware;
|
|
416
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);
|