@salesforce/pwa-kit-runtime 3.17.0-preview.2 → 3.17.1-preview.0

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.17.0-preview.2",
3
+ "version": "3.17.1-preview.0",
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": {
@@ -48,11 +48,11 @@
48
48
  },
49
49
  "devDependencies": {
50
50
  "@loadable/component": "^5.15.3",
51
- "@salesforce/pwa-kit-dev": "3.17.0-preview.2",
51
+ "@salesforce/pwa-kit-dev": "3.17.1-preview.0",
52
52
  "@serverless/event-mocks": "^1.1.1",
53
53
  "aws-lambda-mock-context": "^3.2.1",
54
54
  "fs-extra": "^11.1.1",
55
- "internal-lib-build": "3.17.0-preview.2",
55
+ "internal-lib-build": "3.17.1-preview.0",
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": "3.17.0-preview.2"
63
+ "@salesforce/pwa-kit-dev": "3.17.1-preview.0"
64
64
  },
65
65
  "peerDependenciesMeta": {
66
66
  "@salesforce/pwa-kit-dev": {
@@ -74,5 +74,5 @@
74
74
  "publishConfig": {
75
75
  "directory": "dist"
76
76
  },
77
- "gitHead": "1da061ff7778db2ff9737838b71c2d09975382b1"
77
+ "gitHead": "beabe8a971b6170bddfe08ae3f5ca2f1bfe66aa7"
78
78
  }
@@ -515,8 +515,9 @@ const RemoteServerFactory = exports.RemoteServerFactory = {
515
515
  return next();
516
516
  }
517
517
 
518
- // For other routes, only proceed if path actually starts with base path
519
- if (!req.path.startsWith(basePath)) {
518
+ // For other routes, only proceed if path equals basePath or path starts with basePath + '/'
519
+ const pathMatchesBasePath = req.path === basePath || req.path.startsWith(basePath + '/');
520
+ if (!pathMatchesBasePath) {
520
521
  return next();
521
522
  }
522
523
 
@@ -13,6 +13,7 @@ var _ssrCache = require("../../utils/ssr-cache");
13
13
  var _ssrServer = require("../../utils/ssr-server");
14
14
  var ssrServerUtils = _interopRequireWildcard(require("../../utils/ssr-server/utils"));
15
15
  var ssrConfig = _interopRequireWildcard(require("../../utils/ssr-config"));
16
+ var ssrNamespacePaths = _interopRequireWildcard(require("../../utils/ssr-namespace-paths"));
16
17
  var _buildRemoteServer = require("./build-remote-server");
17
18
  var _constants = require("./constants");
18
19
  var _express2 = require("./express");
@@ -1191,10 +1192,11 @@ describe('SLAS private client proxy', () => {
1191
1192
  }));
1192
1193
  });
