@salesforce/pwa-kit-runtime 4.0.0-extensibility-preview.3 → 4.0.0-extensibility-preview.4
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": "4.0.0-extensibility-preview.
|
|
3
|
+
"version": "4.0.0-extensibility-preview.4",
|
|
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,13 +46,13 @@
|
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
48
|
"@loadable/component": "^5.15.3",
|
|
49
|
-
"@salesforce/pwa-kit-dev": "4.0.0-extensibility-preview.
|
|
50
|
-
"@salesforce/pwa-kit-extension-sdk": "4.0.0-extensibility-preview.
|
|
49
|
+
"@salesforce/pwa-kit-dev": "4.0.0-extensibility-preview.4",
|
|
50
|
+
"@salesforce/pwa-kit-extension-sdk": "4.0.0-extensibility-preview.4",
|
|
51
51
|
"@serverless/event-mocks": "^1.1.1",
|
|
52
52
|
"@types/express": "^4.17.21",
|
|
53
53
|
"aws-lambda-mock-context": "^3.2.1",
|
|
54
54
|
"fs-extra": "^11.1.1",
|
|
55
|
-
"internal-lib-build": "4.0.0-extensibility-preview.
|
|
55
|
+
"internal-lib-build": "4.0.0-extensibility-preview.4",
|
|
56
56
|
"nock": "^13.3.0",
|
|
57
57
|
"nodemon": "^2.0.22",
|
|
58
58
|
"sinon": "^13.0.2",
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
"supertest": "^4.0.2"
|
|
61
61
|
},
|
|
62
62
|
"peerDependencies": {
|
|
63
|
-
"@salesforce/pwa-kit-dev": "4.0.0-extensibility-preview.
|
|
63
|
+
"@salesforce/pwa-kit-dev": "4.0.0-extensibility-preview.4"
|
|
64
64
|
},
|
|
65
65
|
"peerDependenciesMeta": {
|
|
66
66
|
"@salesforce/pwa-kit-dev": {
|
|
@@ -68,11 +68,11 @@
|
|
|
68
68
|
}
|
|
69
69
|
},
|
|
70
70
|
"engines": {
|
|
71
|
-
"node": "^16.11.0 || ^18.0.0 || ^20.0.0",
|
|
72
|
-
"npm": "^8.0.0 || ^9.0.0 || ^10.0.0"
|
|
71
|
+
"node": "^16.11.0 || ^18.0.0 || ^20.0.0 || ^22.0.0",
|
|
72
|
+
"npm": "^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0"
|
|
73
73
|
},
|
|
74
74
|
"publishConfig": {
|
|
75
75
|
"directory": "dist"
|
|
76
76
|
},
|
|
77
|
-
"gitHead": "
|
|
77
|
+
"gitHead": "5ad3b1f0e598cfe0fbdec5b1212b4dabc0a8733c"
|
|
78
78
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build-remote-server.d.ts","sourceRoot":"","sources":["../../../src/ssr/server/build-remote-server.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"build-remote-server.d.ts","sourceRoot":"","sources":["../../../src/ssr/server/build-remote-server.js"],"names":[],"mappings":"AAsEA;;;;GAIG;AACH,gDAKC;;IAWG;;OAEG;IACH,uCAiFC;IAED;;OAEG;IAEH,gDAEC;IAED;;OAEG;IAEH,iDAEC;IAED;;OAEG;IAEH,4CAEC;IAED;;OAEG;IACH,uDAEC;IAED;;OAEG;IAEH,2CAEC;IAED;;OAEG;IACH,6CAEC;IAED;;OAEG;IACH,4DAIC;IAED;;OAEG;IAEH,yCAEC;IAED;;OAEG;IAEH,uCA6CC;IAED;;;OAGG;IACH,uCAcC;IAED;;OAEG;IAEH,+CAEC;IAED;;OAEG;IACH,kDAEC;IAED;;OAEG;IACH,oDAEC;IAED;;OAEG;IACH,+EA2CC;IAED;;OAEG;IACH,sFA2DC;IAED;;OAEG;IAEH,iDAEC;IAED;;;OAGG;IACH,4DAWC;IAED;;OAEG;IACH,6DAwLC;IAED;;OAEG;IAEH,sDAOC;IAED;;OAEG;IACH,yDAOC;IAED;;OAEG;IACH,oEA2EC;IAED;;OAEG;IACH,2CAIC;IAED;;OAEG;IACH,8DAiBC;IAED;;OAEG;IACH,oDA0EC;IAED;;OAEG;IACH,wCAEC;IAED;;OAEG;IACH,gDAGC;IAED;;;;;;;;;;;OAWG;IACH,sDA2CC;IAED;;;;;;;OAOG;IACH,wFAKC;IAED;;OAEG;IACH,4FASC;IAED;;;;;;;;;;OAUG;IACH,6DAWC;IAED;;;;;;;OAOG;IACH;;;;MAmGC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;OA2BG;IACH;;;;;;;;;;;;MAKC;IAED;;OAEG;IAEH,8CAEC;;AA2HE,uDASN"}
|
|
@@ -103,9 +103,9 @@ const RemoteServerFactory = exports.RemoteServerFactory = {
|
|
|
103
103
|
useSLASPrivateClient: false,
|
|
104
104
|
// A regex for identifying which SLAS endpoints the custom SLAS private
|
|
105
105
|
// client secret handler will inject an Authorization header.
|
|
106
|
-
//
|
|
107
|
-
//
|
|
108
|
-
applySLASPrivateClientToEndpoints: /\/oauth2\/token/
|
|
106
|
+
// To allow additional SLAS endpoints, users can override this value in
|
|
107
|
+
// their project's ssr.js.
|
|
108
|
+
applySLASPrivateClientToEndpoints: /\/oauth2\/(token|passwordless\/(login|token)|password\/(reset|action))/
|
|
109
109
|
};
|
|
110
110
|
options = _extends({}, defaults, options);
|
|
111
111
|
(0, _ssrServer.setQuiet)(options.quiet || process.env.SSR_QUIET);
|
|
@@ -320,6 +320,8 @@ const RemoteServerFactory = exports.RemoteServerFactory = {
|
|
|
320
320
|
app.disable('x-powered-by');
|
|
321
321
|
const mixin = {
|
|
322
322
|
options,
|
|
323
|
+
// Forcing a GC is no longer necessary, and will be
|
|
324
|
+
// skipped by default (unless FORCE_GC env-var is set).
|
|
323
325
|
_collectGarbage() {
|
|
324
326
|
// Do global.gc in a separate 'then' handler so
|
|
325
327
|
// that all major variables are out of scope and
|
|
@@ -611,8 +613,7 @@ const RemoteServerFactory = exports.RemoteServerFactory = {
|
|
|
611
613
|
});
|
|
612
614
|
|
|
613
615
|
// We pattern match and add client secrets only to endpoints that
|
|
614
|
-
// match the regex specified by options.applySLASPrivateClientToEndpoints
|
|
615
|
-
// By default, this regex matches only calls to SLAS /oauth2/token
|
|
616
|
+
// match the regex specified by options.applySLASPrivateClientToEndpoints
|
|
616
617
|
// (see option defaults at the top of this file).
|
|
617
618
|
// Other SLAS endpoints, ie. SLAS authenticate (/oauth2/login) and
|
|
618
619
|
// SLAS logout (/oauth2/logout), use the Authorization header for a different
|
|
@@ -765,8 +766,27 @@ const RemoteServerFactory = exports.RemoteServerFactory = {
|
|
|
765
766
|
encoding: 'utf8'
|
|
766
767
|
});
|
|
767
768
|
|
|
769
|
+
// If the service worker is not updated when content security policy headers inside
|
|
770
|
+
// ssr.js are changed, then service worker initiated requests will continue to use
|
|
771
|
+
// the old CSP headers.
|
|
772
|
+
//
|
|
773
|
+
// This is problematic in stacked CDN setups where an old service worker with
|
|
774
|
+
// old CSPs can remain cached if the content of the service worker itself is not changed.
|
|
775
|
+
//
|
|
776
|
+
// To ensure the service worker is refetched when CSPs are changed, we factor in
|
|
777
|
+
// the CSP headers when generating the Etag.
|
|
778
|
+
//
|
|
779
|
+
// See https://gus.lightning.force.com/lightning/r/ADM_Work__c/a07EE000025yeu9YAA/view
|
|
780
|
+
// and https://salesforce-internal.slack.com/archives/C01GLHLBPT5/p1730739370922629
|
|
781
|
+
// for more details.
|
|
782
|
+
|
|
783
|
+
const contentSecurityPolicyHeader = res.getHeaders()[_constants.CONTENT_SECURITY_POLICY] || '';
|
|
784
|
+
|
|
768
785
|
// Serve the file, with a strong ETag
|
|
769
|
-
|
|
786
|
+
// For this to be a valid ETag, the string must be placed between ""
|
|
787
|
+
// See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag#etag_value for
|
|
788
|
+
// more details
|
|
789
|
+
res.set('etag', `"${(0, _ssrServer.getHashForString)(content + contentSecurityPolicyHeader)}"`);
|
|
770
790
|
res.set(_constants.CONTENT_TYPE, 'application/javascript');
|
|
771
791
|
res.send(content);
|
|
772
792
|
},
|
|
@@ -870,12 +890,15 @@ const RemoteServerFactory = exports.RemoteServerFactory = {
|
|
|
870
890
|
// the response to the browser.
|
|
871
891
|
context.callbackWaitsForEmptyEventLoop = false;
|
|
872
892
|
if (lambdaContainerReused) {
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
893
|
+
const forceGarbageCollection = process.env.FORCE_GC;
|
|
894
|
+
if (forceGarbageCollection && forceGarbageCollection.toLowerCase() === 'true') {
|
|
895
|
+
// DESKTOP-434 If this Lambda container is being reused,
|
|
896
|
+
// clean up memory now, so that we start with low usage.
|
|
897
|
+
// These regular GC calls take about 80-100 mS each, as opposed
|
|
898
|
+
// to forced GC calls, which occur randomly and can take several
|
|
899
|
+
// hundred mS.
|
|
900
|
+
app._collectGarbage();
|
|
901
|
+
}
|
|
879
902
|
app.sendMetric('LambdaReused');
|
|
880
903
|
} else {
|
|
881
904
|
// This is the first use of this container, so set the
|
|
@@ -54,6 +54,54 @@ const testFixtures = path.resolve(process.cwd(), 'src/ssr/server/test_fixtures')
|
|
|
54
54
|
const httpsAgent = new https.Agent({
|
|
55
55
|
rejectUnauthorized: false
|
|
56
56
|
});
|
|
57
|
+
function createServerWithGCSpy() {
|
|
58
|
+
const route = jest.fn((req, res) => {
|
|
59
|
+
res.send('<html/>');
|
|
60
|
+
});
|
|
61
|
+
const options = {
|
|
62
|
+
buildDir: testFixtures,
|
|
63
|
+
mobify: testPackageMobify,
|
|
64
|
+
sslFilePath: path.join(testFixtures, 'localhost.pem'),
|
|
65
|
+
quiet: true,
|
|
66
|
+
port: TEST_PORT,
|
|
67
|
+
fetchAgents: {
|
|
68
|
+
https: httpsAgent
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
const {
|
|
72
|
+
handler,
|
|
73
|
+
server,
|
|
74
|
+
app
|
|
75
|
+
} = RemoteServerFactory.createHandler(options, app => {
|
|
76
|
+
app.get('/*', route);
|
|
77
|
+
});
|
|
78
|
+
const collectGarbage = jest.spyOn(app, '_collectGarbage');
|
|
79
|
+
const sendMetric = jest.spyOn(app, 'sendMetric');
|
|
80
|
+
return {
|
|
81
|
+
route,
|
|
82
|
+
handler,
|
|
83
|
+
collectGarbage,
|
|
84
|
+
sendMetric,
|
|
85
|
+
server
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
function createApiGatewayEvent() {
|
|
89
|
+
// Set up a fake event and a fake context for the Lambda call
|
|
90
|
+
const event = createEvent('aws:apiGateway', {
|
|
91
|
+
path: '/',
|
|
92
|
+
body: undefined
|
|
93
|
+
});
|
|
94
|
+
if (event.queryStringParameters) {
|
|
95
|
+
delete event.queryStringParameters;
|
|
96
|
+
}
|
|
97
|
+
const context = AWSMockContext({
|
|
98
|
+
functionName: 'SSRTest'
|
|
99
|
+
});
|
|
100
|
+
return {
|
|
101
|
+
event,
|
|
102
|
+
context
|
|
103
|
+
};
|
|
104
|
+
}
|
|
57
105
|
describe('SSRServer Lambda integration', () => {
|
|
58
106
|
let savedEnvironment;
|
|
59
107
|
let server;
|
|
@@ -329,42 +377,50 @@ describe('SSRServer Lambda integration', () => {
|
|
|
329
377
|
X_HEADERS_TO_REMOVE_ORIGIN.forEach(key => expect(reqHeaders[key]).toBeUndefined());
|
|
330
378
|
});
|
|
331
379
|
});
|
|
332
|
-
test('Lambda reuse
|
|
333
|
-
const route = jest.fn((req, res) => {
|
|
334
|
-
res.send('<html/>');
|
|
335
|
-
});
|
|
336
|
-
const options = {
|
|
337
|
-
buildDir: testFixtures,
|
|
338
|
-
mobify: testPackageMobify,
|
|
339
|
-
sslFilePath: path.join(testFixtures, 'localhost.pem'),
|
|
340
|
-
quiet: true,
|
|
341
|
-
port: TEST_PORT,
|
|
342
|
-
fetchAgents: {
|
|
343
|
-
https: httpsAgent
|
|
344
|
-
}
|
|
345
|
-
};
|
|
380
|
+
test('Lambda reuse -- Default Behavior', () => {
|
|
346
381
|
const {
|
|
347
|
-
|
|
382
|
+
route,
|
|
348
383
|
handler,
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
});
|
|
353
|
-
const
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
const
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
384
|
+
collectGarbage,
|
|
385
|
+
sendMetric,
|
|
386
|
+
new_server
|
|
387
|
+
} = createServerWithGCSpy();
|
|
388
|
+
const {
|
|
389
|
+
event,
|
|
390
|
+
context
|
|
391
|
+
} = createApiGatewayEvent();
|
|
392
|
+
server = new_server;
|
|
393
|
+
const call = event => new Promise(resolve => handler(event, context, (err, response) => resolve(response)));
|
|
394
|
+
return Promise.resolve().then(() => call(event)).then(response => {
|
|
395
|
+
// First request - Lambda container created
|
|
396
|
+
expect(response.statusCode).toBe(200);
|
|
397
|
+
expect(collectGarbage.mock.calls).toHaveLength(0);
|
|
398
|
+
expect(route.mock.calls).toHaveLength(1);
|
|
399
|
+
expect(sendMetric).toHaveBeenCalledWith('LambdaCreated');
|
|
400
|
+
expect(sendMetric).not.toHaveBeenCalledWith('LambdaReused');
|
|
401
|
+
}).then(() => call(event)).then(response => {
|
|
402
|
+
// Second call - Lambda container reused
|
|
403
|
+
expect(response.statusCode).toBe(200);
|
|
404
|
+
expect(collectGarbage.mock.calls).toHaveLength(0);
|
|
405
|
+
expect(route.mock.calls).toHaveLength(2);
|
|
406
|
+
expect(sendMetric).toHaveBeenCalledWith('LambdaCreated');
|
|
407
|
+
expect(sendMetric).toHaveBeenCalledWith('LambdaReused');
|
|
367
408
|
});
|
|
409
|
+
});
|
|
410
|
+
test('Lambda reuse -- with Forced Garbage Collection Enabled', () => {
|
|
411
|
+
process.env.FORCE_GC = 'true';
|
|
412
|
+
const {
|
|
413
|
+
event,
|
|
414
|
+
context
|
|
415
|
+
} = createApiGatewayEvent();
|
|
416
|
+
const {
|
|
417
|
+
route,
|
|
418
|
+
handler,
|
|
419
|
+
collectGarbage,
|
|
420
|
+
sendMetric,
|
|
421
|
+
new_server
|
|
422
|
+
} = createServerWithGCSpy();
|
|
423
|
+
server = new_server;
|
|
368
424
|
const call = event => new Promise(resolve => handler(event, context, (err, response) => resolve(response)));
|
|
369
425
|
return Promise.resolve().then(() => call(event)).then(response => {
|
|
370
426
|
// First request - Lambda container created
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"metrics-sender.d.ts","sourceRoot":"","sources":["../../../src/utils/ssr-server/metrics-sender.js"],"names":[],"mappings":"AASA;;;;;;;GAOG;AACH;IAKQ,iDAAe;IAKf,cAAgB;IAGpB;;;OAGG;IACH,0BAEC;IAED;;;;;;OAMG;IACH,
|
|
1
|
+
{"version":3,"file":"metrics-sender.d.ts","sourceRoot":"","sources":["../../../src/utils/ssr-server/metrics-sender.js"],"names":[],"mappings":"AASA;;;;;;;GAOG;AACH;IAKQ,iDAAe;IAKf,cAAgB;IAGpB;;;OAGG;IACH,0BAEC;IAED;;;;;;OAMG;IACH,eAiBC;IAED;;;;;;;;OAQG;IACH,uBA0BC;IAED;;;;OAIG;IACH,+BAyBC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA6BG;IACH,aA4BC;CACJ;;;IAOD;;;;;OAKG;IACH,oCAKC"}
|
|
@@ -59,7 +59,11 @@ class MetricsSender {
|
|
|
59
59
|
apiVersion: '2010-08-01',
|
|
60
60
|
// The AWS_REGION variable is defined by the Lambda
|
|
61
61
|
// environment.
|
|
62
|
-
region: process.env.AWS_REGION || 'us-east-1'
|
|
62
|
+
region: process.env.AWS_REGION || 'us-east-1',
|
|
63
|
+
// Setting maxRetries to 0 will prevent the SDK from retrying.
|
|
64
|
+
// This is necessary because under high load, there will be backpressure
|
|
65
|
+
// on the Lambda function, and causing severe performance issues (400-500ms latency)
|
|
66
|
+
maxRetries: 0
|
|
63
67
|
});
|
|
64
68
|
}
|
|
65
69
|
return this._CW;
|