From c245d9453ae4fcedf10f96d71aa4260773cffa6f Mon Sep 17 00:00:00 2001 From: Jeff L'Heureux Date: Tue, 14 Sep 2021 18:33:09 -0400 Subject: [PATCH 1/7] Inject Sitecore CDP scripts into the app pages --- .env | 6 ++++++ Website/src/rendering/src/pages/_app.tsx | 27 +++++++++++++++++++++++- docker-compose.override.yml | 3 +++ 3 files changed, 35 insertions(+), 1 deletion(-) diff --git a/.env b/.env index cde264b4f..43620e953 100644 --- a/.env +++ b/.env @@ -87,6 +87,12 @@ DAM_SearchPage= DAM_ExternalRedirectKey= DAM_ExternalRedirectKey=Sitecore +# CDP +CDP_PROXY_HOST=cdp.edge.localhost +CDP_API_TARGET_ENDPOINT= +CDP_CLIENT_KEY= +CDP_API_TOKEN= + # Environment Variables for XM XE_DELIVERY_ENDPOINT= XE_AUTHORITY= diff --git a/Website/src/rendering/src/pages/_app.tsx b/Website/src/rendering/src/pages/_app.tsx index 81e416ccc..eee9af2df 100644 --- a/Website/src/rendering/src/pages/_app.tsx +++ b/Website/src/rendering/src/pages/_app.tsx @@ -4,13 +4,13 @@ import { useEffect } from 'react'; // END CUSTOMIZATION import { I18nProvider } from 'next-localization'; import Head from 'next/head'; +import Script from 'next/script'; import NProgress from 'nprogress'; // Using nprogress are completely optional. // nprogress provides a loading indicator on page/route changes. // Remove it in package.json as well if removed here. import 'nprogress/nprogress.css'; -// TODO: Import Material UI here import 'assets/css/main.css'; NProgress.configure({ showSpinner: false, trickleSpeed: 100 }); @@ -41,6 +41,11 @@ function App({ Component, pageProps, router }: AppProps): JSX.Element { const { dictionary, ...rest } = pageProps; + // DEMO TEAM CUSTOMIZATION - Add CDP + const cdpClientKey = process.env.NEXT_PUBLIC_CDP_CLIENT_KEY || ''; + const cdpApiTargetEndpoint = process.env.NEXT_PUBLIC_CDP_API_TARGET_ENDPOINT || ''; + // END CUSTOMIZATION + // DEMO TEAM CUSTOMIZATION - Add head section return ( <> @@ -59,6 +64,26 @@ function App({ Component, pageProps, router }: AppProps): JSX.Element { + + {/* DEMO TEAM CUSTOMIZATION - Add CDP */} + + + {/* END CUSTOMIZATION*/} ); // END CUSTOMIZATION diff --git a/docker-compose.override.yml b/docker-compose.override.yml index 84d239e5f..bcf97fc97 100644 --- a/docker-compose.override.yml +++ b/docker-compose.override.yml @@ -47,6 +47,9 @@ services: NEXTJS_DIST_DIR: ".next-container" PUBLIC_URL: "https://${RENDERING_HOST}" JSS_EDITING_SECRET: ${JSS_EDITING_SECRET} + CDP_PROXY_URL: https://${CDP_PROXY_HOST} + NEXT_PUBLIC_CDP_CLIENT_KEY: ${CDP_CLIENT_KEY} + NEXT_PUBLIC_CDP_API_TARGET_ENDPOINT: ${CDP_API_TARGET_ENDPOINT} #DEBUG: ${DEBUG} # Uncomment this line and set the DEBUG environment variable value in the .env file to enable debug logging depends_on: - cm From 1879b918370cc84d34a7fb29187f29ee1dece60c Mon Sep 17 00:00:00 2001 From: Jeff L'Heureux Date: Wed, 15 Sep 2021 15:17:53 -0400 Subject: [PATCH 2/7] Do not render the Boxever scripts when the CDP settings are not provided --- Website/src/rendering/src/pages/_app.tsx | 32 ++++++++++++------------ 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/Website/src/rendering/src/pages/_app.tsx b/Website/src/rendering/src/pages/_app.tsx index eee9af2df..eeb90bfc9 100644 --- a/Website/src/rendering/src/pages/_app.tsx +++ b/Website/src/rendering/src/pages/_app.tsx @@ -44,6 +44,7 @@ function App({ Component, pageProps, router }: AppProps): JSX.Element { // DEMO TEAM CUSTOMIZATION - Add CDP const cdpClientKey = process.env.NEXT_PUBLIC_CDP_CLIENT_KEY || ''; const cdpApiTargetEndpoint = process.env.NEXT_PUBLIC_CDP_API_TARGET_ENDPOINT || ''; + const isCdpClientKeyConfigured = !!cdpClientKey && !!cdpApiTargetEndpoint; // END CUSTOMIZATION // DEMO TEAM CUSTOMIZATION - Add head section @@ -66,23 +67,22 @@ function App({ Component, pageProps, router }: AppProps): JSX.Element { {/* DEMO TEAM CUSTOMIZATION - Add CDP */} - - + // Define the Boxever settings + _boxever_settings = { + client_key: '${cdpClientKey}', + target: '${cdpApiTargetEndpoint}', + cookie_domain: '.edge.localhost', + }; + `} + + + )} {/* END CUSTOMIZATION*/} ); From 3572986e49d4622ca1d912e1b0ccd91920ad1622 Mon Sep 17 00:00:00 2001 From: Jeff L'Heureux Date: Wed, 15 Sep 2021 15:54:36 -0400 Subject: [PATCH 3/7] Set CDP environment variables on Vercel deployments --- docker/build/init/Jobs/DeployToVercel.cs | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/docker/build/init/Jobs/DeployToVercel.cs b/docker/build/init/Jobs/DeployToVercel.cs index 4ad97af7b..83bdea19a 100644 --- a/docker/build/init/Jobs/DeployToVercel.cs +++ b/docker/build/init/Jobs/DeployToVercel.cs @@ -54,9 +54,22 @@ public async Task Run() return; } + var cdpClientKey = Environment.GetEnvironmentVariable("CDP_CLIENT_KEY"); + if (string.IsNullOrEmpty(cdpClientKey)) + { + Log.LogWarning($"{this.GetType().Name} will not execute this time, CDP_CLIENT_KEY is not configured"); + return; + } + + var cdpApiTargetEndpoint = Environment.GetEnvironmentVariable("CDP_API_TARGET_ENDPOINT"); + if (string.IsNullOrEmpty(cdpApiTargetEndpoint)) + { + Log.LogWarning($"{this.GetType().Name} will not execute this time, CDP_API_TARGET_ENDPOINT is not configured"); + return; + } DeployTv(ns, contentHubApiKey, token, scope); - DeployWebsite(ns, token, scope); + DeployWebsite(ns, cdpClientKey, cdpApiTargetEndpoint, token, scope); await Complete(); } @@ -90,7 +103,7 @@ private static void DeployTv(string ns, string contentHubApiKey, string token, s cmd.Run($"vercel domains add {ns}-tv.sitecoredemo.com --token {token} --scope {scope}"); } - private static void DeployWebsite(string ns, string token, string scope) + private static void DeployWebsite(string ns, string cdpClientKey, string cdpApiTargetEndpoint, string token, string scope) { var cm = Environment.GetEnvironmentVariable("PUBLIC_HOST_CM"); var js = Environment.GetEnvironmentVariable("SITECORE_JSS_EDITING_SECRET"); @@ -116,6 +129,9 @@ private static void DeployWebsite(string ns, string token, string scope) cmd.Run( $"echo | set /p=\"{SitecoreApiKey}\" | vercel env add SITECORE_API_KEY production --token {token} --scope {scope}"); cmd.Run($"echo | set /p=\"{js}\" | vercel env add JSS_EDITING_SECRET production --token {token} --scope {scope}"); + cmd.Run($"echo | set /p=\"{cdpClientKey}\" | vercel env add NEXT_PUBLIC_CDP_CLIENT_KEY production --token {token} --scope {scope}"); + cmd.Run( + $"echo | set /p=\"{cdpApiTargetEndpoint}\" | vercel env add NEXT_PUBLIC_CDP_API_TARGET_ENDPOINT production --token {token} --scope {scope}"); // Deploy project files cmd.Run($"vercel --confirm --debug --prod --no-clipboard --token {token} --scope {scope}"); From 96680e20452417189994ad2d3aae36aab2779dad Mon Sep 17 00:00:00 2001 From: Jeff L'Heureux Date: Thu, 16 Sep 2021 17:33:37 -0400 Subject: [PATCH 4/7] Log page views in Sitecore CDP --- Website/src/rendering/next-env.d.ts | 3 + Website/src/rendering/package-lock.json | 505 ++++++++++++------ Website/src/rendering/package.json | 6 +- .../src/rendering/scripts/generate-config.ts | 17 +- Website/src/rendering/src/Layout.tsx | 6 + Website/src/rendering/src/lib/util.ts | 7 + Website/src/rendering/src/pages/_app.tsx | 44 +- .../rendering/src/services/BoxeverService.ts | 485 +++++++++++++++++ docker-compose.override.yml | 2 +- 9 files changed, 893 insertions(+), 182 deletions(-) create mode 100644 Website/src/rendering/src/services/BoxeverService.ts diff --git a/Website/src/rendering/next-env.d.ts b/Website/src/rendering/next-env.d.ts index c6643fda1..9bc3dd46b 100644 --- a/Website/src/rendering/next-env.d.ts +++ b/Website/src/rendering/next-env.d.ts @@ -1,3 +1,6 @@ /// /// /// + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/basic-features/typescript for more information. diff --git a/Website/src/rendering/package-lock.json b/Website/src/rendering/package-lock.json index 60c8e78df..4b0f929cd 100644 --- a/Website/src/rendering/package-lock.json +++ b/Website/src/rendering/package-lock.json @@ -542,8 +542,7 @@ "@babel/helper-plugin-utils": { "version": "7.14.5", "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.14.5.tgz", - "integrity": "sha512-/37qQCE3K0vvZKwoK4XU/irIJQdIfCJuhU5eKnNxpFDsOkgFaUAwbv+RYw6eYgsC0E4hS7r5KqGULUogqui0fQ==", - "dev": true + "integrity": "sha512-/37qQCE3K0vvZKwoK4XU/irIJQdIfCJuhU5eKnNxpFDsOkgFaUAwbv+RYw6eYgsC0E4hS7r5KqGULUogqui0fQ==" }, "@babel/helper-remap-async-to-generator": { "version": "7.14.5", @@ -1138,7 +1137,6 @@ "version": "7.14.5", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.14.5.tgz", "integrity": "sha512-ohuFIsOMXJnbOMRfX7/w7LocdR6R7whhuRD4ax8IipLcLPlZGJKkBxgHp++U4N/vKyU16/YDQr2f5seajD3jIw==", - "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.14.5" } @@ -1821,18 +1819,27 @@ "version": "7.12.5", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.12.5.tgz", "integrity": "sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg==", + "dev": true, "requires": { "regenerator-runtime": "^0.13.4" } }, "@babel/runtime-corejs3": { - "version": "7.14.7", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.14.7.tgz", - "integrity": "sha512-Wvzcw4mBYbTagyBVZpAJWI06auSIj033T/yNE0Zn1xcup83MieCddZA7ls3kme17L4NOGBrQ09Q+nKB41RLWBA==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.15.4.tgz", + "integrity": "sha512-lWcAqKeB624/twtTc3w6w/2o9RqJPaNBhPGK6DKLSiwuVWC7WFkypWyNg+CpZoyJH0jVzv1uMtXZ/5/lQOLtCg==", "dev": true, "requires": { - "core-js-pure": "^3.15.0", + "core-js-pure": "^3.16.0", "regenerator-runtime": "^0.13.4" + }, + "dependencies": { + "core-js-pure": { + "version": "3.17.3", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.17.3.tgz", + "integrity": "sha512-YusrqwiOTTn8058JDa0cv9unbXdIiIgcgI9gXso0ey4WgkFLd3lYlV9rp9n7nDCsYxXsMDTjA4m1h3T348mdlQ==", + "dev": true + } } }, "@babel/template": { @@ -1921,6 +1928,7 @@ "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.3.tgz", "integrity": "sha512-jBD+G8+LWpMBBWvVcdr4QysjUE4mU/syrhN17o1u3gx0/WzJB1kwiVZAXRtWbsIPOwW8pF/YJV5+nmetPzepXg==", + "dev": true, "requires": { "esutils": "^2.0.2", "lodash": "^4.17.13", @@ -3060,9 +3068,9 @@ } }, "@hapi/boom": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/@hapi/boom/-/boom-9.1.2.tgz", - "integrity": "sha512-uJEJtiNHzKw80JpngDGBCGAmWjBtzxDCz17A9NO2zCi8LLBlb5Frpq4pXwyN+2JQMod4pKz5BALwyneCgDg89Q==", + "version": "9.1.4", + "resolved": "https://registry.npmjs.org/@hapi/boom/-/boom-9.1.4.tgz", + "integrity": "sha512-Ls1oH8jaN1vNsqcaHVYJrKmgMcKsC1wcp8bujvXrHaAqD2iDYq3HoOwsxwo09Cuda5R5nC0o0IxlrlTuvPuzSw==", "requires": { "@hapi/hoek": "9.x.x" } @@ -3411,34 +3419,42 @@ } } }, + "@napi-rs/triples": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@napi-rs/triples/-/triples-1.0.3.tgz", + "integrity": "sha512-jDJTpta+P4p1NZTFVLHJ/TLFVYVcOqv6l8xwOeBKNPMgY/zDYH/YH7SJbvrr/h1RcS9GzbPcLKGzpuK9cV56UA==" + }, "@next/bundle-analyzer": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/@next/bundle-analyzer/-/bundle-analyzer-11.0.1.tgz", - "integrity": "sha512-/+VqZsEu1Q19PlJyNrXJ0qNuYVH1ZiA9fz3G1tiTLdJmHHt4SeAqiBejHvuy8nkttGnHg7uUTdycvVujK0IcQQ==", + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/@next/bundle-analyzer/-/bundle-analyzer-11.1.1.tgz", + "integrity": "sha512-EdjFY11oFpDKwOFCOKtJqBT7DQTJ4wa4opvhuysf9EZ8AurlcWShTAfnG/k+z9tDJ0SHujanjd5livoTIIPqgA==", "requires": { "webpack-bundle-analyzer": "4.3.0" } }, "@next/env": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/@next/env/-/env-11.0.1.tgz", - "integrity": "sha512-yZfKh2U6R9tEYyNUrs2V3SBvCMufkJ07xMH5uWy8wqcl5gAXoEw6A/1LDqwX3j7pUutF9d1ZxpdGDA3Uag+aQQ==" + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/@next/env/-/env-11.1.2.tgz", + "integrity": "sha512-+fteyVdQ7C/OoulfcF6vd1Yk0FEli4453gr8kSFbU8sKseNSizYq6df5MKz/AjwLptsxrUeIkgBdAzbziyJ3mA==" }, "@next/eslint-plugin-next": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-11.0.1.tgz", - "integrity": "sha512-UzdX3y6XSrj9YuASUb/p4sRvfjP2klj2YgIOfMwrWoLTTPJQMh00hREB9Ftr7m7RIxjVSAaaLXIRLdxvq948GA==", - "dev": true + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-11.1.2.tgz", + "integrity": "sha512-cN+ojHRsufr9Yz0rtvjv8WI5En0RPZRJnt0y16Ha7DD+0n473evz8i1ETEJHmOLeR7iPJR0zxRrxeTN/bJMOjg==", + "dev": true, + "requires": { + "glob": "7.1.7" + } }, "@next/polyfill-module": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/@next/polyfill-module/-/polyfill-module-11.0.1.tgz", - "integrity": "sha512-Cjs7rrKCg4CF4Jhri8PCKlBXhszTfOQNl9AjzdNy4K5jXFyxyoSzuX2rK4IuoyE+yGp5A3XJCBEmOQ4xbUp9Mg==" + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/@next/polyfill-module/-/polyfill-module-11.1.2.tgz", + "integrity": "sha512-xZmixqADM3xxtqBV0TpAwSFzWJP0MOQzRfzItHXf1LdQHWb0yofHHC+7eOrPFic8+ZGz5y7BdPkkgR1S25OymA==" }, "@next/react-dev-overlay": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/@next/react-dev-overlay/-/react-dev-overlay-11.0.1.tgz", - "integrity": "sha512-lvUjMVpLsgzADs9Q8wtC5LNqvfdN+M0BDMSrqr04EDWAyyX0vURHC9hkvLbyEYWyh+WW32pwjKBXdkMnJhoqMg==", + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/@next/react-dev-overlay/-/react-dev-overlay-11.1.2.tgz", + "integrity": "sha512-rDF/mGY2NC69mMg2vDqzVpCOlWqnwPUXB2zkARhvknUHyS6QJphPYv9ozoPJuoT/QBs49JJd9KWaAzVBvq920A==", "requires": { "@babel/code-frame": "7.12.11", "anser": "1.4.9", @@ -3454,9 +3470,9 @@ }, "dependencies": { "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" }, "chalk": { "version": "4.0.0", @@ -3486,9 +3502,41 @@ } }, "@next/react-refresh-utils": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/@next/react-refresh-utils/-/react-refresh-utils-11.0.1.tgz", - "integrity": "sha512-K347DM6Z7gBSE+TfUaTTceWvbj0B6iNAsFZXbFZOlfg3uyz2sbKpzPYYFocCc27yjLaS8OfR8DEdS2mZXi8Saw==" + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/@next/react-refresh-utils/-/react-refresh-utils-11.1.2.tgz", + "integrity": "sha512-hsoJmPfhVqjZ8w4IFzoo8SyECVnN+8WMnImTbTKrRUHOVJcYMmKLL7xf7T0ft00tWwAl/3f3Q3poWIN2Ueql/Q==" + }, + "@next/swc-darwin-arm64": { + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-11.1.2.tgz", + "integrity": "sha512-hZuwOlGOwBZADA8EyDYyjx3+4JGIGjSHDHWrmpI7g5rFmQNltjlbaefAbiU5Kk7j3BUSDwt30quJRFv3nyJQ0w==", + "optional": true + }, + "@next/swc-darwin-x64": { + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-11.1.2.tgz", + "integrity": "sha512-PGOp0E1GisU+EJJlsmJVGE+aPYD0Uh7zqgsrpD3F/Y3766Ptfbe1lEPPWnRDl+OzSSrSrX1lkyM/Jlmh5OwNvA==", + "optional": true + }, + "@next/swc-linux-x64-gnu": { + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-11.1.2.tgz", + "integrity": "sha512-YcDHTJjn/8RqvyJVB6pvEKXihDcdrOwga3GfMv/QtVeLphTouY4BIcEUfrG5+26Nf37MP1ywN3RRl1TxpurAsQ==", + "optional": true + }, + "@next/swc-win32-x64-msvc": { + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-11.1.2.tgz", + "integrity": "sha512-e/pIKVdB+tGQYa1cW3sAeHm8gzEri/HYLZHT4WZojrUxgWXqx8pk7S7Xs47uBcFTqBDRvK3EcQpPLf3XdVsDdg==", + "optional": true + }, + "@node-rs/helper": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@node-rs/helper/-/helper-1.2.1.tgz", + "integrity": "sha512-R5wEmm8nbuQU0YGGmYVjEc0OHtYsuXdpRG+Ut/3wZ9XAvQWyThN08bTh2cBJgoZxHQUPtvRfeQuxcAgLuiBISg==", + "requires": { + "@napi-rs/triples": "^1.0.3" + } }, "@nodelib/fs.scandir": { "version": "2.1.5", @@ -3698,9 +3746,9 @@ } }, "@polka/url": { - "version": "1.0.0-next.15", - "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.15.tgz", - "integrity": "sha512-15spi3V28QdevleWBNXE4pIls3nFZmBbUGrW9IVPwiQczuSb9n76TCB4bsk8TSel+I1OkHEdPhu5QKMfY6rQHA==" + "version": "1.0.0-next.20", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.20.tgz", + "integrity": "sha512-88p7+M0QGxKpmnkfXjS4V26AnoC/eiqZutE8GLdaI5X12NY75bXSdTY9NkmYb2Xyk1O+MmkuO6Frmsj84V6I8Q==" }, "@popperjs/core": { "version": "2.9.2", @@ -6211,8 +6259,7 @@ "@types/node": { "version": "14.17.4", "resolved": "https://registry.npmjs.org/@types/node/-/node-14.17.4.tgz", - "integrity": "sha512-8kQ3+wKGRNN0ghtEn7EGps/B8CzuBz1nXZEIGGLP2GnwbqYn4dbTs7k+VKLTq1HvZLRCIDtN3Snx1Ege8B7L5A==", - "dev": true + "integrity": "sha512-8kQ3+wKGRNN0ghtEn7EGps/B8CzuBz1nXZEIGGLP2GnwbqYn4dbTs7k+VKLTq1HvZLRCIDtN3Snx1Ege8B7L5A==" }, "@types/node-fetch": { "version": "2.5.11", @@ -7228,9 +7275,9 @@ } }, "available-typed-arrays": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.4.tgz", - "integrity": "sha512-SA5mXJWrId1TaQjfxUYghbqQ/hYioKmLJvPJyDuYRtXXenFNMjj4hSSt1Cf1xsuXSXrtxrVC5Ot4eU6cOtBDdA==" + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==" }, "aws-sign2": { "version": "0.7.0", @@ -7245,9 +7292,9 @@ "dev": true }, "axe-core": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.2.3.tgz", - "integrity": "sha512-pXnVMfJKSIWU2Ml4JHP7pZEPIrgBO1Fd3WGx+fPBsS+KRGhE4vxooD8XBGWbQOIVSZsVK7pUDBBkCicNu80yzQ==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.3.3.tgz", + "integrity": "sha512-/lqqLAmuIPi79WYfRpy2i8z+x+vxU3zX2uAm0gs1q52qTuKwolOj1P8XbufpXcsydrpKx2yGn2wzAnxCMV86QA==", "dev": true }, "axios": { @@ -7525,7 +7572,8 @@ "babel-plugin-syntax-jsx": { "version": "6.18.0", "resolved": "https://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz", - "integrity": "sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY=" + "integrity": "sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY=", + "dev": true }, "babel-plugin-syntax-trailing-function-commas": { "version": "7.0.0-beta.0", @@ -10048,19 +10096,19 @@ "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==" }, "cssnano-preset-simple": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/cssnano-preset-simple/-/cssnano-preset-simple-2.0.0.tgz", - "integrity": "sha512-HkufSLkaBJbKBFx/7aj5HmCK9Ni/JedRQm0mT2qBzMG/dEuJOLnMt2lK6K1rwOOyV4j9aSY+knbW9WoS7BYpzg==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssnano-preset-simple/-/cssnano-preset-simple-3.0.0.tgz", + "integrity": "sha512-vxQPeoMRqUT3c/9f0vWeVa2nKQIHFpogtoBvFdW4GQ3IvEJ6uauCP6p3Y5zQDLFcI7/+40FTgX12o7XUL0Ko+w==", "requires": { "caniuse-lite": "^1.0.30001202" } }, "cssnano-simple": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/cssnano-simple/-/cssnano-simple-2.0.0.tgz", - "integrity": "sha512-0G3TXaFxlh/szPEG/o3VcmCwl0N3E60XNb9YZZijew5eIs6fLjJuOPxQd9yEBaX2p/YfJtt49i4vYi38iH6/6w==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssnano-simple/-/cssnano-simple-3.0.0.tgz", + "integrity": "sha512-oU3ueli5Dtwgh0DyeohcIEE00QVfbPR3HzyXdAl89SfnQG3y0/qcpfLVW+jPIh3/rgMZGwuW96rejZGaYE9eUg==", "requires": { - "cssnano-preset-simple": "^2.0.0" + "cssnano-preset-simple": "^3.0.0" } }, "csstype": { @@ -10938,6 +10986,7 @@ "version": "1.18.3", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.3.tgz", "integrity": "sha512-nQIr12dxV7SSxE6r6f1l3DtAeEYdsGpps13dR0TwJg1S8gyp4ZPgy3FZcHBgbiQqnoqSTb+oC+kO4UQ0C/J8vw==", + "dev": true, "requires": { "call-bind": "^1.0.2", "es-to-primitive": "^1.2.1", @@ -11174,12 +11223,12 @@ } }, "eslint-config-next": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-11.0.1.tgz", - "integrity": "sha512-yy63K4Bmy8amE6VMb26CZK6G99cfVX3JaMTvuvmq/LL8/b8vKHcauUZREBTAQ+2DrIvlH4YrFXrkQ1vpYDL9Eg==", + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-11.1.2.tgz", + "integrity": "sha512-dFutecxX2Z5/QVlLwdtKt+gIfmNMP8Qx6/qZh3LM/DFVdGJEAnUKrr4VwGmACB2kx/PQ5bx3R+QxnEg4fDPiTg==", "dev": true, "requires": { - "@next/eslint-plugin-next": "11.0.1", + "@next/eslint-plugin-next": "11.1.2", "@rushstack/eslint-patch": "^1.0.6", "@typescript-eslint/parser": "^4.20.0", "eslint-import-resolver-node": "^0.3.4", @@ -11200,49 +11249,43 @@ } }, "eslint-import-resolver-node": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz", - "integrity": "sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA==", + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz", + "integrity": "sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==", "dev": true, "requires": { - "debug": "^2.6.9", - "resolve": "^1.13.1" + "debug": "^3.2.7", + "resolve": "^1.20.0" }, "dependencies": { "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, "requires": { - "ms": "2.0.0" + "ms": "^2.1.1" } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true } } }, "eslint-import-resolver-typescript": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-2.4.0.tgz", - "integrity": "sha512-useJKURidCcldRLCNKWemr1fFQL1SzB3G4a0li6lFGvlc5xGe1hY343bvG07cbpCzPuM/lK19FIJB3XGFSkplA==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-2.5.0.tgz", + "integrity": "sha512-qZ6e5CFr+I7K4VVhQu3M/9xGv9/YmwsEXrsm3nimw8vWaVHRDrQRp26BgCypTxBp3vUp4o5aVEJRiy0F2DFddQ==", "dev": true, "requires": { - "debug": "^4.1.1", - "glob": "^7.1.6", + "debug": "^4.3.1", + "glob": "^7.1.7", "is-glob": "^4.0.1", - "resolve": "^1.17.0", + "resolve": "^1.20.0", "tsconfig-paths": "^3.9.0" } }, "eslint-module-utils": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.6.1.tgz", - "integrity": "sha512-ZXI9B8cxAJIH4nfkhTwcRTEAnrVfobYqwjWy/QMCZ8rHkZHFjf9yO4BzpiF9kCSfNlMG54eKigISHpX0+AaT4A==", + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.6.2.tgz", + "integrity": "sha512-QG8pcgThYOuqxupd06oYTZoNOGaUdTY1PqK+oS6ElF6vs4pBdk/aYxFVQQXzcrAqp9m7cl7lb2ubazX+g16k2Q==", "dev": true, "requires": { "debug": "^3.2.7", @@ -11313,26 +11356,26 @@ } }, "eslint-plugin-import": { - "version": "2.23.4", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.23.4.tgz", - "integrity": "sha512-6/wP8zZRsnQFiR3iaPFgh5ImVRM1WN5NUWfTIRqwOdeiGJlBcSk82o1FEVq8yXmy4lkIzTo7YhHCIxlU/2HyEQ==", + "version": "2.24.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.24.2.tgz", + "integrity": "sha512-hNVtyhiEtZmpsabL4neEj+6M5DCLgpYyG9nzJY8lZQeQXEn5UPW1DpUdsMHMXsq98dbNm7nt1w9ZMSVpfJdi8Q==", "dev": true, "requires": { "array-includes": "^3.1.3", "array.prototype.flat": "^1.2.4", "debug": "^2.6.9", "doctrine": "^2.1.0", - "eslint-import-resolver-node": "^0.3.4", - "eslint-module-utils": "^2.6.1", + "eslint-import-resolver-node": "^0.3.6", + "eslint-module-utils": "^2.6.2", "find-up": "^2.0.0", "has": "^1.0.3", - "is-core-module": "^2.4.0", + "is-core-module": "^2.6.0", "minimatch": "^3.0.4", - "object.values": "^1.1.3", + "object.values": "^1.1.4", "pkg-up": "^2.0.0", "read-pkg-up": "^3.0.0", "resolve": "^1.20.0", - "tsconfig-paths": "^3.9.0" + "tsconfig-paths": "^3.11.0" }, "dependencies": { "debug": { @@ -11362,6 +11405,15 @@ "locate-path": "^2.0.0" } }, + "is-core-module": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.6.0.tgz", + "integrity": "sha512-wShG8vs60jKfPWpF2KZRaAtvt3a20OAn7+IJ6hLPECpSABLcKtFKTTI4ZtH5QcBruBHlq+WsdHWyz0BCZW7svQ==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, "locate-path": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", @@ -12751,6 +12803,15 @@ "pump": "^3.0.0" } }, + "get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + } + }, "get-value": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", @@ -13256,6 +13317,14 @@ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==" }, + "has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "requires": { + "has-symbols": "^1.0.2" + } + }, "has-unicode": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", @@ -13881,7 +13950,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", - "dev": true, "requires": { "get-intrinsic": "^1.1.0", "has": "^1.0.3", @@ -14119,9 +14187,12 @@ "dev": true }, "is-generator-function": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.9.tgz", - "integrity": "sha512-ZJ34p1uvIfptHCN7sFTjGibB9/oBg17sHqzDLfuwhvmN/qLVvIQXRQ8licZQ35WJ8KuEQt/etnnzQFI9C9Ue/A==" + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "requires": { + "has-tostringtag": "^1.0.0" + } }, "is-glob": { "version": "4.0.1", @@ -14228,6 +14299,7 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.3.tgz", "integrity": "sha512-qSVXFz28HM7y+IWX6vLCsexdlvzT1PJNFSBuaQLQ5o0IEw8UDYW6/2+eCMVyIsbM8CNLX2a/QWmSpyxYEHY7CQ==", + "dev": true, "requires": { "call-bind": "^1.0.2", "has-symbols": "^1.0.2" @@ -14280,15 +14352,69 @@ } }, "is-typed-array": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.5.tgz", - "integrity": "sha512-S+GRDgJlR3PyEbsX/Fobd9cqpZBuvUS+8asRqYDMLCb2qMzt1oz5m5oxQCxOgUDxiWsOVNi4yaF+/uvdlHlYug==", + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.8.tgz", + "integrity": "sha512-HqH41TNZq2fgtGT8WHVFVJhBVGuY3AnP3Q36K8JKXUxSxRgk/d+7NjmwG2vo2mYmXK8UYZKu0qH8bVP5gEisjA==", "requires": { - "available-typed-arrays": "^1.0.2", + "available-typed-arrays": "^1.0.5", "call-bind": "^1.0.2", - "es-abstract": "^1.18.0-next.2", + "es-abstract": "^1.18.5", "foreach": "^2.0.5", - "has-symbols": "^1.0.1" + "has-tostringtag": "^1.0.0" + }, + "dependencies": { + "es-abstract": { + "version": "1.18.6", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.6.tgz", + "integrity": "sha512-kAeIT4cku5eNLNuUKhlmtuk1/TRZvQoYccn6TO0cSVdf1kzB0T7+dYuVK9MWM7l+/53W2Q8M7N2c6MQvhXFcUQ==", + "requires": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "get-intrinsic": "^1.1.1", + "get-symbol-description": "^1.0.0", + "has": "^1.0.3", + "has-symbols": "^1.0.2", + "internal-slot": "^1.0.3", + "is-callable": "^1.2.4", + "is-negative-zero": "^2.0.1", + "is-regex": "^1.1.4", + "is-string": "^1.0.7", + "object-inspect": "^1.11.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.2", + "string.prototype.trimend": "^1.0.4", + "string.prototype.trimstart": "^1.0.4", + "unbox-primitive": "^1.0.1" + } + }, + "is-callable": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", + "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==" + }, + "is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "object-inspect": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.0.tgz", + "integrity": "sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg==" + } } }, "is-typedarray": { @@ -14551,11 +14677,6 @@ "supports-color": "^8.0.0" }, "dependencies": { - "@types/node": { - "version": "15.12.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-15.12.4.tgz", - "integrity": "sha512-zrNj1+yqYF4WskCMOHwN+w9iuD12+dGm0rQ35HLl9/Ouuq52cEtd0CH9qMgrdNmi5ejC1/V7vKEXYubB+65DkA==" - }, "supports-color": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", @@ -16216,16 +16337,21 @@ "dev": true }, "next": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/next/-/next-11.0.1.tgz", - "integrity": "sha512-yR7be7asNbvpVNpi6xxEg28wZ7Gqmj1nOt0sABH9qORmF3+pms2KZ7Cng33oK5nqPIzEEFJD0pp2PCe3/ueMIg==", + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/next/-/next-11.1.2.tgz", + "integrity": "sha512-azEYL0L+wFjv8lstLru3bgvrzPvK0P7/bz6B/4EJ9sYkXeW8r5Bjh78D/Ol7VOg0EIPz0CXoe72hzAlSAXo9hw==", "requires": { - "@babel/runtime": "7.12.5", + "@babel/runtime": "7.15.3", "@hapi/accept": "5.0.2", - "@next/env": "11.0.1", - "@next/polyfill-module": "11.0.1", - "@next/react-dev-overlay": "11.0.1", - "@next/react-refresh-utils": "11.0.1", + "@next/env": "11.1.2", + "@next/polyfill-module": "11.1.2", + "@next/react-dev-overlay": "11.1.2", + "@next/react-refresh-utils": "11.1.2", + "@next/swc-darwin-arm64": "11.1.2", + "@next/swc-darwin-x64": "11.1.2", + "@next/swc-linux-x64-gnu": "11.1.2", + "@next/swc-win32-x64-msvc": "11.1.2", + "@node-rs/helper": "1.2.1", "assert": "2.0.0", "ast-types": "0.13.2", "browserify-zlib": "0.2.0", @@ -16236,7 +16362,7 @@ "chokidar": "3.5.1", "constants-browserify": "1.0.0", "crypto-browserify": "3.12.0", - "cssnano-simple": "2.0.0", + "cssnano-simple": "3.0.0", "domain-browser": "4.19.0", "encoding": "0.1.13", "etag": "1.8.1", @@ -16253,9 +16379,8 @@ "p-limit": "3.1.0", "path-browserify": "1.0.1", "pnp-webpack-plugin": "1.6.4", - "postcss": "8.2.13", + "postcss": "8.2.15", "process": "0.11.10", - "prop-types": "15.7.2", "querystring-es3": "0.2.1", "raw-body": "2.4.1", "react-is": "17.0.2", @@ -16263,15 +16388,23 @@ "stream-browserify": "3.0.0", "stream-http": "3.1.1", "string_decoder": "1.3.0", - "styled-jsx": "3.3.2", + "styled-jsx": "4.0.1", "timers-browserify": "2.0.12", "tty-browserify": "0.0.1", "use-subscription": "1.5.1", - "util": "0.12.3", + "util": "0.12.4", "vm-browserify": "1.1.2", "watchpack": "2.1.1" }, "dependencies": { + "@babel/runtime": { + "version": "7.15.3", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.15.3.tgz", + "integrity": "sha512-OvwMLqNXkCXSz1kSm58sEsNuhqOx/fKpnUnKnFB5v8uDda5bLNEHNgKPvhDN6IU0LDcnHQ90LlJ0Q6jnyBSIBA==", + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, "chokidar": { "version": "3.5.1", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", @@ -16296,12 +16429,12 @@ } }, "postcss": { - "version": "8.2.13", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.2.13.tgz", - "integrity": "sha512-FCE5xLH+hjbzRdpbRb1IMCvPv9yZx2QnDarBEYSN0N0HYk+TcXsEhwdFcFb+SRWOKzKGErhIEbBK2ogyLdTtfQ==", + "version": "8.2.15", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.2.15.tgz", + "integrity": "sha512-2zO3b26eJD/8rb106Qu2o7Qgg52ND5HPjcyQiK2B98O388h43A448LCslC0dI2P97wCAQRJsFvwTRcXxTKds+Q==", "requires": { "colorette": "^1.2.2", - "nanoid": "^3.1.22", + "nanoid": "^3.1.23", "source-map": "^0.6.1" } }, @@ -20313,7 +20446,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "dev": true, "requires": { "call-bind": "^1.0.0", "get-intrinsic": "^1.0.2", @@ -20350,11 +20482,11 @@ } }, "sirv": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/sirv/-/sirv-1.0.12.tgz", - "integrity": "sha512-+jQoCxndz7L2tqQL4ZyzfDhky0W/4ZJip3XoOuxyQWnAwMxindLl3Xv1qT4x1YX/re0leShvTm8Uk0kQspGhBg==", + "version": "1.0.17", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-1.0.17.tgz", + "integrity": "sha512-qx9go5yraB7ekT7bCMqUHJ5jEaOC/GXBxUWv+jeWnb7WzHUFdcQPGWk7YmAwFBaQBrogpuSqd/azbC2lZRqqmw==", "requires": { - "@polka/url": "^1.0.0-next.15", + "@polka/url": "^1.0.0-next.20", "mime": "^2.3.1", "totalist": "^1.0.0" }, @@ -21008,12 +21140,12 @@ } }, "styled-jsx": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-3.3.2.tgz", - "integrity": "sha512-daAkGd5mqhbBhLd6jYAjYBa9LpxYCzsgo/f6qzPdFxVB8yoGbhxvzQgkC0pfmCVvW3JuAEBn0UzFLBfkHVZG1g==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-4.0.1.tgz", + "integrity": "sha512-Gcb49/dRB1k8B4hdK8vhW27Rlb2zujCk1fISrizCcToIs+55B4vmUM0N9Gi4nnVfFZWe55jRdWpAqH1ldAKWvQ==", "requires": { - "@babel/types": "7.8.3", - "babel-plugin-syntax-jsx": "6.18.0", + "@babel/plugin-syntax-jsx": "7.14.5", + "@babel/types": "7.15.0", "convert-source-map": "1.7.0", "loader-utils": "1.2.3", "source-map": "0.7.3", @@ -21022,6 +21154,20 @@ "stylis-rule-sheet": "0.0.10" }, "dependencies": { + "@babel/helper-validator-identifier": { + "version": "7.14.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.9.tgz", + "integrity": "sha512-pQYxPY0UP6IHISRitNe8bsijHex4TWZXi2HwKVsjPiltzlhse2znVcm9Ace510VT1kxIHjGJCZZQBX2gJDbo0g==" + }, + "@babel/types": { + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.15.0.tgz", + "integrity": "sha512-OBvfqnllOIdX4ojTHpwZbpvz4j3EWyjkZEdmjH0/cgsd6QOdSgU8rLSk6ard/pcW7rlmjdVSX/AWOaORR1uNOQ==", + "requires": { + "@babel/helper-validator-identifier": "^7.14.9", + "to-fast-properties": "^2.0.0" + } + }, "source-map": { "version": "0.7.3", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", @@ -22151,9 +22297,9 @@ "integrity": "sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw==" }, "tsconfig-paths": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz", - "integrity": "sha512-dRcuzokWhajtZWkQsDVKbWyY+jgcLC5sqJhg2PSgf4ZkH2aHPvaOY8YWGhmjb68b5qqTfasSsDO9k7RUiEmZAw==", + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.11.0.tgz", + "integrity": "sha512-7ecdYDnIdmv639mmDwslG6KQg1Z9STTz1j7Gcz0xa+nshh/gKDAHcPxRbWOsA3SPp0tXP2leTcY9Kw+NAkfZzA==", "dev": true, "requires": { "@types/json5": "^0.0.29", @@ -22703,9 +22849,9 @@ "integrity": "sha512-ayNkOJdoNSGNDBE46Nkc+l6IXmeugbzahZLSMkwvgRWv5y5ZqNY2IrzcgmkR4z32sj1W3tM3TuTUMqkqBzO+RA==" }, "util": { - "version": "0.12.3", - "resolved": "https://registry.npmjs.org/util/-/util-0.12.3.tgz", - "integrity": "sha512-I8XkoQwE+fPQEhy9v012V+TSdH2kp9ts29i20TaaDUXsg7x/onePbhFJUExBfv/2ay1ZOp/Vsm3nDlmnFGSAog==", + "version": "0.12.4", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.4.tgz", + "integrity": "sha512-bxZ9qtSlGUWSOy9Qa9Xgk11kSslpuZwaxCg4sNIDj6FLucDab2JxnHwyNTCpHMtK1MjoQiWQ6DiUMZYbSrO+Sw==", "requires": { "inherits": "^2.0.3", "is-arguments": "^1.0.4", @@ -23471,19 +23617,19 @@ }, "dependencies": { "acorn": { - "version": "8.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.4.1.tgz", - "integrity": "sha512-asabaBSkEKosYKMITunzX177CXxQ4Q8BSSzMTKD+FefUhipQC70gfW5SiUDhYQ3vk8G+81HqQk7Fv9OXwwn9KA==" + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.5.0.tgz", + "integrity": "sha512-yXbYeFy+jUuYd3/CDcg2NkIYE991XYX/bje7LmjJigUciaeO1JR4XxXgCIV1/Zc/dRuFEyw1L0pbA+qynJkW5Q==" }, "acorn-walk": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.1.1.tgz", - "integrity": "sha512-FbJdceMlPHEAWJOILDk1fXD8lnTlEIWFkqtfk+MvmL5q/qlHfN7GEHcsFZWt/Tea9jRNPWUZG4G976nqAAmU9w==" + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==" }, "chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -23645,17 +23791,70 @@ "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" }, "which-typed-array": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.4.tgz", - "integrity": "sha512-49E0SpUe90cjpoc7BOJwyPHRqSAd12c10Qm2amdEZrJPCY2NDxaW01zHITrem+rnETY3dwrbH3UUrUwagfCYDA==", + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.7.tgz", + "integrity": "sha512-vjxaB4nfDqwKI0ws7wZpxIlde1XrLX5uB0ZjpfshgmapJMD7jJWhZI+yToJTqaFByF0eNBcYxbjmCzoRP7CfEw==", "requires": { - "available-typed-arrays": "^1.0.2", - "call-bind": "^1.0.0", - "es-abstract": "^1.18.0-next.1", + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "es-abstract": "^1.18.5", "foreach": "^2.0.5", - "function-bind": "^1.1.1", - "has-symbols": "^1.0.1", - "is-typed-array": "^1.1.3" + "has-tostringtag": "^1.0.0", + "is-typed-array": "^1.1.7" + }, + "dependencies": { + "es-abstract": { + "version": "1.18.6", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.6.tgz", + "integrity": "sha512-kAeIT4cku5eNLNuUKhlmtuk1/TRZvQoYccn6TO0cSVdf1kzB0T7+dYuVK9MWM7l+/53W2Q8M7N2c6MQvhXFcUQ==", + "requires": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "get-intrinsic": "^1.1.1", + "get-symbol-description": "^1.0.0", + "has": "^1.0.3", + "has-symbols": "^1.0.2", + "internal-slot": "^1.0.3", + "is-callable": "^1.2.4", + "is-negative-zero": "^2.0.1", + "is-regex": "^1.1.4", + "is-string": "^1.0.7", + "object-inspect": "^1.11.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.2", + "string.prototype.trimend": "^1.0.4", + "string.prototype.trimstart": "^1.0.4", + "unbox-primitive": "^1.0.1" + } + }, + "is-callable": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", + "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==" + }, + "is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "object-inspect": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.0.tgz", + "integrity": "sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg==" + } } }, "wide-align": { diff --git a/Website/src/rendering/package.json b/Website/src/rendering/package.json index 94e341f76..d82912b40 100644 --- a/Website/src/rendering/package.json +++ b/Website/src/rendering/package.json @@ -37,12 +37,12 @@ "@fortawesome/free-regular-svg-icons": "^5.15.3", "@fortawesome/free-solid-svg-icons": "^5.15.3", "@fortawesome/react-fontawesome": "^0.1.14", - "@next/bundle-analyzer": "^11.0.1", + "@next/bundle-analyzer": "^11.1.1", "@sitecore-jss/sitecore-jss-nextjs": "^18.0.0-canary.1", "@sitecore-jss/sitecore-jss-tracking": "^18.0.0-canary.1", "graphql": "~15.4.0", "graphql-tag": "^2.11.0", - "next": "^11.0.0", + "next": "^11.1.2", "next-localization": "^0.10.0", "nprogress": "~0.2.0", "postcss-import": "^14.0.2", @@ -82,7 +82,7 @@ "constant-case": "^3.0.4", "cross-env": "~6.0.3", "eslint": "^7.23.0", - "eslint-config-next": "^11.0.0", + "eslint-config-next": "^11.1.2", "eslint-config-prettier": "^6.15.0", "eslint-plugin-prettier": "^3.1.4", "eslint-plugin-react": "^7.21.5", diff --git a/Website/src/rendering/scripts/generate-config.ts b/Website/src/rendering/scripts/generate-config.ts index 0ccf89dd5..755196c5c 100644 --- a/Website/src/rendering/scripts/generate-config.ts +++ b/Website/src/rendering/scripts/generate-config.ts @@ -18,6 +18,11 @@ export function generateConfig(configOverrides?: { [key: string]: string }): voi sitecoreApiKey: 'no-api-key-set', sitecoreApiHost: '', jssAppName: 'Unknown', + // DEMO CUSTOMIZATION - CDP integration + cdpProxyUrl: '', + cdpClientKey: '', + cdpApiTargetEndpoint: '', + // END CUSTOMIZATION }; // require + combine config sources @@ -41,13 +46,17 @@ const config = {};\n`; // Set base configuration values, allowing override with environment variables Object.keys(config).forEach((prop) => { - configText += `config.${prop} = process.env.${constantCase(prop)} || "${config[prop]}",\n`; + // DEMO CUSTOMIZATION - Support config from NEXT_PUBLIC_* environment variables + // eslint-disable-next-line prettier/prettier + configText += `config.${prop} = process.env.${constantCase(prop)} || process.env.NEXT_PUBLIC_${constantCase(prop)} || "${config[prop]}",\n`; + // END CUSTOMIZATION }); // Set computed values, allowing override with environment variables Object.keys(computedConfig).forEach((prop) => { - configText += `config.${prop} = process.env.${constantCase(prop)} || ${ - computedConfig[prop] - };\n`; + // DEMO CUSTOMIZATION - Support config from NEXT_PUBLIC_* environment variables + // eslint-disable-next-line prettier/prettier + configText += `config.${prop} = process.env.${constantCase(prop)} || process.env.NEXT_PUBLIC_${constantCase(prop)} || ${computedConfig[prop]};\n`; + // END CUSTOMIZATION }); configText += `module.exports = config;`; diff --git a/Website/src/rendering/src/Layout.tsx b/Website/src/rendering/src/Layout.tsx index bcaf28861..6ab2d8b08 100644 --- a/Website/src/rendering/src/Layout.tsx +++ b/Website/src/rendering/src/Layout.tsx @@ -7,6 +7,7 @@ import { LayoutServicePageState, } from '@sitecore-jss/sitecore-jss-nextjs'; import { SitecoreContextValue } from 'lib/component-props'; // DEMO TEAM CUSTOMIZATION - Different type name +import { logViewEvent } from './services/BoxeverService'; // DEMO TEAM CUSTOMIZATION - CDP integration // Prefix public assets with a public URL to enable compatibility with Sitecore Experience Editor. // If you're not supporting the Experience Editor, you can remove this. @@ -25,6 +26,11 @@ const Layout = ({ context }: LayoutProps): JSX.Element => { // Note the context object type here matches the initial value in [[...path]].tsx. useEffect(() => { updateSitecoreContext && updateSitecoreContext(context); + + // DEMO TEAM CUSTOMIZATION - Log page views in CDP + const { route } = context; + logViewEvent(route); + // END CUSTOMIZATION }, [context, updateSitecoreContext]); // DEMO TEAM CUSTOMIZATION - Missing effect parameter to fix linting error const { route } = context; diff --git a/Website/src/rendering/src/lib/util.ts b/Website/src/rendering/src/lib/util.ts index 160c14f00..4f9f188ad 100644 --- a/Website/src/rendering/src/lib/util.ts +++ b/Website/src/rendering/src/lib/util.ts @@ -5,3 +5,10 @@ export const getPublicUrl = (): string => { return process.env.PUBLIC_URL || ''; }; + +/** + * Throws an error when the parameter is omitted. + */ +export function required(): undefined { + throw new Error('Missing parameter'); +} diff --git a/Website/src/rendering/src/pages/_app.tsx b/Website/src/rendering/src/pages/_app.tsx index eeb90bfc9..2f13ee493 100644 --- a/Website/src/rendering/src/pages/_app.tsx +++ b/Website/src/rendering/src/pages/_app.tsx @@ -6,6 +6,7 @@ import { I18nProvider } from 'next-localization'; import Head from 'next/head'; import Script from 'next/script'; import NProgress from 'nprogress'; +import config from '../temp/config'; // Using nprogress are completely optional. // nprogress provides a loading indicator on page/route changes. @@ -41,10 +42,26 @@ function App({ Component, pageProps, router }: AppProps): JSX.Element { const { dictionary, ...rest } = pageProps; - // DEMO TEAM CUSTOMIZATION - Add CDP - const cdpClientKey = process.env.NEXT_PUBLIC_CDP_CLIENT_KEY || ''; - const cdpApiTargetEndpoint = process.env.NEXT_PUBLIC_CDP_API_TARGET_ENDPOINT || ''; - const isCdpClientKeyConfigured = !!cdpClientKey && !!cdpApiTargetEndpoint; + // DEMO TEAM CUSTOMIZATION - CDP integration + const cdpClientKey = config.cdpClientKey; + const cdpApiTargetEndpoint = config.cdpApiTargetEndpoint; + const isCdpConfigured = !!cdpClientKey && !!cdpApiTargetEndpoint; + + const cdpScripts = isCdpConfigured ? ( + <> + + + + ) : undefined; // END CUSTOMIZATION // DEMO TEAM CUSTOMIZATION - Add head section @@ -66,23 +83,8 @@ function App({ Component, pageProps, router }: AppProps): JSX.Element { - {/* DEMO TEAM CUSTOMIZATION - Add CDP */} - {isCdpClientKeyConfigured && ( - <> - - - - )} + {/* DEMO TEAM CUSTOMIZATION - CDP integration */} + {cdpScripts} {/* END CUSTOMIZATION*/} ); diff --git a/Website/src/rendering/src/services/BoxeverService.ts b/Website/src/rendering/src/services/BoxeverService.ts new file mode 100644 index 000000000..98008fcea --- /dev/null +++ b/Website/src/rendering/src/services/BoxeverService.ts @@ -0,0 +1,485 @@ +import axios, { AxiosPromise, AxiosRequestConfig } from 'axios'; +import { RouteData } from '@sitecore-jss/sitecore-jss-nextjs'; +import { required } from '../lib/util'; +import config from '../temp/config'; + +// ***** TYPES ***** + +type boxeverQueueFunctionType = () => void; + +interface eventCreateResponse { + status: string; +} + +type eventCreateCallbackType = ( + // eslint-disable-next-line no-unused-vars + response: eventCreateResponse +) => void; + +type callFlowsCallbackType = ( + // eslint-disable-next-line no-unused-vars + response: unknown +) => void; + +interface browserCreateResponse { + ref: string; +} + +type browserCreateCallbackType = ( + // eslint-disable-next-line no-unused-vars + data: browserCreateResponse +) => void; + +interface Boxever { + getID(): string; + eventCreate( + // eslint-disable-next-line no-unused-vars + payload: Record, + // eslint-disable-next-line no-unused-vars + callback: eventCreateCallbackType, + // eslint-disable-next-line no-unused-vars + type: string + ): void; + callFlows( + // eslint-disable-next-line no-unused-vars + payload: Record, + // eslint-disable-next-line no-unused-vars + callback: callFlowsCallbackType, + // eslint-disable-next-line no-unused-vars + type: string + ): void; + storage: { + removeItem( + // eslint-disable-next-line no-unused-vars + name: string + ): void; + setItem( + // eslint-disable-next-line no-unused-vars + name: string, + // eslint-disable-next-line no-unused-vars + browserId: string, + // eslint-disable-next-line no-unused-vars + cookieExpiresDays: number | { TTL: number } + ): void; + }; + cookie_name: string; + browserCreate( + // eslint-disable-next-line no-unused-vars + payload: Record, + // eslint-disable-next-line no-unused-vars + callback: browserCreateCallbackType, + // eslint-disable-next-line no-unused-vars + type: string + ): void; + browser_id: string; + isITPBrowser: boolean; + cookie_expires_days: number; + storage_ttl: number; + initWebFlowSDK(): void; +} + +declare global { + interface Window { + _boxever_settings: { + client_key: string; + target: string; + cookie_domain: string; + }; + Boxever: Boxever; + _boxever: Boxever; + _boxeverq: boxeverQueueFunctionType[]; + __boxeverQueue: { + new (): []; + }; + BoxeverJERS: { + errors: unknown[]; + }; + } +} + +type GuestRef = string; + +interface GuestRefResponse { + guestRef: GuestRef; +} + +interface GuestProfile { + data: { + firstName: string; + lastName: string; + email: string; + }; +} + +type GuestProfileResponse = GuestProfile | undefined; + +// ***** API ***** + +export function isBoxeverConfigured(): boolean { + return !!( + typeof window !== 'undefined' && + window._boxever_settings && + window._boxever_settings.client_key + ); +} + +function getConfigWithCurrentPage(config: Record) { + return Object.assign( + { + page: window.location.pathname + window.location.search, + }, + config + ); +} + +function createEventPayload(eventConfig: Record) { + return Object.assign( + { + browser_id: window.Boxever.getID(), // For eventCreate calls + browserId: window.Boxever.getID(), // For callFlows calls + channel: 'WEBSITE', + language: 'EN', + currency: 'CAD', + pos: 'PLAY! Summit', + }, + eventConfig + ); +} + +function createFlowPayload(flowConfig: Record) { + return Object.assign(createEventPayload(flowConfig), { + clientKey: window._boxever_settings.client_key, + }); +} + +// **************************************************************************** +// Delaying calls is required due to some race conditions where a guest +// identification call was executed before the Boxever library had finished +// initializing. +// **************************************************************************** +function delayUntilBoxeverIsReady(functionToDelay: () => unknown) { + if (window.Boxever && window.Boxever.getID() !== 'anonymous' && window._boxeverq) { + functionToDelay(); + } else { + const timeToWaitInMilliseconds = 100; + console.log(`Boxever is not ready yet. Waiting ${timeToWaitInMilliseconds}ms before retrying.`); + window.setTimeout(delayUntilBoxeverIsReady, timeToWaitInMilliseconds, functionToDelay); + } +} + +function sendEventCreate(eventConfig: Record) { + if (typeof window === 'undefined' || !isBoxeverConfigured()) { + return new Promise(function (resolve) { + resolve(); + }); + } + + // Set the page now as the location might have already changed when createEventPayload will be executed. + const eventWithCurrentPage = getConfigWithCurrentPage(eventConfig); + + return new Promise(function (resolve, reject) { + try { + delayUntilBoxeverIsReady(function () { + window._boxeverq.push(function () { + window.Boxever.eventCreate( + // Set the browserId on the event just before sending it to ensure it is up to date. + createEventPayload(eventWithCurrentPage), + function (response) { + if (!response) { + reject('No response provided.'); + } + if (response.status !== 'OK') { + reject('Response status: ' + response.status); + } + resolve(response); + }, + 'json' + ); + }); + }); + } catch (err) { + reject(err); + } + }); +} + +function callFlows(flowConfig: Record) { + if (typeof window === 'undefined' || !isBoxeverConfigured()) { + return new Promise(function (resolve) { + resolve(); + }); + } + + // Set the page now as the location might have already changed when createFlowPayload will be executed. + const eventWithCurrentPage = getConfigWithCurrentPage(flowConfig); + + return new Promise(function (resolve, reject) { + try { + delayUntilBoxeverIsReady(function () { + window._boxeverq.push(function () { + window.Boxever.callFlows( + // Set the browserId on the flow just before sending it to ensure it is up to date. + createFlowPayload(eventWithCurrentPage), + function (response) { + if (!response) { + reject('No response provided.'); + } + resolve(response); + }, + 'json' + ); + }); + }); + } catch (err) { + reject(err); + } + }); +} + +// Boxever view page tracking +export function logViewEvent(route: RouteData | undefined = required()): Promise { + const eventConfig = { + type: 'VIEW', + sitecoreTemplateName: route?.templateName, + }; + + return sendEventCreate(eventConfig); +} + +// Boxever identification +export function identifyVisitor( + firstname: string | undefined = required(), + lastname: string | undefined = required(), + email: string | undefined = required() +): Promise { + return sendEventCreate({ + type: 'IDENTITY', + firstname: firstname, + lastname: lastname, + email: email, + }); +} + +// Boxever identification from an email address +export function identifyByEmail(email: string | undefined = required()): Promise { + return sendEventCreate({ + type: 'IDENTITY', + email: email, + }); +} + +// **************************************************************************** +// Flush Boxever local storage for current guest and starts a new anonymous +// visitor session. +// Code from that function is coming mostly from the Boxever library and +// organized to return a promise to know when the asynchronous parts are done. +// **************************************************************************** +export function forgetCurrentGuest(): Promise { + if (typeof window === 'undefined' || !isBoxeverConfigured()) { + return new Promise(function (resolve) { + resolve(); + }); + } + + return new Promise(function (resolve, reject) { + try { + // Code copied from Boxever library + window._boxeverq = []; + if (window.Boxever.storage) { + window.Boxever.storage.removeItem(window.Boxever.cookie_name); + } + + window.Boxever.browserCreate( + {}, + function (data) { + try { + if (!data || !data.ref) { + reject('No response or ref provided.'); + } + + // Code copied from Boxever library to make it into a promise + window._boxever.browser_id = data.ref; + // If ITP Version of Safari set storage with storage_ttl + if (window._boxever.isITPBrowser) { + window._boxever.storage.setItem( + window._boxever.cookie_name, + window._boxever.browser_id, + { TTL: window._boxever.storage_ttl } + ); + } else { + // Set the cookie expiration time to be the current time + // plus cookie_expires_days + window._boxever.storage.setItem( + window._boxever.cookie_name, + window._boxever.browser_id, + window._boxever.cookie_expires_days + ); + } + // get the existing _boxeverq array + const _old_boxeverq = window._boxeverq; + // create a new _boxeverq object + window._boxeverq = new window.__boxeverQueue(); + // execute all of the queued up events - apply() + // turns the array entries into individual arguments + // eslint-disable-next-line prefer-spread + window._boxeverq.push.apply(window._boxeverq, _old_boxeverq); + window._boxever.initWebFlowSDK(); + resolve(); + } catch (e) { + window.BoxeverJERS.errors.push(e); + reject(e); + } + }, + 'json' + ); + } catch (err) { + reject(err); + } + }); +} + +// **************************************************************************** +// Gets the current guest ref by calling a CDP flow. +// This is a workaround needed when an anonymous guest identify itself as a +// previously known guest, CDP is merging the 2 guests. The browser keeps the +// anonymous guest ref. From that moment, calls to the authenticated CDP APIs +// like get/set for data extensions are failing when using this old anonymous +// guest ref. These calls require the previously known guest ref which is +// returned by our flow. +// **************************************************************************** +export function getGuestRef(): Promise { + return callFlows({ + friendlyId: 'getguestref', + }) as Promise; +} + +function boxeverPost( + action: string, + payload?: Record +): AxiosPromise { + const url = `${config.cdpProxyUrl}/Boxever${action}`; + + const options: AxiosRequestConfig = { + method: 'POST', + headers: { + 'content-type': 'application/json', + }, + data: payload, + withCredentials: false, + url, + }; + + return axios(options); +} + +function boxeverGet( + action: string, + payload?: Record +): AxiosPromise { + const url = `${config.cdpProxyUrl}/Boxever${action}`; + + const options: AxiosRequestConfig = { + method: 'GET', + params: payload, + withCredentials: false, + url, + }; + + return axios(options); +} + +function boxeverDelete( + action: string, + payload?: Record +): AxiosPromise { + const url = `${config.cdpProxyUrl}/Boxever${action}`; + + const options: AxiosRequestConfig = { + method: 'DELETE', + headers: { + 'content-type': 'application/json', + }, + data: payload, + withCredentials: false, + url, + }; + + return axios(options); +} + +// ******************************** +// Get non-expanded guest profile +// ******************************** +function getGuestProfilePromise(guestRef: GuestRef | undefined = required()): Promise { + return boxeverGet(`/getguestByRef?guestRef=${guestRef}`) as Promise; +} + +export function getGuestProfileResponse(guestRef: GuestRef): Promise { + if (!isBoxeverConfigured()) { + return new Promise(function (resolve) { + resolve(undefined); + }); + } + + if (!guestRef) { + return getGuestRef().then((response) => getGuestProfilePromise(response.guestRef)); + } else { + return getGuestProfilePromise(guestRef); + } +} + +// ******************************** +// isAnonymousGuest +// ******************************** +export function isAnonymousGuestInGuestResponse(guestResponse: GuestProfileResponse | undefined = required()): boolean { + return !guestResponse?.data?.email; +} + +export function isAnonymousGuest(guestRef: GuestRef): Promise { + const defaultValue = true; + + if (!isBoxeverConfigured()) { + return new Promise(function (resolve) { + resolve(defaultValue); + }); + } + + return getGuestProfileResponse(guestRef) + .then((guestResponse) => isAnonymousGuestInGuestResponse(guestResponse)) + .catch((e) => { + console.log(e); + return defaultValue; + }); +} + +// ******************************** +// getGuestFullName +// ******************************** +export function getGuestFullNameInGuestResponse( + guestResponse: GuestProfileResponse | undefined = required() +): string | undefined { + const data = guestResponse?.data; + + if (!data || !data.firstName || !data.lastName) { + return; + } + + return `${data.firstName} ${data.lastName}`; +} + +export function getGuestFullName(guestRef: GuestRef): Promise { + const defaultValue = ''; + + if (!isBoxeverConfigured()) { + return new Promise(function (resolve) { + resolve(defaultValue); + }); + } + + return getGuestProfileResponse(guestRef) + .then((guestResponse) => getGuestFullNameInGuestResponse(guestResponse)) + .catch((e) => { + console.log(e); + return defaultValue; + }); +} diff --git a/docker-compose.override.yml b/docker-compose.override.yml index bcf97fc97..2cddc855e 100644 --- a/docker-compose.override.yml +++ b/docker-compose.override.yml @@ -47,7 +47,7 @@ services: NEXTJS_DIST_DIR: ".next-container" PUBLIC_URL: "https://${RENDERING_HOST}" JSS_EDITING_SECRET: ${JSS_EDITING_SECRET} - CDP_PROXY_URL: https://${CDP_PROXY_HOST} + NEXT_PUBLIC_CDP_PROXY_URL: https://${CDP_PROXY_HOST} NEXT_PUBLIC_CDP_CLIENT_KEY: ${CDP_CLIENT_KEY} NEXT_PUBLIC_CDP_API_TARGET_ENDPOINT: ${CDP_API_TARGET_ENDPOINT} #DEBUG: ${DEBUG} # Uncomment this line and set the DEBUG environment variable value in the .env file to enable debug logging From d77acb41a5991d39a9e4e6859cd474c5aa7788a1 Mon Sep 17 00:00:00 2001 From: Jeff L'Heureux Date: Fri, 17 Sep 2021 10:34:12 -0400 Subject: [PATCH 5/7] Improve CDP environment variables access --- .../src/rendering/scripts/generate-config.ts | 15 ++--------- Website/src/rendering/src/pages/_app.tsx | 10 +++---- .../rendering/src/services/BoxeverService.ts | 26 +++++++++++-------- 3 files changed, 20 insertions(+), 31 deletions(-) diff --git a/Website/src/rendering/scripts/generate-config.ts b/Website/src/rendering/scripts/generate-config.ts index 755196c5c..fba55f829 100644 --- a/Website/src/rendering/scripts/generate-config.ts +++ b/Website/src/rendering/scripts/generate-config.ts @@ -18,11 +18,6 @@ export function generateConfig(configOverrides?: { [key: string]: string }): voi sitecoreApiKey: 'no-api-key-set', sitecoreApiHost: '', jssAppName: 'Unknown', - // DEMO CUSTOMIZATION - CDP integration - cdpProxyUrl: '', - cdpClientKey: '', - cdpApiTargetEndpoint: '', - // END CUSTOMIZATION }; // require + combine config sources @@ -46,17 +41,11 @@ const config = {};\n`; // Set base configuration values, allowing override with environment variables Object.keys(config).forEach((prop) => { - // DEMO CUSTOMIZATION - Support config from NEXT_PUBLIC_* environment variables - // eslint-disable-next-line prettier/prettier - configText += `config.${prop} = process.env.${constantCase(prop)} || process.env.NEXT_PUBLIC_${constantCase(prop)} || "${config[prop]}",\n`; - // END CUSTOMIZATION + configText += `config.${prop} = process.env.${constantCase(prop)} || "${config[prop]}",\n`; }); // Set computed values, allowing override with environment variables Object.keys(computedConfig).forEach((prop) => { - // DEMO CUSTOMIZATION - Support config from NEXT_PUBLIC_* environment variables - // eslint-disable-next-line prettier/prettier - configText += `config.${prop} = process.env.${constantCase(prop)} || process.env.NEXT_PUBLIC_${constantCase(prop)} || ${computedConfig[prop]};\n`; - // END CUSTOMIZATION + configText += `config.${prop} = process.env.${constantCase(prop)} || ${computedConfig[prop]};\n`; }); configText += `module.exports = config;`; diff --git a/Website/src/rendering/src/pages/_app.tsx b/Website/src/rendering/src/pages/_app.tsx index 2f13ee493..c3ff3d030 100644 --- a/Website/src/rendering/src/pages/_app.tsx +++ b/Website/src/rendering/src/pages/_app.tsx @@ -6,7 +6,7 @@ import { I18nProvider } from 'next-localization'; import Head from 'next/head'; import Script from 'next/script'; import NProgress from 'nprogress'; -import config from '../temp/config'; +import { isCdpConfigured, CDP_API_TARGET_ENDPOINT, CDP_CLIENT_KEY } from 'src/services/BoxeverService'; // Using nprogress are completely optional. // nprogress provides a loading indicator on page/route changes. @@ -43,10 +43,6 @@ function App({ Component, pageProps, router }: AppProps): JSX.Element { const { dictionary, ...rest } = pageProps; // DEMO TEAM CUSTOMIZATION - CDP integration - const cdpClientKey = config.cdpClientKey; - const cdpApiTargetEndpoint = config.cdpApiTargetEndpoint; - const isCdpConfigured = !!cdpClientKey && !!cdpApiTargetEndpoint; - const cdpScripts = isCdpConfigured ? ( <> diff --git a/Website/src/rendering/src/services/BoxeverService.ts b/Website/src/rendering/src/services/BoxeverService.ts index 98008fcea..103ab70f1 100644 --- a/Website/src/rendering/src/services/BoxeverService.ts +++ b/Website/src/rendering/src/services/BoxeverService.ts @@ -1,7 +1,6 @@ import axios, { AxiosPromise, AxiosRequestConfig } from 'axios'; import { RouteData } from '@sitecore-jss/sitecore-jss-nextjs'; import { required } from '../lib/util'; -import config from '../temp/config'; // ***** TYPES ***** @@ -115,7 +114,12 @@ type GuestProfileResponse = GuestProfile | undefined; // ***** API ***** -export function isBoxeverConfigured(): boolean { +const CDP_PROXY_URL = process.env.NEXT_PUBLIC_CDP_PROXY_URL || ''; +export const CDP_CLIENT_KEY = process.env.NEXT_PUBLIC_CDP_CLIENT_KEY || ''; +export const CDP_API_TARGET_ENDPOINT = process.env.NEXT_PUBLIC_CDP_API_TARGET_ENDPOINT || ''; +export const isCdpConfigured = !!CDP_CLIENT_KEY && !!CDP_API_TARGET_ENDPOINT; + +function isBoxeverConfiguredInBrowser(): boolean { return !!( typeof window !== 'undefined' && window._boxever_settings && @@ -168,7 +172,7 @@ function delayUntilBoxeverIsReady(functionToDelay: () => unknown) { } function sendEventCreate(eventConfig: Record) { - if (typeof window === 'undefined' || !isBoxeverConfigured()) { + if (typeof window === 'undefined' || !isBoxeverConfiguredInBrowser()) { return new Promise(function (resolve) { resolve(); }); @@ -204,7 +208,7 @@ function sendEventCreate(eventConfig: Record) { } function callFlows(flowConfig: Record) { - if (typeof window === 'undefined' || !isBoxeverConfigured()) { + if (typeof window === 'undefined' || !isBoxeverConfiguredInBrowser()) { return new Promise(function (resolve) { resolve(); }); @@ -275,7 +279,7 @@ export function identifyByEmail(email: string | undefined = required()): Promise // organized to return a promise to know when the asynchronous parts are done. // **************************************************************************** export function forgetCurrentGuest(): Promise { - if (typeof window === 'undefined' || !isBoxeverConfigured()) { + if (typeof window === 'undefined' || !isBoxeverConfiguredInBrowser()) { return new Promise(function (resolve) { resolve(); }); @@ -357,7 +361,7 @@ function boxeverPost( action: string, payload?: Record ): AxiosPromise { - const url = `${config.cdpProxyUrl}/Boxever${action}`; + const url = `${CDP_PROXY_URL}/Boxever${action}`; const options: AxiosRequestConfig = { method: 'POST', @@ -376,7 +380,7 @@ function boxeverGet( action: string, payload?: Record ): AxiosPromise { - const url = `${config.cdpProxyUrl}/Boxever${action}`; + const url = `${CDP_PROXY_URL}/Boxever${action}`; const options: AxiosRequestConfig = { method: 'GET', @@ -392,7 +396,7 @@ function boxeverDelete( action: string, payload?: Record ): AxiosPromise { - const url = `${config.cdpProxyUrl}/Boxever${action}`; + const url = `${CDP_PROXY_URL}/Boxever${action}`; const options: AxiosRequestConfig = { method: 'DELETE', @@ -415,7 +419,7 @@ function getGuestProfilePromise(guestRef: GuestRef | undefined = required()): Pr } export function getGuestProfileResponse(guestRef: GuestRef): Promise { - if (!isBoxeverConfigured()) { + if (!isBoxeverConfiguredInBrowser()) { return new Promise(function (resolve) { resolve(undefined); }); @@ -438,7 +442,7 @@ export function isAnonymousGuestInGuestResponse(guestResponse: GuestProfileRespo export function isAnonymousGuest(guestRef: GuestRef): Promise { const defaultValue = true; - if (!isBoxeverConfigured()) { + if (!isBoxeverConfiguredInBrowser()) { return new Promise(function (resolve) { resolve(defaultValue); }); @@ -470,7 +474,7 @@ export function getGuestFullNameInGuestResponse( export function getGuestFullName(guestRef: GuestRef): Promise { const defaultValue = ''; - if (!isBoxeverConfigured()) { + if (!isBoxeverConfiguredInBrowser()) { return new Promise(function (resolve) { resolve(defaultValue); }); From 921b21977066cfeb373f1f2a68b497172132e82d Mon Sep 17 00:00:00 2001 From: Jeff L'Heureux Date: Fri, 17 Sep 2021 10:36:09 -0400 Subject: [PATCH 6/7] Revert unneeded changes --- Website/src/rendering/scripts/generate-config.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Website/src/rendering/scripts/generate-config.ts b/Website/src/rendering/scripts/generate-config.ts index fba55f829..0ccf89dd5 100644 --- a/Website/src/rendering/scripts/generate-config.ts +++ b/Website/src/rendering/scripts/generate-config.ts @@ -45,7 +45,9 @@ const config = {};\n`; }); // Set computed values, allowing override with environment variables Object.keys(computedConfig).forEach((prop) => { - configText += `config.${prop} = process.env.${constantCase(prop)} || ${computedConfig[prop]};\n`; + configText += `config.${prop} = process.env.${constantCase(prop)} || ${ + computedConfig[prop] + };\n`; }); configText += `module.exports = config;`; From 5153d6a1aa9ea6062eb6bd8da2c483a72fb6b2f8 Mon Sep 17 00:00:00 2001 From: Jeff L'Heureux Date: Fri, 17 Sep 2021 11:28:18 -0400 Subject: [PATCH 7/7] Fix linting errors --- Website/src/rendering/src/pages/_app.tsx | 6 +- Website/src/rendering/src/pages/_document.tsx | 2 + .../rendering/src/services/BoxeverService.ts | 79 +++++++++---------- 3 files changed, 45 insertions(+), 42 deletions(-) diff --git a/Website/src/rendering/src/pages/_app.tsx b/Website/src/rendering/src/pages/_app.tsx index c3ff3d030..47269108f 100644 --- a/Website/src/rendering/src/pages/_app.tsx +++ b/Website/src/rendering/src/pages/_app.tsx @@ -6,7 +6,11 @@ import { I18nProvider } from 'next-localization'; import Head from 'next/head'; import Script from 'next/script'; import NProgress from 'nprogress'; -import { isCdpConfigured, CDP_API_TARGET_ENDPOINT, CDP_CLIENT_KEY } from 'src/services/BoxeverService'; +import { + isCdpConfigured, + CDP_API_TARGET_ENDPOINT, + CDP_CLIENT_KEY, +} from 'src/services/BoxeverService'; // Using nprogress are completely optional. // nprogress provides a loading indicator on page/route changes. diff --git a/Website/src/rendering/src/pages/_document.tsx b/Website/src/rendering/src/pages/_document.tsx index 4d8cc3844..e1bf7f8f0 100644 --- a/Website/src/rendering/src/pages/_document.tsx +++ b/Website/src/rendering/src/pages/_document.tsx @@ -1,3 +1,5 @@ +// TODO: Remove the below eslint disable when eslint-config-next is updated to support tsx files for that validation +// eslint-disable-next-line @next/next/no-document-import-in-page import Document, { Html, Head, Main, NextScript } from 'next/document'; class MyDocument extends Document { diff --git a/Website/src/rendering/src/services/BoxeverService.ts b/Website/src/rendering/src/services/BoxeverService.ts index 103ab70f1..0e8064194 100644 --- a/Website/src/rendering/src/services/BoxeverService.ts +++ b/Website/src/rendering/src/services/BoxeverService.ts @@ -357,29 +357,24 @@ export function getGuestRef(): Promise { }) as Promise; } -function boxeverPost( - action: string, - payload?: Record -): AxiosPromise { - const url = `${CDP_PROXY_URL}/Boxever${action}`; - - const options: AxiosRequestConfig = { - method: 'POST', - headers: { - 'content-type': 'application/json', - }, - data: payload, - withCredentials: false, - url, - }; - - return axios(options); -} - -function boxeverGet( - action: string, - payload?: Record -): AxiosPromise { +// TEMP: Keeping this commented method for near future use +// function boxeverPost(action: string, payload?: Record): AxiosPromise { +// const url = `${CDP_PROXY_URL}/Boxever${action}`; + +// const options: AxiosRequestConfig = { +// method: 'POST', +// headers: { +// 'content-type': 'application/json', +// }, +// data: payload, +// withCredentials: false, +// url, +// }; + +// return axios(options); +// } + +function boxeverGet(action: string, payload?: Record): AxiosPromise { const url = `${CDP_PROXY_URL}/Boxever${action}`; const options: AxiosRequestConfig = { @@ -392,29 +387,29 @@ function boxeverGet( return axios(options); } -function boxeverDelete( - action: string, - payload?: Record -): AxiosPromise { - const url = `${CDP_PROXY_URL}/Boxever${action}`; +// TEMP: Keeping this commented method for near future use +// function boxeverDelete(action: string, payload?: Record): AxiosPromise { +// const url = `${CDP_PROXY_URL}/Boxever${action}`; - const options: AxiosRequestConfig = { - method: 'DELETE', - headers: { - 'content-type': 'application/json', - }, - data: payload, - withCredentials: false, - url, - }; +// const options: AxiosRequestConfig = { +// method: 'DELETE', +// headers: { +// 'content-type': 'application/json', +// }, +// data: payload, +// withCredentials: false, +// url, +// }; - return axios(options); -} +// return axios(options); +// } // ******************************** // Get non-expanded guest profile // ******************************** -function getGuestProfilePromise(guestRef: GuestRef | undefined = required()): Promise { +function getGuestProfilePromise( + guestRef: GuestRef | undefined = required() +): Promise { return boxeverGet(`/getguestByRef?guestRef=${guestRef}`) as Promise; } @@ -435,7 +430,9 @@ export function getGuestProfileResponse(guestRef: GuestRef): Promise