1193
1194
  describe('Base path tests', () => {
1195
+ afterEach(() => {
1196
+ jest.restoreAllMocks();
1197
+ });
1194
1198
  test('Base path is removed from /mobify request path and still gets through to /mobify endpoint', /*#__PURE__*/_asyncToGenerator(function* () {
1195
- jest.spyOn(ssrConfig, 'getConfig').mockReturnValue({
1196
- envBasePath: '/basepath'
1197
- });
1199
+ jest.spyOn(ssrNamespacePaths, 'getEnvBasePath').mockReturnValue('/basepath');
1198
1200
  const app = _buildRemoteServer.RemoteServerFactory._createApp(opts());
1199
1201
  return (0, _supertest.default)(app).get('/basepath/mobify/ping').then(response => {
1200
1202
  expect(response.status).toBe(200);
@@ -1202,9 +1204,7 @@ describe('Base path tests', () => {
1202
1204
  }), 15000);
1203
1205
  test('should not remove base path from non /mobify non-express routes', /*#__PURE__*/_asyncToGenerator(function* () {
1204
1206
  // Set base path to something that might also be a site id used by react router routes
1205
- jest.spyOn(ssrConfig, 'getConfig').mockReturnValue({
1206
- envBasePath: '/us'
1207
- });
1207
+ jest.spyOn(ssrNamespacePaths, 'getEnvBasePath').mockReturnValue('/us');
1208
1208
  const app = _buildRemoteServer.RemoteServerFactory._createApp(opts());
1209
1209
 
1210
1210
  // Add a middleware to capture the request path after base path processing
@@ -1221,9 +1221,7 @@ describe('Base path tests', () => {
1221
1221
  });
1222
1222
  }), 15000);
1223
1223
  test('should remove base path from routes with path parameters', /*#__PURE__*/_asyncToGenerator(function* () {
1224
- jest.spyOn(ssrConfig, 'getConfig').mockReturnValue({
1225
- envBasePath: '/basepath'
1226
- });
1224
+ jest.spyOn(ssrNamespacePaths, 'getEnvBasePath').mockReturnValue('/basepath');
1227
1225
  const app = _buildRemoteServer.RemoteServerFactory._createApp(opts());
1228
1226
  app.get('/api/users/:id', (req, res) => {
1229
1227
  res.status(200).json({
@@ -1236,9 +1234,7 @@ describe('Base path tests', () => {
1236
1234
  });
1237
1235
  }), 15000);
1238
1236
  test('should remove base path from routes defined with regex', /*#__PURE__*/_asyncToGenerator(function* () {
1239
- jest.spyOn(ssrConfig, 'getConfig').mockReturnValue({
1240
- envBasePath: '/basepath'
1241
- });
1237
+ jest.spyOn(ssrNamespacePaths, 'getEnvBasePath').mockReturnValue('/basepath');
1242
1238
  const app = _buildRemoteServer.RemoteServerFactory._createApp(opts());
1243
1239
  app.get(/\/api\/users\/\d+/, (req, res) => {
1244
1240
  // Extract the user ID from the URL path since regex routes don't create req.params automatically
@@ -1253,25 +1249,8 @@ describe('Base path tests', () => {
1253
1249
  expect(response.body.userId).toBe('123');
1254
1250
  });
1255
1251
  }), 15000);
1256
- test('remove base path can handle multi-part base paths', /*#__PURE__*/_asyncToGenerator(function* () {
1257
- jest.spyOn(ssrConfig, 'getConfig').mockReturnValue({
1258
- envBasePath: '/my/base/path'
1259
- });
1260
- const app = _buildRemoteServer.RemoteServerFactory._createApp(opts());
1261
- app.get('/api/test', (req, res) => {
1262
- res.status(200).json({
1263
- message: 'test'
1264
- });
1265
- });
1266
- return (0, _supertest.default)(app).get('/my/base/path/api/test').then(response => {
1267
- expect(response.status).toBe(200);
1268
- expect(response.body.message).toBe('test');
1269
- });
1270
- }), 15000);
1271
1252
  test('should handle optional characters in route pattern', /*#__PURE__*/_asyncToGenerator(function* () {
1272
- jest.spyOn(ssrConfig, 'getConfig').mockReturnValue({
1273
- envBasePath: '/basepath'
1274
- });
1253
+ jest.spyOn(ssrNamespacePaths, 'getEnvBasePath').mockReturnValue('/basepath');
1275
1254
  const app = _buildRemoteServer.RemoteServerFactory._createApp(opts());
1276
1255
 
1277
1256
  // This route is intentionally made complex to test the following:
@@ -4,9 +4,6 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.ssrNamespace = exports.slasPrivateProxyPath = exports.proxyBasePath = exports.healthCheckPath = exports.getEnvBasePath = exports.cachingBasePath = exports.bundleBasePath = void 0;
7
- var _ssrConfig = require("./ssr-config");
8
- var _loggerInstance = _interopRequireDefault(require("./logger-instance"));
9
- function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
10
7
  /*
11
8
  * Copyright (c) 2025, salesforce.com, inc.
12
9
  * All rights reserved.
@@ -32,35 +29,39 @@ const SLAS_PRIVATE_CLIENT_PROXY_PATH = `${MOBIFY_PATH}/slas/private`;
32
29
 
33
30
  /*
34
31
  * Returns the base path. This is prepended to a /mobify path.
35
- * Returns an empty string if the base path is not set or is '/'.
32
+ * Returns an empty string if the base path is not set.
33
+ * Throws an error if the base path is not valid.
34
+ *
35
+ * Use this function if you are working with an express route
36
+ * (ie. The route is defined in ssr.js).
37
+ *
38
+ * Use getRouterBasePath (pwa-kit-react-sdk) if you are working
39
+ * with a React Router route
40
+ * (ie. The route is defined in routes.jsx).
36
41
  */
37
42
  const getEnvBasePath = () => {
38
- const config = (0, _ssrConfig.getConfig)();
39
- let basePath = (config === null || config === void 0 ? void 0 : config.envBasePath) || '';
40
- if (typeof basePath !== 'string') {
41
- _loggerInstance.default.warn('Invalid envBasePath configuration. No base path is applied.', {
42
- namespace: 'ssr-namespace-paths.getEnvBasePath'
43
- });
44
- return '';
43
+ let basePath = '';
44
+ if (typeof window !== 'undefined') {
45
+ basePath = window.__MRT_ENV_BASE_PATH__ || '';
46
+ } else {
47
+ basePath = process.env.MRT_ENV_BASE_PATH || '';
45
48
  }
46
49
 
47
- // Normalize the base path
48
- basePath = basePath.trim().replace(/^\/?/, '/') // Ensure leading slash
49
- .replace(/\/+/g, '/') // Normalize multiple slashes
50
- .replace(/\/$/, ''); // Remove trailing slash
51
-
52
- // Return empty string for root path or empty result
53
- if (basePath === '/' || !basePath) {
50
+ // Return empty string if no base path is set
51
+ if (!basePath) {
54
52
  return '';
55
53
  }
56
54
 
57
- // only allow simple, safe characters
58
- // eslint-disable-next-line no-useless-escape
59
- if (!/^\/[a-zA-Z0-9\-_\/]*$/.test(basePath)) {
60
- _loggerInstance.default.warn('Invalid envBasePath configuration. Only letters, numbers, hyphens, underscores, and slashes allowed. No base path is applied.', {
61
- namespace: 'ssr-namespace-paths.getEnvBasePath'
62
- });
63
- return '';
55
+ // MRT will throw an error on bundle upload if the base path does not match
56
+ // the following regex: /^\/[a-zA-Z0-9_.+$~"'@:-]{1,63}$/
57
+ // This validates:
58
+ // - Starts with /
59
+ // - Followed by 1-63 characters (letters, numbers, and special chars: - _ . + $ ~ " ' @ :)
60
+ // - No additional slashes (multi-part paths not allowed, no trailing slashes)
61
+ // - No spaces
62
+ // - Total max length of 64 characters (1 slash + 63 chars)
63
+ if (!/^\/[a-zA-Z0-9_.+$~"'@:-]{1,63}$/.test(basePath)) {
64
+ throw new Error("Invalid envBasePath configuration. Base path must start with '/' followed by 1-63 characters. Only letters, numbers, and the following special characters are allowed: - _ . + $ ~ \" ' @ :");
64
65
  }
65
66
  return basePath;
66
67
  };
@@ -1,55 +1,98 @@
1
1
  "use strict";
2
2
 
3
3
  var _ssrNamespacePaths = require("./ssr-namespace-paths");
4
- var ssrConfig = _interopRequireWildcard(require("./ssr-config"));
5
- function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
6
- /*
4
+ 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; }
5
+ 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; }
6
+ 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; }
7
+ function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
8
+ 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); } /*
7
9
  * Copyright (c) 2024, Salesforce, Inc.
8
10
  * All rights reserved.
9
11
  * SPDX-License-Identifier: BSD-3-Clause
10
12
  * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
11
13
  */
12
-
13
- jest.mock('./ssr-config');
14
14
  describe('ssr-namespace-paths tests', () => {
15
- test('getEnvBasePath returns base path from config', () => {
16
- jest.spyOn(ssrConfig, 'getConfig').mockReturnValue({
17
- envBasePath: '/sample'
18
- });
19
- expect((0, _ssrNamespacePaths.getEnvBasePath)()).toBe('/sample');
15
+ const originalEnv = process.env;
16
+ beforeEach(() => {
17
+ jest.resetModules();
18
+ process.env = _objectSpread({}, originalEnv);
19
+ delete process.env.MRT_ENV_BASE_PATH;
20
+ // Ensure we're in Node environment (no window)
21
+ delete global.window;
20
22
  });
21
- test('getEnvBasePath returns empty string if no base path is set', () => {
22
- jest.spyOn(ssrConfig, 'getConfig').mockReturnValue({});
23
- expect((0, _ssrNamespacePaths.getEnvBasePath)()).toBe('');
23
+ afterEach(() => {
24
+ process.env = originalEnv;
25
+ delete global.window;
24
26
  });
25
- test('getEnvBasePath returns empty string if envBasePath is not a string', () => {
26
- jest.spyOn(ssrConfig, 'getConfig').mockReturnValue({
27
- envBasePath: 123
27
+ describe('Node environment (process.env)', () => {
28
+ test('getEnvBasePath returns base path from environment variable', () => {
29
+ process.env.MRT_ENV_BASE_PATH = '/sample';
30
+ expect((0, _ssrNamespacePaths.getEnvBasePath)()).toBe('/sample');
28
31
  });
29
- expect((0, _ssrNamespacePaths.getEnvBasePath)()).toBe('');
30
- });
31
- test('getEnvBasePath removes trailing slash', () => {
32
- jest.spyOn(ssrConfig, 'getConfig').mockReturnValue({
33
- envBasePath: '/sample/'
32
+ test('getEnvBasePath returns empty string if no base path is set', () => {
33
+ expect((0, _ssrNamespacePaths.getEnvBasePath)()).toBe('');
34
34
  });
35
- expect((0, _ssrNamespacePaths.getEnvBasePath)()).toBe('/sample');
36
- });
37
- test('getEnvBasePath returns empty string if invalid cahracters are detected in envBasePath', () => {
38
- jest.spyOn(ssrConfig, 'getConfig').mockReturnValue({
39
- envBasePath: '/sample.*'
35
+ test('getEnvBasePath throws error for base path with trailing slash', () => {
36
+ process.env.MRT_ENV_BASE_PATH = '/sample/';
37
+ expect(() => (0, _ssrNamespacePaths.getEnvBasePath)()).toThrow('Invalid envBasePath configuration');
40
38
  });
41
- expect((0, _ssrNamespacePaths.getEnvBasePath)()).toBe('');
42
- });
43
- test('getEnvBasePath normalizes envBasePath', () => {
44
- jest.spyOn(ssrConfig, 'getConfig').mockReturnValue({
45
- envBasePath: ' //sample/ '
39
+ test('getEnvBasePath throws error for just a slash', () => {
40
+ process.env.MRT_ENV_BASE_PATH = '/';
41
+ expect(() => (0, _ssrNamespacePaths.getEnvBasePath)()).toThrow('Invalid envBasePath configuration');
42
+ });
43
+ test('getEnvBasePath throws error if invalid characters are detected in envBasePath', () => {
44
+ process.env.MRT_ENV_BASE_PATH = '/sample<script>';
45
+ expect(() => (0, _ssrNamespacePaths.getEnvBasePath)()).toThrow('Invalid envBasePath configuration');
46
+ });
47
+ test('getEnvBasePath throws error for envBasePath with whitespace', () => {
48
+ process.env.MRT_ENV_BASE_PATH = ' /sample ';
49
+ expect(() => (0, _ssrNamespacePaths.getEnvBasePath)()).toThrow('Invalid envBasePath configuration');
50
+ });
51
+ test('getEnvBasePath throws error for multi-part base paths with slashes', () => {
52
+ process.env.MRT_ENV_BASE_PATH = '/test/sample';
53
+ expect(() => (0, _ssrNamespacePaths.getEnvBasePath)()).toThrow('Invalid envBasePath configuration');
54
+ });
55
+ test('getEnvBasePath allows special characters: . + $ ~ " \' @ : -', () => {
56
+ process.env.MRT_ENV_BASE_PATH = '/a.b+c$d~e"f\'g@h:i-j_k';
57
+ expect((0, _ssrNamespacePaths.getEnvBasePath)()).toBe('/a.b+c$d~e"f\'g@h:i-j_k');
58
+ });
59
+ test('getEnvBasePath throws error if base path exceeds 64 characters', () => {
60
+ // 65 characters total (1 slash + 64 chars)
61
+ process.env.MRT_ENV_BASE_PATH = '/' + 'a'.repeat(64);
62
+ expect(() => (0, _ssrNamespacePaths.getEnvBasePath)()).toThrow('Invalid envBasePath configuration');
63
+ });
64
+ test('getEnvBasePath allows base path of exactly 64 characters', () => {
65
+ // 64 characters total (1 slash + 63 chars)
66
+ process.env.MRT_ENV_BASE_PATH = '/' + 'a'.repeat(63);
67
+ expect((0, _ssrNamespacePaths.getEnvBasePath)()).toBe('/' + 'a'.repeat(63));
46
68
  });
47
- expect((0, _ssrNamespacePaths.getEnvBasePath)()).toBe('/sample');
48
69
  });
49
- test('getEnvBasePath works with multiple part base path', () => {
50
- jest.spyOn(ssrConfig, 'getConfig').mockReturnValue({
51
- envBasePath: '//test/sample/ '
70
+ describe('Browser environment (window)', () => {
71
+ beforeEach(() => {
72
+ global.window = {};
73
+ });
74
+ test('getEnvBasePath returns base path from window global', () => {
75
+ global.window.__MRT_ENV_BASE_PATH__ = '/sample';
76
+ expect((0, _ssrNamespacePaths.getEnvBasePath)()).toBe('/sample');
77
+ });
78
+ test('getEnvBasePath returns empty string if window global is not set', () => {
79
+ expect((0, _ssrNamespacePaths.getEnvBasePath)()).toBe('');
80
+ });
81
+ test('getEnvBasePath throws error for base path with trailing slash from window global', () => {
82
+ global.window.__MRT_ENV_BASE_PATH__ = '/sample/';
83
+ expect(() => (0, _ssrNamespacePaths.getEnvBasePath)()).toThrow('Invalid envBasePath configuration');
84
+ });
85
+ test('getEnvBasePath throws error for window global value with whitespace', () => {
86
+ global.window.__MRT_ENV_BASE_PATH__ = ' /sample ';
87
+ expect(() => (0, _ssrNamespacePaths.getEnvBasePath)()).toThrow('Invalid envBasePath configuration');
88
+ });
89
+ test('getEnvBasePath throws error if invalid characters in window global', () => {
90
+ global.window.__MRT_ENV_BASE_PATH__ = '/sample<script>';
91
+ expect(() => (0, _ssrNamespacePaths.getEnvBasePath)()).toThrow('Invalid envBasePath configuration');
92
+ });
93
+ test('getEnvBasePath throws error for multi-part base paths in window global', () => {
94
+ global.window.__MRT_ENV_BASE_PATH__ = '/test/sample';
95
+ expect(() => (0, _ssrNamespacePaths.getEnvBasePath)()).toThrow('Invalid envBasePath configuration');
52
96
  });
53
- expect((0, _ssrNamespacePaths.getEnvBasePath)()).toBe('/test/sample');
54
97
  });
55
98
  });