From dcf9e3b80407dca33190a4928a3f290d7c01ada9 Mon Sep 17 00:00:00 2001 From: Tim Barton Date: Thu, 18 Jan 2024 15:00:24 +0000 Subject: [PATCH 1/5] feat(optionalIntl): created an env var for intl polyfill --- __tests__/server/config/env/runTime.spec.js | 17 +++++++++++++++++ .../server/plugins/reactHtml/index.spec.jsx | 17 ++++++++++++++++- src/client/polyfill/Intl.js | 6 ++++-- src/server/config/env/runTime.js | 10 ++++++++++ src/server/plugins/reactHtml/index.jsx | 10 +++++++++- src/server/polyfill/intl.js | 6 +++++- 6 files changed, 61 insertions(+), 5 deletions(-) diff --git a/__tests__/server/config/env/runTime.spec.js b/__tests__/server/config/env/runTime.spec.js index bebc5128a..71d2f5697 100644 --- a/__tests__/server/config/env/runTime.spec.js +++ b/__tests__/server/config/env/runTime.spec.js @@ -100,6 +100,7 @@ describe('runTime', () => { 'ONE_CLIENT_ROOT_MODULE_NAME', 'ONE_ENABLE_POST_TO_MODULE_ROUTES', 'ONE_MAX_POST_REQUEST_PAYLOAD', + 'ONE_CONFIG_USE_NATIVE_POLYFILL', ].forEach((name) => { origEnvVarVals[name] = process.env[name]; }); @@ -547,4 +548,20 @@ describe('runTime', () => { expect(otelServiceName.defaultValue).toBe('One App'); }); }); + + describe('ONE_CONFIG_USE_NATIVE_POLYFILL', () => { + const useNativePolyfill = getEnvVarConfig('ONE_CONFIG_USE_NATIVE_POLYFILL'); + + it('should have a default value of false', () => { + expect(useNativePolyfill.defaultValue).toBe('false'); + }); + + it('should normalise the value to either to false when not explicitly true', () => { + expect(useNativePolyfill.normalize('Value')).toBe('false'); + expect(useNativePolyfill.normalize('VALUE')).toBe('false'); + expect(useNativePolyfill.normalize('true')).toBe('true'); + expect(useNativePolyfill.normalize('TRUE')).toBe('true'); + expect(useNativePolyfill.normalize('FALSE')).toBe('false'); + }); + }); }); diff --git a/__tests__/server/plugins/reactHtml/index.spec.jsx b/__tests__/server/plugins/reactHtml/index.spec.jsx index 23c739c01..25580b319 100644 --- a/__tests__/server/plugins/reactHtml/index.spec.jsx +++ b/__tests__/server/plugins/reactHtml/index.spec.jsx @@ -22,7 +22,7 @@ import reactHtml, { sendHtml, renderModuleScripts, renderExternalFallbacks, - checkStateForRedirectAndStatusCode, + checkStateForRedirectAndStatusCode, renderUseNativeIntlPolyfill, } from '../../../../src/server/plugins/reactHtml'; // _client is a method to control the mock // eslint-disable-next-line import/named @@ -1262,4 +1262,19 @@ describe('reactHtml', () => { expect(reply.send.mock.calls[0][0]).toContain('One App'); }); }); + + describe('renderUseNativePolyfill', () => { + it('should not add the polyfill script if there is no environment variable', () => { + expect(renderUseNativeIntlPolyfill('')).toMatchInlineSnapshot('""'); + }); + + it('should add the polyfill script if there is the environment variable and it is true', () => { + process.env.ONE_CONFIG_USE_NATIVE_POLYFILL = 'true'; + expect(renderUseNativeIntlPolyfill('')).toMatchInlineSnapshot(` + "" + `); + }); + }); }); diff --git a/src/client/polyfill/Intl.js b/src/client/polyfill/Intl.js index 8cbb70143..49f235938 100644 --- a/src/client/polyfill/Intl.js +++ b/src/client/polyfill/Intl.js @@ -19,5 +19,7 @@ import Intl from 'lean-intl'; -global.Intl = Intl; -global.IntlPolyfill = Intl; +if (!(window && window.useNativePolyfill)) { + global.Intl = Intl; + global.IntlPolyfill = Intl; +} diff --git a/src/server/config/env/runTime.js b/src/server/config/env/runTime.js index 7eb82f252..c038926e0 100644 --- a/src/server/config/env/runTime.js +++ b/src/server/config/env/runTime.js @@ -186,6 +186,16 @@ const runTime = [ validate: (value) => { if (!value) { throw new Error('The `ONE_CLIENT_ROOT_MODULE_NAME` environment variable must be defined.'); } }, defaultValue: () => (process.env.NODE_ENV === 'development' ? argv.rootModuleName : undefined), }, + { + name: 'ONE_CONFIG_USE_NATIVE_POLYFILL', + defaultValue: 'false', + normalize: (input) => { + if (input?.toLowerCase() === 'true') { + return 'true'; + } + return 'false'; + }, + }, { name: 'ONE_REFERRER_POLICY_OVERRIDE', defaultValue: () => '', diff --git a/src/server/plugins/reactHtml/index.jsx b/src/server/plugins/reactHtml/index.jsx index b56e4b55a..5cfce5702 100644 --- a/src/server/plugins/reactHtml/index.jsx +++ b/src/server/plugins/reactHtml/index.jsx @@ -236,6 +236,12 @@ export function getHead({ `; } +export function renderUseNativeIntlPolyfill(nonce) { + return process.env.ONE_CONFIG_USE_NATIVE_POLYFILL === 'true' ? `` : ''; +} + export function getBody({ isLegacy, helmetInfo, @@ -253,13 +259,14 @@ export function getBody({ const bundle = isLegacy ? 'legacyBrowser' : 'browser'; const { bodyAttributes, script } = helmetInfo; const bundlePrefixForBrowser = isLegacy ? `${appBundlesURLPrefix}/legacy` : appBundlesURLPrefix; + const nonce = scriptNonce ? `nonce="${scriptNonce}"` : ''; return `
${appHtml || ''}
${disableScripts ? '' : ` - + ${renderUseNativeIntlPolyfill(nonce)} ${assets} ${renderI18nScript(clientInitialState, bundlePrefixForBrowser)} ${renderExternalFallbacks({ diff --git a/src/server/polyfill/intl.js b/src/server/polyfill/intl.js index 4d78166a8..0ad8d332d 100644 --- a/src/server/polyfill/intl.js +++ b/src/server/polyfill/intl.js @@ -14,4 +14,8 @@ * permissions and limitations under the License. */ -global.Intl = require('lean-intl'); +import Intl from 'lean-intl'; + +if (!process.env.ONE_CONFIG_USE_NATIVE_POLYFIL) { + global.Intl = Intl; +} From a97cd542126b441a59e8a17e18b2e9c116d2022e Mon Sep 17 00:00:00 2001 From: Tim Barton Date: Tue, 23 Jan 2024 10:00:37 +0000 Subject: [PATCH 2/5] feat(optionalIntl): added documentation --- docs/api/server/Environment-Variables.md | 27 +++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/docs/api/server/Environment-Variables.md b/docs/api/server/Environment-Variables.md index afa38be77..af11eca41 100644 --- a/docs/api/server/Environment-Variables.md +++ b/docs/api/server/Environment-Variables.md @@ -29,6 +29,7 @@ One App can be configured via Environment Variables: * [`ONE_CLIENT_ROOT_MODULE_NAME`](#one_client_root_module_name) ⚠️ * [`ONE_CLIENT_CDN_URL`](#one_client_cdn_url) ⚠️ * [`ONE_CONFIG_ENV`](#one_config_env) ⚠️ + * [`ONE_CONFIG_USE_NATIVE_POLYFILL`](#one_config_use_native_polyfill) * Running in Development * [`NODE_ENV`](#node_env) ⚠️ * [`ONE_CLIENT_ROOT_MODULE_NAME`](#one_client_root_module_name) ⚠️ @@ -36,6 +37,7 @@ One App can be configured via Environment Variables: * [`ONE_DANGEROUSLY_ACCEPT_BREAKING_EXTERNALS`](#ONE_DANGEROUSLY_ACCEPT_BREAKING_EXTERNALS) * [`ONE_CSP_ALLOW_INLINE_SCRIPTS`](#ONE_CSP_ALLOW_INLINE_SCRIPTS) * [`ONE_DANGEROUSLY_DISABLE_CSP`](#ONE_DANGEROUSLY_DISABLE_CSP) + * [`ONE_CONFIG_USE_NATIVE_POLYFILL`](#one_config_use_native_polyfill) * Server Settings * [`HOLOCRON_SERVER_MAX_MODULES_RETRY`](#holocron_server_max_modules_retry) * [`HOLOCRON_SERVER_MAX_SIM_MODULES_FETCH`](#holocron_server_max_sim_modules_fetch) @@ -74,6 +76,7 @@ One App can be configured via Environment Variables: * [`ONE_CLIENT_REPORTING_URL`](#one_client_reporting_url) ⚠️ * [`ONE_CLIENT_ROOT_MODULE_NAME`](#one_client_root_module_name) ⚠️ * [`ONE_CONFIG_ENV`](#one_config_env) ⚠️ + * [`ONE_CONFIG_USE_NATIVE_POLYFILL`](#one_config_use_native_polyfill) * [`ONE_ENABLE_POST_TO_MODULE_ROUTES`](#one_enable_post_to_module_routes) * [`ONE_MAP_POLLING_MAX`](#one_map_polling_max) * [`ONE_MAP_POLLING_MIN`](#one_map_polling_min) @@ -524,6 +527,28 @@ ONE_CONFIG_ENV=staging ONE_CONFIG_ENV=undefined ``` +## `ONE_CONFIG_USE_NATIVE_POLYFILL` + +**Runs In** +* ✅ Production +* ✅ Development + +Feature flag to disable lean-intl polyfill. + +**Shape** +```bash +ONE_CONFIG_USE_NATIVE_POLYFILL=Boolean +``` +**Example** +```bash +ONE_CONFIG_USE_NATIVE_POLYFILL=true +``` + +**Default Value** +```bash +ONE_CONFIG_USE_NATIVE_POLYFILL=false +``` + ## `ONE_DANGEROUSLY_ACCEPT_BREAKING_EXTERNALS` **Runs In** @@ -824,4 +849,4 @@ OTEL_RESOURCE_ATTRIBUTES="foo=bar;baz=qux" [`HTTPS_PRIVATE_KEY_PASS_FILE_PATH`]: #https_private_key_pass_file_path [`HTTPS_PORT`]: #https_port [OTel Environment Variable Specification]: https://opentelemetry.io/docs/specs/otel/configuration/sdk-environment-variables/ -[OTel Resource SDK documentation]: https://opentelemetry.io/docs/specs/otel/resource/sdk/#specifying-resource-information-via-an-environment-variable \ No newline at end of file +[OTel Resource SDK documentation]: https://opentelemetry.io/docs/specs/otel/resource/sdk/#specifying-resource-information-via-an-environment-variable From 29ab5dddf040e1f1807d45464d72ee24b623fedc Mon Sep 17 00:00:00 2001 From: Tim Barton Date: Tue, 23 Jan 2024 16:28:54 +0000 Subject: [PATCH 3/5] feat(optionalIntl): longer doc desc --- docs/api/server/Environment-Variables.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/api/server/Environment-Variables.md b/docs/api/server/Environment-Variables.md index af11eca41..d24ce7e65 100644 --- a/docs/api/server/Environment-Variables.md +++ b/docs/api/server/Environment-Variables.md @@ -534,6 +534,7 @@ ONE_CONFIG_ENV=undefined * ✅ Development Feature flag to disable lean-intl polyfill. +This allows you to use modern intl features such as timezones, but will result in your application supporting fewer older browsers. **Shape** ```bash From 941a1d7f8f2f9065bd3b53f05458c38e64566b90 Mon Sep 17 00:00:00 2001 From: Tim Barton Date: Wed, 24 Jan 2024 16:54:58 +0000 Subject: [PATCH 4/5] feat(optionalIntl): test typo --- __tests__/server/config/env/runTime.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__tests__/server/config/env/runTime.spec.js b/__tests__/server/config/env/runTime.spec.js index 71d2f5697..cd6eb5ae5 100644 --- a/__tests__/server/config/env/runTime.spec.js +++ b/__tests__/server/config/env/runTime.spec.js @@ -556,7 +556,7 @@ describe('runTime', () => { expect(useNativePolyfill.defaultValue).toBe('false'); }); - it('should normalise the value to either to false when not explicitly true', () => { + it('should normalise the value to false when not explicitly true', () => { expect(useNativePolyfill.normalize('Value')).toBe('false'); expect(useNativePolyfill.normalize('VALUE')).toBe('false'); expect(useNativePolyfill.normalize('true')).toBe('true'); From 8f2b89ef88d95e3e6d335d1449be9f75ceee6e76 Mon Sep 17 00:00:00 2001 From: Tim Barton Date: Wed, 24 Jan 2024 17:20:22 +0000 Subject: [PATCH 5/5] feat(optionalIntl): env var typo --- .github/labeler.yml | 3 +- .github/workflows/on-pr-and-trunk_codeql.yml | 6 +- .github/workflows/on-pr_pr-labeler.yml | 3 +- CHANGELOG.md | 32 - README.md | 4 +- __performance__/README.md | 4 +- __performance__/docker-compose.yml | 6 +- __tests__/integration/helpers/testRunner.js | 5 +- __tests__/integration/one-app.spec.js | 6 +- __tests__/server/index.spec.js | 130 +--- .../server/plugins/reactHtml/index.spec.jsx | 3 +- __tests__/server/ssrServer.spec.js | 349 +++++++-- __tests__/server/utils/cdnCache.spec.js | 12 - .../attachRequestSpies.spec.js.snap | 107 --- .../__snapshots__/attachSpy.spec.js.snap | 5 - .../utils/logging/attachRequestSpies.spec.js | 131 ---- .../server/utils/logging/attachSpy.spec.js | 84 -- .../server/utils/logging/config/otel.spec.js | 24 +- __tests__/server/utils/logging/setup.spec.js | 69 +- one-app-statics/package.json | 2 +- package-lock.json | 733 ++++++------------ package.json | 24 +- prod-sample/README.md | 28 +- prod-sample/api/Dockerfile | 4 +- prod-sample/docker-compose.yml | 4 +- prod-sample/generate-certs.sh | 5 + scripts/build-one-app-docker-setup.js | 117 +-- src/server/index.js | 8 - src/server/polyfill/intl.js | 2 +- src/server/ssrServer.js | 51 +- src/server/utils/cdnCache.js | 5 +- .../utils/logging/attachRequestSpies.js | 89 --- src/server/utils/logging/attachSpy.js | 47 -- src/server/utils/logging/config/otel.js | 8 +- src/server/utils/logging/setup.js | 12 +- 35 files changed, 760 insertions(+), 1362 deletions(-) delete mode 100644 __tests__/server/utils/logging/__snapshots__/attachRequestSpies.spec.js.snap delete mode 100644 __tests__/server/utils/logging/__snapshots__/attachSpy.spec.js.snap delete mode 100644 __tests__/server/utils/logging/attachRequestSpies.spec.js delete mode 100644 __tests__/server/utils/logging/attachSpy.spec.js delete mode 100644 src/server/utils/logging/attachRequestSpies.js delete mode 100644 src/server/utils/logging/attachSpy.js diff --git a/.github/labeler.yml b/.github/labeler.yml index 5bf08d368..f5e446c81 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -1,3 +1,2 @@ one-app-team-review-requested: - - changed-files: - - any-glob-to-any-file: "*/**" + - '**/*' diff --git a/.github/workflows/on-pr-and-trunk_codeql.yml b/.github/workflows/on-pr-and-trunk_codeql.yml index 4541129db..90503fbfb 100644 --- a/.github/workflows/on-pr-and-trunk_codeql.yml +++ b/.github/workflows/on-pr-and-trunk_codeql.yml @@ -34,7 +34,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v3 + uses: github/codeql-action/init@v2 with: languages: ${{ matrix.language }} config-file: ./.github/codeql/codeql-config.yml @@ -46,7 +46,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@v3 + uses: github/codeql-action/autobuild@v2 # ℹ️ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl @@ -60,4 +60,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v3 + uses: github/codeql-action/analyze@v2 diff --git a/.github/workflows/on-pr_pr-labeler.yml b/.github/workflows/on-pr_pr-labeler.yml index 82c55626f..0fff16208 100644 --- a/.github/workflows/on-pr_pr-labeler.yml +++ b/.github/workflows/on-pr_pr-labeler.yml @@ -12,7 +12,6 @@ jobs: configuration-path: .github/pr-labeler.yml # optional, .github/pr-labeler.yml is the default value env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - uses: actions/checkout@v4 - - uses: actions/labeler@v5 + - uses: actions/labeler@v4 with: repo-token: "${{ secrets.GITHUB_TOKEN }}" diff --git a/CHANGELOG.md b/CHANGELOG.md index 643d1d16a..6a8e3bdf0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,38 +2,6 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. -## [6.9.2](https://github.com/americanexpress/one-app/compare/v6.9.1...v6.9.2) (2024-01-23) - - -### Bug Fixes - -* **cdnCache:** make clear cache message generic ([#1248](https://github.com/americanexpress/one-app/issues/1248)) ([cac2e81](https://github.com/americanexpress/one-app/commit/cac2e81d633f1a0a051c9b1f864d9819f219e68f)) -* **deps:** revert proxy agent ([#1249](https://github.com/americanexpress/one-app/issues/1249)) ([badbd98](https://github.com/americanexpress/one-app/commit/badbd98459dfab8db3765f554beeeda96782ce64)) -* **errorHandler:** application errors would result in a 200 response ([#1243](https://github.com/americanexpress/one-app/issues/1243)) ([26a7740](https://github.com/americanexpress/one-app/commit/26a77403ced6fc11fd581788dba32d5e2d866bcb)) -* **ssrServer:** fastify scope resulted in hooks being called more than once ([#1242](https://github.com/americanexpress/one-app/issues/1242)) ([2d84d6a](https://github.com/americanexpress/one-app/commit/2d84d6a628799e75d1412226edeeeef6ce1e71c2)) - - -## [6.9.1](https://github.com/americanexpress/one-app/compare/v6.9.0...v6.9.1) (2024-01-22) - - -### Bug Fixes - -* **proxy:** fix proxy configuration ([dd89ecd](https://github.com/americanexpress/one-app/commit/dd89ecd771647e85d17e964e3f6704b0d304a243)) - - -# [6.9.0](https://github.com/americanexpress/one-app/compare/v6.8.3...v6.9.0) (2024-01-17) - - -### Bug Fixes - -* **devCdn:** handle different module files ([48aa35f](https://github.com/americanexpress/one-app/commit/48aa35fc73c718f87531822e2cba754100993799)) - - -### Features - -* **otel:** log notice to STDOUT when using OTel ([#1215](https://github.com/americanexpress/one-app/issues/1215)) ([b4bcf21](https://github.com/americanexpress/one-app/commit/b4bcf2189e99c08ca6f8a4dad577047d9e73ffc0)) - - ## [6.8.3](https://github.com/americanexpress/one-app/compare/v6.8.2...v6.8.3) (2023-12-06) diff --git a/README.md b/README.md index 7aabee12c..89dd4b09b 100644 --- a/README.md +++ b/README.md @@ -6,8 +6,8 @@ ![One App Docker Release](https://github.com/americanexpress/one-app/actions/workflows/release-step-4_automatic_docker-prod-build-and-publish.yml/badge.svg) -![One App Integration Tests](https://github.com/americanexpress/one-app/actions/workflows/on-pr_one-app-integration-tests.yml/badge.svg) -![One App Unit and Lint Tests](https://github.com/americanexpress/one-app/actions/workflows/on-pr-and-trunk_one-app-unit-and-lint-tests.yml/badge.svg) +![One App Integration Tests](https://github.com/americanexpress/one-app/workflows/One%20App%20Integration%20Tests/badge.svg) +![One App Unit and Lint Tests](https://github.com/americanexpress/one-app/workflows/One%20App%20Unit%20and%20Lint%20Tests/badge.svg) One App is a fresh take on web application development. It consists of a [Node.js](https://nodejs.org) server that serves up a single page app built using [React] components diff --git a/__performance__/README.md b/__performance__/README.md index 64fffb0ed..4993e67b9 100644 --- a/__performance__/README.md +++ b/__performance__/README.md @@ -92,7 +92,7 @@ You can now view the [Grafana metrics on localhost:3030](http://localhost:3030/d In another new window from within `one-app/__performance__` run the following: ```bash -docker compose -f docker-compose.yml -f docker-compose.prod-sample.yml run k6 run --insecure-skip-tls-verify /scripts/smoke.js +docker-compose -f docker-compose.yml -f docker-compose.prod-sample.yml run k6 run --insecure-skip-tls-verify /scripts/smoke.js ``` Each file under `__perforamance__/scripts` correlates to a different test: @@ -137,7 +137,7 @@ You can now view the [Grafana metrics on localhost:3030](http://localhost:3030/d In another new window from within `one-app/__performance__` run the following: ```bash -docker compose run -e TARGET_URL=http://host.docker.internal:3000/success k6 run /scripts/smoke.js +docker-compose run -e TARGET_URL=http://host.docker.internal:3000/success k6 run /scripts/smoke.js ``` Replace the `TARGET_URL` value with your intended target domain. diff --git a/__performance__/docker-compose.yml b/__performance__/docker-compose.yml index 6e427ccd1..b5f9880dd 100644 --- a/__performance__/docker-compose.yml +++ b/__performance__/docker-compose.yml @@ -16,7 +16,7 @@ services: # receives one-app internals metrics prometheus: - image: prom/prometheus:v2.49.1 + image: prom/prometheus:latest networks: - one-app-performance volumes: @@ -26,7 +26,7 @@ services: # graphing of metrics grafana: - image: grafana/grafana:10.0.10 + image: grafana/grafana:latest networks: - one-app-performance ports: @@ -43,7 +43,7 @@ services: # runs performance scripts k6: - image: grafana/k6:0.48.0 + image: loadimpact/k6:latest networks: - one-app-performance ports: diff --git a/__tests__/integration/helpers/testRunner.js b/__tests__/integration/helpers/testRunner.js index e3d55fc4b..60e807064 100644 --- a/__tests__/integration/helpers/testRunner.js +++ b/__tests__/integration/helpers/testRunner.js @@ -44,8 +44,6 @@ const setUpTestRunner = async ({ 'selenium-chrome': { ports: [ `${seleniumServerPort}:4444`, - '7901:7900', - '5901:5900', ], }, ...oneAppLocalPortToUse && { @@ -67,7 +65,7 @@ const setUpTestRunner = async ({ fs.writeFileSync(pathToDockerComposeTestFile, yaml.dump(testDockerComposeFileContents)); - const dockerComposeUpCommand = `docker compose -f ${pathToDockerComposeTestFile} up --abort-on-container-exit --force-recreate`; + const dockerComposeUpCommand = `docker-compose -f ${pathToDockerComposeTestFile} up --abort-on-container-exit --force-recreate`; const dockerComposeUpProcess = childProcess.spawn(`${dockerComposeUpCommand}`, { shell: true }); const serverStartupTimeout = 90000; @@ -105,7 +103,6 @@ const setUpTestRunner = async ({ path: '/wd/hub', port: seleniumServerPort, capabilities: { - acceptInsecureCerts: true, 'goog:chromeOptions': { ...!oneAppLocalPortToUse && process.env.HTTPS_PROXY && { args: [`--proxy-server=${process.env.HTTPS_PROXY}`] }, diff --git a/__tests__/integration/one-app.spec.js b/__tests__/integration/one-app.spec.js index 5b43154ef..2e69a66d2 100644 --- a/__tests__/integration/one-app.spec.js +++ b/__tests__/integration/one-app.spec.js @@ -178,7 +178,7 @@ describe('Tests that require Docker setup', () => { }, }); - expect(response.status).toBe(404); + expect(response.status).toBe(200); // preflight-only headers const rawHeaders = response.headers.raw(); expect(rawHeaders).not.toHaveProperty('access-control-max-age'); @@ -599,7 +599,7 @@ describe('Tests that require Docker setup', () => { { level: 'SEVERE', message: expect.stringMatching( - /https:\/\/one-app:8443\/demo\/healthy-frank - Failed to find a valid digest in the 'integrity' attribute for resource 'https:\/\/sample-cdn\.frank\/modules\/.+\/healthy-frank\/0\.0\.0\/healthy-frank.browser.js' with computed SHA-384 integrity '.+'\. The resource has been blocked\./ + /https:\/\/one-app:8443\/demo\/healthy-frank - Failed to find a valid digest in the 'integrity' attribute for resource 'https:\/\/sample-cdn\.frank\/modules\/.+\/healthy-frank\/0\.0\.0\/healthy-frank.browser.js' with computed SHA-256 integrity '.+'\. The resource has been blocked\./ ), source: 'security', timestamp: expect.any(Number), @@ -1711,7 +1711,7 @@ describe('Tests that can run against either local Docker setup or remote One App 'accept-language': 'en-US,en;q=0.9', host: expect.any(String), 'user-agent': - 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', + 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.75 Safari/537.36', }, method: 'GET', originalUrl: '/vitruvius', diff --git a/__tests__/server/index.spec.js b/__tests__/server/index.spec.js index c55b848c6..060180c94 100644 --- a/__tests__/server/index.spec.js +++ b/__tests__/server/index.spec.js @@ -24,10 +24,8 @@ import { Map as ImmutableMap } from 'immutable'; jest.unmock('yargs'); describe('server index', () => { - jest.spyOn(console, 'log').mockImplementation(() => {}); - jest.spyOn(console, 'error').mockImplementation(() => {}); - jest.spyOn(process.stdout, 'write').mockImplementation(() => {}); - jest.spyOn(process.stderr, 'write').mockImplementation(() => {}); + jest.spyOn(console, 'log').mockImplementation(() => { }); + jest.spyOn(console, 'error').mockImplementation(() => { }); const origFsExistsSync = fs.existsSync; let addServer; @@ -57,8 +55,7 @@ describe('server index', () => { jest.doMock('@americanexpress/one-app-dev-proxy', () => ({ default: jest.fn(() => ({ listen: jest.fn((port, cb) => { - setTimeout(() => (oneAppDevProxyError ? cb(new Error('test error')) : cb(null, { port })) - ); + setTimeout(() => (oneAppDevProxyError ? cb(new Error('test error')) : cb(null, { port }))); return { close: 'one-app-dev-proxy' }; }), })), @@ -71,20 +68,21 @@ describe('server index', () => { jest.doMock('cross-fetch'); - jest.doMock('../../src/server/utils/loadModules', () => jest.fn(() => Promise.resolve())); + jest.doMock( + '../../src/server/utils/loadModules', + () => jest.fn(() => Promise.resolve()) + ); jest.doMock('babel-polyfill', () => { // jest includes babel-polyfill // if included twice, babel-polyfill will complain that it should be only once }); // jest.doMock('../../src/server/polyfill/intl'); - jest.doMock('../../src/server/utils/logging/setup', () => {}); + jest.doMock('../../src/server/utils/logging/setup', () => { }); ssrServerListen = jest.fn(async () => { if (ssrServerError) { - const error = new Error('ssr server test error'); - error.stack = `${error.toString()}\n at :1:1`; - throw error; + throw new Error('test error'); } }); ssrServer = jest.fn(() => ({ @@ -95,9 +93,7 @@ describe('server index', () => { metricsServerListen = jest.fn(async () => { if (metricsServerError) { - const error = new Error('metrics server test error'); - error.stack = `${error.toString()}\n at :1:1`; - throw error; + throw new Error('test error'); } }); jest.doMock('../../src/server/metricsServer', () => () => ({ @@ -107,7 +103,7 @@ describe('server index', () => { devHolocronCDNListen = jest.fn(async () => { if (devHolocronCdnError) { - throw new Error('dev cdn test error'); + throw new Error('test error'); } }); jest.doMock('../../src/server/devHolocronCDN', () => ({ @@ -122,7 +118,7 @@ describe('server index', () => { jest.doMock('../../src/server/shutdown', () => ({ addServer, shutdown })); jest.doMock('../../src/server/utils/pollModuleMap', () => jest.fn()); jest.doMock('../../src/server/config/env/runTime', () => jest.fn()); - jest.doMock('../../src/server/utils/heapdump', () => {}); + jest.doMock('../../src/server/utils/heapdump', () => { }); jest.doMock('../../src/server/utils/watchLocalModules', () => ({ default: jest.fn() })); jest.doMock('../../src/server/utils/getHttpsConfig', () => () => 'https-config-mock'); @@ -177,15 +173,13 @@ describe('server index', () => { const endpointsFilePath = path.join(process.cwd(), '.dev', 'endpoints', 'index.js'); process.env.NODE_ENV = 'development'; fs.existsSync = () => true; - jest.doMock( - endpointsFilePath, - () => () => ({ - oneTestEndpointUrl: { - devProxyPath: 'test', - destination: 'https://example.com', - }, - }), - { virtual: true } + jest.doMock(endpointsFilePath, () => () => ({ + oneTestEndpointUrl: { + devProxyPath: 'test', + destination: 'https://example.com', + }, + }), + { virtual: true } ); await load(); fs.existsSync = origFsExistsSync; @@ -198,15 +192,13 @@ describe('server index', () => { const endpointsFilePath = path.join(process.cwd(), '.dev', 'endpoints', 'index.js'); process.env.NODE_ENV = 'development'; fs.existsSync = () => false; - jest.doMock( - endpointsFilePath, - () => () => ({ - oneTestEndpointUrl: { - devProxyPath: 'test', - destination: 'https://example.com', - }, - }), - { virtual: true } + jest.doMock(endpointsFilePath, () => () => ({ + oneTestEndpointUrl: { + devProxyPath: 'test', + destination: 'https://example.com', + }, + }), + { virtual: true } ); await load(); fs.existsSync = origFsExistsSync; @@ -358,7 +350,9 @@ describe('server index', () => { await load(); - expect(ssrServerListen).toHaveBeenCalledWith({ host: '0.0.0.0', port: '5555' }); + expect(ssrServerListen).toHaveBeenCalledWith( + { host: '0.0.0.0', port: '5555' } + ); expect(ssrServer).toHaveBeenCalledWith({ https: 'https-config-mock' }); }); @@ -370,26 +364,6 @@ describe('server index', () => { expect(util.format(...console.error.mock.calls[0])).toMatchSnapshot(); }); - it('does not log a notice directly to STDERR when not using OTel and listening on the server fails', async () => { - delete process.env.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT; - await load({ ssrServerError: true }); - expect(process.stderr.write).not.toHaveBeenCalled(); - }); - - it('logs a notice directly to STDERR when using OTel and listening on the server fails', async () => { - process.env.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT = 'http://0.0.0.0:4317/v1/logs'; - await load({ ssrServerError: true }); - expect(process.stderr.write).toHaveBeenCalledTimes(1); - expect(process.stderr.write.mock.calls[0][0]).toMatchInlineSnapshot(` - " - one-app failed to start. Logs are being sent to OTel via gRPC at http://0.0.0.0:4317/v1/logs - - Error: ssr server test error - at :1:1" - `); - delete process.env.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT; - }); - it('logs errors when listening on the metrics server fails', async () => { await load({ metricsServerError: true }); @@ -398,26 +372,6 @@ describe('server index', () => { expect(util.format(...console.error.mock.calls[0])).toMatchSnapshot(); }); - it('does not log a notice directly to STDERR when not using OTel and listening on the metrics server fails', async () => { - delete process.env.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT; - await load({ metricsServerError: true }); - expect(process.stderr.write).not.toHaveBeenCalled(); - }); - - it('logs a notice directly to STDERR when using OTel and listening on the metrics server fails', async () => { - process.env.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT = 'http://0.0.0.0:4317/v1/logs'; - await load({ metricsServerError: true }); - expect(process.stderr.write).toHaveBeenCalledTimes(1); - expect(process.stderr.write.mock.calls[0][0]).toMatchInlineSnapshot(` - " - one-app failed to start. Logs are being sent to OTel via gRPC at http://0.0.0.0:4317/v1/logs - - Error: metrics server test error - at :1:1" - `); - delete process.env.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT; - }); - it('closes servers when starting ssrServer fails', async () => { process.env.NODE_ENV = 'development'; @@ -432,9 +386,7 @@ describe('server index', () => { await load(); expect(console.log).toHaveBeenCalled(); - expect(util.format(...console.log.mock.calls[1])).toMatch( - '🌎 One App server listening on port 3000' - ); + expect(util.format(...console.log.mock.calls[1])).toMatch('🌎 One App server listening on port 3000'); }); it('logs when metrics server is successfully listening on the port', async () => { @@ -444,9 +396,7 @@ describe('server index', () => { await load(); expect(console.log).toHaveBeenCalled(); - expect(util.format(...console.log.mock.calls[0])).toMatch( - '📊 Metrics server listening on port 3005' - ); + expect(util.format(...console.log.mock.calls[0])).toMatch('📊 Metrics server listening on port 3005'); }); it('initiates module-map polling if successfully listening on port', async () => { @@ -457,24 +407,6 @@ describe('server index', () => { expect(pollModuleMap).toHaveBeenCalledTimes(1); }); - it('does not log a notice to STDOUT when not using OTel', async () => { - delete process.env.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT; - await load(); - expect(process.stdout.write).not.toHaveBeenCalled(); - }); - - it('logs a notice to STDOUT when using OTel', async () => { - process.env.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT = 'http://0.0.0.0:4317/v1/logs'; - await load(); - expect(process.stdout.write).toHaveBeenCalledTimes(1); - expect(process.stdout.write.mock.calls[0][0]).toMatchInlineSnapshot(` - " - one-app started successfully. Logs are being sent to OTel via gRPC at http://0.0.0.0:4317/v1/logs - " - `); - delete process.env.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT; - }); - afterAll(() => { delete process.env.ONE_CLIENT_ROOT_MODULE_NAME; }); diff --git a/__tests__/server/plugins/reactHtml/index.spec.jsx b/__tests__/server/plugins/reactHtml/index.spec.jsx index 25580b319..34054a2c6 100644 --- a/__tests__/server/plugins/reactHtml/index.spec.jsx +++ b/__tests__/server/plugins/reactHtml/index.spec.jsx @@ -22,7 +22,8 @@ import reactHtml, { sendHtml, renderModuleScripts, renderExternalFallbacks, - checkStateForRedirectAndStatusCode, renderUseNativeIntlPolyfill, + checkStateForRedirectAndStatusCode, + renderUseNativeIntlPolyfill, } from '../../../../src/server/plugins/reactHtml'; // _client is a method to control the mock // eslint-disable-next-line import/named diff --git a/__tests__/server/ssrServer.spec.js b/__tests__/server/ssrServer.spec.js index 452d8b43b..4ced03a43 100644 --- a/__tests__/server/ssrServer.spec.js +++ b/__tests__/server/ssrServer.spec.js @@ -86,33 +86,30 @@ afterAll(() => { }); describe('ssrServer', () => { - const mockFastifyInstance = { - register: jest.fn(async (plugin) => { - plugin(mockFastifyInstance, null, jest.fn()); - }), - setNotFoundHandler: jest.fn(), - setErrorHandler: jest.fn(), - ready: jest.fn(), - addContentTypeParser: jest.fn(), - get: jest.fn(), - post: jest.fn(), - }; - const { - setNotFoundHandler, - setErrorHandler, - ready, - get, - post, - register, - } = mockFastifyInstance; - - Fastify.mockImplementation(() => mockFastifyInstance); + let register; + let setNotFoundHandler; + let setErrorHandler; + let ready; beforeEach(() => { + // jest.resetModules(); jest.clearAllMocks(); process.env.NODE_ENV = 'development'; delete process.env.ONE_ENABLE_POST_TO_MODULE_ROUTES; delete process.env.ONE_MAX_POST_REQUEST_PAYLOAD; + + register = jest.fn(); + setNotFoundHandler = jest.fn(); + setErrorHandler = jest.fn(); + ready = jest.fn(); + + Fastify.mockImplementationOnce(() => ({ + register, + setNotFoundHandler, + setErrorHandler, + ready, + addContentTypeParser: jest.fn(), + })); }); test('builds the fastify server and registers plugins in the correct order', async () => { @@ -125,13 +122,13 @@ describe('ssrServer', () => { disableRequestLogging: true, }); - expect(register).toHaveBeenCalledTimes(22); - expect(register.mock.calls[1][0]).toEqual(fastifySensible); - expect(register.mock.calls[2][0]).toEqual(ensureCorrelationId); - expect(register.mock.calls[3][0]).toEqual(fastifyCookie); - expect(register.mock.calls[4][0]).toEqual(logging); - expect(register.mock.calls[5][0]).toEqual(fastifyMetrics); - expect(register.mock.calls[6]).toEqual([ + expect(register).toHaveBeenCalledTimes(13); + expect(register.mock.calls[0][0]).toEqual(fastifySensible); + expect(register.mock.calls[1][0]).toEqual(ensureCorrelationId); + expect(register.mock.calls[2][0]).toEqual(fastifyCookie); + expect(register.mock.calls[3][0]).toEqual(logging); + expect(register.mock.calls[4][0]).toEqual(fastifyMetrics); + expect(register.mock.calls[5]).toEqual([ compress, { zlibOptions: { @@ -140,17 +137,30 @@ describe('ssrServer', () => { encodings: ['gzip'], }, ]); - expect(register.mock.calls[7][0]).toEqual(fastifyFormbody); - expect(register.mock.calls[8]).toEqual([ + expect(register.mock.calls[6][0]).toEqual(fastifyFormbody); + expect(register.mock.calls[7]).toEqual([ addSecurityHeadersPlugin, { matchGetRoutes: ['/_/status', '/_/pwa/service-worker.js', '/_/pwa/manifest.webmanifest'], }, ]); - expect(register.mock.calls[9][0]).toEqual(setAppVersionHeader); - expect(register.mock.calls[10][0]).toEqual(forwardedHeaderParser); + expect(register.mock.calls[8][0]).toEqual(setAppVersionHeader); + expect(register.mock.calls[9][0]).toEqual(forwardedHeaderParser); + expect(register.mock.calls[10][0]).toEqual(expect.any(Function)); // abstraction expect(register.mock.calls[11][0]).toEqual(expect.any(Function)); // abstraction - expect(register.mock.calls[12]).toEqual([ + expect(register.mock.calls[12][0]).toEqual(expect.any(Function)); // abstraction + + const staticRegister = jest.fn(); + register.mock.calls[10][0]( + { + register: staticRegister, + get: jest.fn(), + }, + null, + jest.fn() + ); + + expect(staticRegister.mock.calls[0]).toEqual([ fastifyStatic, { root: path.join(__dirname, '../../build'), @@ -158,12 +168,35 @@ describe('ssrServer', () => { maxAge: '182d', }, ]); - expect(register.mock.calls[13][0]).toEqual(expect.any(Function)); // abstraction - expect(register.mock.calls[14][0]).toEqual(addCacheHeaders); - expect(register.mock.calls[15][0]).toEqual(csp); - expect(register.mock.calls[17][0]).toEqual(addCacheHeaders); - expect(register.mock.calls[18][0]).toEqual(csp); - expect(register.mock.calls[19]).toEqual([ + + const pwaRegister = jest.fn(); + register.mock.calls[11][0]( + { + register: pwaRegister, + get: jest.fn(), + post: jest.fn(), + }, + null, + jest.fn() + ); + + expect(pwaRegister.mock.calls[0][0]).toEqual(addCacheHeaders); + expect(pwaRegister.mock.calls[1][0]).toEqual(csp); + + const renderRegister = jest.fn(); + register.mock.calls[12][0]( + { + register: renderRegister, + get: jest.fn(), + post: jest.fn(), + }, + null, + jest.fn() + ); + + expect(renderRegister.mock.calls[0][0]).toEqual(addCacheHeaders); + expect(renderRegister.mock.calls[1][0]).toEqual(csp); + expect(renderRegister.mock.calls[2]).toEqual([ fastifyHelmet, { crossOriginEmbedderPolicy: false, @@ -173,8 +206,8 @@ describe('ssrServer', () => { contentSecurityPolicy: false, }, ]); - expect(register.mock.calls[20][0]).toEqual(addFrameOptionsHeader); - expect(register.mock.calls[21][0]).toEqual(renderHtml); + expect(renderRegister.mock.calls[3][0]).toEqual(addFrameOptionsHeader); + expect(renderRegister.mock.calls[4][0]).toEqual(renderHtml); expect(setNotFoundHandler).toHaveBeenCalledTimes(1); expect(setErrorHandler).toHaveBeenCalledTimes(1); @@ -208,6 +241,18 @@ describe('ssrServer', () => { describe('static routes', () => { test('/_/status responds with 200', async () => { await ssrServer(); + + const get = jest.fn(); + + register.mock.calls[10][0]( + { + register: jest.fn(), + get, + }, + null, + jest.fn() + ); + const reply = { status: jest.fn(() => reply), send: jest.fn(() => reply), @@ -223,6 +268,17 @@ describe('ssrServer', () => { test('/_/pwa/service-worker.js uses the serviceWorkerHandler handler', async () => { await ssrServer(); + const get = jest.fn(); + + register.mock.calls[10][0]( + { + register: jest.fn(), + get, + }, + null, + jest.fn() + ); + const reply = { status: jest.fn(() => reply), send: jest.fn(() => reply), @@ -237,13 +293,25 @@ describe('ssrServer', () => { test('/_/pwa/manifest.webmanifest uses the webManifestMiddleware handler', async () => { await ssrServer(); + const get = jest.fn(); + + register.mock.calls[11][0]( + { + register: jest.fn(), + get, + post: jest.fn(), + }, + null, + jest.fn() + ); + const reply = { status: jest.fn(() => reply), send: jest.fn(() => reply), }; - expect(get.mock.calls[2][0]).toEqual('/_/pwa/manifest.webmanifest'); - expect(get.mock.calls[2][1]).toEqual(webManifestMiddleware); + expect(get.mock.calls[0][0]).toEqual('/_/pwa/manifest.webmanifest'); + expect(get.mock.calls[0][1]).toEqual(webManifestMiddleware); }); describe('DEVELOPMENT', () => { @@ -251,6 +319,18 @@ describe('ssrServer', () => { test('/_/report/security/csp-violation report with no data', async () => { await ssrServer(); + const post = jest.fn(); + + register.mock.calls[11][0]( + { + register: jest.fn(), + get: jest.fn(), + post, + }, + null, + jest.fn() + ); + const request = { log: { warn: jest.fn() }, }; @@ -274,6 +354,18 @@ describe('ssrServer', () => { test('/_/report/security/csp-violation report with no data', async () => { await ssrServer(); + const post = jest.fn(); + + register.mock.calls[11][0]( + { + register: jest.fn(), + get: jest.fn(), + post, + }, + null, + jest.fn() + ); + const request = { headers: { 'Content-Type': 'application/csp-report', @@ -300,6 +392,18 @@ describe('ssrServer', () => { test('/_/report/security/csp-violation reports data', async () => { await ssrServer(); + const post = jest.fn(); + + register.mock.calls[11][0]( + { + register: jest.fn(), + get: jest.fn(), + post, + }, + null, + jest.fn() + ); + const request = { body: JSON.stringify({ 'csp-report': { @@ -331,6 +435,18 @@ describe('ssrServer', () => { test('/_/report/errors responds with 204', async () => { await ssrServer(); + const post = jest.fn(); + + register.mock.calls[11][0]( + { + register: jest.fn(), + get: jest.fn(), + post, + }, + null, + jest.fn() + ); + const request = { log: { warn: jest.fn(), @@ -358,6 +474,18 @@ describe('ssrServer', () => { process.env.NODE_ENV = 'production'; await ssrServer(); + const post = jest.fn(); + + register.mock.calls[11][0]( + { + register: jest.fn(), + get: jest.fn(), + post, + }, + null, + jest.fn() + ); + const request = { headers: {}, log: { warn: jest.fn() }, @@ -383,6 +511,18 @@ describe('ssrServer', () => { process.env.NODE_ENV = 'production'; await ssrServer(); + const post = jest.fn(); + + register.mock.calls[11][0]( + { + register: jest.fn(), + get: jest.fn(), + post, + }, + null, + jest.fn() + ); + const request = { headers: {}, body: JSON.stringify({ @@ -411,6 +551,18 @@ describe('ssrServer', () => { await ssrServer(); + const post = jest.fn(); + + register.mock.calls[11][0]( + { + register: jest.fn(), + get: jest.fn(), + post, + }, + null, + jest.fn() + ); + const request = { headers: { 'content-type': 'text/plain', @@ -439,6 +591,18 @@ describe('ssrServer', () => { await ssrServer(); + const post = jest.fn(); + + register.mock.calls[11][0]( + { + register: jest.fn(), + get: jest.fn(), + post, + }, + null, + jest.fn() + ); + const request = { headers: { 'content-type': 'application/json', @@ -472,6 +636,18 @@ describe('ssrServer', () => { await ssrServer(); + const post = jest.fn(); + + register.mock.calls[11][0]( + { + register: jest.fn(), + get: jest.fn(), + post, + }, + null, + jest.fn() + ); + const request = { headers: { 'content-type': 'application/json', @@ -511,6 +687,18 @@ describe('ssrServer', () => { await ssrServer(); + const post = jest.fn(); + + register.mock.calls[11][0]( + { + register: jest.fn(), + get: jest.fn(), + post, + }, + null, + jest.fn() + ); + const request = { headers: { 'content-type': 'application/json', @@ -564,13 +752,23 @@ describe('ssrServer', () => { await ssrServer(); + const get = jest.fn(); + register.mock.calls[12][0]( + { + register: jest.fn(), + get, + }, + null, + jest.fn() + ); + const reply = { sendHtml: jest.fn(() => reply), }; - get.mock.calls[3][1](null, reply); + get.mock.calls[0][1](null, reply); - expect(get.mock.calls[3][0]).toEqual('/_/pwa/shell'); + expect(get.mock.calls[0][0]).toEqual('/_/pwa/shell'); expect(reply.sendHtml).toHaveBeenCalled(); }); @@ -581,14 +779,24 @@ describe('ssrServer', () => { await ssrServer(); + const get = jest.fn(); + register.mock.calls[12][0]( + { + register: jest.fn(), + get, + }, + null, + jest.fn() + ); + const reply = { status: jest.fn(() => reply), send: jest.fn(() => reply), }; - get.mock.calls[3][1](null, reply); + get.mock.calls[0][1](null, reply); - expect(get.mock.calls[3][0]).toEqual('/_/pwa/shell'); + expect(get.mock.calls[0][0]).toEqual('/_/pwa/shell'); expect(reply.status).toHaveBeenCalledWith(404); expect(reply.send).toHaveBeenCalledWith('Not found'); }); @@ -596,22 +804,41 @@ describe('ssrServer', () => { test('any other GET route renders html', async () => { await ssrServer(); + const get = jest.fn(); + register.mock.calls[12][0]( + { + register: jest.fn(), + get, + }, + null, + jest.fn() + ); + const reply = { sendHtml: jest.fn(() => reply), }; - get.mock.calls[4][1](null, reply); + get.mock.calls[1][1](null, reply); - expect(get.mock.calls[4][0]).toEqual('/*'); + expect(get.mock.calls[1][0]).toEqual('/*'); expect(reply.sendHtml).toHaveBeenCalled(); }); test('any other POST route renders html is disabled', async () => { await ssrServer(); - expect(post).toHaveBeenCalledTimes(2); - expect(post.mock.calls[0][0]).toEqual('/_/report/security/csp-violation'); - expect(post.mock.calls[1][0]).toEqual('/_/report/errors'); + const post = jest.fn(); + register.mock.calls[12][0]( + { + register: jest.fn(), + get: jest.fn(), + post, + }, + null, + jest.fn() + ); + + expect(post).not.toHaveBeenCalled(); }); test('any other POST route renders html', async () => { @@ -619,14 +846,24 @@ describe('ssrServer', () => { await ssrServer(); + const post = jest.fn(); + register.mock.calls[12][0]( + { + register: jest.fn(), + get: jest.fn(), + post, + }, + null, + jest.fn() + ); + const reply = { sendHtml: jest.fn(() => reply), }; - post.mock.calls[2][1](null, reply); + post.mock.calls[0][1](null, reply); - expect(post).toHaveBeenCalledTimes(3); - expect(post.mock.calls[2][0]).toEqual('/*'); + expect(post.mock.calls[0][0]).toEqual('/*'); expect(reply.sendHtml).toHaveBeenCalled(); }); }); @@ -676,7 +913,6 @@ describe('ssrServer', () => { '"Fastify application error: method get, url "/example", correlationId "undefined", headersSent: false [Error: testing]"' ); expect(renderStaticErrorPage).toHaveBeenCalledWith(request, reply); - expect(reply.code).toHaveBeenCalledWith(500); }); test('setErrorHandler logs an error and renders the static error page with "headersSent" and "correlationId"', async () => { @@ -710,7 +946,6 @@ describe('ssrServer', () => { '"Fastify application error: method get, url "/example", correlationId "123", headersSent: true [Error: testing]"' ); expect(renderStaticErrorPage).toHaveBeenCalledWith(request, reply); - expect(reply.code).toHaveBeenCalledWith(500); }); }); }); diff --git a/__tests__/server/utils/cdnCache.spec.js b/__tests__/server/utils/cdnCache.spec.js index 212db823e..b51646cd7 100644 --- a/__tests__/server/utils/cdnCache.spec.js +++ b/__tests__/server/utils/cdnCache.spec.js @@ -93,18 +93,6 @@ describe('cacheUtils', () => { expect(logSpy).not.toHaveBeenCalled(); expect(errorSpy).toHaveBeenCalledWith('There was error checking file stat', expectedError); }); - - it('showCacheInfo should show file to delete', async () => { - const mockStats = { - size: 1024 * 1024 * 5, // 5 MB - }; - fsPromises.stat.mockResolvedValue(mockStats); - - await showCacheInfo(); - - expect(logSpy).toHaveBeenCalledWith('To clear the cache, please delete this file:'); - expect(logSpy).toHaveBeenCalledWith(' ~/.one-app/.one-app-module-cache'); - }); }); describe('setupCacheFile', () => { diff --git a/__tests__/server/utils/logging/__snapshots__/attachRequestSpies.spec.js.snap b/__tests__/server/utils/logging/__snapshots__/attachRequestSpies.spec.js.snap deleted file mode 100644 index cc95d7561..000000000 --- a/__tests__/server/utils/logging/__snapshots__/attachRequestSpies.spec.js.snap +++ /dev/null @@ -1,107 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`attachRequestSpies requestSpy is called with object options 1`] = ` -Url { - "auth": "user:password", - "hash": "#so-blue", - "host": "example.tld:8080", - "hostname": "example.tld", - "href": "https://user:password@example.tld:8080/somewhere?over=rainbow#so-blue", - "path": "/somewhere?over=rainbow", - "pathname": "/somewhere", - "port": "8080", - "protocol": "https:", - "query": "over=rainbow", - "search": "?over=rainbow", - "slashes": true, -} -`; - -exports[`attachRequestSpies requestSpy is called with object options 2`] = ` -Url { - "auth": "user:password", - "hash": "#so-blue", - "host": "example.tld:8080", - "hostname": "example.tld", - "href": "http://user:password@example.tld:8080/somewhere?over=rainbow#so-blue", - "path": "/somewhere?over=rainbow", - "pathname": "/somewhere", - "port": "8080", - "protocol": "http:", - "query": "over=rainbow", - "search": "?over=rainbow", - "slashes": true, -} -`; - -exports[`attachRequestSpies requestSpy is called with parsed options 1`] = ` -Url { - "auth": "user:password", - "hash": "#so-blue", - "host": "example.tld:8080", - "hostname": "example.tld", - "href": "http://user:password@example.tld:8080/somewhere?over=rainbow#so-blue", - "path": "/somewhere?over=rainbow", - "pathname": "/somewhere", - "port": "8080", - "protocol": "http:", - "query": "over=rainbow", - "search": "?over=rainbow", - "slashes": true, -} -`; - -exports[`attachRequestSpies requestSpy is called with sparse object options 1`] = ` -Url { - "auth": null, - "hash": null, - "host": "localhost", - "hostname": "localhost", - "href": "https://localhost/", - "path": "/", - "pathname": "/", - "port": null, - "protocol": "https:", - "query": null, - "search": null, - "slashes": true, -} -`; - -exports[`attachRequestSpies requestSpy is called with sparse object options 2`] = ` -Url { - "auth": null, - "hash": null, - "host": "localhost", - "hostname": "localhost", - "href": "http://localhost/", - "path": "/", - "pathname": "/", - "port": null, - "protocol": "http:", - "query": null, - "search": null, - "slashes": true, -} -`; - -exports[`attachRequestSpies socketCloseSpy is called when the request socket closes 1`] = ` -Url { - "auth": null, - "hash": null, - "host": "example.tld", - "hostname": "example.tld", - "href": "http://example.tld/", - "path": "/", - "pathname": "/", - "port": null, - "protocol": "http:", - "query": null, - "search": null, - "slashes": true, -} -`; - -exports[`attachRequestSpies throws if requestSpy is not a function 1`] = `"requestSpy must be a function (was "undefined")"`; - -exports[`attachRequestSpies throws if socketCloseSpy is provided but is not a function 1`] = `"socketCloseSpy must be function if provided (was "string")"`; diff --git a/__tests__/server/utils/logging/__snapshots__/attachSpy.spec.js.snap b/__tests__/server/utils/logging/__snapshots__/attachSpy.spec.js.snap deleted file mode 100644 index 4212324da..000000000 --- a/__tests__/server/utils/logging/__snapshots__/attachSpy.spec.js.snap +++ /dev/null @@ -1,5 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`attachSpy throws if the method name is not a function on the object 1`] = `"method is not a function"`; - -exports[`attachSpy throws if the spy is not a function 1`] = `"spy must be a function (was "string")"`; diff --git a/__tests__/server/utils/logging/attachRequestSpies.spec.js b/__tests__/server/utils/logging/attachRequestSpies.spec.js deleted file mode 100644 index 2b1802b48..000000000 --- a/__tests__/server/utils/logging/attachRequestSpies.spec.js +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright 2023 American Express Travel Related Services Company, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,either express - * or implied. See the License for the specific language governing permissions and limitations - * under the License. - */ - -import http from 'node:http'; -import https from 'node:https'; -import onFinished from 'on-finished'; - -import attachRequestSpies from '../../../../src/server/utils/logging/attachRequestSpies'; - -jest.mock('node:http', () => ({ request: jest.fn() })); -jest.mock('node:https', () => ({ request: jest.fn() })); -jest.mock('on-finished'); - -describe('attachRequestSpies', () => { - it('throws if requestSpy is not a function', () => { - expect(attachRequestSpies).toThrowErrorMatchingSnapshot(); - }); - - it('does not throw if socketCloseSpy is not provided', () => { - expect(() => attachRequestSpies(() => {})).not.toThrow(); - }); - - it('throws if socketCloseSpy is provided but is not a function', () => { - expect(() => attachRequestSpies(() => {}, 'apples')).toThrowErrorMatchingSnapshot(); - }); - - it('attaches http and https spies', () => { - const requestSpy = jest.fn(); - const originalHttpRequest = http.request; - const originalHttpsRequest = https.request; - attachRequestSpies(requestSpy); - expect(http.request).not.toEqual(originalHttpRequest); - expect(https.request).not.toEqual(originalHttpsRequest); - http.request('http://example.com'); - https.request('https://example.com'); - expect(requestSpy).toHaveBeenCalledTimes(2); - }); - - describe('requestSpy', () => { - it('is called with clientRequest', () => { - const fakeOriginalHttpRequest = jest.fn(); - const fakeOriginalHttpsRequest = jest.fn(); - http.request = fakeOriginalHttpRequest; - https.request = fakeOriginalHttpsRequest; - - const requestSpy = jest.fn(); - attachRequestSpies(requestSpy); - http.request('http://example.tld'); - https.request('https://example.tld'); - - expect(requestSpy).toHaveBeenCalledTimes(2); - expect(fakeOriginalHttpRequest).toHaveBeenCalledWith('http://example.tld'); - expect(fakeOriginalHttpsRequest).toHaveBeenCalledWith('https://example.tld'); - }); - - it('is called with object options', () => { - const requestSpy = jest.fn(); - attachRequestSpies(requestSpy); - - https.request({ - protocol: 'https', - hostname: 'example.tld', - port: 8080, - method: 'GET', - path: '/somewhere?over=rainbow#so-blue', - auth: 'user:password', - }); - - http.request({ - protocol: 'http', - hostname: 'example.tld', - port: 8080, - method: 'GET', - path: '/somewhere?over=rainbow#so-blue', - auth: 'user:password', - }); - - expect(requestSpy).toHaveBeenCalledTimes(2); - expect(requestSpy.mock.calls[0][1]).toMatchSnapshot(); - expect(requestSpy.mock.calls[1][1]).toMatchSnapshot(); - }); - - it('is called with sparse object options', () => { - const requestSpy = jest.fn(); - attachRequestSpies(requestSpy); - - https.request({ method: 'GET' }); - http.request({ method: 'GET' }); - - expect(requestSpy).toHaveBeenCalledTimes(2); - expect(requestSpy.mock.calls[0][1]).toMatchSnapshot(); - expect(requestSpy.mock.calls[1][1]).toMatchSnapshot(); - }); - - it('is called with parsed options', () => { - const requestSpy = jest.fn(); - attachRequestSpies(requestSpy); - http.request('http://user:password@example.tld:8080/somewhere?over=rainbow#so-blue'); - expect(requestSpy).toHaveBeenCalledTimes(1); - expect(requestSpy.mock.calls[0][1]).toMatchSnapshot(); - }); - }); - - describe('socketCloseSpy', () => { - it('is called when the request socket closes', () => { - const fakeOriginalRequest = jest.fn(); - http.request = fakeOriginalRequest; - onFinished.mockClear(); - const socketCloseSpy = jest.fn(); - attachRequestSpies(jest.fn(), socketCloseSpy); - - http.request('http://example.tld'); - expect(onFinished).toHaveBeenCalledTimes(1); - onFinished.mock.calls[0][1](); - expect(socketCloseSpy).toHaveBeenCalledTimes(1); - expect(socketCloseSpy.mock.calls[0][1]).toMatchSnapshot(); - expect(fakeOriginalRequest).toHaveBeenCalledTimes(1); - }); - }); -}); diff --git a/__tests__/server/utils/logging/attachSpy.spec.js b/__tests__/server/utils/logging/attachSpy.spec.js deleted file mode 100644 index ad47854b7..000000000 --- a/__tests__/server/utils/logging/attachSpy.spec.js +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright 2023 American Express Travel Related Services Company, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,either express - * or implied. See the License for the specific language governing permissions and limitations - * under the License. - */ - -import attachSpy from '../../../../src/server/utils/logging/attachSpy'; - -describe('attachSpy', () => { - it('throws if the method name is not a function on the object', () => { - expect(() => attachSpy({}, 'method', () => {})).toThrowErrorMatchingSnapshot(); - }); - it('throws if the spy is not a function', () => { - expect(() => attachSpy({ method: () => {} }, 'method', 'hello')).toThrowErrorMatchingSnapshot(); - }); - describe('monkeypatched method', () => { - const originalMethod = jest.fn(() => 'some return value'); - const obj = {}; - - beforeEach(() => { - obj.method = originalMethod; - originalMethod.mockClear(); - }); - - it('invokes the spy', () => { - const spy = jest.fn(); - attachSpy(obj, 'method', spy); - expect(spy).not.toHaveBeenCalled(); - obj.method(); - expect(spy).toHaveBeenCalledTimes(1); - }); - - it('invokes the spy with args', () => { - const spy = jest.fn(); - attachSpy(obj, 'method', spy); - obj.method('g', { h: 'i' }, 9); - expect(spy).toHaveBeenCalledTimes(1); - expect(spy.mock.calls[0][0]).toEqual(['g', { h: 'i' }, 9]); - }); - - it('invokes the spy with callOriginal', () => { - const spy = jest.fn(); - attachSpy(obj, 'method', spy); - obj.method(); - expect(spy).toHaveBeenCalledTimes(1); - expect(typeof spy.mock.calls[0][1]).toEqual('function'); - }); - - it('returns the original methods return value from callOriginal', () => { - expect.assertions(1); - attachSpy(obj, 'method', (args, callOriginal) => { - expect(callOriginal()).toEqual('some return value'); - }); - obj.method(); - }); - - it('calls the original method when the spy does not', () => { - attachSpy(obj, 'method', () => {}); - obj.method(); - expect(originalMethod).toHaveBeenCalledTimes(1); - }); - - it('does not call the original method when the spy already had', () => { - attachSpy(obj, 'method', (args, callOriginal) => { - callOriginal(); - }); - obj.method(); - expect(originalMethod).toHaveBeenCalledTimes(1); - }); - - it('returns the original methods return value to the caller of the monkeypatched method', () => { - attachSpy(obj, 'method', jest.fn()); - expect(obj.method()).toEqual('some return value'); - }); - }); -}); diff --git a/__tests__/server/utils/logging/config/otel.spec.js b/__tests__/server/utils/logging/config/otel.spec.js index 88a942cd2..35b9fd0dc 100644 --- a/__tests__/server/utils/logging/config/otel.spec.js +++ b/__tests__/server/utils/logging/config/otel.spec.js @@ -125,12 +125,14 @@ describe('OpenTelemetry logging', () => { expect(pino.transport.mock.calls[0][0]).toMatchInlineSnapshot(` { "options": { - "logRecordProcessorOptions": { - "exporterOptions": { - "protocol": "grpc", + "logRecordProcessorOptions": [ + { + "exporterOptions": { + "protocol": "grpc", + }, + "recordProcessorType": "batch", }, - "recordProcessorType": "batch", - }, + ], "loggerName": "Mock Service Name", "messageKey": "message", "resourceAttributes": { @@ -178,12 +180,14 @@ describe('OpenTelemetry logging', () => { expect(pino.transport).toHaveBeenCalledTimes(1); expect(pino.transport.mock.calls[0][0].options.logRecordProcessorOptions) .toMatchInlineSnapshot(` - { - "exporterOptions": { - "protocol": "grpc", + [ + { + "exporterOptions": { + "protocol": "grpc", + }, + "recordProcessorType": "batch", }, - "recordProcessorType": "batch", - } + ] `); expect(pino.transport.mock.results[0].value).toBe(transport); }); diff --git a/__tests__/server/utils/logging/setup.spec.js b/__tests__/server/utils/logging/setup.spec.js index 29cd6581e..fe4c7f639 100644 --- a/__tests__/server/utils/logging/setup.spec.js +++ b/__tests__/server/utils/logging/setup.spec.js @@ -23,7 +23,7 @@ jest.mock('yargs', () => ({ })); describe('setup', () => { - let attachRequestSpies; + let monkeypatches; let logger; let startTimer; @@ -40,12 +40,12 @@ describe('setup', () => { function load() { jest.resetModules(); - jest.mock('../../../../src/server/utils/logging/attachRequestSpies'); + jest.mock('@americanexpress/lumberjack'); jest.mock('../../../../src/server/utils/logging/timing', () => ({ startTimer: jest.fn(), measureTime: jest.fn(() => 12), })); - attachRequestSpies = require('../../../../src/server/utils/logging/attachRequestSpies').default; + ({ monkeypatches } = require('@americanexpress/lumberjack')); ({ startTimer } = require('../../../../src/server/utils/logging/timing')); logger = require('../../../../src/server/utils/logging/logger').default; require('../../../../src/server/utils/logging/setup'); @@ -60,9 +60,36 @@ describe('setup', () => { }); }); - it('spies on requests', () => { + it('spies on HTTP requests', () => { load(); - expect(attachRequestSpies).toHaveBeenCalledTimes(1); + expect(monkeypatches.attachHttpRequestSpy).toHaveBeenCalledTimes(1); + }); + + it('spies on HTTPS requests when the node major version is higher than 8', () => { + Object.defineProperty(process, 'version', { + writable: true, + value: 'v10.0.0', + }); + load(); + expect(monkeypatches.attachHttpsRequestSpy).toHaveBeenCalledTimes(1); + }); + + it('does not spy on HTTPS requests when the node major version is 8 or lower', () => { + Object.defineProperty(process, 'version', { + writable: true, + value: 'v8.0.0', + }); + load(); + expect(monkeypatches.attachHttpsRequestSpy).not.toHaveBeenCalled(); + }); + + it('does not spy on HTTPS requests when the node major version is 6 or lower', () => { + Object.defineProperty(process, 'version', { + writable: true, + value: 'v6.0.0', + }); + load(); + expect(monkeypatches.attachHttpsRequestSpy).not.toHaveBeenCalled(); }); describe('logging outgoing requests', () => { @@ -88,8 +115,8 @@ describe('setup', () => { it('starts a timer when the request starts', () => { const { externalRequest } = createExternalRequestAndParsedUrl(); load(); - expect(attachRequestSpies).toHaveBeenCalledTimes(1); - const outgoingRequestSpy = attachRequestSpies.mock.calls[0][0]; + expect(monkeypatches.attachHttpRequestSpy).toHaveBeenCalledTimes(1); + const outgoingRequestSpy = monkeypatches.attachHttpRequestSpy.mock.calls[0][0]; outgoingRequestSpy(externalRequest); expect(startTimer).toHaveBeenCalledTimes(1); expect(startTimer).toHaveBeenCalledWith(externalRequest); @@ -98,8 +125,8 @@ describe('setup', () => { it('is level info', () => { const { externalRequest, parsedUrl } = createExternalRequestAndParsedUrl(); load(); - expect(attachRequestSpies).toHaveBeenCalledTimes(1); - const outgoingRequestEndSpy = attachRequestSpies.mock.calls[0][1]; + expect(monkeypatches.attachHttpRequestSpy).toHaveBeenCalledTimes(1); + const outgoingRequestEndSpy = monkeypatches.attachHttpRequestSpy.mock.calls[0][1]; outgoingRequestEndSpy(externalRequest, parsedUrl); expect(logger.info).toHaveBeenCalledTimes(1); expect(logger.info.mock.calls[0]).toMatchSnapshot(); @@ -113,8 +140,8 @@ describe('setup', () => { } = createExternalRequestAndParsedUrl(); externalRequestHeaders['correlation-id'] = '1234'; load(); - expect(attachRequestSpies).toHaveBeenCalledTimes(1); - const outgoingRequestEndSpy = attachRequestSpies.mock.calls[0][1]; + expect(monkeypatches.attachHttpRequestSpy).toHaveBeenCalledTimes(1); + const outgoingRequestEndSpy = monkeypatches.attachHttpRequestSpy.mock.calls[0][1]; outgoingRequestEndSpy(externalRequest, parsedUrl); expect(logger.info).toHaveBeenCalledTimes(1); const entry = logger.info.mock.calls[0][0]; @@ -130,8 +157,8 @@ describe('setup', () => { } = createExternalRequestAndParsedUrl(); delete externalRequestHeaders['correlation-id']; load(); - expect(attachRequestSpies).toHaveBeenCalledTimes(1); - const outgoingRequestEndSpy = attachRequestSpies.mock.calls[0][1]; + expect(monkeypatches.attachHttpRequestSpy).toHaveBeenCalledTimes(1); + const outgoingRequestEndSpy = monkeypatches.attachHttpRequestSpy.mock.calls[0][1]; outgoingRequestEndSpy(externalRequest, parsedUrl); const entry = logger.info.mock.calls[0][0]; expect(entry.request.metaData).toHaveProperty('correlationId', undefined); @@ -141,8 +168,8 @@ describe('setup', () => { const { externalRequest, parsedUrl } = createExternalRequestAndParsedUrl(); externalRequest.res.statusCode = 200; load(); - expect(attachRequestSpies).toHaveBeenCalledTimes(1); - const outgoingRequestEndSpy = attachRequestSpies.mock.calls[0][1]; + expect(monkeypatches.attachHttpRequestSpy).toHaveBeenCalledTimes(1); + const outgoingRequestEndSpy = monkeypatches.attachHttpRequestSpy.mock.calls[0][1]; outgoingRequestEndSpy(externalRequest, parsedUrl); const entry = logger.info.mock.calls[0][0]; expect(entry.request).toHaveProperty('statusCode', 200); @@ -152,8 +179,8 @@ describe('setup', () => { const { externalRequest, parsedUrl } = createExternalRequestAndParsedUrl(); delete externalRequest.res.statusCode; load(); - expect(attachRequestSpies).toHaveBeenCalledTimes(1); - const outgoingRequestEndSpy = attachRequestSpies.mock.calls[0][1]; + expect(monkeypatches.attachHttpRequestSpy).toHaveBeenCalledTimes(1); + const outgoingRequestEndSpy = monkeypatches.attachHttpRequestSpy.mock.calls[0][1]; outgoingRequestEndSpy(externalRequest, parsedUrl); const entry = logger.info.mock.calls[0][0]; expect(entry.request).toHaveProperty('statusCode', null); @@ -163,8 +190,8 @@ describe('setup', () => { const { externalRequest, parsedUrl } = createExternalRequestAndParsedUrl(); externalRequest.res.statusMessage = 'OK'; load(); - expect(attachRequestSpies).toHaveBeenCalledTimes(1); - const outgoingRequestEndSpy = attachRequestSpies.mock.calls[0][1]; + expect(monkeypatches.attachHttpRequestSpy).toHaveBeenCalledTimes(1); + const outgoingRequestEndSpy = monkeypatches.attachHttpRequestSpy.mock.calls[0][1]; outgoingRequestEndSpy(externalRequest, parsedUrl); const entry = logger.info.mock.calls[0][0]; expect(entry.request).toHaveProperty('statusText', 'OK'); @@ -174,8 +201,8 @@ describe('setup', () => { const { externalRequest, parsedUrl } = createExternalRequestAndParsedUrl(); delete externalRequest.res.statusMessage; load(); - expect(attachRequestSpies).toHaveBeenCalledTimes(1); - const outgoingRequestEndSpy = attachRequestSpies.mock.calls[0][1]; + expect(monkeypatches.attachHttpRequestSpy).toHaveBeenCalledTimes(1); + const outgoingRequestEndSpy = monkeypatches.attachHttpRequestSpy.mock.calls[0][1]; outgoingRequestEndSpy(externalRequest, parsedUrl); const entry = logger.info.mock.calls[0][0]; expect(entry.request).toHaveProperty('statusText', null); diff --git a/one-app-statics/package.json b/one-app-statics/package.json index ab47b27d7..d1e25e6b4 100644 --- a/one-app-statics/package.json +++ b/one-app-statics/package.json @@ -1,6 +1,6 @@ { "name": "@americanexpress/one-app-statics", - "version": "6.9.2", + "version": "6.8.3", "description": "One App Static Assets", "main": "index.js", "scripts": { diff --git a/package-lock.json b/package-lock.json index f993fe0ef..d821cb65e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,17 +1,18 @@ { "name": "@americanexpress/one-app", - "version": "6.9.2", + "version": "6.8.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@americanexpress/one-app", - "version": "6.9.2", + "version": "6.8.3", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "@americanexpress/env-config-utils": "^2.0.4", + "@americanexpress/env-config-utils": "^2.0.2", "@americanexpress/fetch-enhancers": "^1.1.3", + "@americanexpress/lumberjack": "^1.1.4", "@americanexpress/one-app-bundler": "^6.21.1", "@americanexpress/one-app-ducks": "^4.4.2", "@americanexpress/one-app-router": "^1.2.1", @@ -21,7 +22,7 @@ "@fastify/cors": "^8.4.0", "@fastify/formbody": "^7.4.0", "@fastify/helmet": "^11.1.1", - "@fastify/rate-limit": "^9.1.0", + "@fastify/rate-limit": "^8.0.3", "@fastify/sensible": "^5.5.0", "@fastify/static": "^6.12.0", "@opentelemetry/semantic-conventions": "^1.15.2", @@ -31,12 +32,12 @@ "bytes": "^3.1.2", "cacheable-lookup": "^6.0.4", "chalk": "^4.1.2", - "core-js": "^3.35.0", + "core-js": "^3.33.2", "create-shared-react-context": "^1.0.5", "cross-fetch": "^4.0.0", "deepmerge": "^4.3.1", - "fastify": "^4.25.2", - "fastify-metrics": "^10.5.0", + "fastify": "^4.24.3", + "fastify-metrics": "^10.3.2", "fastify-plugin": "^4.5.1", "flat": "^5.0.2", "helmet": "^7.0.0", @@ -48,7 +49,6 @@ "lean-intl": "^4.2.2", "matcher": "^4.0.0", "object-hash": "^3.0.0", - "on-finished": "^2.4.1", "opossum": "^8.1.3", "opossum-prometheus": "^0.3.0", "pidusage": "^3.0.2", @@ -131,7 +131,7 @@ "standard-version": "^9.5.0", "supertest": "^6.2.4", "tar": "^6.1.15", - "webdriverio": "^7.34.0" + "webdriverio": "^7.20.5" }, "engines": { "node": ">=16 <=18", @@ -139,9 +139,9 @@ } }, "node_modules/@americanexpress/env-config-utils": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@americanexpress/env-config-utils/-/env-config-utils-2.0.4.tgz", - "integrity": "sha512-vEYbO4G/3uGG3swvIG9rxWXN6FaLOnZNpu7Twba6jY6swtH0RpeYEbQzU2piPVK53/jbV+YZ9mIBxb4UgckfiA==" + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@americanexpress/env-config-utils/-/env-config-utils-2.0.2.tgz", + "integrity": "sha512-knvdZTJYQs66qnyl19XRegq2yICiS0sIH2H2zOUixScnei/DqI9pm45Zag1QmCSjZg6zd/K4I5Yn1P7cc2bnxg==" }, "node_modules/@americanexpress/eslint-plugin-one-app": { "version": "6.13.5", @@ -164,6 +164,14 @@ "npm": ">=6" } }, + "node_modules/@americanexpress/lumberjack": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@americanexpress/lumberjack/-/lumberjack-1.1.4.tgz", + "integrity": "sha512-jsatpSbz4nTtrV29WYkZgydw80mGXjz9lVvfzdI8LsiaFM7uPrC0BYUDcg/JeXTFgqtnsLeBn0ZnLlg7Owm4Xg==", + "dependencies": { + "on-finished": "^2.3.0" + } + }, "node_modules/@americanexpress/one-app-bundler": { "version": "6.21.5", "resolved": "https://registry.npmjs.org/@americanexpress/one-app-bundler/-/one-app-bundler-6.21.5.tgz", @@ -3332,13 +3340,13 @@ } }, "node_modules/@fastify/rate-limit": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/@fastify/rate-limit/-/rate-limit-9.1.0.tgz", - "integrity": "sha512-h5dZWCkuZXN0PxwqaFQLxeln8/LNwQwH9popywmDCFdKfgpi4b/HoMH1lluy6P+30CG9yzzpSpwTCIPNB9T1JA==", + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/@fastify/rate-limit/-/rate-limit-8.0.3.tgz", + "integrity": "sha512-7wbSKXGKKLI1VkpW2XvS7SFg4n4/uzYt0YA5O2pfCcM6PYaBSV3VhSKGJ9/hJceCSH+zNEDRrWpufqxbcDkTZg==", "dependencies": { - "@lukeed/ms": "^2.0.1", "fastify-plugin": "^4.0.0", - "toad-cache": "^3.3.1" + "ms": "^2.1.3", + "tiny-lru": "^11.0.0" } }, "node_modules/@fastify/send": { @@ -6039,16 +6047,6 @@ "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==", "dev": true }, - "node_modules/@types/glob": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@types/glob/-/glob-8.1.0.tgz", - "integrity": "sha512-IO+MJPVhoqz+28h1qLAcBEH2+xHMK6MTyHJc7MTnnYb6wsoLR29POVGJ7LycmVXIqyy/4/2ShP5sUwTXuOwb/w==", - "dev": true, - "dependencies": { - "@types/minimatch": "^5.1.2", - "@types/node": "*" - } - }, "node_modules/@types/graceful-fs": { "version": "4.1.6", "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.6.tgz", @@ -6073,9 +6071,9 @@ "integrity": "sha512-h4lTMgMJctJybDp8CQrxTUiiYmedihHWkjnF/8Pxseu2S6Nlfcy8kwboQ8yejh456rP2yWoEVm1sS/FVsfM48w==" }, "node_modules/@types/http-cache-semantics": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", - "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz", + "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==", "dev": true }, "node_modules/@types/istanbul-lib-coverage": { @@ -6133,12 +6131,6 @@ "@types/node": "*" } }, - "node_modules/@types/minimatch": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", - "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==", - "dev": true - }, "node_modules/@types/minimist": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.2.tgz", @@ -6146,12 +6138,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "20.10.6", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.6.tgz", - "integrity": "sha512-Vac8H+NlRNNlAmDfGUP7b5h/KA+AtWIzuXy0E6OyP8f1tCLYAtPvKRRDJjAPqhpCb0t6U2j7/xqAuLEebW2kiw==", - "dependencies": { - "undici-types": "~5.26.4" - } + "version": "18.14.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.14.4.tgz", + "integrity": "sha512-VhCw7I7qO2X49+jaKcAUwi3rR+hbxT5VcYF493+Z5kMLI0DL568b7JI4IDJaxWFH0D/xwmGJNoXisyX+w7GH/g==" }, "node_modules/@types/normalize-package-data": { "version": "2.4.1", @@ -6238,9 +6227,9 @@ "dev": true }, "node_modules/@types/ua-parser-js": { - "version": "0.7.39", - "resolved": "https://registry.npmjs.org/@types/ua-parser-js/-/ua-parser-js-0.7.39.tgz", - "integrity": "sha512-P/oDfpofrdtF5xw433SPALpdSchtJmY7nsJItf8h3KXqOslkbySh8zq4dSWXH2oTjRvJ5PczVEoCZPow6GicLg==", + "version": "0.7.36", + "resolved": "https://registry.npmjs.org/@types/ua-parser-js/-/ua-parser-js-0.7.36.tgz", + "integrity": "sha512-N1rW+njavs70y2cApeIw1vLMYXRwfBy+7trgavGuuTfOd7j1Yh7QTRc/yqsPl6ncokt72ZXuxEU0PiCp9bSwNQ==", "dev": true }, "node_modules/@types/uglify-js": { @@ -6312,9 +6301,9 @@ "dev": true }, "node_modules/@types/yauzl": { - "version": "2.10.3", - "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", - "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==", "dev": true, "optional": true, "dependencies": { @@ -6438,15 +6427,14 @@ } }, "node_modules/@wdio/config": { - "version": "7.33.0", - "resolved": "https://registry.npmjs.org/@wdio/config/-/config-7.33.0.tgz", - "integrity": "sha512-SaCZNKrDtBghf7ujyaxTiU4pBW+1Kms32shSoXpJ/wFop6/MiA7nb19qpUPoJtEDw5/NOKevUKz8nBMBXphiew==", + "version": "7.30.0", + "resolved": "https://registry.npmjs.org/@wdio/config/-/config-7.30.0.tgz", + "integrity": "sha512-/38rol9WCfFTMtXyd/C856/aexxIZnfVvXg7Fw2WXpqZ9qadLA+R4N35S2703n/RByjK/5XAYtHoljtvh3727w==", "dev": true, "dependencies": { - "@types/glob": "^8.1.0", "@wdio/logger": "7.26.0", - "@wdio/types": "7.33.0", - "@wdio/utils": "7.33.0", + "@wdio/types": "7.26.0", + "@wdio/utils": "7.26.0", "deepmerge": "^4.0.0", "glob": "^8.0.3" }, @@ -6454,19 +6442,10 @@ "node": ">=12.0.0" } }, - "node_modules/@wdio/config/node_modules/@types/node": { - "version": "18.19.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.4.tgz", - "integrity": "sha512-xNzlUhzoHotIsnFoXmJB+yWmBvFZgKCI9TtPIEdYIMM1KWfwuY8zh7wvc1u1OAXlC7dlf6mZVx/s+Y5KfFz19A==", - "dev": true, - "dependencies": { - "undici-types": "~5.26.4" - } - }, "node_modules/@wdio/config/node_modules/@wdio/types": { - "version": "7.33.0", - "resolved": "https://registry.npmjs.org/@wdio/types/-/types-7.33.0.tgz", - "integrity": "sha512-tNcuN5Kl+i5CffaeTYV1omzAo4rVjiI1m9raIA8ph6iVteWdCzYv2/ImpGgFiBPb7Mf6VokU3+q9Slh5Jitaww==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@wdio/types/-/types-7.26.0.tgz", + "integrity": "sha512-mOTfWAGQ+iT58iaZhJMwlUkdEn3XEWE4jthysMLXFnSuZ2eaODVAiK31SmlS/eUqgSIaupeGqYUrtCuSNbLefg==", "dev": true, "dependencies": { "@types/node": "^18.0.0", @@ -6576,44 +6555,35 @@ } }, "node_modules/@wdio/repl": { - "version": "7.33.0", - "resolved": "https://registry.npmjs.org/@wdio/repl/-/repl-7.33.0.tgz", - "integrity": "sha512-17KM9NCg+UVpZNbS8koT/917vklF5M8IQnw0kGwmJEo444ifTMxmLwQymbS2ovQKAKAQxlfdM7bpqMeI15kzsQ==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@wdio/repl/-/repl-7.26.0.tgz", + "integrity": "sha512-2YxbXNfYVGVLrffUJzl/l5s8FziDPl917eLP62gkEH/H5IV27Pnwx3Iyu0KOEaBzgntnURANlwhCZFXQ4OPq8Q==", "dev": true, "dependencies": { - "@wdio/utils": "7.33.0" + "@wdio/utils": "7.26.0" }, "engines": { "node": ">=12.0.0" } }, "node_modules/@wdio/utils": { - "version": "7.33.0", - "resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-7.33.0.tgz", - "integrity": "sha512-4kQQ86EvEN6fBY5+u7M08cT6LfJtpk1rHd203xyxmbmV9lpNv/OCl4CsC+SD0jGT0aZZqYSIJ/Pil07pAh5K0g==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-7.26.0.tgz", + "integrity": "sha512-pVq2MPXZAYLkKGKIIHktHejnHqg4TYKoNYSi2EDv+I3GlT8VZKXHazKhci82ov0tD+GdF27+s4DWNDCfGYfBdQ==", "dev": true, "dependencies": { "@wdio/logger": "7.26.0", - "@wdio/types": "7.33.0", + "@wdio/types": "7.26.0", "p-iteration": "^1.1.8" }, "engines": { "node": ">=12.0.0" } }, - "node_modules/@wdio/utils/node_modules/@types/node": { - "version": "18.19.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.4.tgz", - "integrity": "sha512-xNzlUhzoHotIsnFoXmJB+yWmBvFZgKCI9TtPIEdYIMM1KWfwuY8zh7wvc1u1OAXlC7dlf6mZVx/s+Y5KfFz19A==", - "dev": true, - "dependencies": { - "undici-types": "~5.26.4" - } - }, "node_modules/@wdio/utils/node_modules/@wdio/types": { - "version": "7.33.0", - "resolved": "https://registry.npmjs.org/@wdio/types/-/types-7.33.0.tgz", - "integrity": "sha512-tNcuN5Kl+i5CffaeTYV1omzAo4rVjiI1m9raIA8ph6iVteWdCzYv2/ImpGgFiBPb7Mf6VokU3+q9Slh5Jitaww==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@wdio/types/-/types-7.26.0.tgz", + "integrity": "sha512-mOTfWAGQ+iT58iaZhJMwlUkdEn3XEWE4jthysMLXFnSuZ2eaODVAiK31SmlS/eUqgSIaupeGqYUrtCuSNbLefg==", "dev": true, "dependencies": { "@types/node": "^18.0.0", @@ -7135,16 +7105,16 @@ "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" }, "node_modules/archiver": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/archiver/-/archiver-5.3.2.tgz", - "integrity": "sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/archiver/-/archiver-5.3.1.tgz", + "integrity": "sha512-8KyabkmbYrH+9ibcTScQ1xCJC/CGcugdVIwB+53f5sZziXgwUh3iXlAlANMxcZyDEfTHMe6+Z5FofV8nopXP7w==", "dev": true, "dependencies": { "archiver-utils": "^2.1.0", - "async": "^3.2.4", + "async": "^3.2.3", "buffer-crc32": "^0.2.1", "readable-stream": "^3.6.0", - "readdir-glob": "^1.1.2", + "readdir-glob": "^1.0.0", "tar-stream": "^2.2.0", "zip-stream": "^4.1.0" }, @@ -7204,9 +7174,9 @@ } }, "node_modules/archiver/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.1.tgz", + "integrity": "sha512-+rQmrWMYGA90yenhTYsLWAsLsqVC8osOw6PKE1HDYiO0gdPeKe/xDHNzIAIn4C91YQ6oenEhfYqqc1883qHbjQ==", "dev": true, "dependencies": { "inherits": "^2.0.3", @@ -7474,9 +7444,9 @@ } }, "node_modules/async": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", - "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", + "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==", "dev": true }, "node_modules/async-each": { @@ -8087,9 +8057,9 @@ } }, "node_modules/bl/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.1.tgz", + "integrity": "sha512-+rQmrWMYGA90yenhTYsLWAsLsqVC8osOw6PKE1HDYiO0gdPeKe/xDHNzIAIn4C91YQ6oenEhfYqqc1883qHbjQ==", "dev": true, "dependencies": { "inherits": "^2.0.3", @@ -8717,9 +8687,9 @@ } }, "node_modules/chrome-launcher": { - "version": "0.15.2", - "resolved": "https://registry.npmjs.org/chrome-launcher/-/chrome-launcher-0.15.2.tgz", - "integrity": "sha512-zdLEwNo3aUVzIhKhTtXfxhdvZhUghrnmkvcAq2NoDd+LeOHKf03H5jwZ8T/STsAlzyALkBVK552iaG1fGf1xVQ==", + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/chrome-launcher/-/chrome-launcher-0.15.1.tgz", + "integrity": "sha512-UugC8u59/w2AyX5sHLZUHoxBAiSiunUhZa3zZwMH6zPVis0C3dDKiRWyUGIo14tTbZHGVviWxv3PQWZ7taZ4fg==", "dev": true, "dependencies": { "@types/node": "*", @@ -8990,9 +8960,9 @@ "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" }, "node_modules/compress-commons": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-4.1.2.tgz", - "integrity": "sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-4.1.1.tgz", + "integrity": "sha512-QLdDLCKNV2dtoTorqgxngQCMA+gWXkM/Nwu7FpeBhk/RdkzimqC3jueb/FDmaZeXh+uby1jkBqE3xArsLBE5wQ==", "dev": true, "dependencies": { "buffer-crc32": "^0.2.13", @@ -9005,9 +8975,9 @@ } }, "node_modules/compress-commons/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.1.tgz", + "integrity": "sha512-+rQmrWMYGA90yenhTYsLWAsLsqVC8osOw6PKE1HDYiO0gdPeKe/xDHNzIAIn4C91YQ6oenEhfYqqc1883qHbjQ==", "dev": true, "dependencies": { "inherits": "^2.0.3", @@ -9643,9 +9613,9 @@ } }, "node_modules/core-js": { - "version": "3.35.0", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.35.0.tgz", - "integrity": "sha512-ntakECeqg81KqMueeGJ79Q5ZgQNR+6eaE8sxGCx62zMbAIj65q+uYvatToew3m6eAGdU4gNZwpZ34NMe4GYswg==", + "version": "3.33.2", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.33.2.tgz", + "integrity": "sha512-XeBzWI6QL3nJQiHmdzbAOiMYqjrb7hwU7A39Qhvd/POSa/t9E1AeZyEZx3fNvp/vtM8zXwhoL0FsiS0hD0pruQ==", "hasInstallScript": true, "funding": { "type": "opencollective", @@ -9740,9 +9710,9 @@ } }, "node_modules/crc32-stream": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-4.0.3.tgz", - "integrity": "sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-4.0.2.tgz", + "integrity": "sha512-DxFZ/Hk473b/muq1VJ///PMNLj0ZMnzye9thBpmjpJKCc5eMgB95aK8zCGrGfQ90cWo561Te6HK9D+j4KPdM6w==", "dev": true, "dependencies": { "crc-32": "^1.2.0", @@ -9753,9 +9723,9 @@ } }, "node_modules/crc32-stream/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.1.tgz", + "integrity": "sha512-+rQmrWMYGA90yenhTYsLWAsLsqVC8osOw6PKE1HDYiO0gdPeKe/xDHNzIAIn4C91YQ6oenEhfYqqc1883qHbjQ==", "dev": true, "dependencies": { "inherits": "^2.0.3", @@ -10677,15 +10647,6 @@ "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==", "dev": true }, - "node_modules/dequal": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", - "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/des.js": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", @@ -10723,21 +10684,21 @@ } }, "node_modules/devtools": { - "version": "7.33.0", - "resolved": "https://registry.npmjs.org/devtools/-/devtools-7.33.0.tgz", - "integrity": "sha512-9sxWcdZLOUtgvw4kotL8HqvIFkO/yuHUecgqCYXnqIzwdWSoxWCeKAyZhOJNMeFtzjEnHGvIrUIquEuifk2STg==", + "version": "7.30.0", + "resolved": "https://registry.npmjs.org/devtools/-/devtools-7.30.0.tgz", + "integrity": "sha512-liC2nLMt/pEFTGwF+sCpciNPzQHsIfS+cQMBIBDWZBADBXLceftJxz1rBVrWsD3lM2t8dvpM4ZU7PiMScFDx8Q==", "dev": true, "dependencies": { "@types/node": "^18.0.0", "@types/ua-parser-js": "^0.7.33", - "@wdio/config": "7.33.0", + "@wdio/config": "7.30.0", "@wdio/logger": "7.26.0", "@wdio/protocols": "7.27.0", - "@wdio/types": "7.33.0", - "@wdio/utils": "7.33.0", + "@wdio/types": "7.26.0", + "@wdio/utils": "7.26.0", "chrome-launcher": "^0.15.0", "edge-paths": "^2.1.0", - "puppeteer-core": "13.1.3", + "puppeteer-core": "^13.1.3", "query-selector-shadow-dom": "^1.0.0", "ua-parser-js": "^1.0.1", "uuid": "^9.0.0" @@ -10747,24 +10708,15 @@ } }, "node_modules/devtools-protocol": { - "version": "0.0.1237913", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1237913.tgz", - "integrity": "sha512-Pxtmz2ZIqBkpU82HaIdsvCQBG94yTC4xajrEsWx9p38QKEfBCJktSazsHkrjf9j3dVVNPhg5LR21F6KWeXpjiQ==", + "version": "0.0.1102555", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1102555.tgz", + "integrity": "sha512-OmVCJhmZCpab9UW4gKyp8EJ7ZETaCg2FteiTSik6nNF/FmCGVVrIzzzhMBIm1yncM0X/L6f8zk5LFq0JOeE0Mg==", "dev": true }, - "node_modules/devtools/node_modules/@types/node": { - "version": "18.19.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.4.tgz", - "integrity": "sha512-xNzlUhzoHotIsnFoXmJB+yWmBvFZgKCI9TtPIEdYIMM1KWfwuY8zh7wvc1u1OAXlC7dlf6mZVx/s+Y5KfFz19A==", - "dev": true, - "dependencies": { - "undici-types": "~5.26.4" - } - }, "node_modules/devtools/node_modules/@wdio/types": { - "version": "7.33.0", - "resolved": "https://registry.npmjs.org/@wdio/types/-/types-7.33.0.tgz", - "integrity": "sha512-tNcuN5Kl+i5CffaeTYV1omzAo4rVjiI1m9raIA8ph6iVteWdCzYv2/ImpGgFiBPb7Mf6VokU3+q9Slh5Jitaww==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@wdio/types/-/types-7.26.0.tgz", + "integrity": "sha512-mOTfWAGQ+iT58iaZhJMwlUkdEn3XEWE4jthysMLXFnSuZ2eaODVAiK31SmlS/eUqgSIaupeGqYUrtCuSNbLefg==", "dev": true, "dependencies": { "@types/node": "^18.0.0", @@ -10782,161 +10734,6 @@ } } }, - "node_modules/devtools/node_modules/debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/devtools/node_modules/devtools-protocol": { - "version": "0.0.948846", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.948846.tgz", - "integrity": "sha512-5fGyt9xmMqUl2VI7+rnUkKCiAQIpLns8sfQtTENy5L70ktbNw0Z3TFJ1JoFNYdx/jffz4YXU45VF75wKZD7sZQ==", - "dev": true - }, - "node_modules/devtools/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/devtools/node_modules/https-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", - "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", - "dev": true, - "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/devtools/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/devtools/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/devtools/node_modules/node-fetch": { - "version": "2.6.7", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", - "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", - "dev": true, - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/devtools/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/devtools/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/devtools/node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/devtools/node_modules/puppeteer-core": { - "version": "13.1.3", - "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-13.1.3.tgz", - "integrity": "sha512-96pzvVBzq5lUGt3L/QrIH3mxn3NfZylHeusNhq06xBAHPI0Upc0SC/9u7tXjL0oRnmcExeVRJivr1lj7Ah/yDQ==", - "dev": true, - "dependencies": { - "debug": "4.3.2", - "devtools-protocol": "0.0.948846", - "extract-zip": "2.0.1", - "https-proxy-agent": "5.0.0", - "node-fetch": "2.6.7", - "pkg-dir": "4.2.0", - "progress": "2.0.3", - "proxy-from-env": "1.1.0", - "rimraf": "3.0.2", - "tar-fs": "2.1.1", - "unbzip2-stream": "1.4.3", - "ws": "8.2.3" - }, - "engines": { - "node": ">=10.18.1" - } - }, - "node_modules/devtools/node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "dev": true - }, "node_modules/devtools/node_modules/typescript": { "version": "4.9.5", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", @@ -10952,43 +10749,6 @@ "node": ">=4.2.0" } }, - "node_modules/devtools/node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "dev": true - }, - "node_modules/devtools/node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "dev": true, - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, - "node_modules/devtools/node_modules/ws": { - "version": "8.2.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz", - "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==", - "dev": true, - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, "node_modules/dezalgo": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", @@ -11935,14 +11695,15 @@ } }, "node_modules/escodegen": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", - "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz", + "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==", "dev": true, "dependencies": { "esprima": "^4.0.1", "estraverse": "^5.2.0", - "esutils": "^2.0.2" + "esutils": "^2.0.2", + "optionator": "^0.8.1" }, "bin": { "escodegen": "bin/escodegen.js", @@ -11955,6 +11716,45 @@ "source-map": "~0.6.1" } }, + "node_modules/escodegen/node_modules/levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", + "dev": true, + "dependencies": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/escodegen/node_modules/optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dev": true, + "dependencies": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/escodegen/node_modules/prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/escodegen/node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -11965,6 +11765,18 @@ "node": ">=0.10.0" } }, + "node_modules/escodegen/node_modules/type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", + "dev": true, + "dependencies": { + "prelude-ls": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/eslint": { "version": "7.32.0", "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.32.0.tgz", @@ -13471,9 +13283,9 @@ "integrity": "sha512-cIusKBIt/R/oI6z/1nyfe2FvGKVTohVRfvkOhvx0nCEW+xf5NoCXjAHcWp93uOUBchzYcsvPlrapAdX1uW+YGg==" }, "node_modules/fastify": { - "version": "4.25.2", - "resolved": "https://registry.npmjs.org/fastify/-/fastify-4.25.2.tgz", - "integrity": "sha512-SywRouGleDHvRh054onj+lEZnbC1sBCLkR0UY3oyJwjD4BdZJUrxBqfkfCaqn74pVCwBaRHGuL3nEWeHbHzAfw==", + "version": "4.24.3", + "resolved": "https://registry.npmjs.org/fastify/-/fastify-4.24.3.tgz", + "integrity": "sha512-6HHJ+R2x2LS3y1PqxnwEIjOTZxFl+8h4kSC/TuDPXtA+v2JnV9yEtOsNSKK1RMD7sIR2y1ZsA4BEFaid/cK5pg==", "dependencies": { "@fastify/ajv-compiler": "^3.5.0", "@fastify/error": "^3.4.0", @@ -13484,8 +13296,8 @@ "fast-json-stringify": "^5.8.0", "find-my-way": "^7.7.0", "light-my-request": "^5.11.0", - "pino": "^8.17.0", - "process-warning": "^3.0.0", + "pino": "^8.16.0", + "process-warning": "^2.2.0", "proxy-addr": "^2.0.7", "rfdc": "^1.3.0", "secure-json-parse": "^2.7.0", @@ -13494,9 +13306,9 @@ } }, "node_modules/fastify-metrics": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/fastify-metrics/-/fastify-metrics-10.5.0.tgz", - "integrity": "sha512-xxLN0o0U5GDGoBV2aWZnHYR10BgsHBTDC8kGWQPBoEPd8GnQskBA05xxnmCq3HCMYXvP/wXE0MUm6s6xYILRJA==", + "version": "10.3.2", + "resolved": "https://registry.npmjs.org/fastify-metrics/-/fastify-metrics-10.3.2.tgz", + "integrity": "sha512-02SEIGH02zfguqRMho0LB8L7YVAj5cIgWM0iqZslIErqaUWc1iHVAOC+YXYG3S2DZU6VHdFaMyuxjEOCQHAETA==", "dependencies": { "fastify-plugin": "^4.3.0", "prom-client": "^14.2.0" @@ -13510,11 +13322,6 @@ "resolved": "https://registry.npmjs.org/fastify-plugin/-/fastify-plugin-4.5.1.tgz", "integrity": "sha512-stRHYGeuqpEZTL1Ef0Ovr2ltazUT9g844X5z/zEBFLG8RYlpDiOCIG+ATvYEp+/zmc7sN29mcIMp8gvYplYPIQ==" }, - "node_modules/fastify/node_modules/process-warning": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-3.0.0.tgz", - "integrity": "sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ==" - }, "node_modules/fastq": { "version": "1.15.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", @@ -13961,9 +13768,9 @@ } }, "node_modules/fs-extra": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", - "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.0.tgz", + "integrity": "sha512-0rcTq621PD5jM/e0a3EJoGC/1TC5ZBCERW82LQuwfGnCa1V8w7dpYH1yNu+SLb6E5dkeCBzKEyLGlFrnr+dUyw==", "dev": true, "dependencies": { "graceful-fs": "^4.2.0", @@ -18507,9 +18314,9 @@ "dev": true }, "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.2.tgz", + "integrity": "sha512-5MHbFaKn8cNSmVW7BYnijeAVlE4cYA/SVkifVgrh7yotnfhKmjuXpDKjrABLnT0SfHWV21P8ow07OGfRrNDg8g==", "dev": true, "dependencies": { "json-buffer": "3.0.1" @@ -18656,9 +18463,9 @@ } }, "node_modules/lighthouse-logger": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/lighthouse-logger/-/lighthouse-logger-1.4.2.tgz", - "integrity": "sha512-gPWxznF6TKmUHrOQjlVo2UbaL2EJ71mb2CCeRs/2qBpi4L/g4LUVc9+3lKQ6DTUZwJswfM7ainGrLO1+fOqa2g==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/lighthouse-logger/-/lighthouse-logger-1.3.0.tgz", + "integrity": "sha512-BbqAKApLb9ywUli+0a+PcV04SyJ/N1q/8qgCNe6U97KbPCS1BTksEuHFLYdvc8DltuhfxIUBqDZsC0bBGtl3lA==", "dev": true, "dependencies": { "debug": "^2.6.9", @@ -21064,16 +20871,16 @@ } }, "node_modules/pino": { - "version": "8.17.2", - "resolved": "https://registry.npmjs.org/pino/-/pino-8.17.2.tgz", - "integrity": "sha512-LA6qKgeDMLr2ux2y/YiUt47EfgQ+S9LznBWOJdN3q1dx2sv0ziDLUBeVpyVv17TEcGCBuWf0zNtg3M5m1NhhWQ==", + "version": "8.16.1", + "resolved": "https://registry.npmjs.org/pino/-/pino-8.16.1.tgz", + "integrity": "sha512-3bKsVhBmgPjGV9pyn4fO/8RtoVDR8ssW1ev819FsRXlRNgW8gR/9Kx+gCK4UPWd4JjrRDLWpzd/pb1AyWm3MGA==", "dependencies": { "atomic-sleep": "^1.0.0", "fast-redact": "^3.1.1", "on-exit-leak-free": "^2.1.0", "pino-abstract-transport": "v1.1.0", "pino-std-serializers": "^6.0.0", - "process-warning": "^3.0.0", + "process-warning": "^2.0.0", "quick-format-unescaped": "^4.0.3", "real-require": "^0.2.0", "safe-stable-stringify": "^2.3.1", @@ -21155,11 +20962,6 @@ "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-6.2.1.tgz", "integrity": "sha512-wHuWB+CvSVb2XqXM0W/WOYUkVSPbiJb9S5fNB7TBhd8s892Xq910bRxwHtC4l71hgztObTjXL6ZheZXFjhDrDQ==" }, - "node_modules/pino/node_modules/process-warning": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-3.0.0.tgz", - "integrity": "sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ==" - }, "node_modules/pinpoint": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/pinpoint/-/pinpoint-1.1.0.tgz", @@ -22916,9 +22718,9 @@ } }, "node_modules/readdir-glob": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz", - "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.2.tgz", + "integrity": "sha512-6RLVvwJtVwEDfPdn6X6Ille4/lxGl0ATOY4FN/B9nxQcgOazvvI0nodiD19ScKq0PvA/29VpaOQML36o5IzZWA==", "dev": true, "dependencies": { "minimatch": "^5.1.0" @@ -25297,9 +25099,9 @@ } }, "node_modules/tar-stream/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.1.tgz", + "integrity": "sha512-+rQmrWMYGA90yenhTYsLWAsLsqVC8osOw6PKE1HDYiO0gdPeKe/xDHNzIAIn4C91YQ6oenEhfYqqc1883qHbjQ==", "dev": true, "dependencies": { "inherits": "^2.0.3", @@ -25641,6 +25443,14 @@ "node": ">=0.6.0" } }, + "node_modules/tiny-lru": { + "version": "11.2.3", + "resolved": "https://registry.npmjs.org/tiny-lru/-/tiny-lru-11.2.3.tgz", + "integrity": "sha512-mF9jPTrvN7UHk0bekOk3RlFdFwfyS4CJYVsGc7nInL3pVgUCYj5r9X6GpZBFQgLr0TKJo8Dp+F3oRvYzxU9xiA==", + "engines": { + "node": ">=12" + } + }, "node_modules/tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", @@ -25794,9 +25604,9 @@ } }, "node_modules/toad-cache": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/toad-cache/-/toad-cache-3.4.1.tgz", - "integrity": "sha512-T0m3MxP3wcqW0LaV3dF1mHBU294sgYSm4FOpa5eEJaYO7PqJZBOjZEQI1y4YaKNnih1FXCEYTWDS9osCoTUefg==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/toad-cache/-/toad-cache-3.3.0.tgz", + "integrity": "sha512-3oDzcogWGHZdkwrHyvJVpPjA7oNzY6ENOV3PsWJY9XYPZ6INo94Yd47s5may1U+nleBPwDhrRiTPMIvKaa3MQg==", "engines": { "node": ">=12" } @@ -26100,9 +25910,9 @@ } }, "node_modules/ua-parser-js": { - "version": "1.0.37", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.37.tgz", - "integrity": "sha512-bhTyI94tZofjo+Dn8SN6Zv8nBDvyXTymAdM3LDI/0IboIUwTu1rEhW7v2TfiVsoYWgkQ4kOVqnI8APUFbIQIFQ==", + "version": "1.0.33", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.33.tgz", + "integrity": "sha512-RqshF7TPTE0XLYAqmjlu5cLLuGdKrNu9O1KLA/qp39QtbZwuzwv1dT46DZSopoUMsYgXpB3Cv8a03FI8b74oFQ==", "dev": true, "funding": [ { @@ -26112,10 +25922,6 @@ { "type": "paypal", "url": "https://paypal.me/faisalman" - }, - { - "type": "github", - "url": "https://github.com/sponsors/faisalman" } ], "engines": { @@ -26165,11 +25971,6 @@ "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", "dev": true }, - "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" - }, "node_modules/unherit": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/unherit/-/unherit-1.1.3.tgz", @@ -26671,7 +26472,6 @@ "version": "3.9.19", "resolved": "https://registry.npmjs.org/vm2/-/vm2-3.9.19.tgz", "integrity": "sha512-J637XF0DHDMV57R6JyVsTak7nIL8gy5KH4r1HiwWLf/4GBbb5MKL5y7LpmF4A8E2nR6XmzpmMFQ7V7ppPTmUQg==", - "deprecated": "The library contains critical security issues and should not be used for production! The maintenance of the project has been discontinued. Consider migrating your code to isolated-vm.", "dev": true, "dependencies": { "acorn": "^8.7.0", @@ -27069,17 +26869,17 @@ } }, "node_modules/webdriver": { - "version": "7.33.0", - "resolved": "https://registry.npmjs.org/webdriver/-/webdriver-7.33.0.tgz", - "integrity": "sha512-cyMRAVUHgQhEBHojOeNet2e8GkfyvvjpioNCPcF6qUtT+URdagr8Mh0t4Fs+Jr0tpuMqFnw70xZexAcV/6I/jg==", + "version": "7.30.0", + "resolved": "https://registry.npmjs.org/webdriver/-/webdriver-7.30.0.tgz", + "integrity": "sha512-bQE4oVgjjg5sb3VkCD+Eb8mscEvf3TioP0mnEZK0f5OJUNI045gMCJgpX8X4J8ScGyEhzlhn1KvlAn3yzxjxog==", "dev": true, "dependencies": { "@types/node": "^18.0.0", - "@wdio/config": "7.33.0", + "@wdio/config": "7.30.0", "@wdio/logger": "7.26.0", "@wdio/protocols": "7.27.0", - "@wdio/types": "7.33.0", - "@wdio/utils": "7.33.0", + "@wdio/types": "7.26.0", + "@wdio/utils": "7.26.0", "got": "^11.0.2", "ky": "0.30.0", "lodash.merge": "^4.6.1" @@ -27088,19 +26888,10 @@ "node": ">=12.0.0" } }, - "node_modules/webdriver/node_modules/@types/node": { - "version": "18.19.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.4.tgz", - "integrity": "sha512-xNzlUhzoHotIsnFoXmJB+yWmBvFZgKCI9TtPIEdYIMM1KWfwuY8zh7wvc1u1OAXlC7dlf6mZVx/s+Y5KfFz19A==", - "dev": true, - "dependencies": { - "undici-types": "~5.26.4" - } - }, "node_modules/webdriver/node_modules/@wdio/types": { - "version": "7.33.0", - "resolved": "https://registry.npmjs.org/@wdio/types/-/types-7.33.0.tgz", - "integrity": "sha512-tNcuN5Kl+i5CffaeTYV1omzAo4rVjiI1m9raIA8ph6iVteWdCzYv2/ImpGgFiBPb7Mf6VokU3+q9Slh5Jitaww==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@wdio/types/-/types-7.26.0.tgz", + "integrity": "sha512-mOTfWAGQ+iT58iaZhJMwlUkdEn3XEWE4jthysMLXFnSuZ2eaODVAiK31SmlS/eUqgSIaupeGqYUrtCuSNbLefg==", "dev": true, "dependencies": { "@types/node": "^18.0.0", @@ -27134,26 +26925,26 @@ } }, "node_modules/webdriverio": { - "version": "7.34.0", - "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-7.34.0.tgz", - "integrity": "sha512-zlbWlA7izh/OwtrdCWJNDw16YWaOx2uRhBFrpk2zaSEn5HOWw7jHzLtvQFJ4qy2i2yguRPOcDudkRA5YOAeTnQ==", + "version": "7.30.1", + "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-7.30.1.tgz", + "integrity": "sha512-Eozs5JB91nogU42EQSvwsQyP9ovydXKSGzCTaI8xwpy2oSFKXWOI0ZPNIc+VcrDKWsa8SLomsuI/3lWl97o1Ew==", "dev": true, "dependencies": { "@types/aria-query": "^5.0.0", "@types/node": "^18.0.0", - "@wdio/config": "7.33.0", + "@wdio/config": "7.30.0", "@wdio/logger": "7.26.0", "@wdio/protocols": "7.27.0", - "@wdio/repl": "7.33.0", - "@wdio/types": "7.33.0", - "@wdio/utils": "7.33.0", + "@wdio/repl": "7.26.0", + "@wdio/types": "7.26.0", + "@wdio/utils": "7.26.0", "archiver": "^5.0.0", - "aria-query": "^5.2.1", + "aria-query": "^5.0.0", "css-shorthand-properties": "^1.1.1", "css-value": "^0.0.1", - "devtools": "7.33.0", - "devtools-protocol": "^0.0.1237913", - "fs-extra": "^11.1.1", + "devtools": "7.30.0", + "devtools-protocol": "^0.0.1102555", + "fs-extra": "^10.0.0", "grapheme-splitter": "^1.0.2", "lodash.clonedeep": "^4.5.0", "lodash.isobject": "^3.0.2", @@ -27165,31 +26956,22 @@ "resq": "^1.9.1", "rgb2hex": "0.2.5", "serialize-error": "^8.0.0", - "webdriver": "7.33.0" + "webdriver": "7.30.0" }, "engines": { "node": ">=12.0.0" } }, "node_modules/webdriverio/node_modules/@types/aria-query": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", - "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.1.tgz", + "integrity": "sha512-XTIieEY+gvJ39ChLcB4If5zHtPxt3Syj5rgZR+e1ctpmK8NjPf0zFqsz4JpLJT0xla9GFDKjy8Cpu331nrmE1Q==", "dev": true }, - "node_modules/webdriverio/node_modules/@types/node": { - "version": "18.19.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.4.tgz", - "integrity": "sha512-xNzlUhzoHotIsnFoXmJB+yWmBvFZgKCI9TtPIEdYIMM1KWfwuY8zh7wvc1u1OAXlC7dlf6mZVx/s+Y5KfFz19A==", - "dev": true, - "dependencies": { - "undici-types": "~5.26.4" - } - }, "node_modules/webdriverio/node_modules/@wdio/types": { - "version": "7.33.0", - "resolved": "https://registry.npmjs.org/@wdio/types/-/types-7.33.0.tgz", - "integrity": "sha512-tNcuN5Kl+i5CffaeTYV1omzAo4rVjiI1m9raIA8ph6iVteWdCzYv2/ImpGgFiBPb7Mf6VokU3+q9Slh5Jitaww==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@wdio/types/-/types-7.26.0.tgz", + "integrity": "sha512-mOTfWAGQ+iT58iaZhJMwlUkdEn3XEWE4jthysMLXFnSuZ2eaODVAiK31SmlS/eUqgSIaupeGqYUrtCuSNbLefg==", "dev": true, "dependencies": { "@types/node": "^18.0.0", @@ -27208,12 +26990,12 @@ } }, "node_modules/webdriverio/node_modules/aria-query": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", - "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz", + "integrity": "sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==", "dev": true, "dependencies": { - "dequal": "^2.0.3" + "deep-equal": "^2.0.5" } }, "node_modules/webdriverio/node_modules/brace-expansion": { @@ -27225,6 +27007,20 @@ "balanced-match": "^1.0.0" } }, + "node_modules/webdriverio/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/webdriverio/node_modules/minimatch": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-6.2.0.tgz", @@ -27916,9 +27712,9 @@ } }, "node_modules/ws": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", - "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==", + "version": "8.12.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.12.1.tgz", + "integrity": "sha512-1qo+M9Ba+xNhPB+YTWUlK6M17brTut5EXbcBaMRN5pH5dFrXz7lzz1ChFSUq3bOUl8yEvSenhHmYUNJxFzdJew==", "engines": { "node": ">=10.0.0" }, @@ -28073,34 +27869,13 @@ } }, "node_modules/zip-stream": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-4.1.1.tgz", - "integrity": "sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==", - "dev": true, - "dependencies": { - "archiver-utils": "^3.0.4", - "compress-commons": "^4.1.2", - "readable-stream": "^3.6.0" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/zip-stream/node_modules/archiver-utils": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-3.0.4.tgz", - "integrity": "sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-4.1.0.tgz", + "integrity": "sha512-zshzwQW7gG7hjpBlgeQP9RuyPGNxvJdzR8SUM3QhxCnLjWN2E7j3dOvpeDcQoETfHx0urRS7EtmVToql7YpU4A==", "dev": true, "dependencies": { - "glob": "^7.2.3", - "graceful-fs": "^4.2.0", - "lazystream": "^1.0.0", - "lodash.defaults": "^4.2.0", - "lodash.difference": "^4.5.0", - "lodash.flatten": "^4.4.0", - "lodash.isplainobject": "^4.0.6", - "lodash.union": "^4.6.0", - "normalize-path": "^3.0.0", + "archiver-utils": "^2.1.0", + "compress-commons": "^4.1.0", "readable-stream": "^3.6.0" }, "engines": { @@ -28108,9 +27883,9 @@ } }, "node_modules/zip-stream/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.1.tgz", + "integrity": "sha512-+rQmrWMYGA90yenhTYsLWAsLsqVC8osOw6PKE1HDYiO0gdPeKe/xDHNzIAIn4C91YQ6oenEhfYqqc1883qHbjQ==", "dev": true, "dependencies": { "inherits": "^2.0.3", diff --git a/package.json b/package.json index 6400a8afe..3f4d8f3e4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@americanexpress/one-app", - "version": "6.9.2", + "version": "6.8.3", "description": "One Amex SPA technology stack.", "main": "index.js", "engines": { @@ -25,15 +25,15 @@ "start:watch": "node scripts/watch-server.js", "prestart:prod-sample": "npm run build:prod-sample", "prestart:prod-sample:debug": "npm run build:prod-sample", - "start:prod-sample": "docker compose -f ./prod-sample/docker-compose.yml up --abort-on-container-exit", - "start:prod-sample:debug": "docker compose -f ./prod-sample/docker-compose.yml -f ./prod-sample/docker-compose.debug.yml up --abort-on-container-exit", - "monitor:performance": "docker compose -f ./__performance__/docker-compose.yml up --abort-on-container-exit influxdb grafana prometheus", + "start:prod-sample": "docker-compose -f ./prod-sample/docker-compose.yml up --abort-on-container-exit", + "start:prod-sample:debug": "docker-compose -f ./prod-sample/docker-compose.yml -f ./prod-sample/docker-compose.debug.yml up --abort-on-container-exit", + "monitor:performance": "docker-compose -f ./__performance__/docker-compose.yml up --abort-on-container-exit influxdb grafana prometheus", "test:lockfile": "lockfile-lint -p package-lock.json -t npm -a npm -o https: -c -i", "test:lint": "eslint --ext js,jsx,md,snap .", "start": "node --dns-result-order=ipv4first --no-experimental-fetch lib/server/index.js", "start:inspect": "node --inspect --expose-gc lib/server/index.js", "test:unit": "jest --testPathIgnorePatterns integration --config jest.config.js", - "pretest:integration": "concurrently \"npm run build:prod-sample\" \"docker compose -f ./prod-sample/docker-compose.yml pull nginx selenium-chrome\" --kill-others-on-fail -n build-prod-sample,build-dependency-images", + "pretest:integration": "concurrently \"npm run build:prod-sample\" \"docker-compose -f ./prod-sample/docker-compose.yml pull nginx selenium-chrome\" --kill-others-on-fail -n build-prod-sample,build-dependency-images", "test:integration": "JEST_TEST_REPORT_PATH=./test-results/integration-test-report.html jest integration --config jest.integration.config.js --forceExit", "test:danger": "if-env NODE_ENV=production && danger ci || danger local", "test": "npm run test:lint && npm run test:lockfile && npm run test:unit && npm run test:integration && npm run test:danger && npm run test:git-history-main", @@ -81,8 +81,9 @@ "one-app" ], "dependencies": { - "@americanexpress/env-config-utils": "^2.0.4", + "@americanexpress/env-config-utils": "^2.0.2", "@americanexpress/fetch-enhancers": "^1.1.3", + "@americanexpress/lumberjack": "^1.1.4", "@americanexpress/one-app-bundler": "^6.21.1", "@americanexpress/one-app-ducks": "^4.4.2", "@americanexpress/one-app-router": "^1.2.1", @@ -92,7 +93,7 @@ "@fastify/cors": "^8.4.0", "@fastify/formbody": "^7.4.0", "@fastify/helmet": "^11.1.1", - "@fastify/rate-limit": "^9.1.0", + "@fastify/rate-limit": "^8.0.3", "@fastify/sensible": "^5.5.0", "@fastify/static": "^6.12.0", "@opentelemetry/semantic-conventions": "^1.15.2", @@ -102,12 +103,12 @@ "bytes": "^3.1.2", "cacheable-lookup": "^6.0.4", "chalk": "^4.1.2", - "core-js": "^3.35.0", + "core-js": "^3.33.2", "create-shared-react-context": "^1.0.5", "cross-fetch": "^4.0.0", "deepmerge": "^4.3.1", - "fastify": "^4.25.2", - "fastify-metrics": "^10.5.0", + "fastify": "^4.24.3", + "fastify-metrics": "^10.3.2", "fastify-plugin": "^4.5.1", "flat": "^5.0.2", "helmet": "^7.0.0", @@ -119,7 +120,6 @@ "lean-intl": "^4.2.2", "matcher": "^4.0.0", "object-hash": "^3.0.0", - "on-finished": "^2.4.1", "opossum": "^8.1.3", "opossum-prometheus": "^0.3.0", "pidusage": "^3.0.2", @@ -202,7 +202,7 @@ "standard-version": "^9.5.0", "supertest": "^6.2.4", "tar": "^6.1.15", - "webdriverio": "^7.34.0" + "webdriverio": "^7.20.5" }, "standard-version": { "bumpFiles": [ diff --git a/prod-sample/README.md b/prod-sample/README.md index eaa98878a..81db5e2e3 100644 --- a/prod-sample/README.md +++ b/prod-sample/README.md @@ -15,7 +15,7 @@ One App → nginx (mimics a CDN server and hosts the module map, module bundles, ## Running Locally > Note [Docker](https://docs.docker.com/install/) is required in order to run this sample -setup as [docker compose](https://docs.docker.com/compose/) is internally used +setup as [docker-compose](https://docs.docker.com/compose/) is internally used to orchestrate the building and starting up of One App and the nginx server. Running `npm run start:prod-sample` builds and starts a production One App Docker container that will use @@ -33,24 +33,16 @@ On Mac OS, you can do so natively: Click connect and a window will appear with Google Chrome open. This is where you can access One App at `https://one-app:8443`! -Alternatively you visit `http://localhost:7901/` to interact using no-vnc in your own browser. - If you wish to run with node debug mode: `npm run start:prod-sample:debug`. ### Options The entire setup invoked by `npm run start:prod-sample` can take ~10 minutes and is often unnecessary if no changes have been made to the One App source code or the sample modules contained in -`prod-sample/sample-modules` since the last time `npm run start:prod-sample` was ran. -For these cases there are environment variables available to opt out of -rebuilding the select Docker images. - -| Environment Variable | | -| ------------------------------------------- | -------------------------------------- | -| `ONE_DANGEROUSLY_SKIP_ONE_APP_IMAGE_BUILD` | Don't rebuild One App Docker Image | -| `ONE_DANGEROUSLY_SKIP_SAMPLE_MODULES_BUILD` | Don't rebuild Sample Modules | -| `ONE_DANGEROUSLY_SKIP_API_IMAGES_BUILD` | Don't rebuild API server Docker Images | - +`prod-sample/sample-modules` since the last time `npm run start:prod-sample` was ran. For these cases +you can use the `ONE_DANGEROUSLY_SKIP_ONE_APP_IMAGE_BUILD` and/or +`ONE_DANGEROUSLY_SKIP_SAMPLE_MODULES_BUILD` environment variables to opt out of +rebuilding the One App Docker image and/or sample modules respectively. Use these environment variables with caution as they can result in you running a version of One App and/or the sample modules that do not reflect your source code. @@ -80,9 +72,9 @@ $ node ./scripts/deploy-prod-sample-module.js --module-path="../path-to-module/[ ### Options -| Argument | Description | Example | Required | -| ---------------- | ------------------------------------ | ------------------------------------------------ | -------- | -| `--module-path` | relative path to the module | `--module-path="../relative-path/[your-module]"` | X | -| `--skip-install` | don't install before building module | `--skip-install=true` | | -| `--skip-build` | don't build the module | `--skip-build=true` | | +| Argument | Description | Example | Required | +|---- |--------- |----------|- | +| `--module-path` | relative path to the module |`--module-path="../relative-path/[your-module]"` | X | +| `--skip-install` | don't install before building module |`--skip-install=true` | | +| `--skip-build` | don't build the module |`--skip-build=true` | | diff --git a/prod-sample/api/Dockerfile b/prod-sample/api/Dockerfile index 42dc58fe9..5a4191ff7 100644 --- a/prod-sample/api/Dockerfile +++ b/prod-sample/api/Dockerfile @@ -1,6 +1,4 @@ -ARG VERSION=lts - -FROM node:$VERSION as builder +FROM node:16-alpine MAINTAINER One App Team WORKDIR / diff --git a/prod-sample/docker-compose.yml b/prod-sample/docker-compose.yml index 0a6f15592..547a6efa0 100644 --- a/prod-sample/docker-compose.yml +++ b/prod-sample/docker-compose.yml @@ -93,7 +93,7 @@ services: - sample-cdn.frank selenium-chrome: # specify docker image sha to ensure consistency - image: seleniarm/standalone-chromium:120.0 + image: selenium/standalone-chrome-debug@sha256:e8bf805eca673e6788fb50249b105be860d991ee0fa3696422b4cb92acb5c07a # https://github.com/SeleniumHQ/docker-selenium#running-the-images volumes: - /dev/shm:/dev/shm @@ -103,4 +103,4 @@ services: networks: one-app-at-test-network: # sleep 5s to make sure set up is completed prior to opening up chrome - entrypoint: bash -c '/opt/bin/entry_point.sh & sleep 5s && chromium --ignore-certificate-errors --no-first-run --autofill-server-url https://one-app:8443/success' \ No newline at end of file + entrypoint: bash -c '/opt/bin/entry_point.sh & sleep 5s && google-chrome --ignore-certificate-errors --no-first-run --autofill-server-url https://one-app:8443/success' diff --git a/prod-sample/generate-certs.sh b/prod-sample/generate-certs.sh index d5b92991c..681218a71 100755 --- a/prod-sample/generate-certs.sh +++ b/prod-sample/generate-certs.sh @@ -1,3 +1,8 @@ #!/bin/bash openssl req -x509 -newkey rsa:2048 -nodes -sha256 -subj "/CN=$1" \ -keyout ./$2/$2-privkey.pem -out ./$2/$2-cert.pem + +# Append cert to aggregate cert file +cat ./$2/$2-cert.pem >> extra-certs.pem +# Append a new line +echo >> extra-certs.pem diff --git a/scripts/build-one-app-docker-setup.js b/scripts/build-one-app-docker-setup.js index a2093edae..dbf14b1c1 100644 --- a/scripts/build-one-app-docker-setup.js +++ b/scripts/build-one-app-docker-setup.js @@ -32,34 +32,19 @@ const { const nginxOriginStaticsAppDir = path.resolve(nginxOriginStaticsRootDir, 'app'); const userIntendsToSkipOneAppImageBuild = process.env.ONE_DANGEROUSLY_SKIP_ONE_APP_IMAGE_BUILD; -const userIntendsToSkipApiImagesBuild = process.env.ONE_DANGEROUSLY_SKIP_API_IMAGES_BUILD; -const nodeVersion = fs.readFileSync('.nvmrc', { encoding: 'utf8' }).trim(); const sanitizedEnvVars = sanitizeEnvVars(); -const buildApiImages = async () => { - console.time('Docker API Images Build'); - console.log('🛠 Building fast-api, slow-api, and extra-slow-api Docker images'); +const buildDockerImages = async (skipOneAppImageBuild) => { + console.time('Docker Images Build'); + console.log(`🛠 Building ${skipOneAppImageBuild ? ' ' : 'one-app, '}fast-api, slow-api, and extra-slow-api Docker images`); try { - await promisifySpawn(`docker compose build --build-arg VERSION=${nodeVersion} --no-cache --parallel fast-api slow-api extra-slow-api`, { shell: true, cwd: sampleProdDir, env: { ...sanitizedEnvVars } }); + await promisifySpawn(`docker-compose build --no-cache --parallel ${skipOneAppImageBuild ? '' : 'one-app'} fast-api slow-api extra-slow-api`, { shell: true, cwd: sampleProdDir, env: { ...sanitizedEnvVars } }); } catch (error) { - console.log('🚨 Docker API Images could not be built!\n'); + console.log('🚨 Docker images could not be built!\n'); throw error; } - console.log('✅ Docker API images built\n'); - console.timeEnd('Docker API Images Build'); -}; - -const buildOneAppImage = async () => { - console.time('Docker Build One App Image'); - console.log('🛠 Building one-app Docker image'); - try { - await promisifySpawn(`docker compose build --build-arg VERSION=${nodeVersion} --no-cache one-app`, { shell: true, cwd: sampleProdDir, env: { ...sanitizedEnvVars } }); - } catch (error) { - console.log('🚨 Docker could not build the one-app image!\n'); - throw error; - } - console.log('✅ Docker built One App Image \n'); - console.timeEnd('Docker Build One App Image'); + console.log('✅ Docker images built \n'); + console.timeEnd('Docker Images Build'); }; const generateCertsFor = async (container, commonName) => { @@ -73,20 +58,6 @@ const generateCertsFor = async (container, commonName) => { console.log(`✅ ${container} certs created! \n`); }; -const buildExtraCerts = async () => { - console.log('🛠 Building extra-certs.pem'); - try { - const nginxCert = await fs.readFile(path.join(sampleProdDir, 'nginx', 'nginx-cert.pem'), 'utf8'); - const apiCert = await fs.readFile(path.join(sampleProdDir, 'api', 'api-cert.pem'), 'utf8'); - const appCert = await fs.readFile(path.join(sampleProdDir, 'one-app', 'one-app-cert.pem'), 'utf8'); - await fs.writeFile(path.join(sampleProdDir, 'extra-certs.pem'), `${appCert}\n${apiCert}\n${nginxCert}`); - } catch (error) { - console.log('🚨 extra-certs.pem could not be built\n'); - throw error; - } - console.log('✅ extra-certs.pem built! \n'); -}; - const collectOneAppStaticFiles = async () => { console.log('🛠 Collecting One App static files from Docker image'); try { @@ -108,43 +79,20 @@ const collectOneAppStaticFiles = async () => { console.log('✅ One App Statics successfully pulled from Docker image and moved to Nginx origin dir! \n'); }; -const handleCertGeneration = async ({ skipApiImagesBuild, skipOneAppImageBuild }) => { - try { - await fs.access(path.join(sampleProdDir, 'extra-certs.pem')); - await fs.unlink(path.join(sampleProdDir, 'extra-certs.pem')); - console.log('✅ Removed old extra-certs.pem'); - } catch { - // swallow error - } - - await generateCertsFor('nginx', 'sample-cdn.frank'); - - const oneCertExists = fs.existsSync(path.join(sampleProdDir, 'one-app', 'one-app-cert.pem')); - if (!skipOneAppImageBuild || !oneCertExists) { - await generateCertsFor('one-app', 'localhost'); - } - const apiCertExists = fs.existsSync(path.join(sampleProdDir, 'api', 'api-cert.pem')); - if (!skipApiImagesBuild || !apiCertExists) { await generateCertsFor('api', '*.api.frank'); } - - await buildExtraCerts(); -}; - -const prodSampleApiImagesAlreadyBuilt = () => execSync('docker images prod-sample-fast-api', { encoding: 'utf8' }).includes('prod-sample-fast-api') - && execSync('docker images prod-sample-slow-api', { encoding: 'utf8' }).includes('prod-sample-slow-api') - && execSync('docker images prod-sample-extra-slow-api', { encoding: 'utf8' }).includes('prod-sample-extra-slow-api'); - const doWork = async () => { const oneAppImageAlreadyBuilt = execSync('docker images one-app:at-test', { encoding: 'utf8' }).includes('at-test'); const skipOneAppImageBuild = userIntendsToSkipOneAppImageBuild && oneAppImageAlreadyBuilt; - const skipApiImagesBuild = userIntendsToSkipApiImagesBuild && prodSampleApiImagesAlreadyBuilt(); - - // clear cdn of statics. - await fs.emptyDir(nginxOriginStaticsAppDir); + if (skipOneAppImageBuild) { + console.warn( + '⚠️ Skipping One App Docker image build since the "ONE_DANGEROUSLY_SKIP_ONE_APP_IMAGE_BUILD"' + + 'environment variable is set.\n\nNote that your tests **may** be running against an out of date ' + + 'version of One App that does not reflect changes you have made to the source code.' + ); + } - await handleCertGeneration({ - skipApiImagesBuild, - skipOneAppImageBuild, - }); + await Promise.all([ + fs.emptyDir(nginxOriginStaticsAppDir), + ]); if (userIntendsToSkipOneAppImageBuild && !oneAppImageAlreadyBuilt) { console.warn( @@ -153,25 +101,26 @@ const doWork = async () => { ); } - if (!skipOneAppImageBuild) { - await buildOneAppImage(); - } else { - console.warn( - '⚠️ Skipping One App Docker image build since the "ONE_DANGEROUSLY_SKIP_ONE_APP_IMAGE_BUILD"' - + 'environment variable is set.\n\nNote that your tests **may** be running against an out of date ' - + 'version of One App that does not reflect changes you have made to the source code.' - ); + let extraCertsExists = true; + + try { + await fs.access(path.join(sampleProdDir, 'extra-certs.pem')); + } catch (err) { + extraCertsExists = false; } - if (!skipApiImagesBuild) { - await buildApiImages(); - } else { - console.warn( - '⚠️ Skipping API Docker images build since the "ONE_DANGEROUSLY_SKIP_API_IMAGES_BUILD"' - + 'environment variable is set.' - ); + if (extraCertsExists) { + await fs.unlink(path.join(sampleProdDir, 'extra-certs.pem')); + console.log('✅ Removed old extra-certs.pem'); } + await Promise.all([ + generateCertsFor('one-app', 'localhost'), + generateCertsFor('nginx', 'sample-cdn.frank'), + generateCertsFor('api', '*.api.frank'), + ]); + + await buildDockerImages(skipOneAppImageBuild); await collectOneAppStaticFiles(); }; diff --git a/src/server/index.js b/src/server/index.js index 490d00d2c..75168b6cf 100644 --- a/src/server/index.js +++ b/src/server/index.js @@ -15,7 +15,6 @@ * permissions and limitations under the License. */ -import util from 'node:util'; import path from 'path'; import fs from 'fs'; import Intl from 'lean-intl'; @@ -90,10 +89,6 @@ async function ssrServerStart() { }); await pollModuleMap(); - - if (process.env.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT) { - process.stdout.write(util.format('\none-app started successfully. Logs are being sent to OTel via gRPC at %s\n', process.env.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT)); - } } async function metricsServerStart() { @@ -158,8 +153,5 @@ const serverChain = process.env.NODE_ENV === 'development' export default serverChain.catch((err) => { console.error(err); - if (process.env.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT) { - process.stderr.write(util.format('\none-app failed to start. Logs are being sent to OTel via gRPC at %s\n\n%s', process.env.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT, err.stack)); - } shutdown(); }); diff --git a/src/server/polyfill/intl.js b/src/server/polyfill/intl.js index 0ad8d332d..952e213a9 100644 --- a/src/server/polyfill/intl.js +++ b/src/server/polyfill/intl.js @@ -16,6 +16,6 @@ import Intl from 'lean-intl'; -if (!process.env.ONE_CONFIG_USE_NATIVE_POLYFIL) { +if (!process.env.ONE_CONFIG_USE_NATIVE_POLYFILL) { global.Intl = Intl; } diff --git a/src/server/ssrServer.js b/src/server/ssrServer.js index ba85ac77b..a9cd05064 100644 --- a/src/server/ssrServer.js +++ b/src/server/ssrServer.js @@ -46,11 +46,28 @@ import logger from './utils/logging/logger'; const nodeEnvIsDevelopment = () => process.env.NODE_ENV === 'development'; /** - * Registers all the plugins and routes for the Fastify app - * @param {import('fastify').FastifyInstance} fastify Fastify instance + * Creates a Fastify app with built-in routes and configuration + * @param {import('fastify').FastifyHttp2Options} opts Fastify app options + * @returns {import('fastify').FastifyInstance} */ -async function appPlugin(fastify) { +export async function createApp(opts = {}) { + const enablePostToModuleRoutes = process.env.ONE_ENABLE_POST_TO_MODULE_ROUTES === 'true'; + const fastify = Fastify({ + logger, + disableRequestLogging: true, + frameworkErrors: function frameworkErrors(error, request, reply) { + const { method, url, headers } = request; + const correlationId = headers['correlation-id']; + + request.log.error('Fastify internal error: method %s, url "%s", correlationId "%s"', method, url, correlationId, error); + + return renderStaticErrorPage(request, reply); + }, + bodyLimit: bytes(process.env.ONE_MAX_POST_REQUEST_PAYLOAD || '10mb'), // Note: this applies to all routes + ...opts, + }); + fastify.register(fastifySensible); fastify.register(ensureCorrelationId); fastify.register(fastifyCookie); @@ -207,7 +224,7 @@ async function appPlugin(fastify) { reply.sendHtml(); }); - if (process.env.ONE_ENABLE_POST_TO_MODULE_ROUTES === 'true') { + if (enablePostToModuleRoutes) { instance.post('/*', (_request, reply) => { reply.sendHtml(); }); @@ -227,34 +244,8 @@ async function appPlugin(fastify) { request.log.error('Fastify application error: method %s, url "%s", correlationId "%s", headersSent: %s', method, url, correlationId, headersSent, error); - reply.code(500); return renderStaticErrorPage(request, reply); }); -} - -/** - * Creates a Fastify app with built-in routes and configuration - * @param {import('fastify').FastifyHttp2Options} opts Fastify app options - * @returns {import('fastify').FastifyInstance} - */ - -export async function createApp(opts = {}) { - const fastify = Fastify({ - logger, - disableRequestLogging: true, - frameworkErrors: function frameworkErrors(error, request, reply) { - const { method, url, headers } = request; - const correlationId = headers['correlation-id']; - - request.log.error('Fastify internal error: method %s, url "%s", correlationId "%s"', method, url, correlationId, error); - - return renderStaticErrorPage(request, reply); - }, - bodyLimit: bytes(process.env.ONE_MAX_POST_REQUEST_PAYLOAD || '10mb'), // Note: this applies to all routes - ...opts, - }); - - fastify.register(appPlugin); await fastify.ready(); diff --git a/src/server/utils/cdnCache.js b/src/server/utils/cdnCache.js index 46b882faf..264226675 100644 --- a/src/server/utils/cdnCache.js +++ b/src/server/utils/cdnCache.js @@ -18,6 +18,7 @@ import chalk from 'chalk'; export const getUserHomeDirectory = () => process.env.HOME || process.env.USERPROFILE; export const cacheFileName = '.one-app-module-cache'; +export const moduleCacheFileName = '.one-app-module-map-cache'; export const oneAppDirectoryName = '.one-app'; export const oneAppDirectoryPath = path.join(getUserHomeDirectory(), oneAppDirectoryName); export const oneAppModuleCachePath = path.join(oneAppDirectoryPath, cacheFileName); @@ -32,8 +33,8 @@ export const showCacheInfo = async () => { console.log(chalk.bold.cyanBright(separator)); console.log(chalk.bold.cyanBright('CACHE INFORMATION')); console.log(message); - console.log('To clear the cache, please delete this file:'); - console.log(` ${chalk.bold.cyanBright(path.join('~', oneAppDirectoryName, cacheFileName))}`); + console.log('To delete cache, please run'); + console.log(` ${chalk.bold.cyanBright(' rm ', oneAppModuleCachePath)}`); console.log(chalk.bold.cyanBright(separator)); } catch (error) { console.error('There was error checking file stat', error); diff --git a/src/server/utils/logging/attachRequestSpies.js b/src/server/utils/logging/attachRequestSpies.js deleted file mode 100644 index 870bfb80c..000000000 --- a/src/server/utils/logging/attachRequestSpies.js +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright 2023 American Express Travel Related Services Company, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -import https from 'node:https'; -import http from 'node:http'; -import url from 'node:url'; -import onFinished from 'on-finished'; - -import attachSpy from './attachSpy'; - -function buildUrlObject(options, defaultProtocol) { - // TODO: url.parse is deprecated, use new URL() instead - // this is not a 1:1 replacement and will require changes. - // Currently the parsed url is used to provide href to - // loggers outgoingRequestEndSpy function. - // https://github.com/americanexpress/one-app/blob/main/src/server/utils/logging/setup.js#L42 - // https://nodejs.org/api/url.html#urlparseurlstring-parsequerystring-slashesdenotehost - const parsedPath = url.parse(options.path || '/'); - const protocol = options.protocol || `${defaultProtocol}:`; - const urlObject = { - auth: options.auth, - hostname: options.hostname || options.host || 'localhost', - protocol, - port: options.port || (protocol === 'http:' ? 80 : 443), - hash: parsedPath.hash, - path: parsedPath.path, - pathname: parsedPath.pathname, - query: parsedPath.query, - search: parsedPath.search, - }; - if ( - (protocol === 'http:' && urlObject.port === 80) - || (protocol === 'https:' && urlObject.port === 443) - ) { - urlObject.port = undefined; - } - return urlObject; -} - -function parseUrl(options, defaultProtocol) { - // some data is not stored in the clientRequest, have to duplicate some parsing - // adapted from https://github.com/nodejs/node/blob/894203dae39c7f1f36fc6ba126bb5d782d79b744/lib/_http_client.js#L22 - if (typeof options === 'string') { - return url.parse(options); - } - - return url.parse(url.format(buildUrlObject(options, defaultProtocol))); -} - -function httpSpy(defaultProtocol, requestSpy, socketCloseSpy) { - return (args, callOriginal) => { - const options = args[0]; - const clientRequest = callOriginal(); - const parsedUrl = parseUrl(options, defaultProtocol); - - requestSpy(clientRequest, parsedUrl); - - if (socketCloseSpy) { - onFinished(clientRequest, () => socketCloseSpy(clientRequest, parsedUrl)); - } - }; -} - -export default function attachRequestSpies(requestSpy, socketCloseSpy) { - if (typeof requestSpy !== 'function') { - throw new TypeError(`requestSpy must be a function (was "${typeof requestSpy}")`); - } - - if (socketCloseSpy && (typeof socketCloseSpy !== 'function')) { - throw new TypeError( - `socketCloseSpy must be function if provided (was "${typeof socketCloseSpy}")`); - } - - attachSpy(https, 'request', httpSpy('https', requestSpy, socketCloseSpy)); - attachSpy(http, 'request', httpSpy('http', requestSpy, socketCloseSpy)); -} diff --git a/src/server/utils/logging/attachSpy.js b/src/server/utils/logging/attachSpy.js deleted file mode 100644 index 967ce325d..000000000 --- a/src/server/utils/logging/attachSpy.js +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2023 American Express Travel Related Services Company, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -export default function attachSpy(obj, methodName, spy) { - const origMethod = obj[methodName]; - - if (typeof origMethod !== 'function') { - throw new TypeError(`${methodName} is not a function`); - } - - if (typeof spy !== 'function') { - throw new TypeError(`spy must be a function (was "${typeof spy}")`); - } - - // we're monkeypatching, we need to reassign a property of the obj argument - // eslint-disable-next-line no-param-reassign -- see above comment - obj[methodName] = function monkeypatchedMethod(...args) { - let returnValue; - let originalCalled = false; - const callOriginal = () => { - originalCalled = true; - returnValue = origMethod.apply(obj, args); - return returnValue; - }; - - spy([...args], callOriginal); - - if (!originalCalled) { - callOriginal(); - } - - return returnValue; - }; -} diff --git a/src/server/utils/logging/config/otel.js b/src/server/utils/logging/config/otel.js index 8232bde5a..12d9e4d4a 100644 --- a/src/server/utils/logging/config/otel.js +++ b/src/server/utils/logging/config/otel.js @@ -53,16 +53,16 @@ export function createOtelTransport() { }; }, {}) : {}; - let logRecordProcessorOptions = { + const logRecordProcessorOptions = [{ recordProcessorType: 'batch', exporterOptions: { protocol: 'grpc' }, - }; + }]; if (process.env.NODE_ENV === 'development') { - logRecordProcessorOptions = [logRecordProcessorOptions, { + logRecordProcessorOptions.push({ recordProcessorType: 'simple', exporterOptions: { protocol: 'console' }, - }]; + }); } return pino.transport({ diff --git a/src/server/utils/logging/setup.js b/src/server/utils/logging/setup.js index ecaa15649..aebfe64c8 100644 --- a/src/server/utils/logging/setup.js +++ b/src/server/utils/logging/setup.js @@ -14,9 +14,9 @@ * permissions and limitations under the License. */ +import { monkeypatches } from '@americanexpress/lumberjack'; import logger from './logger'; import { startTimer, measureTime } from './timing'; -import attachRequestSpies from './attachRequestSpies'; const COLON_AT_THE_END_REGEXP = /:$/; function formatProtocol(parsedUrl) { @@ -69,4 +69,12 @@ function outgoingRequestEndSpy(externalRequest, parsedUrl) { }); } -attachRequestSpies(outgoingRequestSpy, outgoingRequestEndSpy); +// In Node.js v8 and earlier https.request internally called http.request, but this is changed in +// later versions +// https://github.com/nodejs/node/blob/v6.x/lib/https.js#L206 +// https://github.com/nodejs/node/blob/v8.x/lib/https.js#L239 +// https://github.com/nodejs/node/blob/v10.x/lib/https.js#L271 +if (Number.parseInt(/^v(\d+)/.exec(process.version)[1], 10) > 8) { + monkeypatches.attachHttpsRequestSpy(outgoingRequestSpy, outgoingRequestEndSpy); +} +monkeypatches.attachHttpRequestSpy(outgoingRequestSpy, outgoingRequestEndSpy);