From 5ff0215a4fe3bc1f964891c0c7c48dc22d6acfab Mon Sep 17 00:00:00 2001 From: Jeffrey Dowdle Date: Thu, 28 Nov 2024 10:01:23 +1100 Subject: [PATCH 1/2] build(@dpc-sdp/nuxt-ripple-analytics): added new relic browser agent script configurable via env vars and feature flags, can be completely disabled, disabled by default --- packages/nuxt-ripple-analytics/app.config.ts | 7 ++ .../composables/useNewRelicBrowserAgent.ts | 63 +++++++++++++++ packages/nuxt-ripple-analytics/nuxt.config.ts | 10 +++ packages/nuxt-ripple-analytics/package.json | 3 +- .../plugins/newRelicBrowser.ts | 29 +++++++ .../nuxt-ripple-analytics/utils/trackError.ts | 15 ++++ pnpm-lock.yaml | 76 ++++++++++++++++++- 7 files changed, 200 insertions(+), 3 deletions(-) create mode 100644 packages/nuxt-ripple-analytics/composables/useNewRelicBrowserAgent.ts create mode 100644 packages/nuxt-ripple-analytics/plugins/newRelicBrowser.ts create mode 100644 packages/nuxt-ripple-analytics/utils/trackError.ts diff --git a/packages/nuxt-ripple-analytics/app.config.ts b/packages/nuxt-ripple-analytics/app.config.ts index bbed285cd5..bddca4729c 100644 --- a/packages/nuxt-ripple-analytics/app.config.ts +++ b/packages/nuxt-ripple-analytics/app.config.ts @@ -15,6 +15,13 @@ export default defineAppConfig({ ripple: { analytics: { eventListeners + }, + featureFlags: { + newRelicBrowserBeacon: 'bam.nr-data.net', + newRelicBrowserJSErrorsEnabled: true, + newRelicBrowserCookiesEnabled: false, + newRelicBrowserDistributedTracingEnabled: false, + newRelicBrowserAjaxDenyList: ['bam.nr-data.net'] } } }) diff --git a/packages/nuxt-ripple-analytics/composables/useNewRelicBrowserAgent.ts b/packages/nuxt-ripple-analytics/composables/useNewRelicBrowserAgent.ts new file mode 100644 index 0000000000..6ab491c29a --- /dev/null +++ b/packages/nuxt-ripple-analytics/composables/useNewRelicBrowserAgent.ts @@ -0,0 +1,63 @@ +import type { IRplFeatureFlags } from '@dpc-sdp/ripple-tide-api/types' + +export default () => { + const config = useRuntimeConfig() + const browserAgentConfig = config?.public?.newRelic?.browser + + if (!browserAgentConfig.enabled) { + return + } + + const featureFlags: IRplFeatureFlags = inject('featureFlags', { + newRelicBrowserBeacon: 'bam.nr-data.net', + newRelicBrowserJSErrorsEnabled: true, + newRelicBrowserCookiesEnabled: false, + newRelicBrowserDistributedTracingEnabled: false, + newRelicBrowserAjaxDenyList: ['bam.nr-data.net'] + }) + + // Scripts from new relic need to be dynamically imported, otherwise they will throw an error + const initBrowserAgent = async () => { + // Error module has to be manually added when using new relic via npm + const { JSErrors } = await import( + '@newrelic/browser-agent/features/jserrors' + ) + + const options = { + info: { + beacon: featureFlags.newRelicBrowserBeacon, + licenseKey: browserAgentConfig.licenseKey, + applicationID: browserAgentConfig.applicationID, + sa: 1 // 'sa' stands for 'standalone' https://github.com/newrelic/newrelic-browser-agent/blob/bbf414c0d6a483141f32e2dd31b1f8a23ad1dda5/src/features/logging/aggregate/index.js#L100 + }, + init: { + distributed_tracing: { + enabled: featureFlags.newRelicBrowserDistributedTracingEnabled + }, + privacy: { + cookies_enabled: featureFlags.newRelicBrowserCookiesEnabled + }, + jserrors: { enabled: featureFlags.newRelicBrowserJSErrorsEnabled }, + ajax: { deny_list: featureFlags.newRelicBrowserAjaxDenyList } + }, + loader_config: { + accountID: browserAgentConfig.accountID, + trustKey: browserAgentConfig.trustKey, + agentID: browserAgentConfig.agentID, + licenseKey: browserAgentConfig.licenseKey, + applicationID: browserAgentConfig.applicationID + }, + features: [JSErrors] + } + + const { BrowserAgent } = await import( + '@newrelic/browser-agent/loaders/browser-agent' + ) + + new BrowserAgent(options) + } + + if (process.client) { + initBrowserAgent() + } +} diff --git a/packages/nuxt-ripple-analytics/nuxt.config.ts b/packages/nuxt-ripple-analytics/nuxt.config.ts index 57d7d8c74f..aa47ccbde5 100644 --- a/packages/nuxt-ripple-analytics/nuxt.config.ts +++ b/packages/nuxt-ripple-analytics/nuxt.config.ts @@ -7,6 +7,16 @@ export default defineNuxtConfig({ analytics: { GTM: 'GTM-KF8NCW2' } + }, + newRelic: { + browser: { + enabled: false, + accountID: '', + trustKey: '', + agentID: '', + licenseKey: '', + applicationID: '' + } } } } diff --git a/packages/nuxt-ripple-analytics/package.json b/packages/nuxt-ripple-analytics/package.json index 441289a6cf..4621d55af6 100644 --- a/packages/nuxt-ripple-analytics/package.json +++ b/packages/nuxt-ripple-analytics/package.json @@ -11,7 +11,8 @@ }, "dependencies": { "@dpc-sdp/ripple-ui-core": "workspace:*", - "@gtm-support/core": "^2.0.0" + "@gtm-support/core": "^2.0.0", + "@newrelic/browser-agent": "^1.273.0" }, "devDependencies": { "@dpc-sdp/nuxt-ripple": "workspace:*", diff --git a/packages/nuxt-ripple-analytics/plugins/newRelicBrowser.ts b/packages/nuxt-ripple-analytics/plugins/newRelicBrowser.ts new file mode 100644 index 0000000000..aa03fe6d4d --- /dev/null +++ b/packages/nuxt-ripple-analytics/plugins/newRelicBrowser.ts @@ -0,0 +1,29 @@ +import { defineNuxtPlugin } from '#app' +import trackError from '../utils/trackError' + +export default defineNuxtPlugin((nuxtApp) => { + /* @ts-ignore process is extended by webpack */ + if (process.client) { + nuxtApp.vueApp.use({ + install(app: any) { + useNewRelicBrowserAgent() + + const existingErrorHandler = app.config.errorHandler + + // Catch all vue errors + app.config.errorHandler = (error: Error) => { + console.error(error) + trackError(error) + + // Allow multiple error handlers to run + if ( + existingErrorHandler && + typeof existingErrorHandler === 'function' + ) { + existingErrorHandler(error) + } + } + } + }) + } +}) diff --git a/packages/nuxt-ripple-analytics/utils/trackError.ts b/packages/nuxt-ripple-analytics/utils/trackError.ts new file mode 100644 index 0000000000..25f5d56a1e --- /dev/null +++ b/packages/nuxt-ripple-analytics/utils/trackError.ts @@ -0,0 +1,15 @@ +declare global { + interface Window { + newrelic?: { + noticeError: (error: Error) => void + } + } +} + +const trackError = (error: Error) => { + if (window?.newrelic?.noticeError) { + window.newrelic.noticeError(error) + } +} + +export default trackError diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b6c33e13b1..cc0354352b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -368,6 +368,9 @@ importers: '@gtm-support/core': specifier: ^2.0.0 version: 2.0.0 + '@newrelic/browser-agent': + specifier: ^1.273.0 + version: 1.274.0 devDependencies: '@dpc-sdp/nuxt-ripple': specifier: workspace:* @@ -3997,6 +4000,10 @@ packages: resolution: {integrity: sha512-2KYkyluThg1AKfd0JWI7FzpS4A/fzVVGYIf6AM4ydWyNj8eI/86GQVLeRgDoH7CNOxt243R5tutWlmHpVq0/Ew==} engines: {node: '>=18.0.0'} + '@newrelic/browser-agent@1.274.0': + resolution: {integrity: sha512-j5emhb/ST4zv4GZ32W4khvUultYhbEspWCS9URDTyqRPqYZqcxbs8Z1cQcqpEDzm1lBwFlzMMYs8wgqO0diLoQ==} + engines: {node: '>=12.17.0 < 13.0.0 || >=13.7.0'} + '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -5117,6 +5124,12 @@ packages: cpu: [x64] os: [win32] + '@rrweb/types@2.0.0-alpha.17': + resolution: {integrity: sha512-AfDTVUuCyCaIG0lTSqYtrZqJX39ZEYzs4fYKnexhQ+id+kbZIpIJtaut5cto6dWZbB3SEe4fW0o90Po3LvTmfg==} + + '@rrweb/utils@2.0.0-alpha.17': + resolution: {integrity: sha512-HCsasPERBwOS9/LQeOytO2ETKTCqRj1wORBuxiy3t41hKhmi225DdrUPiWnyDdTQm1GdVbOymMRknJVPnZaSXw==} + '@rushstack/eslint-patch@1.2.0': resolution: {integrity: sha512-sXo/qW2/pAcmT43VoRKOJbDOfV3cYpq3szSVfIThQXNt+E4DfKj361vaAt3c88U5tPUxzEswam7GW48PJqtKAg==} @@ -5661,6 +5674,9 @@ packages: '@types/cross-spawn@6.0.2': resolution: {integrity: sha512-KuwNhp3eza+Rhu8IFI5HUXRP0LIhqH5cAjubUvGXXthh4YYBuP2ntwEX+Cz8GJoZUHlKo247wPWOfA9LYEq4cw==} + '@types/css-font-loading-module@0.0.7': + resolution: {integrity: sha512-nl09VhutdjINdWyXxHWN/w9zlNCfr60JUqJbd24YXUuCwgeL0TpFSdElCwb6cxfB6ybE19Gjj4g0jsgkXxKv1Q==} + '@types/debug@4.1.7': resolution: {integrity: sha512-9AonUzyTjXXhEOa0DnqpzZi6VHlqKMswga9EXjpXnnqxwLtdvPPtlO8evrI5D9S6asFRCQ6v+wpiUKbw+vKqyg==} @@ -6500,6 +6516,9 @@ packages: '@webassemblyjs/wast-printer@1.12.1': resolution: {integrity: sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA==} + '@xstate/fsm@1.6.5': + resolution: {integrity: sha512-b5o1I6aLNeYlU/3CPlj/Z91ybk1gUsKT+5NAJI+2W4UjvS5KLG28K9v5UvNoFVjHV8PajVZ00RH3vnjyQO7ZAw==} + '@xtuc/ieee754@1.2.0': resolution: {integrity: sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==} @@ -9276,6 +9295,9 @@ packages: fflate@0.4.8: resolution: {integrity: sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA==} + fflate@0.7.4: + resolution: {integrity: sha512-5u2V/CDW15QM1XbbgS+0DfPxVB+jUKhWEKuuFuHncbk3tEEqzmoXL+2KyOFuKGqOnmdIy0/davWF1CkuwtibCw==} + figures@3.2.0: resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==} engines: {node: '>=8'} @@ -14249,6 +14271,15 @@ packages: engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true + rrdom@2.0.0-alpha.17: + resolution: {integrity: sha512-b6caDiNcFO96Opp7TGdcVd4OLGSXu5dJe+A0IDiAu8mk7OmhqZCSDlgQdTKmdO5wMf4zPsUTgb8H/aNvR3kDHA==} + + rrweb-snapshot@2.0.0-alpha.17: + resolution: {integrity: sha512-GBg5pV8LHOTbeVmH2VHLEFR0mc2QpQMzAvcoxEGfPNWgWHc8UvKCyq7pqN1vA+fDZ+yXXbixeO0kB2pzVvFCBw==} + + rrweb@2.0.0-alpha.17: + resolution: {integrity: sha512-GQxBkCC4r9XL2bwSdv7iIS49M3cEA8OtObVq0rrQ4GUT4+h7omucGQ4x7m5YN5Vq1oalStBaBlYqF7yRnfG3JA==} + run-applescript@5.0.0: resolution: {integrity: sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg==} engines: {node: '>=12'} @@ -16343,6 +16374,9 @@ packages: web-namespaces@2.0.1: resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==} + web-vitals@4.2.3: + resolution: {integrity: sha512-/CFAm1mNxSmOj6i0Co+iGFJ58OS4NRGVP+AWS/l509uIK5a1bSoIVaHz/ZumpHTfHSZBpgrJ+wjfpAOrTHok5Q==} + web-worker@1.3.0: resolution: {integrity: sha512-BSR9wyRsy/KOValMgd5kMyr3JzpdeoR9KVId8u5GVlTTAtNChlsE4yTxeY7zMdNSyOmoKBv8NH2qeRY9Tg+IaA==} @@ -20812,6 +20846,12 @@ snapshots: '@netlify/node-cookies': 0.1.0 urlpattern-polyfill: 8.0.2 + '@newrelic/browser-agent@1.274.0': + dependencies: + fflate: 0.7.4 + rrweb: 2.0.0-alpha.17 + web-vitals: 4.2.3 + '@nodelib/fs.scandir@2.1.5': dependencies: '@nodelib/fs.stat': 2.0.5 @@ -22615,6 +22655,12 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.9.1': optional: true + '@rrweb/types@2.0.0-alpha.17': + dependencies: + rrweb-snapshot: 2.0.0-alpha.17 + + '@rrweb/utils@2.0.0-alpha.17': {} + '@rushstack/eslint-patch@1.2.0': {} '@rushstack/node-core-library@3.55.1(@types/node@18.15.10)': @@ -23685,6 +23731,8 @@ snapshots: dependencies: '@types/node': 18.15.10 + '@types/css-font-loading-module@0.0.7': {} + '@types/debug@4.1.7': dependencies: '@types/ms': 0.7.31 @@ -25083,6 +25131,8 @@ snapshots: '@webassemblyjs/ast': 1.12.1 '@xtuc/long': 4.2.2 + '@xstate/fsm@1.6.5': {} + '@xtuc/ieee754@1.2.0': {} '@xtuc/long@4.2.2': {} @@ -25795,8 +25845,7 @@ snapshots: base64-arraybuffer@0.1.5: {} - base64-arraybuffer@1.0.2: - optional: true + base64-arraybuffer@1.0.2: {} base64-js@1.5.1: {} @@ -28695,6 +28744,8 @@ snapshots: fflate@0.4.8: {} + fflate@0.7.4: {} + figures@3.2.0: dependencies: escape-string-regexp: 1.0.5 @@ -35631,6 +35682,25 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.9.1 fsevents: 2.3.3 + rrdom@2.0.0-alpha.17: + dependencies: + rrweb-snapshot: 2.0.0-alpha.17 + + rrweb-snapshot@2.0.0-alpha.17: + dependencies: + postcss: 8.4.47 + + rrweb@2.0.0-alpha.17: + dependencies: + '@rrweb/types': 2.0.0-alpha.17 + '@rrweb/utils': 2.0.0-alpha.17 + '@types/css-font-loading-module': 0.0.7 + '@xstate/fsm': 1.6.5 + base64-arraybuffer: 1.0.2 + mitt: 3.0.1 + rrdom: 2.0.0-alpha.17 + rrweb-snapshot: 2.0.0-alpha.17 + run-applescript@5.0.0: dependencies: execa: 5.1.1 @@ -38268,6 +38338,8 @@ snapshots: web-namespaces@2.0.1: {} + web-vitals@4.2.3: {} + web-worker@1.3.0: {} webidl-conversions@3.0.1: {} From 390b28baf49cf6b9ad0e7c1234abefd369f0b1dd Mon Sep 17 00:00:00 2001 From: Jeffrey Dowdle Date: Thu, 28 Nov 2024 12:12:11 +1100 Subject: [PATCH 2/2] feat(@dpc-sdp/nuxt-ripple-analytics): added tracking for various caught errors --- packages/nuxt-ripple-analytics/utils/trackError.ts | 4 ++-- packages/nuxt-ripple/components/TideAlerts.vue | 1 + packages/nuxt-ripple/composables/use-tide-error.ts | 9 ++++++++- packages/nuxt-ripple/composables/use-tide-page.ts | 2 +- packages/nuxt-ripple/composables/use-tide-site.ts | 2 +- .../composables/use-tide-publication-children.ts | 2 +- .../composables/use-tide-publication-menu.ts | 2 +- .../components/global/TideSearchAddressLookup.vue | 1 + packages/ripple-tide-search/composables/useTideSearch.ts | 1 + .../composables/use-webform-schema.ts | 2 +- .../composables/use-webform-submit.ts | 2 ++ 11 files changed, 20 insertions(+), 8 deletions(-) diff --git a/packages/nuxt-ripple-analytics/utils/trackError.ts b/packages/nuxt-ripple-analytics/utils/trackError.ts index 25f5d56a1e..594112c50b 100644 --- a/packages/nuxt-ripple-analytics/utils/trackError.ts +++ b/packages/nuxt-ripple-analytics/utils/trackError.ts @@ -1,12 +1,12 @@ declare global { interface Window { newrelic?: { - noticeError: (error: Error) => void + noticeError: (error: any) => void } } } -const trackError = (error: Error) => { +const trackError = (error: any) => { if (window?.newrelic?.noticeError) { window.newrelic.noticeError(error) } diff --git a/packages/nuxt-ripple/components/TideAlerts.vue b/packages/nuxt-ripple/components/TideAlerts.vue index 23c17450ee..7ef2905292 100644 --- a/packages/nuxt-ripple/components/TideAlerts.vue +++ b/packages/nuxt-ripple/components/TideAlerts.vue @@ -71,6 +71,7 @@ const filteredAlerts = computed(() => { return !dismissedIds.includes(alert.alertId) }) } catch (e) { + trackError(e) console.error( 'Something went wrong when trying to get dismissed alerts cookie' ) diff --git a/packages/nuxt-ripple/composables/use-tide-error.ts b/packages/nuxt-ripple/composables/use-tide-error.ts index 0e344fff00..a3849941b1 100644 --- a/packages/nuxt-ripple/composables/use-tide-error.ts +++ b/packages/nuxt-ripple/composables/use-tide-error.ts @@ -1,4 +1,7 @@ -export const useTideError = (statusCode: number): void => { +export const useTideError = ( + statusCode: number, + originalError?: Error +): void => { if (statusCode) { switch (statusCode) { case 404: @@ -28,6 +31,10 @@ export const useTideError = (statusCode: number): void => { break default: + if (originalError) { + trackError(originalError) + } + throw createError({ statusCode: 500, statusMessage: 'We have a glitch in our system.', diff --git a/packages/nuxt-ripple/composables/use-tide-page.ts b/packages/nuxt-ripple/composables/use-tide-page.ts index 67e7be6899..36828ce756 100644 --- a/packages/nuxt-ripple/composables/use-tide-page.ts +++ b/packages/nuxt-ripple/composables/use-tide-page.ts @@ -151,7 +151,7 @@ export const useTidePage = async ( } if (error && error.value?.statusCode) { - useTideError(error.value?.statusCode) + useTideError(error.value?.statusCode, error.value) } debugLogger('Page data fetched', { diff --git a/packages/nuxt-ripple/composables/use-tide-site.ts b/packages/nuxt-ripple/composables/use-tide-site.ts index a36a58ffb3..bc581a9e6c 100644 --- a/packages/nuxt-ripple/composables/use-tide-site.ts +++ b/packages/nuxt-ripple/composables/use-tide-site.ts @@ -34,7 +34,7 @@ export const useTideSite = async (id?: number): Promise => { if (error && error.value?.statusCode) { console.log(error) console.log('API error fetching site data') - useTideError(500) + useTideError(500, error.value) } // Section.io cache tags must be set on the response header to invalidate the cache after a change in drupal diff --git a/packages/ripple-tide-publication/composables/use-tide-publication-children.ts b/packages/ripple-tide-publication/composables/use-tide-publication-children.ts index 3af9f2d8bc..a51bde977b 100644 --- a/packages/ripple-tide-publication/composables/use-tide-publication-children.ts +++ b/packages/ripple-tide-publication/composables/use-tide-publication-children.ts @@ -23,7 +23,7 @@ export const useTidePublicationChildren = async ( } }) if (error && error.value?.statusCode) { - useTideError(error.value?.statusCode) + useTideError(error.value?.statusCode, error.value) } return data.value } diff --git a/packages/ripple-tide-publication/composables/use-tide-publication-menu.ts b/packages/ripple-tide-publication/composables/use-tide-publication-menu.ts index efb4af5758..c1ee50964e 100644 --- a/packages/ripple-tide-publication/composables/use-tide-publication-menu.ts +++ b/packages/ripple-tide-publication/composables/use-tide-publication-menu.ts @@ -15,7 +15,7 @@ export const useTidePublicationMenu = async ( } }) if (error && error.value?.statusCode) { - useTideError(error.value?.statusCode) + useTideError(error.value?.statusCode, error.value) } return data.value } diff --git a/packages/ripple-tide-search/components/global/TideSearchAddressLookup.vue b/packages/ripple-tide-search/components/global/TideSearchAddressLookup.vue index 897b2b65c4..b912331925 100644 --- a/packages/ripple-tide-search/components/global/TideSearchAddressLookup.vue +++ b/packages/ripple-tide-search/components/global/TideSearchAddressLookup.vue @@ -155,6 +155,7 @@ const fetchSuggestions = async (query: string) => { props.mapResultsFnName ) } catch (e) { + trackError(e) console.error(e) } } diff --git a/packages/ripple-tide-search/composables/useTideSearch.ts b/packages/ripple-tide-search/composables/useTideSearch.ts index 6f2bac35d8..f19383fd78 100644 --- a/packages/ripple-tide-search/composables/useTideSearch.ts +++ b/packages/ripple-tide-search/composables/useTideSearch.ts @@ -633,6 +633,7 @@ export default ({ nextTick(onMapResultsHook.value) } } catch (error) { + trackError(error) console.error(error) searchError.value = error } finally { diff --git a/packages/ripple-tide-webform/composables/use-webform-schema.ts b/packages/ripple-tide-webform/composables/use-webform-schema.ts index 5422d08dbe..2713ae25ce 100644 --- a/packages/ripple-tide-webform/composables/use-webform-schema.ts +++ b/packages/ripple-tide-webform/composables/use-webform-schema.ts @@ -20,7 +20,7 @@ export const useWebformSchema = async ( } }) if (error && error.value?.statusCode) { - useTideError(error.value?.statusCode) + useTideError(error.value?.statusCode, error.value) } return data.value } diff --git a/packages/ripple-tide-webform/composables/use-webform-submit.ts b/packages/ripple-tide-webform/composables/use-webform-submit.ts index 2c86c62475..f46205543f 100644 --- a/packages/ripple-tide-webform/composables/use-webform-submit.ts +++ b/packages/ripple-tide-webform/composables/use-webform-submit.ts @@ -110,6 +110,7 @@ export function useWebformSubmit( window ) } catch (e) { + trackError(e) console.error(e) submissionState.value = { @@ -144,6 +145,7 @@ export function useWebformSubmit( } } } catch (error) { + trackError(error) console.error(error) submissionState.value = {