@salesforce/pwa-kit-runtime 3.7.0 → 3.7.1
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@salesforce/pwa-kit-runtime",
|
|
3
|
-
"version": "3.7.
|
|
3
|
+
"version": "3.7.1",
|
|
4
4
|
"description": "The PWAKit Runtime",
|
|
5
5
|
"homepage": "https://github.com/SalesforceCommerceCloud/pwa-kit/tree/develop/packages/pwa-kit-runtime#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -46,11 +46,11 @@
|
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
48
|
"@loadable/component": "^5.15.3",
|
|
49
|
-
"@salesforce/pwa-kit-dev": "3.7.
|
|
49
|
+
"@salesforce/pwa-kit-dev": "3.7.1",
|
|
50
50
|
"@serverless/event-mocks": "^1.1.1",
|
|
51
51
|
"aws-lambda-mock-context": "^3.2.1",
|
|
52
52
|
"fs-extra": "^11.1.1",
|
|
53
|
-
"internal-lib-build": "3.7.
|
|
53
|
+
"internal-lib-build": "3.7.1",
|
|
54
54
|
"nock": "^13.3.0",
|
|
55
55
|
"nodemon": "^2.0.22",
|
|
56
56
|
"sinon": "^13.0.2",
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
"supertest": "^4.0.2"
|
|
59
59
|
},
|
|
60
60
|
"peerDependencies": {
|
|
61
|
-
"@salesforce/pwa-kit-dev": "3.7.
|
|
61
|
+
"@salesforce/pwa-kit-dev": "3.7.1"
|
|
62
62
|
},
|
|
63
63
|
"peerDependenciesMeta": {
|
|
64
64
|
"@salesforce/pwa-kit-dev": {
|
|
@@ -72,5 +72,5 @@
|
|
|
72
72
|
"publishConfig": {
|
|
73
73
|
"directory": "dist"
|
|
74
74
|
},
|
|
75
|
-
"gitHead": "
|
|
75
|
+
"gitHead": "9dde49caa598178c7a8dd406966f0d635fa8102c"
|
|
76
76
|
}
|
|
@@ -641,6 +641,9 @@ const RemoteServerFactory = exports.RemoteServerFactory = {
|
|
|
641
641
|
// to add in their projects, like in any regular Express app.
|
|
642
642
|
app.use(ssrMiddleware);
|
|
643
643
|
app.use(errorHandlerMiddleware);
|
|
644
|
+
if (options !== null && options !== void 0 && options.encodeNonAsciiHttpHeaders) {
|
|
645
|
+
app.use(encodeNonAsciiMiddleware);
|
|
646
|
+
}
|
|
644
647
|
applyPatches(options);
|
|
645
648
|
},
|
|
646
649
|
/**
|
|
@@ -799,13 +802,29 @@ const RemoteServerFactory = exports.RemoteServerFactory = {
|
|
|
799
802
|
* @param app {Express} - an Express App
|
|
800
803
|
* @private
|
|
801
804
|
*/
|
|
802
|
-
_createHandler(app) {
|
|
805
|
+
_createHandler(app, options) {
|
|
803
806
|
// This flag is initially false, and is set true on the first request
|
|
804
807
|
// handled by a Lambda. If it is true on entry to the handler function,
|
|
805
808
|
// it indicates that the Lambda container has been reused.
|
|
806
809
|
let lambdaContainerReused = false;
|
|
807
810
|
const server = _awsServerlessExpress.default.createServer(app, null, binaryMimeTypes);
|
|
808
811
|
const handler = (event, context, callback) => {
|
|
812
|
+
// encode non ASCII request headers
|
|
813
|
+
if (options !== null && options !== void 0 && options.encodeNonAsciiHttpHeaders) {
|
|
814
|
+
Object.keys(event.headers).forEach(key => {
|
|
815
|
+
if (!isASCII(event.headers[key])) {
|
|
816
|
+
event.headers[key] = encodeURIComponent(event.headers[key]);
|
|
817
|
+
// x-encoded-headers keeps track of which headers have been modified and encoded
|
|
818
|
+
if (event.headers[_constants.X_ENCODED_HEADERS]) {
|
|
819
|
+
// append header key
|
|
820
|
+
event.headers[_constants.X_ENCODED_HEADERS] = `${event.headers[_constants.X_ENCODED_HEADERS]},${key}`;
|
|
821
|
+
} else {
|
|
822
|
+
event.headers[_constants.X_ENCODED_HEADERS] = key;
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
});
|
|
826
|
+
}
|
|
827
|
+
|
|
809
828
|
// We don't want to wait for an empty event loop once the response
|
|
810
829
|
// has been sent. Setting this to false will "send the response
|
|
811
830
|
// right away when the callback executes", but any pending events
|
|
@@ -907,8 +926,8 @@ const RemoteServerFactory = exports.RemoteServerFactory = {
|
|
|
907
926
|
createHandler(options, customizeApp) {
|
|
908
927
|
process.on('unhandledRejection', _ssrServer.catchAndLog);
|
|
909
928
|
const app = this._createApp(options);
|
|
910
|
-
customizeApp(app);
|
|
911
|
-
return this._createHandler(app);
|
|
929
|
+
customizeApp(app, options);
|
|
930
|
+
return this._createHandler(app, options);
|
|
912
931
|
},
|
|
913
932
|
/**
|
|
914
933
|
* @private
|
|
@@ -991,6 +1010,37 @@ const errorHandlerMiddleware = (err, req, res, next) => {
|
|
|
991
1010
|
res.sendStatus(500);
|
|
992
1011
|
};
|
|
993
1012
|
|
|
1013
|
+
/**
|
|
1014
|
+
* Helper function that checks if a string is composed of ASCII characters
|
|
1015
|
+
* We only check printable ASCII characters and not special ASCII characters
|
|
1016
|
+
* such as NULL
|
|
1017
|
+
*
|
|
1018
|
+
* @private
|
|
1019
|
+
*/
|
|
1020
|
+
const isASCII = str => {
|
|
1021
|
+
return /^[\x20-\x7E]*$/.test(str);
|
|
1022
|
+
};
|
|
1023
|
+
|
|
1024
|
+
/**
|
|
1025
|
+
* Express Middleware applied to responses that encode any non ASCII headers
|
|
1026
|
+
*
|
|
1027
|
+
* @private
|
|
1028
|
+
*/
|
|
1029
|
+
const encodeNonAsciiMiddleware = (req, res, next) => {
|
|
1030
|
+
const originalSetHeader = res.setHeader;
|
|
1031
|
+
res.setHeader = function (key, value) {
|
|
1032
|
+
if (!isASCII(value)) {
|
|
1033
|
+
originalSetHeader.call(this, key, encodeURIComponent(value));
|
|
1034
|
+
let encodedHeaders = res.getHeader(_constants.X_ENCODED_HEADERS);
|
|
1035
|
+
encodedHeaders = encodedHeaders ? `${encodedHeaders},${key}` : key;
|
|
1036
|
+
originalSetHeader.call(this, _constants.X_ENCODED_HEADERS, encodedHeaders);
|
|
1037
|
+
} else {
|
|
1038
|
+
originalSetHeader.call(this, key, value);
|
|
1039
|
+
}
|
|
1040
|
+
};
|
|
1041
|
+
next();
|
|
1042
|
+
};
|
|
1043
|
+
|
|
994
1044
|
/**
|
|
995
1045
|
* Wrap the function fn in such a way that it will be called at most once. Subsequent
|
|
996
1046
|
* calls will always return the same value.
|
|
@@ -1,13 +1,25 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
var _buildRemoteServer = require("./build-remote-server");
|
|
4
|
-
|
|
4
|
+
var _constants = require("./constants");
|
|
5
|
+
var _awsServerlessExpress = _interopRequireDefault(require("aws-serverless-express"));
|
|
6
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
7
|
+
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
8
|
+
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
9
|
+
function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
|
|
10
|
+
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
|
|
11
|
+
function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } /*
|
|
5
12
|
* Copyright (c) 2021, salesforce.com, inc.
|
|
6
13
|
* All rights reserved.
|
|
7
14
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
8
15
|
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
|
|
9
16
|
*/
|
|
10
|
-
|
|
17
|
+
jest.mock('aws-serverless-express', () => {
|
|
18
|
+
return {
|
|
19
|
+
createServer: jest.fn(),
|
|
20
|
+
proxy: jest.fn()
|
|
21
|
+
};
|
|
22
|
+
});
|
|
11
23
|
describe('the once function', () => {
|
|
12
24
|
test('should prevent a function being called more than once', () => {
|
|
13
25
|
const fn = jest.fn(() => ({
|
|
@@ -33,4 +45,70 @@ describe('remote server factory test coverage', () => {
|
|
|
33
45
|
});
|
|
34
46
|
expect(endpoint).toBeDefined();
|
|
35
47
|
});
|
|
48
|
+
});
|
|
49
|
+
describe('encodeNonAsciiHttpHeaders flag in options to createHandler', () => {
|
|
50
|
+
test('encodes request headers', () => {
|
|
51
|
+
const mockApp = {
|
|
52
|
+
sendMetric: jest.fn()
|
|
53
|
+
};
|
|
54
|
+
const mockOptions = {
|
|
55
|
+
encodeNonAsciiHttpHeaders: true
|
|
56
|
+
};
|
|
57
|
+
const originalHeaders = {
|
|
58
|
+
'x-non-ascii-header-one': 'テスト',
|
|
59
|
+
'x-non-ascii-header-two': '测试',
|
|
60
|
+
'x-regular-header': 'ascii-str'
|
|
61
|
+
};
|
|
62
|
+
const event = {
|
|
63
|
+
headers: _objectSpread({}, originalHeaders)
|
|
64
|
+
};
|
|
65
|
+
const expectedHeaders = {
|
|
66
|
+
'x-non-ascii-header-one': '%E3%83%86%E3%82%B9%E3%83%88',
|
|
67
|
+
'x-non-ascii-header-two': '%E6%B5%8B%E8%AF%95',
|
|
68
|
+
'x-encoded-headers': 'x-non-ascii-header-one,x-non-ascii-header-two',
|
|
69
|
+
'x-regular-header': 'ascii-str'
|
|
70
|
+
};
|
|
71
|
+
const {
|
|
72
|
+
handler
|
|
73
|
+
} = _buildRemoteServer.RemoteServerFactory._createHandler(mockApp, mockOptions);
|
|
74
|
+
expect(event.headers).toEqual(originalHeaders);
|
|
75
|
+
handler(event, {}, {});
|
|
76
|
+
expect(event.headers).toEqual(expectedHeaders);
|
|
77
|
+
expect(decodeURIComponent(event.headers['x-non-ascii-header-one'])).toEqual(originalHeaders['x-non-ascii-header-one']);
|
|
78
|
+
});
|
|
79
|
+
test('encodes response headers', () => {
|
|
80
|
+
const mockApp = {
|
|
81
|
+
use: jest.fn()
|
|
82
|
+
};
|
|
83
|
+
const mockOptions = {
|
|
84
|
+
encodeNonAsciiHttpHeaders: true
|
|
85
|
+
};
|
|
86
|
+
const res = {
|
|
87
|
+
headers: {},
|
|
88
|
+
setHeader: (key, value) => {
|
|
89
|
+
res.headers[key] = value;
|
|
90
|
+
},
|
|
91
|
+
getHeader: key => {
|
|
92
|
+
return res.headers[key];
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
const nonASCIIheader = 'x-non-ascii-header';
|
|
96
|
+
const nonASCIIstr = 'テスト';
|
|
97
|
+
const expectedEncoding = '%E3%83%86%E3%82%B9%E3%83%88';
|
|
98
|
+
const regularHeaderKey = 'x-regular-header';
|
|
99
|
+
const regularHeaderValue = 'ascii-str';
|
|
100
|
+
_buildRemoteServer.RemoteServerFactory._setupCommonMiddleware(mockApp, mockOptions);
|
|
101
|
+
const encodeNonAsciiMiddleware = mockApp.use.mock.calls[3][0];
|
|
102
|
+
res.setHeader(nonASCIIheader, nonASCIIstr);
|
|
103
|
+
expect(res.getHeader(nonASCIIheader)).toEqual(nonASCIIstr);
|
|
104
|
+
encodeNonAsciiMiddleware({}, res, () => {});
|
|
105
|
+
res.setHeader(nonASCIIheader, nonASCIIstr);
|
|
106
|
+
expect(res.getHeader(nonASCIIheader)).toEqual(expectedEncoding);
|
|
107
|
+
expect(decodeURI(expectedEncoding)).toEqual(nonASCIIstr);
|
|
108
|
+
expect(res.getHeader(_constants.X_ENCODED_HEADERS)).toEqual(nonASCIIheader);
|
|
109
|
+
|
|
110
|
+
// confirm ASCII headers are not modified
|
|
111
|
+
res.setHeader(regularHeaderKey, regularHeaderValue);
|
|
112
|
+
expect(res.getHeader(regularHeaderKey)).toEqual(regularHeaderValue);
|
|
113
|
+
});
|
|
36
114
|
});
|
package/ssr/server/constants.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.X_ORIGINAL_CONTENT_TYPE = exports.X_MOBIFY_QUERYSTRING = exports.X_MOBIFY_FROM_CACHE = exports.STRICT_TRANSPORT_SECURITY = exports.STATIC_ASSETS = exports.SLAS_CUSTOM_PROXY_PATH = exports.SET_COOKIE = exports.PROXY_PATH_PREFIX = exports.NO_CACHE = exports.CONTENT_TYPE = exports.CONTENT_SECURITY_POLICY = exports.CONTENT_ENCODING = exports.CACHE_CONTROL = exports.BUILD = exports.APPLICATION_OCTET_STREAM = void 0;
|
|
6
|
+
exports.X_ORIGINAL_CONTENT_TYPE = exports.X_MOBIFY_QUERYSTRING = exports.X_MOBIFY_FROM_CACHE = exports.X_ENCODED_HEADERS = exports.STRICT_TRANSPORT_SECURITY = exports.STATIC_ASSETS = exports.SLAS_CUSTOM_PROXY_PATH = exports.SET_COOKIE = exports.PROXY_PATH_PREFIX = exports.NO_CACHE = exports.CONTENT_TYPE = exports.CONTENT_SECURITY_POLICY = exports.CONTENT_ENCODING = exports.CACHE_CONTROL = exports.BUILD = exports.APPLICATION_OCTET_STREAM = void 0;
|
|
7
7
|
/*
|
|
8
8
|
* Copyright (c) 2021, salesforce.com, inc.
|
|
9
9
|
* All rights reserved.
|
|
@@ -23,6 +23,7 @@ const CONTENT_ENCODING = exports.CONTENT_ENCODING = 'content-encoding';
|
|
|
23
23
|
const X_ORIGINAL_CONTENT_TYPE = exports.X_ORIGINAL_CONTENT_TYPE = 'x-original-content-type';
|
|
24
24
|
const X_MOBIFY_QUERYSTRING = exports.X_MOBIFY_QUERYSTRING = 'x-mobify-querystring';
|
|
25
25
|
const X_MOBIFY_FROM_CACHE = exports.X_MOBIFY_FROM_CACHE = 'x-mobify-from-cache';
|
|
26
|
+
const X_ENCODED_HEADERS = exports.X_ENCODED_HEADERS = 'x-encoded-headers';
|
|
26
27
|
const SET_COOKIE = exports.SET_COOKIE = 'set-cookie';
|
|
27
28
|
const CACHE_CONTROL = exports.CACHE_CONTROL = 'cache-control';
|
|
28
29
|
const NO_CACHE = exports.NO_CACHE = 'max-age=0, nocache, nostore, must-revalidate';
|