From 18352e6ac4c22f311204e31ee39b9f22813d4dfd Mon Sep 17 00:00:00 2001 From: "ktiller@flip.to" Date: Mon, 13 Nov 2023 13:59:40 -0500 Subject: [PATCH 01/19] Allow in-memory Domain User Id --- libraries/browser-tracker-core/src/tracker/index.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/libraries/browser-tracker-core/src/tracker/index.ts b/libraries/browser-tracker-core/src/tracker/index.ts index a6443a932..5a180ce32 100755 --- a/libraries/browser-tracker-core/src/tracker/index.ts +++ b/libraries/browser-tracker-core/src/tracker/index.ts @@ -689,9 +689,10 @@ export function Tracker( * Load visitor ID cookie */ function loadDomainUserIdCookie() { - if (configStateStorageStrategy == 'none') { - return emptyIdCookie(); - } + // KEVIN TILLER - Removing this so we can have in-memory domainuserid when cookies are disallowed. + // if (configStateStorageStrategy == 'none') { + // return emptyIdCookie(); + // } const id = getSnowplowCookieValue('id') || undefined; return parseIdCookie(id, domainUserId, memorizedSessionId, memorizedVisitCount); } From 85e0814e213a42edff23bca6467d9c9ee7003978 Mon Sep 17 00:00:00 2001 From: "ktiller@flip.to" Date: Mon, 13 Nov 2023 17:03:50 -0500 Subject: [PATCH 02/19] Remove emptyIdCookie --- _FLIPTO-README.md | 20 +++++++++++++++++++ .../src/tracker/id_cookie.ts | 5 ----- .../browser-tracker-core/src/tracker/index.ts | 1 - rush.json | 2 +- .../javascript-tracker/tracker.lite.config.ts | 8 ++++---- 5 files changed, 25 insertions(+), 11 deletions(-) create mode 100644 _FLIPTO-README.md diff --git a/_FLIPTO-README.md b/_FLIPTO-README.md new file mode 100644 index 000000000..08a24784c --- /dev/null +++ b/_FLIPTO-README.md @@ -0,0 +1,20 @@ +This is a fork of the main repo to add support for in-memory domain user ids. +The main change is removing emptyIdCookie from id_cookie and index +to build a whitelabel build +https://docs.snowplow.io/docs/collecting-data/collecting-from-own-applications/javascript-trackers/web-tracker/hosting-the-javascript-tracker/creating-a-whitelabel-build/ + +updated tracker.lite.config.ts to contain plugins necessary + +HOW TO BUILD + +cd C:\Git\snowplow-javascript-tracker\ +rush update +rush build + +CURRENTLY BROKEN! +--cd .\trackers\javascript-tracker\ +--rushx build --whitelabel=FliptoGlobalSnowplowNamespace + +Take the code built for snowplow.lite and rename to ftsa.js and ftsa.js.map +Ensure you rename the mapping url and remove any header text from ftsa as well! +Publish to Azure Storage and clear CDN \ No newline at end of file diff --git a/libraries/browser-tracker-core/src/tracker/id_cookie.ts b/libraries/browser-tracker-core/src/tracker/id_cookie.ts index 831d672c1..c183e2ef1 100644 --- a/libraries/browser-tracker-core/src/tracker/id_cookie.ts +++ b/libraries/browser-tracker-core/src/tracker/id_cookie.ts @@ -61,11 +61,6 @@ export type ParsedIdCookie = [ number // eventIndex ]; -export function emptyIdCookie() { - const idCookie: ParsedIdCookie = ['1', '', 0, 0, 0, undefined, '', '', '', undefined, 0]; - return idCookie; -} - /** * Parses the cookie values from its string representation. * diff --git a/libraries/browser-tracker-core/src/tracker/index.ts b/libraries/browser-tracker-core/src/tracker/index.ts index 5a180ce32..16e027030 100755 --- a/libraries/browser-tracker-core/src/tracker/index.ts +++ b/libraries/browser-tracker-core/src/tracker/index.ts @@ -90,7 +90,6 @@ import { ParsedIdCookie, clientSessionFromIdCookie, incrementEventIndexInIdCookie, - emptyIdCookie, eventIndexFromIdCookie, } from './id_cookie'; import { CLIENT_SESSION_SCHEMA, WEB_PAGE_SCHEMA, BROWSER_CONTEXT_SCHEMA } from './schemata'; diff --git a/rush.json b/rush.json index 0ff69c59a..dabbade6a 100644 --- a/rush.json +++ b/rush.json @@ -118,7 +118,7 @@ * Specify a SemVer range to ensure developers use a Node.js version that is appropriate * for your repo. */ - "nodeSupportedVersionRange": ">=14.15.0 <15.0.0 || >=16.16.0 <17.0.0", + "nodeSupportedVersionRange": ">=14.15.0 <15.0.0 || >=16.16.0 <21.0.0", /** * Odd-numbered major versions of Node.js are experimental. Even-numbered releases diff --git a/trackers/javascript-tracker/tracker.lite.config.ts b/trackers/javascript-tracker/tracker.lite.config.ts index 4ad8c4046..f51e3b592 100644 --- a/trackers/javascript-tracker/tracker.lite.config.ts +++ b/trackers/javascript-tracker/tracker.lite.config.ts @@ -28,18 +28,18 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -export const performanceTiming = false; -export const gaCookies = false; +export const performanceTiming = true; +export const gaCookies = true; export const geolocation = false; export const optimizelyX = false; -export const clientHints = false; +export const clientHints = true; export const consent = false; export const linkClickTracking = false; export const formTracking = false; export const errorTracking = false; export const timezone = false; export const ecommerce = false; -export const enhancedEcommerce = false; +export const enhancedEcommerce = true; export const adTracking = false; export const siteTracking = false; export const snowplowEcommerceTracking = false; From 9a916d4f6da459e207f5fe6d1b408415f3a61f11 Mon Sep 17 00:00:00 2001 From: "ktiller@flip.to" Date: Tue, 14 Nov 2023 17:15:08 -0500 Subject: [PATCH 03/19] Add hacky workaround for GTM preventing .apply so we can access domainUserId from fliptoSa tracker --- libraries/browser-tracker-core/src/tracker/index.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libraries/browser-tracker-core/src/tracker/index.ts b/libraries/browser-tracker-core/src/tracker/index.ts index 16e027030..96da60f48 100755 --- a/libraries/browser-tracker-core/src/tracker/index.ts +++ b/libraries/browser-tracker-core/src/tracker/index.ts @@ -1332,6 +1332,11 @@ export function Tracker( }, }; + // KEVIN TILLER - Workaround until getDomainUserId works from GTM + if (namespace === 'fliptoSa') { + ((window as any).fliptoDataLayer = (window as any).fliptoDataLayer || []).snowplow = tracker; + } + // Initialise each plugin with the tracker browserPlugins.forEach((p) => { p.activateBrowserPlugin?.(tracker); From 05ea8f028e837c3d7de66d34a436aedd5c6a00eb Mon Sep 17 00:00:00 2001 From: "ktiller@flip.to" Date: Tue, 19 Dec 2023 15:04:02 -0500 Subject: [PATCH 04/19] always attempt to read a cookie, even if storage is set to 'none' --- libraries/browser-tracker-core/src/tracker/index.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/libraries/browser-tracker-core/src/tracker/index.ts b/libraries/browser-tracker-core/src/tracker/index.ts index 96da60f48..45262c87b 100755 --- a/libraries/browser-tracker-core/src/tracker/index.ts +++ b/libraries/browser-tracker-core/src/tracker/index.ts @@ -479,10 +479,11 @@ export function Tracker( const fullName = getSnowplowCookieName(cookieName); if (configStateStorageStrategy == 'localStorage') { return attemptGetLocalStorage(fullName); - } else if (configStateStorageStrategy == 'cookie' || configStateStorageStrategy == 'cookieAndLocalStorage') { - return cookie(fullName); - } - return undefined; + } + // KEVIN TILLER - It always makes sense to READ a cookie that pre-exists in case of + // configurations where each page starts with no consent, then "updates" availability + // as third-party consent management widgets load + return cookie(fullName); } /* @@ -1336,7 +1337,7 @@ export function Tracker( if (namespace === 'fliptoSa') { ((window as any).fliptoDataLayer = (window as any).fliptoDataLayer || []).snowplow = tracker; } - + // Initialise each plugin with the tracker browserPlugins.forEach((p) => { p.activateBrowserPlugin?.(tracker); From eec21b73ff3a8160e57edc2e2eb71f340c466dc6 Mon Sep 17 00:00:00 2001 From: "ktiller@flip.to" Date: Tue, 19 Dec 2023 15:04:32 -0500 Subject: [PATCH 05/19] Add snowplow tracker to fliptoDataLayer --- libraries/browser-tracker-core/src/tracker/index.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/libraries/browser-tracker-core/src/tracker/index.ts b/libraries/browser-tracker-core/src/tracker/index.ts index 45262c87b..87a23b73b 100755 --- a/libraries/browser-tracker-core/src/tracker/index.ts +++ b/libraries/browser-tracker-core/src/tracker/index.ts @@ -1334,9 +1334,7 @@ export function Tracker( }; // KEVIN TILLER - Workaround until getDomainUserId works from GTM - if (namespace === 'fliptoSa') { - ((window as any).fliptoDataLayer = (window as any).fliptoDataLayer || []).snowplow = tracker; - } + ((window as any).fliptoDataLayer = (window as any).fliptoDataLayer || []).snowplow = tracker; // Initialise each plugin with the tracker browserPlugins.forEach((p) => { From 96314023f68c6d0a59e10c963055f39db3bbd88f Mon Sep 17 00:00:00 2001 From: Kevin Date: Tue, 20 Feb 2024 09:21:30 -0500 Subject: [PATCH 06/19] Delete common/git-hooks directory --- common/git-hooks/pre-commit | 24 ------------------------ 1 file changed, 24 deletions(-) delete mode 100644 common/git-hooks/pre-commit diff --git a/common/git-hooks/pre-commit b/common/git-hooks/pre-commit deleted file mode 100644 index 07b96ad6c..000000000 --- a/common/git-hooks/pre-commit +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/sh -# Called by "git commit" with no arguments. The hook should -# exit with non-zero status after issuing an appropriate message if -# it wants to stop the commit. - -# Invoke the "rush prettier" custom command to reformat files whenever they -# are committed. The command is defined in common/config/rush/command-line.json -# and uses the "rush-prettier" autoinstaller. -node common/scripts/install-run-rush.js prettier || exit $? - -ERROR_COLOR='\033[0;31m' -WARNING_COLOR='\033[0;33m' -STOP_COLOR='\033[0m' - -if ! command -v gitleaks > /dev/null 2>&1 -then - echo "${ERROR_COLOR}\nGitleaks not found. To commit you safely, you need to install it per the instructions at https://github.com/gitleaks/gitleaks.${STOP_COLOR}" - exit 1 -elif [ "$SKIP" == "gitleaks" ] -then - echo "${WARNING_COLOR}\nGitleaks skipped for this commit.${STOP_COLOR}" -else - gitleaks protect -v --staged --report-path ./findings.json -fi From b2d3a848858fc13414d26c79eb88792a4a6f6cff Mon Sep 17 00:00:00 2001 From: "ktiller@flip.to" Date: Tue, 20 Feb 2024 17:13:08 -0500 Subject: [PATCH 07/19] Add error plugin and build script --- _FLIPTO-README.md | 2 ++ _FLIPTO_BUILD.ps1 | 24 +++++++++++++++++++ .../javascript-tracker/tracker.lite.config.ts | 2 +- 3 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 _FLIPTO_BUILD.ps1 diff --git a/_FLIPTO-README.md b/_FLIPTO-README.md index 08a24784c..20c8db81b 100644 --- a/_FLIPTO-README.md +++ b/_FLIPTO-README.md @@ -15,6 +15,8 @@ CURRENTLY BROKEN! --cd .\trackers\javascript-tracker\ --rushx build --whitelabel=FliptoGlobalSnowplowNamespace +Output is in: +C:\Git\snowplow-javascript-tracker\trackers\javascript-tracker\dist Take the code built for snowplow.lite and rename to ftsa.js and ftsa.js.map Ensure you rename the mapping url and remove any header text from ftsa as well! Publish to Azure Storage and clear CDN \ No newline at end of file diff --git a/_FLIPTO_BUILD.ps1 b/_FLIPTO_BUILD.ps1 new file mode 100644 index 000000000..edd23e48f --- /dev/null +++ b/_FLIPTO_BUILD.ps1 @@ -0,0 +1,24 @@ +$distFolder = "C:\Git\snowplow-javascript-tracker\trackers\javascript-tracker\dist\" +$spLite = "sp.lite.js" +$ftSa = "ftsa2.js" +$spLitePath = $distFolder + $spLite +$spLiteMapPath = $spLitePath + ".map" +$ftSaPath = $distFolder + $ftSa +$ftsaMapPath = $ftSaPath + ".map"; + +cd C:\Git\snowplow-javascript-tracker\ +if (Test-Path $ftSaPath) { + Remove-Item $ftSaPath +} +if (Test-Path $ftsaMapPath) { + Remove-Item $ftsaMapPath +} + +rush update +rush build + +(Get-Content $spLitePath).Replace($spLite, $ftSa) | Set-Content $ftSaPath +(Get-Content $spLiteMapPath).Replace($spLite, $ftSa) | Set-Content $ftsaMapPath + +# Rename-Item -Path $spLitePath -NewName $ftSaPath +# Rename-Item -Path $spLiteMapPath -NewName $ftsaMapPath \ No newline at end of file diff --git a/trackers/javascript-tracker/tracker.lite.config.ts b/trackers/javascript-tracker/tracker.lite.config.ts index 45f67b335..3fed1607d 100644 --- a/trackers/javascript-tracker/tracker.lite.config.ts +++ b/trackers/javascript-tracker/tracker.lite.config.ts @@ -36,7 +36,7 @@ export const clientHints = true; export const consent = false; export const linkClickTracking = false; export const formTracking = false; -export const errorTracking = false; +export const errorTracking = true; export const timezone = false; export const ecommerce = false; export const enhancedEcommerce = true; From 793cbd9d8258022beb5a7c7f92c18066afcf957d Mon Sep 17 00:00:00 2001 From: "ktiller@flip.to" Date: Tue, 20 Feb 2024 17:14:19 -0500 Subject: [PATCH 08/19] Updating readme for commits --- _FLIPTO-README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/_FLIPTO-README.md b/_FLIPTO-README.md index 20c8db81b..0e3c09436 100644 --- a/_FLIPTO-README.md +++ b/_FLIPTO-README.md @@ -19,4 +19,7 @@ Output is in: C:\Git\snowplow-javascript-tracker\trackers\javascript-tracker\dist Take the code built for snowplow.lite and rename to ftsa.js and ftsa.js.map Ensure you rename the mapping url and remove any header text from ftsa as well! -Publish to Azure Storage and clear CDN \ No newline at end of file +Publish to Azure Storage and clear CDN + +HOW TO COMMIT +Workaround gitleaks by using --no-verify i.e. git commit --no-verify -m "Adding cool new feature" \ No newline at end of file From defd5cdeb0df9fe63a8f0c2cac266873d343d539 Mon Sep 17 00:00:00 2001 From: "ktiller@flip.to" Date: Tue, 9 Apr 2024 14:02:39 -0400 Subject: [PATCH 09/19] Add fallback to null for invalid screen resolutions --- libraries/browser-tracker-core/src/helpers/browser_props.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/browser-tracker-core/src/helpers/browser_props.ts b/libraries/browser-tracker-core/src/helpers/browser_props.ts index 4c74d3ab7..ab2ed4fd5 100644 --- a/libraries/browser-tracker-core/src/helpers/browser_props.ts +++ b/libraries/browser-tracker-core/src/helpers/browser_props.ts @@ -61,8 +61,9 @@ function detectDocumentSize() { return isNaN(w) || isNaN(h) ? '' : w + DIMENSION_SEPARATOR + h; } +// KEVIN TILLER - Fix resolution being sent as NaNxNaN function detectScreenResolution() { - return screen.width + DIMENSION_SEPARATOR + screen.height; + return screen.width && screen.height ? screen.width + DIMENSION_SEPARATOR + screen.height : null; } export function floorDimensionFields(field?: string | null) { From 8a2cb733e3eef387d281db4af1e31fa278cd55a2 Mon Sep 17 00:00:00 2001 From: "ktiller@flip.to" Date: Mon, 10 Jun 2024 13:01:21 -0400 Subject: [PATCH 10/19] Adding check for second run of tracker script --- trackers/javascript-tracker/src/index.ts | 7 +++++-- trackers/javascript-tracker/tags/tag.js | 6 +++--- trackers/javascript-tracker/tags/tag.min.js | 2 +- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/trackers/javascript-tracker/src/index.ts b/trackers/javascript-tracker/src/index.ts index c5fae6eda..9dd91e678 100644 --- a/trackers/javascript-tracker/src/index.ts +++ b/trackers/javascript-tracker/src/index.ts @@ -42,7 +42,10 @@ declare global { } const functionName = window.GlobalSnowplowNamespace.shift() as string, - queue = window[functionName] as { q: Queue | Array }; + queue = window[functionName] as { q: Queue | Array | null }; // Now replace initialization array with queue manager object -queue.q = InQueueManager(functionName, queue.q as Array); +// KEVIN TILLER - Fix error when running tracker script twice +if (queue) { + queue.q = InQueueManager(functionName, queue.q as Array); +} diff --git a/trackers/javascript-tracker/tags/tag.js b/trackers/javascript-tracker/tags/tag.js index e0eea9ea8..c54bc7db5 100644 --- a/trackers/javascript-tracker/tags/tag.js +++ b/trackers/javascript-tracker/tags/tag.js @@ -45,11 +45,11 @@ // Stop if the Snowplow namespace i already exists if (!p[i]) { - // Initialise the 'GlobalSnowplowNamespace' array - p['GlobalSnowplowNamespace'] = p['GlobalSnowplowNamespace'] || []; + // Initialise the 'FliptoFliptoFliptoFliptoGlobalSnowplowNamespace' array + p['FliptoFliptoFliptoFliptoGlobalSnowplowNamespace'] = p['FliptoFliptoFliptoFliptoGlobalSnowplowNamespace'] || []; // Add the new Snowplow namespace to the global array so sp.js can find it - p['GlobalSnowplowNamespace'].push(i); + p['FliptoFliptoFliptoFliptoGlobalSnowplowNamespace'].push(i); // Create the Snowplow function p[i] = function() { diff --git a/trackers/javascript-tracker/tags/tag.min.js b/trackers/javascript-tracker/tags/tag.min.js index 076a35071..eb2e8956e 100644 --- a/trackers/javascript-tracker/tags/tag.min.js +++ b/trackers/javascript-tracker/tags/tag.min.js @@ -27,4 +27,4 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -;(function(p,l,o,w,i,n,g){if(!p[i]){p.GlobalSnowplowNamespace=p.GlobalSnowplowNamespace||[]; p.GlobalSnowplowNamespace.push(i);p[i]=function(){(p[i].q=p[i].q||[]).push(arguments) };p[i].q=p[i].q||[];n=l.createElement(o);g=l.getElementsByTagName(o)[0];n.async=1; n.src=w;g.parentNode.insertBefore(n,g)}}(window,document,'script','//{{url}}/sp.js','new_name_here')); +;(function(p,l,o,w,i,n,g){if(!p[i]){p.FliptoFliptoFliptoFliptoGlobalSnowplowNamespace=p.FliptoFliptoFliptoFliptoGlobalSnowplowNamespace||[]; p.FliptoFliptoFliptoFliptoGlobalSnowplowNamespace.push(i);p[i]=function(){(p[i].q=p[i].q||[]).push(arguments) };p[i].q=p[i].q||[];n=l.createElement(o);g=l.getElementsByTagName(o)[0];n.async=1; n.src=w;g.parentNode.insertBefore(n,g)}}(window,document,'script','//{{url}}/sp.js','new_name_here')); From f1f33d29b6017a0fe750b5fb40fd1642c3f03f47 Mon Sep 17 00:00:00 2001 From: "ktiller@flip.to" Date: Wed, 6 Nov 2024 16:59:50 -0500 Subject: [PATCH 11/19] Remove deprecated performance logging and update build script --- _FLIPTO-README.md | 6 +++--- _FLIPTO_BUILD.ps1 | 10 +++++----- trackers/javascript-tracker/tracker.lite.config.ts | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/_FLIPTO-README.md b/_FLIPTO-README.md index 0e3c09436..0fb59385b 100644 --- a/_FLIPTO-README.md +++ b/_FLIPTO-README.md @@ -10,10 +10,10 @@ HOW TO BUILD cd C:\Git\snowplow-javascript-tracker\ rush update rush build +cd .\trackers\javascript-tracker\ +rushx build --whitelabel=FliptoGlobalSnowplowNamespace -CURRENTLY BROKEN! ---cd .\trackers\javascript-tracker\ ---rushx build --whitelabel=FliptoGlobalSnowplowNamespace +-- WARNING! tag.js is broken with whitelabels! Output is in: C:\Git\snowplow-javascript-tracker\trackers\javascript-tracker\dist diff --git a/_FLIPTO_BUILD.ps1 b/_FLIPTO_BUILD.ps1 index edd23e48f..363fe4474 100644 --- a/_FLIPTO_BUILD.ps1 +++ b/_FLIPTO_BUILD.ps1 @@ -1,6 +1,6 @@ $distFolder = "C:\Git\snowplow-javascript-tracker\trackers\javascript-tracker\dist\" $spLite = "sp.lite.js" -$ftSa = "ftsa2.js" +$ftSa = "ftsa.js" $spLitePath = $distFolder + $spLite $spLiteMapPath = $spLitePath + ".map" $ftSaPath = $distFolder + $ftSa @@ -14,11 +14,11 @@ if (Test-Path $ftsaMapPath) { Remove-Item $ftsaMapPath } -rush update -rush build +# rush update +cd .\trackers\javascript-tracker\ +rushx build --whitelabel=FliptoGlobalSnowplowNamespace (Get-Content $spLitePath).Replace($spLite, $ftSa) | Set-Content $ftSaPath (Get-Content $spLiteMapPath).Replace($spLite, $ftSa) | Set-Content $ftsaMapPath -# Rename-Item -Path $spLitePath -NewName $ftSaPath -# Rename-Item -Path $spLiteMapPath -NewName $ftsaMapPath \ No newline at end of file +cd C:\Git\snowplow-javascript-tracker\ \ No newline at end of file diff --git a/trackers/javascript-tracker/tracker.lite.config.ts b/trackers/javascript-tracker/tracker.lite.config.ts index 67072c0aa..6c7bc1553 100644 --- a/trackers/javascript-tracker/tracker.lite.config.ts +++ b/trackers/javascript-tracker/tracker.lite.config.ts @@ -1,5 +1,5 @@ -export const performanceTiming = true; -export const performanceNavigationTiming = false; +export const performanceTiming = false; +export const performanceNavigationTiming = true; export const gaCookies = true; export const geolocation = false; export const optimizelyX = false; From e5b3a91c97f017d6de2304cc07c69c8b03fa7910 Mon Sep 17 00:00:00 2001 From: "ktiller@flip.to" Date: Tue, 17 Dec 2024 14:05:02 -0500 Subject: [PATCH 12/19] Add Core Web Vitals --- _FLIPTO_BUILD.ps1 | 1 + .../rollup.config.js | 11 +++--- .../browser-plugin-web-vitals/src/index.ts | 22 ++--------- .../browser-plugin-web-vitals/src/utils.ts | 39 ++++--------------- trackers/javascript-tracker/rollup.config.js | 17 ++++---- trackers/javascript-tracker/tags/tag.js | 6 +-- trackers/javascript-tracker/tags/tag.min.js | 2 +- .../javascript-tracker/tracker.lite.config.ts | 2 +- 8 files changed, 30 insertions(+), 70 deletions(-) diff --git a/_FLIPTO_BUILD.ps1 b/_FLIPTO_BUILD.ps1 index 363fe4474..4c13d0266 100644 --- a/_FLIPTO_BUILD.ps1 +++ b/_FLIPTO_BUILD.ps1 @@ -7,6 +7,7 @@ $ftSaPath = $distFolder + $ftSa $ftsaMapPath = $ftSaPath + ".map"; cd C:\Git\snowplow-javascript-tracker\ +rush build if (Test-Path $ftSaPath) { Remove-Item $ftSaPath } diff --git a/plugins/browser-plugin-web-vitals/rollup.config.js b/plugins/browser-plugin-web-vitals/rollup.config.js index 48d2287f5..d8fc0d682 100644 --- a/plugins/browser-plugin-web-vitals/rollup.config.js +++ b/plugins/browser-plugin-web-vitals/rollup.config.js @@ -1,12 +1,11 @@ -import { nodeResolve } from '@rollup/plugin-node-resolve'; +import compiler from '@ampproject/rollup-plugin-closure-compiler'; import commonjs from '@rollup/plugin-commonjs'; +import { nodeResolve } from '@rollup/plugin-node-resolve'; +import { builtinModules } from 'module'; +import { terser } from 'rollup-plugin-terser'; import ts from 'rollup-plugin-ts'; // Preferred over @rollup/plugin-typescript as it bundles .d.ts files import { banner } from '../../banner'; -import compiler from '@ampproject/rollup-plugin-closure-compiler'; -import { terser } from 'rollup-plugin-terser'; -import cleanup from 'rollup-plugin-cleanup'; import pkg from './package.json'; -import { builtinModules } from 'module'; const umdPlugins = [nodeResolve({ browser: true }), commonjs(), ts()]; const umdName = 'snowplowWebVitals'; @@ -21,7 +20,7 @@ export default [ }, { input: './src/index.ts', - plugins: [...umdPlugins, compiler(), terser(), cleanup({ comments: 'none' }), banner()], + plugins: [...umdPlugins, compiler(), terser(), banner()], treeshake: { moduleSideEffects: ['sha1'] }, output: [{ file: pkg.main.replace('.js', '.min.js'), format: 'umd', sourcemap: true, name: umdName }], }, diff --git a/plugins/browser-plugin-web-vitals/src/index.ts b/plugins/browser-plugin-web-vitals/src/index.ts index 600291bcb..edcf667e4 100644 --- a/plugins/browser-plugin-web-vitals/src/index.ts +++ b/plugins/browser-plugin-web-vitals/src/index.ts @@ -1,36 +1,28 @@ import { BrowserPlugin, BrowserTracker, dispatchToTrackersInCollection } from '@snowplow/browser-tracker-core'; -import { DynamicContext, buildSelfDescribingEvent, resolveDynamicContext } from '@snowplow/tracker-core'; +import { buildSelfDescribingEvent, DynamicContext, resolveDynamicContext } from '@snowplow/tracker-core'; import { WEB_VITALS_SCHEMA } from './schemata'; -import { attachWebVitalsPageListeners, createWebVitalsScript, webVitalsListener } from './utils'; +import { attachWebVitalsPageListeners, webVitalsListener } from './utils'; const _trackers: Record = {}; -const WEB_VITALS_SOURCE = 'https://unpkg.com/web-vitals@3/dist/web-vitals.iife.js'; let listenersAttached = false; - interface WebVitalsPluginOptions { loadWebVitalsScript?: boolean; webVitalsSource?: string; context?: DynamicContext; } - const defaultPluginOptions = { - loadWebVitalsScript: true, - webVitalsSource: WEB_VITALS_SOURCE, context: [], }; /** * Adds Web Vitals measurement events * - * @param pluginOptions.loadWebVitalsScript - Should the plugin immediately load the Core Web Vitals measurement script from UNPKG CDN. - * @param pluginOptions.webVitalsSource - The URL endpoint the Web Vitals script should be loaded from. Defaults to the UNPKG CDN. * @remarks */ export function WebVitalsPlugin(pluginOptions: WebVitalsPluginOptions = defaultPluginOptions): BrowserPlugin { const webVitalsObject: Record = {}; const options = { ...defaultPluginOptions, ...pluginOptions }; let trackerId: string; - let webVitalsScript: HTMLScriptElement | undefined; return { activateBrowserPlugin: (tracker) => { trackerId = tracker.id; @@ -53,20 +45,12 @@ export function WebVitalsPlugin(pluginOptions: WebVitalsPluginOptions = defaultP }); } - if (options.loadWebVitalsScript) { - webVitalsScript = createWebVitalsScript(options.webVitalsSource); - } - /* * Attach page listeners only once per page. * Prevent multiple trackers from attaching listeners multiple times. */ if (!listenersAttached) { - if (webVitalsScript) { - webVitalsScript.addEventListener('load', () => webVitalsListener(webVitalsObject)); - } else { - webVitalsListener(webVitalsObject); - } + webVitalsListener(webVitalsObject); attachWebVitalsPageListeners(sendWebVitals); listenersAttached = true; } diff --git a/plugins/browser-plugin-web-vitals/src/utils.ts b/plugins/browser-plugin-web-vitals/src/utils.ts index 31916bfee..ec78f757c 100644 --- a/plugins/browser-plugin-web-vitals/src/utils.ts +++ b/plugins/browser-plugin-web-vitals/src/utils.ts @@ -1,5 +1,5 @@ -import { LOG } from '@snowplow/tracker-core'; -import { ReportCallback, WebVitals } from './types'; +import { onCLS, onFCP, onFID, onINP, onLCP, onTTFB } from 'web-vitals'; +import { ReportCallback } from './types'; /** * Attach page listeners to collect the Web Vitals values @@ -27,23 +27,6 @@ export function attachWebVitalsPageListeners(callback: () => void) { } } -/** - * - * @param {string} webVitalsSource Web Vitals script source. - * @returns {string} The script element of the Web Vitals script. Used for attaching listeners on it. - */ -export function createWebVitalsScript(webVitalsSource: string) { - const webVitalsScript = document.createElement('script'); - webVitalsScript.setAttribute('src', webVitalsSource); - webVitalsScript.setAttribute('async', '1'); - webVitalsScript.addEventListener('error', () => { - LOG.error(`Failed to load ${webVitalsSource}`); - }); - - document.head.appendChild(webVitalsScript); - return webVitalsScript; -} - /** * * Adds the Web Vitals measurements on the object used by the trackers to store metric properties. @@ -57,16 +40,10 @@ export function webVitalsListener(webVitalsObject: Record) { webVitalsObject.navigationType = arg.navigationType; }; } - if (!window.webVitals) { - LOG.warn('The window.webVitals API is currently unavailable. web_vitals events will not be collected.'); - return; - } - - const webVitals = window.webVitals as WebVitals; - webVitals.onCLS(addWebVitalsMeasurement('cls')); - webVitals.onFID(addWebVitalsMeasurement('fid')); - webVitals.onLCP(addWebVitalsMeasurement('lcp')); - webVitals.onFCP(addWebVitalsMeasurement('fcp')); - webVitals.onINP(addWebVitalsMeasurement('inp')); - webVitals.onTTFB(addWebVitalsMeasurement('ttfb')); + onCLS(addWebVitalsMeasurement('cls')); + onFID(addWebVitalsMeasurement('fid')); + onLCP(addWebVitalsMeasurement('lcp')); + onFCP(addWebVitalsMeasurement('fcp')); + onINP(addWebVitalsMeasurement('inp')); + onTTFB(addWebVitalsMeasurement('ttfb')); } diff --git a/trackers/javascript-tracker/rollup.config.js b/trackers/javascript-tracker/rollup.config.js index 6b7c7ddf2..80d74c736 100644 --- a/trackers/javascript-tracker/rollup.config.js +++ b/trackers/javascript-tracker/rollup.config.js @@ -28,18 +28,17 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -import { nodeResolve } from '@rollup/plugin-node-resolve'; -import ts from 'rollup-plugin-ts'; // Prefered over @rollup/plugin-typescript as it bundles .d.ts files +import compiler from '@ampproject/rollup-plugin-closure-compiler'; +import alias from '@rollup/plugin-alias'; import commonjs from '@rollup/plugin-commonjs'; import json from '@rollup/plugin-json'; +import { nodeResolve } from '@rollup/plugin-node-resolve'; +import filesize from 'rollup-plugin-filesize'; +import sizes from 'rollup-plugin-sizes'; +import { terser } from 'rollup-plugin-terser'; +import ts from 'rollup-plugin-ts'; // Prefered over @rollup/plugin-typescript as it bundles .d.ts files import { banner } from '../../banner'; import { whitelabelBuild } from './build-config/index'; -import compiler from '@ampproject/rollup-plugin-closure-compiler'; -import { terser } from 'rollup-plugin-terser'; -import cleanup from 'rollup-plugin-cleanup'; -import sizes from 'rollup-plugin-sizes'; -import filesize from 'rollup-plugin-filesize'; -import alias from '@rollup/plugin-alias'; import pkg from './package.json'; export default (cmdlineArgs) => { @@ -50,7 +49,7 @@ export default (cmdlineArgs) => { ts(), compiler(), terser(), - cleanup({ comments: 'none' }), + // cleanup({ comments: 'none' }), banner(), sizes(), filesize({ showMinifiedSize: false, showBeforeSizes: 'build' }), diff --git a/trackers/javascript-tracker/tags/tag.js b/trackers/javascript-tracker/tags/tag.js index c54bc7db5..dfbfb1eb1 100644 --- a/trackers/javascript-tracker/tags/tag.js +++ b/trackers/javascript-tracker/tags/tag.js @@ -45,11 +45,11 @@ // Stop if the Snowplow namespace i already exists if (!p[i]) { - // Initialise the 'FliptoFliptoFliptoFliptoGlobalSnowplowNamespace' array - p['FliptoFliptoFliptoFliptoGlobalSnowplowNamespace'] = p['FliptoFliptoFliptoFliptoGlobalSnowplowNamespace'] || []; + // Initialise the 'FliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoGlobalSnowplowNamespace' array + p['FliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoGlobalSnowplowNamespace'] = p['FliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoGlobalSnowplowNamespace'] || []; // Add the new Snowplow namespace to the global array so sp.js can find it - p['FliptoFliptoFliptoFliptoGlobalSnowplowNamespace'].push(i); + p['FliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoGlobalSnowplowNamespace'].push(i); // Create the Snowplow function p[i] = function() { diff --git a/trackers/javascript-tracker/tags/tag.min.js b/trackers/javascript-tracker/tags/tag.min.js index eb2e8956e..534cf5074 100644 --- a/trackers/javascript-tracker/tags/tag.min.js +++ b/trackers/javascript-tracker/tags/tag.min.js @@ -27,4 +27,4 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -;(function(p,l,o,w,i,n,g){if(!p[i]){p.FliptoFliptoFliptoFliptoGlobalSnowplowNamespace=p.FliptoFliptoFliptoFliptoGlobalSnowplowNamespace||[]; p.FliptoFliptoFliptoFliptoGlobalSnowplowNamespace.push(i);p[i]=function(){(p[i].q=p[i].q||[]).push(arguments) };p[i].q=p[i].q||[];n=l.createElement(o);g=l.getElementsByTagName(o)[0];n.async=1; n.src=w;g.parentNode.insertBefore(n,g)}}(window,document,'script','//{{url}}/sp.js','new_name_here')); +;(function(p,l,o,w,i,n,g){if(!p[i]){p.FliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoGlobalSnowplowNamespace=p.FliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoGlobalSnowplowNamespace||[]; p.FliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoGlobalSnowplowNamespace.push(i);p[i]=function(){(p[i].q=p[i].q||[]).push(arguments) };p[i].q=p[i].q||[];n=l.createElement(o);g=l.getElementsByTagName(o)[0];n.async=1; n.src=w;g.parentNode.insertBefore(n,g)}}(window,document,'script','//{{url}}/sp.js','new_name_here')); diff --git a/trackers/javascript-tracker/tracker.lite.config.ts b/trackers/javascript-tracker/tracker.lite.config.ts index 6c7bc1553..8dc8f2a53 100644 --- a/trackers/javascript-tracker/tracker.lite.config.ts +++ b/trackers/javascript-tracker/tracker.lite.config.ts @@ -20,4 +20,4 @@ export const vimeoTracking = false; export const privacySandbox = false; export const buttonClickTracking = false; export const eventSpecifications = false; -export const webVitals = false; +export const webVitals = true; From 598db6fd52754368186feb1fb95ae4ad7969e006 Mon Sep 17 00:00:00 2001 From: "ktiller@flip.to" Date: Wed, 18 Dec 2024 16:46:07 -0500 Subject: [PATCH 13/19] Change snowplowOutQueue to ftOutQueue --- _FLIPTO_BUILD.ps1 | 2 +- .../src/tracker/local_storage_event_store.ts | 12 ++++++++++-- trackers/javascript-tracker/tags/tag.js | 6 +++--- trackers/javascript-tracker/tags/tag.min.js | 2 +- 4 files changed, 15 insertions(+), 7 deletions(-) diff --git a/_FLIPTO_BUILD.ps1 b/_FLIPTO_BUILD.ps1 index 4c13d0266..e03abf24b 100644 --- a/_FLIPTO_BUILD.ps1 +++ b/_FLIPTO_BUILD.ps1 @@ -17,7 +17,7 @@ if (Test-Path $ftsaMapPath) { # rush update cd .\trackers\javascript-tracker\ -rushx build --whitelabel=FliptoGlobalSnowplowNamespace +rushx build --whitelabel=ftSpacetimeGlobalNamespace (Get-Content $spLitePath).Replace($spLite, $ftSa) | Set-Content $ftSaPath (Get-Content $spLiteMapPath).Replace($spLite, $ftSa) | Set-Content $ftsaMapPath diff --git a/libraries/browser-tracker-core/src/tracker/local_storage_event_store.ts b/libraries/browser-tracker-core/src/tracker/local_storage_event_store.ts index 52f7db9aa..77724cc8f 100644 --- a/libraries/browser-tracker-core/src/tracker/local_storage_event_store.ts +++ b/libraries/browser-tracker-core/src/tracker/local_storage_event_store.ts @@ -1,4 +1,4 @@ -import { EventStore, newInMemoryEventStore, EventStorePayload } from '@snowplow/tracker-core'; +import { EventStore, EventStorePayload, newInMemoryEventStore } from '@snowplow/tracker-core'; import { LocalStorageEventStoreConfigurationBase } from './types'; export interface LocalStorageEventStoreConfiguration extends LocalStorageEventStoreConfigurationBase { @@ -17,7 +17,9 @@ export function newLocalStorageEventStore({ maxLocalStorageQueueSize = 1000, useLocalStorage = true, }: LocalStorageEventStoreConfiguration): LocalStorageEventStore { - const queueName = `snowplowOutQueue_${trackerId}`; + // KEVIN TILLER + // Remove the name snowplow from our queue + const queueName = `ftOutQueue_${trackerId}`; function newInMemoryEventStoreFromLocalStorage() { if (useLocalStorage) { @@ -56,6 +58,12 @@ export function newLocalStorageEventStore({ getAllPayloads, setUseLocalStorage: (use: boolean) => { useLocalStorage = use; + // KEVIN TILLER + // If we lost permission to access the local storage, delete the queues. This prevents duplicate + // of page views when we have initial access, but lose access before the queue can be purged. + if (!useLocalStorage) { + window.localStorage.removeItem(queueName); + } }, }; } diff --git a/trackers/javascript-tracker/tags/tag.js b/trackers/javascript-tracker/tags/tag.js index dfbfb1eb1..c32d549d7 100644 --- a/trackers/javascript-tracker/tags/tag.js +++ b/trackers/javascript-tracker/tags/tag.js @@ -45,11 +45,11 @@ // Stop if the Snowplow namespace i already exists if (!p[i]) { - // Initialise the 'FliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoGlobalSnowplowNamespace' array - p['FliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoGlobalSnowplowNamespace'] = p['FliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoGlobalSnowplowNamespace'] || []; + // Initialise the 'FliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoftSaGlobalNamespace' array + p['FliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoftSaGlobalNamespace'] = p['FliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoftSaGlobalNamespace'] || []; // Add the new Snowplow namespace to the global array so sp.js can find it - p['FliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoGlobalSnowplowNamespace'].push(i); + p['FliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoftSaGlobalNamespace'].push(i); // Create the Snowplow function p[i] = function() { diff --git a/trackers/javascript-tracker/tags/tag.min.js b/trackers/javascript-tracker/tags/tag.min.js index 534cf5074..4bb3d162c 100644 --- a/trackers/javascript-tracker/tags/tag.min.js +++ b/trackers/javascript-tracker/tags/tag.min.js @@ -27,4 +27,4 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -;(function(p,l,o,w,i,n,g){if(!p[i]){p.FliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoGlobalSnowplowNamespace=p.FliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoGlobalSnowplowNamespace||[]; p.FliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoGlobalSnowplowNamespace.push(i);p[i]=function(){(p[i].q=p[i].q||[]).push(arguments) };p[i].q=p[i].q||[];n=l.createElement(o);g=l.getElementsByTagName(o)[0];n.async=1; n.src=w;g.parentNode.insertBefore(n,g)}}(window,document,'script','//{{url}}/sp.js','new_name_here')); +;(function(p,l,o,w,i,n,g){if(!p[i]){p.FliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoftSaGlobalNamespace=p.FliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoftSaGlobalNamespace||[]; p.FliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoFliptoftSaGlobalNamespace.push(i);p[i]=function(){(p[i].q=p[i].q||[]).push(arguments) };p[i].q=p[i].q||[];n=l.createElement(o);g=l.getElementsByTagName(o)[0];n.async=1; n.src=w;g.parentNode.insertBefore(n,g)}}(window,document,'script','//{{url}}/sp.js','new_name_here')); From a542d380179a9e18311a163c51140600e3033196 Mon Sep 17 00:00:00 2001 From: bkirii Date: Tue, 22 Jul 2025 16:39:38 +0200 Subject: [PATCH 14/19] FTK: + add fallback for localstorage --- libraries/browser-tracker-core/src/tracker/index.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/libraries/browser-tracker-core/src/tracker/index.ts b/libraries/browser-tracker-core/src/tracker/index.ts index 91b229e0c..a3074821d 100755 --- a/libraries/browser-tracker-core/src/tracker/index.ts +++ b/libraries/browser-tracker-core/src/tracker/index.ts @@ -479,11 +479,14 @@ export function Tracker( const fullName = getSnowplowCookieName(cookieName); if (configStateStorageStrategy == 'localStorage') { return attemptGetLocalStorage(fullName); - } - // KEVIN TILLER - It always makes sense to READ a cookie that pre-exists in case of + } + // KEVIN TILLER - It always makes sense to READ a cookie that pre-exists in case of // configurations where each page starts with no consent, then "updates" availability // as third-party consent management widgets load - return cookieStorage.getCookie(fullName); + const receivedCookieValue = cookieStorage.getCookie(fullName); + + // fallback to localstorage + return receivedCookieValue ?? attemptGetLocalStorage(fullName); } /* @@ -608,6 +611,7 @@ export function Tracker( if (configStateStorageStrategy == 'localStorage') { return attemptWriteLocalStorage(name, value, timeout); } else if (configStateStorageStrategy == 'cookie' || configStateStorageStrategy == 'cookieAndLocalStorage') { + attemptWriteLocalStorage(name, value, timeout); return cookieStorage.setCookie( name, value, @@ -1396,7 +1400,7 @@ export function Tracker( // KEVIN TILLER - Workaround until getDomainUserId works from GTM ((window as any).fliptoDataLayer = (window as any).fliptoDataLayer || []).snowplow = tracker; - + // Initialise each plugin with the tracker browserPlugins.forEach((p) => { p.activateBrowserPlugin?.(tracker); From 4b1070b7a6dbdabfb6ba207435ad843b3fa6b028 Mon Sep 17 00:00:00 2001 From: Kevin Date: Tue, 22 Jul 2025 11:02:00 -0400 Subject: [PATCH 15/19] Add comments --- libraries/browser-tracker-core/src/tracker/index.ts | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/libraries/browser-tracker-core/src/tracker/index.ts b/libraries/browser-tracker-core/src/tracker/index.ts index a3074821d..3400b672c 100755 --- a/libraries/browser-tracker-core/src/tracker/index.ts +++ b/libraries/browser-tracker-core/src/tracker/index.ts @@ -480,13 +480,11 @@ export function Tracker( if (configStateStorageStrategy == 'localStorage') { return attemptGetLocalStorage(fullName); } - // KEVIN TILLER - It always makes sense to READ a cookie that pre-exists in case of + // BOHDAN KIRII / KEVIN TILLER - It always makes sense to READ a cookie that pre-exists in case of // configurations where each page starts with no consent, then "updates" availability // as third-party consent management widgets load - const receivedCookieValue = cookieStorage.getCookie(fullName); - - // fallback to localstorage - return receivedCookieValue ?? attemptGetLocalStorage(fullName); + // fallback to localstorage if cookie doesn't exist + return cookieStorage.getCookie(fullName) ?? attemptGetLocalStorage(fullName); } /* @@ -611,6 +609,7 @@ export function Tracker( if (configStateStorageStrategy == 'localStorage') { return attemptWriteLocalStorage(name, value, timeout); } else if (configStateStorageStrategy == 'cookie' || configStateStorageStrategy == 'cookieAndLocalStorage') { + // BOHDAN KIRII / KEVIN TILLER Add local storage backup when writing to cookie attemptWriteLocalStorage(name, value, timeout); return cookieStorage.setCookie( name, @@ -1400,7 +1399,6 @@ export function Tracker( // KEVIN TILLER - Workaround until getDomainUserId works from GTM ((window as any).fliptoDataLayer = (window as any).fliptoDataLayer || []).snowplow = tracker; - // Initialise each plugin with the tracker browserPlugins.forEach((p) => { p.activateBrowserPlugin?.(tracker); From 06bbebc9ed8c0145de1900472016f4b52ab691f9 Mon Sep 17 00:00:00 2001 From: dsinitsyn Date: Mon, 19 Jan 2026 17:16:43 +0100 Subject: [PATCH 16/19] Restore user ID from localStorage to cookie when cookie is missing --- .../browser-tracker-core/src/tracker/index.ts | 224 ++++++++++-------- 1 file changed, 128 insertions(+), 96 deletions(-) diff --git a/libraries/browser-tracker-core/src/tracker/index.ts b/libraries/browser-tracker-core/src/tracker/index.ts index 3400b672c..de23b8d15 100755 --- a/libraries/browser-tracker-core/src/tracker/index.ts +++ b/libraries/browser-tracker-core/src/tracker/index.ts @@ -72,6 +72,7 @@ declare global { interface Navigator { msDoNotTrack: boolean; } + interface Window { doNotTrack: boolean; } @@ -123,8 +124,8 @@ export function Tracker( version: string, endpoint: string, sharedState: SharedState, - trackerConfiguration: TrackerConfiguration = {} -): BrowserTracker { + trackerConfiguration: TrackerConfiguration = {}, +): BrowserTracker{ const browserPlugins: Array = []; const newTracker = ( @@ -133,7 +134,7 @@ export function Tracker( version: string, endpoint: string, state: SharedState, - trackerConfiguration: TrackerConfiguration + trackerConfiguration: TrackerConfiguration, ) => { /************************************************************ * Private members @@ -281,7 +282,7 @@ export function Tracker( configStateStorageStrategy == 'localStorage' || configStateStorageStrategy == 'cookieAndLocalStorage', ...trackerConfiguration, }, - state + state, ), // Whether pageViewId should be regenerated after each trackPageView. Affect web_page context preservePageViewId = false, @@ -300,7 +301,7 @@ export function Tracker( onSessionUpdateCallback = trackerConfiguration.onSessionUpdateCallback, manualSessionUpdateCalled = false, { useExtendedCrossDomainLinker, collectCrossDomainAttributes } = getExtendedCrossDomainTrackingConfiguration( - trackerConfiguration.useExtendedCrossDomainLinker || false + trackerConfiguration.useExtendedCrossDomainLinker || false, ); if (discoverRootDomain && !configCookieDomain) { @@ -336,7 +337,7 @@ export function Tracker( /** * Recalculate the domain, URL, and referrer */ - function refreshUrl() { + function refreshUrl(){ locationArray = fixupUrl(window.location.hostname, window.location.href, getReferrer()); // If this is a single-page app and the page URL has changed, then: @@ -355,7 +356,7 @@ export function Tracker( * * @param event - e The event targeting the link */ - function addLinkDecorationHandler(extended: boolean): (evt: Event) => void { + function addLinkDecorationHandler(extended: boolean): (evt: Event) => void{ const CROSS_DOMAIN_PARAMETER_NAME = '_sp'; return (evt) => { @@ -383,7 +384,7 @@ export function Tracker( * * @param crossDomainLinker - Function used to determine which links to decorate */ - function decorateLinks(crossDomainLinker: (elt: HTMLAnchorElement | HTMLAreaElement) => boolean) { + function decorateLinks(crossDomainLinker: (elt: HTMLAnchorElement | HTMLAreaElement) => boolean){ const crossDomainLinkHandler = addLinkDecorationHandler(useExtendedCrossDomainLinker); for (let i = 0; i < document.links.length; i++) { const elt = document.links[i]; @@ -403,7 +404,7 @@ export function Tracker( * URLs are purified before being recorded in the cookie, * or before being sent as GET parameters */ - function purify(url: string) { + function purify(url: string){ let targetPattern; if (configDiscardHashTag) { @@ -421,7 +422,7 @@ export function Tracker( /* * Extract scheme/protocol from URL */ - function getProtocolScheme(url: string) { + function getProtocolScheme(url: string){ const e = new RegExp('^([a-z]+):'), matches = e.exec(url); @@ -433,7 +434,7 @@ export function Tracker( * * Note: not as described in rfc3986 section 5.2 */ - function resolveRelativeReference(baseUrl: string, url: string) { + function resolveRelativeReference(baseUrl: string, url: string){ let protocol = getProtocolScheme(url), i; @@ -459,7 +460,7 @@ export function Tracker( /* * Send request */ - function sendRequest(request: PayloadBuilder) { + function sendRequest(request: PayloadBuilder){ if (!(configDoNotTrack || toOptoutByCookie)) { outQueue.enqueueRequest(request.build()); } @@ -468,14 +469,14 @@ export function Tracker( /* * Get cookie name with prefix and domain hash */ - function getSnowplowCookieName(baseName: string) { + function getSnowplowCookieName(baseName: string){ return configCookieNamePrefix + baseName + '.' + domainHash; } /* * Cookie getter. */ - function getSnowplowCookieValue(cookieName: string) { + function getSnowplowCookieValue(cookieName: string){ const fullName = getSnowplowCookieName(cookieName); if (configStateStorageStrategy == 'localStorage') { return attemptGetLocalStorage(fullName); @@ -490,7 +491,7 @@ export function Tracker( /* * Update domain hash */ - function updateDomainHash() { + function updateDomainHash(){ refreshUrl(); domainHash = hash((configCookieDomain || domainAlias) + (configCookiePath || '/')).slice(0, 4); // 4 hexits = 16 bits } @@ -499,7 +500,7 @@ export function Tracker( * Process all "activity" events. * For performance, this function must have low overhead. */ - function activityHandler() { + function activityHandler(){ const now = new Date(); lastActivityTime = now.getTime(); } @@ -507,7 +508,7 @@ export function Tracker( /* * Process all "scroll" events. */ - function scrollHandler() { + function scrollHandler(){ updateMaxScrolls(); activityHandler(); } @@ -515,7 +516,7 @@ export function Tracker( /* * Returns [pageXOffset, pageYOffset] */ - function getPageOffsets() { + function getPageOffsets(){ const documentElement = document.documentElement; if (documentElement) { return [documentElement.scrollLeft || window.pageXOffset, documentElement.scrollTop || window.pageYOffset]; @@ -527,7 +528,7 @@ export function Tracker( /* * Quick initialization/reset of max scroll levels */ - function resetMaxScrolls() { + function resetMaxScrolls(){ const offsets = getPageOffsets(); const x = offsets[0]; @@ -542,7 +543,7 @@ export function Tracker( /* * Check the max scroll levels, updating as necessary */ - function updateMaxScrolls() { + function updateMaxScrolls(){ const offsets = getPageOffsets(); const x = offsets[0]; @@ -564,7 +565,7 @@ export function Tracker( * Prevents offsets from being decimal or NaN * See https://github.com/snowplow/snowplow-javascript-tracker/issues/324 */ - function cleanOffset(offset: number) { + function cleanOffset(offset: number){ return Math.round(offset); } @@ -573,7 +574,7 @@ export function Tracker( * Responsible for calling the `onSessionUpdateCallback` callback. * @returns {boolean} If the value persisted in cookies or LocalStorage */ - function setSessionCookie() { + function setSessionCookie(){ const cookieName = getSnowplowCookieName('ses'); const cookieValue = '*'; return persistValue(cookieName, cookieValue, configSessionCookieTimeout); @@ -584,7 +585,7 @@ export function Tracker( * @param {ParsedIdCookie} idCookie * @returns {boolean} If the value persisted in cookies or LocalStorage */ - function setDomainUserIdCookie(idCookie: ParsedIdCookie) { + function setDomainUserIdCookie(idCookie: ParsedIdCookie){ const cookieName = getSnowplowCookieName('id'); const cookieValue = serializeIdCookie(idCookie, configAnonymousTracking); return persistValue(cookieName, cookieValue, configVisitorCookieTimeout); @@ -601,7 +602,7 @@ export function Tracker( * @param {number} timeout Used as the expiration date for cookies or as a TTL to be checked on LocalStorage * @returns {boolean} If the operation was successful or not */ - function persistValue(name: string, value: string, timeout: number): boolean { + function persistValue(name: string, value: string, timeout: number): boolean{ if (configAnonymousTracking && !configAnonymousSessionTracking) { return false; } @@ -618,7 +619,7 @@ export function Tracker( configCookiePath, configCookieDomain, configCookieSameSite, - configCookieSecure + configCookieSecure, ); } return false; @@ -627,7 +628,7 @@ export function Tracker( /** * Clears all cookie and local storage for id and ses values */ - function clearUserDataAndCookies(configuration?: ClearUserDataConfiguration) { + function clearUserDataAndCookies(configuration?: ClearUserDataConfiguration){ const idname = getSnowplowCookieName('id'); const sesname = getSnowplowCookieName('ses'); attemptDeleteLocalStorage(idname); @@ -637,14 +638,14 @@ export function Tracker( configCookiePath, configCookieDomain, configCookieSameSite, - configCookieSecure + configCookieSecure, ); cookieStorage.deleteCookie( sesname, configCookiePath, configCookieDomain, configCookieSameSite, - configCookieSecure + configCookieSecure, ); if (!configuration?.preserveSession) { memorizedSessionId = uuid(); @@ -660,8 +661,8 @@ export function Tracker( * Toggle Anonymous Tracking */ function toggleAnonymousTracking( - configuration?: EnableAnonymousTrackingConfiguration | DisableAnonymousTrackingConfiguration - ) { + configuration?: EnableAnonymousTrackingConfiguration | DisableAnonymousTrackingConfiguration, + ){ if (configuration && configuration.stateStorageStrategy) { trackerConfiguration.stateStorageStrategy = configuration.stateStorageStrategy; configStateStorageStrategy = getStateStorageStrategy(trackerConfiguration); @@ -672,7 +673,7 @@ export function Tracker( configAnonymousServerTracking = getAnonymousServerTracking(trackerConfiguration); outQueue.setUseLocalStorage( - configStateStorageStrategy == 'localStorage' || configStateStorageStrategy == 'cookieAndLocalStorage' + configStateStorageStrategy == 'localStorage' || configStateStorageStrategy == 'cookieAndLocalStorage', ); outQueue.setAnonymousTracking(configAnonymousServerTracking); } @@ -681,7 +682,7 @@ export function Tracker( * Load the domain user ID and the session ID * Set the cookies (if cookies are enabled) */ - function initializeIdsAndCookies() { + function initializeIdsAndCookies(){ if (configAnonymousTracking && !configAnonymousSessionTracking) { return; } @@ -710,12 +711,41 @@ export function Tracker( /* * Load visitor ID cookie */ - function loadDomainUserIdCookie() { + function loadDomainUserIdCookie(){ // KEVIN TILLER - Removing this so we can have in-memory domainuserid when cookies are disallowed. // if (configStateStorageStrategy == 'none') { // return emptyIdCookie(); // } + + // Get the full cookie name with prefix and domain hash + const fullName = getSnowplowCookieName('id'); + + // Try to get ID cookie value (first from cookie, then from localStorage as fallback) const id = getSnowplowCookieValue('id') || undefined; + + // DIMA SINITSYN - Restore user ID from localStorage to cookie when cookie is missing + // Problem: When cookie is deleted (by user or browser) but value still exists in localStorage, + // the system was creating a new ID instead of restoring the existing one from localStorage. + // This causes loss of user tracking continuity and creates duplicate users in analytics. + // Solution: If cookie is missing but localStorage has a value, restore it back to cookie + // to maintain user identity across sessions and prevent tracking loss. + if (!id && configStateStorageStrategy !== 'none' && configStateStorageStrategy !== 'localStorage') { + const localStorageValue = attemptGetLocalStorage(fullName); + + // If localStorage has a saved ID value + if (localStorageValue) { + // Parse the existing ID from localStorage + const restoredCookie = parseIdCookie(localStorageValue, domainUserId, memorizedSessionId, memorizedVisitCount); + + // Restore the cookie from localStorage to maintain user tracking continuity + // This is critical for preserving user identity and session history + setDomainUserIdCookie(restoredCookie); + + return restoredCookie; + } + } + + // Parse the cookie (if exists) or create new one (if neither cookie nor localStorage exists) return parseIdCookie(id, domainUserId, memorizedSessionId, memorizedVisitCount); } @@ -725,7 +755,7 @@ export function Tracker( * @param string - collectorUrl The collector URL with or without protocol * @returns string collectorUrl The tracker URL with protocol */ - function asCollectorUrl(collectorUrl: string) { + function asCollectorUrl(collectorUrl: string){ if (collectorUrl.indexOf('http') === 0) { return collectorUrl; } @@ -737,7 +767,7 @@ export function Tracker( * Initialize new `pageViewId` if it shouldn't be preserved. * Should be called when `trackPageView` is invoked */ - function resetPageView() { + function resetPageView(){ if (!preservePageViewId || state.pageViewId == null) { state.pageViewId = uuid(); state.pageViewUrl = configCustomUrl || locationHrefAlias; @@ -748,7 +778,7 @@ export function Tracker( * Safe function to get `pageViewId`. * Generates it if it wasn't initialized by other tracker */ - function getPageViewId() { + function getPageViewId(){ if (shouldGenerateNewPageViewId()) { state.pageViewId = uuid(); state.pageViewUrl = configCustomUrl || locationHrefAlias; @@ -756,7 +786,7 @@ export function Tracker( return state.pageViewId!; } - function shouldGenerateNewPageViewId() { + function shouldGenerateNewPageViewId(){ // If pageViewId is not initialized, generate it if (state.pageViewId == null) { return true; @@ -791,7 +821,7 @@ export function Tracker( * Safe function to get `tabId`. * Generates it if it is not yet initialized. Shared between trackers. */ - function getTabId() { + function getTabId(){ if (configStateStorageStrategy === 'none' || configAnonymousTracking || !isWebPageContextAvailable) { return null; } @@ -809,7 +839,7 @@ export function Tracker( * * @returns web_page context */ - function getWebPagePlugin() { + function getWebPagePlugin(){ return { contexts: () => { return [ @@ -824,7 +854,7 @@ export function Tracker( }; } - function getBrowserContextPlugin() { + function getBrowserContextPlugin(){ return { contexts: () => { return [ @@ -844,7 +874,7 @@ export function Tracker( * Attaches common web fields to every request (resolution, url, referrer, etc.) * Also sets the required cookies. */ - function getBrowserDataPlugin() { + function getBrowserDataPlugin(){ const anonymizeOr = (value?: string | number | null) => (configAnonymousTracking ? null : value); const anonymizeSessionOr = (value?: string | number | null) => configAnonymousSessionTracking ? value : anonymizeOr(value); @@ -908,7 +938,7 @@ export function Tracker( const clientSession = clientSessionFromIdCookie( idCookie, configStateStorageStrategy, - configAnonymousTracking + configAnonymousTracking, ); if (configSessionContext && (!configAnonymousTracking || configAnonymousSessionTracking)) { addSessionContextToPayload(payloadBuilder, clientSession); @@ -934,7 +964,7 @@ export function Tracker( }; } - function addSessionContextToPayload(payloadBuilder: PayloadBuilder, clientSession: ClientSession) { + function addSessionContextToPayload(payloadBuilder: PayloadBuilder, clientSession: ClientSession){ let sessionContext: SelfDescribingJson = { schema: CLIENT_SESSION_SCHEMA, data: clientSession, @@ -945,7 +975,7 @@ export function Tracker( /** * Expires current session and starts a new session. */ - function newSession() { + function newSession(){ // If cookies are enabled, base visit count and session ID on the cookies let idCookie = loadDomainUserIdCookie(); @@ -990,12 +1020,12 @@ export function Tracker( */ function finalizeContexts( staticContexts?: Array | null, - contextCallback?: (() => Array) | null - ) { + contextCallback?: (() => Array) | null, + ){ return (staticContexts || []).concat(contextCallback ? contextCallback() : []); } - function logPageView({ title, context, timestamp, contextCallback }: PageViewEvent & CommonEventProperties) { + function logPageView({ title, context, timestamp, contextCallback }: PageViewEvent & CommonEventProperties){ refreshUrl(); if (lastSentPageViewId && lastSentPageViewId == getPageViewId()) { // Do not reset pageViewId if a page view was not tracked yet or a different page view ID was used (in order to support multiple trackers with shared state) @@ -1023,7 +1053,7 @@ export function Tracker( referrer: purify(customReferrer || configReferrerUrl), }), finalizeContexts(context, contextCallback), - timestamp + timestamp, ); // Send ping (to log that user has stayed on page) @@ -1036,20 +1066,22 @@ export function Tracker( // Add mousewheel event handler, detect passive event listeners for performance const detectPassiveEvents: { update: () => void; hasSupport?: boolean } = { - update: function update() { + update: function update(){ if (typeof window !== 'undefined' && typeof window.addEventListener === 'function') { let passive = false; const options = Object.defineProperty({}, 'passive', { - get: function get() { + get: function get(){ passive = true; }, - set: function set() {}, + set: function set(){ + }, }); // note: have to set and remove a no-op listener instead of null // (which was used previously), because Edge v15 throws an error // when providing a null callback. // https://github.com/rafrex/detect-passive-events/pull/3 - const noop = function noop() {}; + const noop = function noop(){ + }; window.addEventListener('testPassiveEventSupport', noop, options); window.removeEventListener('testPassiveEventSupport', noop, options); detectPassiveEvents.hasSupport = passive; @@ -1063,8 +1095,8 @@ export function Tracker( 'onwheel' in document.createElement('div') ? 'wheel' // Modern browsers support "wheel" : (document as any).onmousewheel !== undefined - ? 'mousewheel' // Webkit and IE support at least "mousewheel" - : 'DOMMouseScroll'; // let's assume that remaining browsers are older Firefox + ? 'mousewheel' // Webkit and IE support at least "mousewheel" + : 'DOMMouseScroll'; // let's assume that remaining browsers are older Firefox if (Object.prototype.hasOwnProperty.call(detectPassiveEvents, 'hasSupport')) { addEventListener(document, wheelEvent, activityHandler, { passive: true }); @@ -1091,8 +1123,8 @@ export function Tracker( const windowHandlers = ['resize', 'focus', 'blur']; const listener = (_: Document | Window, handler = activityHandler) => - (ev: string) => - addEventListener(document, ev, handler); + (ev: string) => + addEventListener(document, ev, handler); documentHandlers.forEach(listener(document)); windowHandlers.forEach(listener(window)); @@ -1119,8 +1151,8 @@ export function Tracker( function scheduleActivityInterval( config: ActivityConfig, context?: Array | null, - contextCallback?: (() => Array) | null - ) { + contextCallback?: (() => Array) | null, + ){ const executePagePing = (cb: ActivityCallback, context: Array) => { refreshUrl(); cb({ context, pageViewId: getPageViewId(), minXOffset, minYOffset, maxXOffset, maxYOffset }); @@ -1160,8 +1192,8 @@ export function Tracker( * Configure the activity tracking and ensures integer values for min visit and heartbeat */ function configureActivityTracking( - configuration: ActivityTrackingConfiguration & ActivityTrackingConfigurationCallback - ): ActivityConfig | undefined { + configuration: ActivityTrackingConfiguration & ActivityTrackingConfigurationCallback, + ): ActivityConfig | undefined{ const { minimumVisitLength, heartbeatDelay, callback } = configuration; if (isInteger(minimumVisitLength) && isInteger(heartbeatDelay)) { return { @@ -1179,7 +1211,7 @@ export function Tracker( * Log that a user is still viewing a given page by sending a page ping. * Not part of the public API - only called from logPageView() above. */ - function logPagePing({ context, minXOffset, minYOffset, maxXOffset, maxYOffset }: ActivityCallbackData) { + function logPagePing({ context, minXOffset, minYOffset, maxXOffset, maxYOffset }: ActivityCallbackData){ const newDocumentTitle = document.title; if (newDocumentTitle !== lastDocumentTitle) { lastDocumentTitle = newDocumentTitle; @@ -1195,11 +1227,11 @@ export function Tracker( minYOffset: cleanOffset(minYOffset), maxYOffset: cleanOffset(maxYOffset), }), - context + context, ); } - function disableActivityTrackingAction(actionKey: keyof ActivityConfigurations) { + function disableActivityTrackingAction(actionKey: keyof ActivityConfigurations){ const callbackConfiguration = activityTrackingConfig.configurations[actionKey]; if (callbackConfiguration?.configMinimumVisitLength === 0) { window.clearTimeout(callbackConfiguration?.activityInterval); @@ -1211,7 +1243,7 @@ export function Tracker( } const apiMethods = { - getDomainSessionIndex: function () { + getDomainSessionIndex: function(){ return memorizedVisitCount; }, @@ -1221,60 +1253,60 @@ export function Tracker( newSession, - getCookieName: function (basename: string) { + getCookieName: function(basename: string){ return getSnowplowCookieName(basename); }, - getUserId: function () { + getUserId: function(){ return businessUserId; }, - getDomainUserId: function () { + getDomainUserId: function(){ return loadDomainUserIdCookie()[1]; }, - getDomainUserInfo: function () { + getDomainUserInfo: function(){ return loadDomainUserIdCookie(); }, - setReferrerUrl: function (url: string) { + setReferrerUrl: function(url: string){ customReferrer = url; }, - setCustomUrl: function (url: string) { + setCustomUrl: function(url: string){ refreshUrl(); configCustomUrl = resolveRelativeReference(locationHrefAlias, url); }, - setDocumentTitle: function (title: string) { + setDocumentTitle: function(title: string){ // So we know what document.title was at the time of trackPageView lastDocumentTitle = document.title; lastConfigTitle = title; lastConfigTitleFromTrackPageView = false; }, - discardHashTag: function (enableFilter: boolean) { + discardHashTag: function(enableFilter: boolean){ configDiscardHashTag = enableFilter; }, - discardBrace: function (enableFilter: boolean) { + discardBrace: function(enableFilter: boolean){ configDiscardBrace = enableFilter; }, - setCookiePath: function (path: string) { + setCookiePath: function(path: string){ configCookiePath = path; updateDomainHash(); }, - setVisitorCookieTimeout: function (timeout: number) { + setVisitorCookieTimeout: function(timeout: number){ configVisitorCookieTimeout = timeout; }, - crossDomainLinker: function (crossDomainLinkerCriterion: (elt: HTMLAnchorElement | HTMLAreaElement) => boolean) { + crossDomainLinker: function(crossDomainLinkerCriterion: (elt: HTMLAnchorElement | HTMLAreaElement) => boolean){ decorateLinks(crossDomainLinkerCriterion); }, - enableActivityTracking: function (configuration: ActivityTrackingConfiguration) { + enableActivityTracking: function(configuration: ActivityTrackingConfiguration){ if (!activityTrackingConfig.configurations.pagePing) { activityTrackingConfig.enabled = true; activityTrackingConfig.configurations.pagePing = configureActivityTracking({ @@ -1284,77 +1316,77 @@ export function Tracker( } }, - enableActivityTrackingCallback: function ( - configuration: ActivityTrackingConfiguration & ActivityTrackingConfigurationCallback - ) { + enableActivityTrackingCallback: function( + configuration: ActivityTrackingConfiguration & ActivityTrackingConfigurationCallback, + ){ if (!activityTrackingConfig.configurations.callback) { activityTrackingConfig.enabled = true; activityTrackingConfig.configurations.callback = configureActivityTracking(configuration); } }, - disableActivityTracking: function () { + disableActivityTracking: function(){ disableActivityTrackingAction('pagePing'); }, - disableActivityTrackingCallback: function () { + disableActivityTrackingCallback: function(){ disableActivityTrackingAction('callback'); }, - updatePageActivity: function () { + updatePageActivity: function(){ activityHandler(); }, - setOptOutCookie: function (name?: string | null) { + setOptOutCookie: function(name?: string | null){ configOptOutCookie = name; }, - setUserId: function (userId?: string | null) { + setUserId: function(userId?: string | null){ businessUserId = userId; }, - setUserIdFromLocation: function (querystringField: string) { + setUserIdFromLocation: function(querystringField: string){ refreshUrl(); businessUserId = fromQuerystring(querystringField, locationHrefAlias); }, - setUserIdFromReferrer: function (querystringField: string) { + setUserIdFromReferrer: function(querystringField: string){ refreshUrl(); businessUserId = fromQuerystring(querystringField, configReferrerUrl); }, - setUserIdFromCookie: function (cookieName: string) { + setUserIdFromCookie: function(cookieName: string){ businessUserId = cookieStorage.getCookie(cookieName); }, - setCollectorUrl: function (collectorUrl: string) { + setCollectorUrl: function(collectorUrl: string){ outQueue.setCollectorUrl(asCollectorUrl(collectorUrl)); }, - setBufferSize: function (newBufferSize: number) { + setBufferSize: function(newBufferSize: number){ outQueue.setBufferSize(newBufferSize); }, - flushBuffer: function (configuration: FlushBufferConfiguration = {}) { + flushBuffer: function(configuration: FlushBufferConfiguration = {}){ outQueue.executeQueue(); if (configuration.newBufferSize) { outQueue.setBufferSize(configuration.newBufferSize); } }, - trackPageView: function (event: PageViewEvent & CommonEventProperties = {}) { + trackPageView: function(event: PageViewEvent & CommonEventProperties = {}){ logPageView(event); }, - preservePageViewId: function () { + preservePageViewId: function(){ preservePageViewId = true; }, - preservePageViewIdForUrl: function (preserve: PreservePageViewIdForUrl) { + preservePageViewIdForUrl: function(preserve: PreservePageViewIdForUrl){ preservePageViewIdForUrl = preserve; }, - disableAnonymousTracking: function (configuration?: DisableAnonymousTrackingConfiguration) { + disableAnonymousTracking: function(configuration?: DisableAnonymousTrackingConfiguration){ trackerConfiguration.anonymousTracking = false; toggleAnonymousTracking(configuration); @@ -1364,7 +1396,7 @@ export function Tracker( outQueue.executeQueue(); // There might be some events in the queue we've been unable to send in anonymous mode }, - enableAnonymousTracking: function (configuration?: EnableAnonymousTrackingConfiguration) { + enableAnonymousTracking: function(configuration?: EnableAnonymousTrackingConfiguration){ trackerConfiguration.anonymousTracking = (configuration && configuration?.options) ?? true; toggleAnonymousTracking(configuration); From 9f4632b4eb20f9b348c0cd1612cbd83595fd2b33 Mon Sep 17 00:00:00 2001 From: dsinitsyn Date: Wed, 21 Jan 2026 17:18:35 +0100 Subject: [PATCH 17/19] Mega large fix, review CAREFULLY! --- libraries/browser-tracker-core/src/tracker/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/browser-tracker-core/src/tracker/index.ts b/libraries/browser-tracker-core/src/tracker/index.ts index de23b8d15..6dce5716c 100755 --- a/libraries/browser-tracker-core/src/tracker/index.ts +++ b/libraries/browser-tracker-core/src/tracker/index.ts @@ -485,7 +485,7 @@ export function Tracker( // configurations where each page starts with no consent, then "updates" availability // as third-party consent management widgets load // fallback to localstorage if cookie doesn't exist - return cookieStorage.getCookie(fullName) ?? attemptGetLocalStorage(fullName); + return cookieStorage.getCookie(fullName) || attemptGetLocalStorage(fullName); } /* From 47c9fc5fd26f659b55a023952c09b3a568060433 Mon Sep 17 00:00:00 2001 From: dsinitsyn Date: Wed, 21 Jan 2026 17:25:18 +0100 Subject: [PATCH 18/19] spaces markup fix --- .../browser-tracker-core/src/tracker/index.ts | 216 ++++++++---------- 1 file changed, 92 insertions(+), 124 deletions(-) diff --git a/libraries/browser-tracker-core/src/tracker/index.ts b/libraries/browser-tracker-core/src/tracker/index.ts index 6dce5716c..d526cbbe5 100755 --- a/libraries/browser-tracker-core/src/tracker/index.ts +++ b/libraries/browser-tracker-core/src/tracker/index.ts @@ -72,7 +72,6 @@ declare global { interface Navigator { msDoNotTrack: boolean; } - interface Window { doNotTrack: boolean; } @@ -124,8 +123,8 @@ export function Tracker( version: string, endpoint: string, sharedState: SharedState, - trackerConfiguration: TrackerConfiguration = {}, -): BrowserTracker{ + trackerConfiguration: TrackerConfiguration = {} +): BrowserTracker { const browserPlugins: Array = []; const newTracker = ( @@ -134,7 +133,7 @@ export function Tracker( version: string, endpoint: string, state: SharedState, - trackerConfiguration: TrackerConfiguration, + trackerConfiguration: TrackerConfiguration ) => { /************************************************************ * Private members @@ -282,7 +281,7 @@ export function Tracker( configStateStorageStrategy == 'localStorage' || configStateStorageStrategy == 'cookieAndLocalStorage', ...trackerConfiguration, }, - state, + state ), // Whether pageViewId should be regenerated after each trackPageView. Affect web_page context preservePageViewId = false, @@ -301,7 +300,7 @@ export function Tracker( onSessionUpdateCallback = trackerConfiguration.onSessionUpdateCallback, manualSessionUpdateCalled = false, { useExtendedCrossDomainLinker, collectCrossDomainAttributes } = getExtendedCrossDomainTrackingConfiguration( - trackerConfiguration.useExtendedCrossDomainLinker || false, + trackerConfiguration.useExtendedCrossDomainLinker || false ); if (discoverRootDomain && !configCookieDomain) { @@ -337,7 +336,7 @@ export function Tracker( /** * Recalculate the domain, URL, and referrer */ - function refreshUrl(){ + function refreshUrl() { locationArray = fixupUrl(window.location.hostname, window.location.href, getReferrer()); // If this is a single-page app and the page URL has changed, then: @@ -356,7 +355,7 @@ export function Tracker( * * @param event - e The event targeting the link */ - function addLinkDecorationHandler(extended: boolean): (evt: Event) => void{ + function addLinkDecorationHandler(extended: boolean): (evt: Event) => void { const CROSS_DOMAIN_PARAMETER_NAME = '_sp'; return (evt) => { @@ -384,7 +383,7 @@ export function Tracker( * * @param crossDomainLinker - Function used to determine which links to decorate */ - function decorateLinks(crossDomainLinker: (elt: HTMLAnchorElement | HTMLAreaElement) => boolean){ + function decorateLinks(crossDomainLinker: (elt: HTMLAnchorElement | HTMLAreaElement) => boolean) { const crossDomainLinkHandler = addLinkDecorationHandler(useExtendedCrossDomainLinker); for (let i = 0; i < document.links.length; i++) { const elt = document.links[i]; @@ -404,7 +403,7 @@ export function Tracker( * URLs are purified before being recorded in the cookie, * or before being sent as GET parameters */ - function purify(url: string){ + function purify(url: string) { let targetPattern; if (configDiscardHashTag) { @@ -422,7 +421,7 @@ export function Tracker( /* * Extract scheme/protocol from URL */ - function getProtocolScheme(url: string){ + function getProtocolScheme(url: string) { const e = new RegExp('^([a-z]+):'), matches = e.exec(url); @@ -434,7 +433,7 @@ export function Tracker( * * Note: not as described in rfc3986 section 5.2 */ - function resolveRelativeReference(baseUrl: string, url: string){ + function resolveRelativeReference(baseUrl: string, url: string) { let protocol = getProtocolScheme(url), i; @@ -460,7 +459,7 @@ export function Tracker( /* * Send request */ - function sendRequest(request: PayloadBuilder){ + function sendRequest(request: PayloadBuilder) { if (!(configDoNotTrack || toOptoutByCookie)) { outQueue.enqueueRequest(request.build()); } @@ -469,14 +468,14 @@ export function Tracker( /* * Get cookie name with prefix and domain hash */ - function getSnowplowCookieName(baseName: string){ + function getSnowplowCookieName(baseName: string) { return configCookieNamePrefix + baseName + '.' + domainHash; } /* * Cookie getter. */ - function getSnowplowCookieValue(cookieName: string){ + function getSnowplowCookieValue(cookieName: string) { const fullName = getSnowplowCookieName(cookieName); if (configStateStorageStrategy == 'localStorage') { return attemptGetLocalStorage(fullName); @@ -491,7 +490,7 @@ export function Tracker( /* * Update domain hash */ - function updateDomainHash(){ + function updateDomainHash() { refreshUrl(); domainHash = hash((configCookieDomain || domainAlias) + (configCookiePath || '/')).slice(0, 4); // 4 hexits = 16 bits } @@ -500,7 +499,7 @@ export function Tracker( * Process all "activity" events. * For performance, this function must have low overhead. */ - function activityHandler(){ + function activityHandler() { const now = new Date(); lastActivityTime = now.getTime(); } @@ -508,7 +507,7 @@ export function Tracker( /* * Process all "scroll" events. */ - function scrollHandler(){ + function scrollHandler() { updateMaxScrolls(); activityHandler(); } @@ -516,7 +515,7 @@ export function Tracker( /* * Returns [pageXOffset, pageYOffset] */ - function getPageOffsets(){ + function getPageOffsets() { const documentElement = document.documentElement; if (documentElement) { return [documentElement.scrollLeft || window.pageXOffset, documentElement.scrollTop || window.pageYOffset]; @@ -528,7 +527,7 @@ export function Tracker( /* * Quick initialization/reset of max scroll levels */ - function resetMaxScrolls(){ + function resetMaxScrolls() { const offsets = getPageOffsets(); const x = offsets[0]; @@ -543,7 +542,7 @@ export function Tracker( /* * Check the max scroll levels, updating as necessary */ - function updateMaxScrolls(){ + function updateMaxScrolls() { const offsets = getPageOffsets(); const x = offsets[0]; @@ -565,7 +564,7 @@ export function Tracker( * Prevents offsets from being decimal or NaN * See https://github.com/snowplow/snowplow-javascript-tracker/issues/324 */ - function cleanOffset(offset: number){ + function cleanOffset(offset: number) { return Math.round(offset); } @@ -574,7 +573,7 @@ export function Tracker( * Responsible for calling the `onSessionUpdateCallback` callback. * @returns {boolean} If the value persisted in cookies or LocalStorage */ - function setSessionCookie(){ + function setSessionCookie() { const cookieName = getSnowplowCookieName('ses'); const cookieValue = '*'; return persistValue(cookieName, cookieValue, configSessionCookieTimeout); @@ -585,7 +584,7 @@ export function Tracker( * @param {ParsedIdCookie} idCookie * @returns {boolean} If the value persisted in cookies or LocalStorage */ - function setDomainUserIdCookie(idCookie: ParsedIdCookie){ + function setDomainUserIdCookie(idCookie: ParsedIdCookie) { const cookieName = getSnowplowCookieName('id'); const cookieValue = serializeIdCookie(idCookie, configAnonymousTracking); return persistValue(cookieName, cookieValue, configVisitorCookieTimeout); @@ -602,7 +601,7 @@ export function Tracker( * @param {number} timeout Used as the expiration date for cookies or as a TTL to be checked on LocalStorage * @returns {boolean} If the operation was successful or not */ - function persistValue(name: string, value: string, timeout: number): boolean{ + function persistValue(name: string, value: string, timeout: number): boolean { if (configAnonymousTracking && !configAnonymousSessionTracking) { return false; } @@ -619,7 +618,7 @@ export function Tracker( configCookiePath, configCookieDomain, configCookieSameSite, - configCookieSecure, + configCookieSecure ); } return false; @@ -628,7 +627,7 @@ export function Tracker( /** * Clears all cookie and local storage for id and ses values */ - function clearUserDataAndCookies(configuration?: ClearUserDataConfiguration){ + function clearUserDataAndCookies(configuration?: ClearUserDataConfiguration) { const idname = getSnowplowCookieName('id'); const sesname = getSnowplowCookieName('ses'); attemptDeleteLocalStorage(idname); @@ -638,14 +637,14 @@ export function Tracker( configCookiePath, configCookieDomain, configCookieSameSite, - configCookieSecure, + configCookieSecure ); cookieStorage.deleteCookie( sesname, configCookiePath, configCookieDomain, configCookieSameSite, - configCookieSecure, + configCookieSecure ); if (!configuration?.preserveSession) { memorizedSessionId = uuid(); @@ -661,8 +660,8 @@ export function Tracker( * Toggle Anonymous Tracking */ function toggleAnonymousTracking( - configuration?: EnableAnonymousTrackingConfiguration | DisableAnonymousTrackingConfiguration, - ){ + configuration?: EnableAnonymousTrackingConfiguration | DisableAnonymousTrackingConfiguration + ) { if (configuration && configuration.stateStorageStrategy) { trackerConfiguration.stateStorageStrategy = configuration.stateStorageStrategy; configStateStorageStrategy = getStateStorageStrategy(trackerConfiguration); @@ -673,7 +672,7 @@ export function Tracker( configAnonymousServerTracking = getAnonymousServerTracking(trackerConfiguration); outQueue.setUseLocalStorage( - configStateStorageStrategy == 'localStorage' || configStateStorageStrategy == 'cookieAndLocalStorage', + configStateStorageStrategy == 'localStorage' || configStateStorageStrategy == 'cookieAndLocalStorage' ); outQueue.setAnonymousTracking(configAnonymousServerTracking); } @@ -682,7 +681,7 @@ export function Tracker( * Load the domain user ID and the session ID * Set the cookies (if cookies are enabled) */ - function initializeIdsAndCookies(){ + function initializeIdsAndCookies() { if (configAnonymousTracking && !configAnonymousSessionTracking) { return; } @@ -711,41 +710,12 @@ export function Tracker( /* * Load visitor ID cookie */ - function loadDomainUserIdCookie(){ + function loadDomainUserIdCookie() { // KEVIN TILLER - Removing this so we can have in-memory domainuserid when cookies are disallowed. // if (configStateStorageStrategy == 'none') { // return emptyIdCookie(); // } - - // Get the full cookie name with prefix and domain hash - const fullName = getSnowplowCookieName('id'); - - // Try to get ID cookie value (first from cookie, then from localStorage as fallback) const id = getSnowplowCookieValue('id') || undefined; - - // DIMA SINITSYN - Restore user ID from localStorage to cookie when cookie is missing - // Problem: When cookie is deleted (by user or browser) but value still exists in localStorage, - // the system was creating a new ID instead of restoring the existing one from localStorage. - // This causes loss of user tracking continuity and creates duplicate users in analytics. - // Solution: If cookie is missing but localStorage has a value, restore it back to cookie - // to maintain user identity across sessions and prevent tracking loss. - if (!id && configStateStorageStrategy !== 'none' && configStateStorageStrategy !== 'localStorage') { - const localStorageValue = attemptGetLocalStorage(fullName); - - // If localStorage has a saved ID value - if (localStorageValue) { - // Parse the existing ID from localStorage - const restoredCookie = parseIdCookie(localStorageValue, domainUserId, memorizedSessionId, memorizedVisitCount); - - // Restore the cookie from localStorage to maintain user tracking continuity - // This is critical for preserving user identity and session history - setDomainUserIdCookie(restoredCookie); - - return restoredCookie; - } - } - - // Parse the cookie (if exists) or create new one (if neither cookie nor localStorage exists) return parseIdCookie(id, domainUserId, memorizedSessionId, memorizedVisitCount); } @@ -755,7 +725,7 @@ export function Tracker( * @param string - collectorUrl The collector URL with or without protocol * @returns string collectorUrl The tracker URL with protocol */ - function asCollectorUrl(collectorUrl: string){ + function asCollectorUrl(collectorUrl: string) { if (collectorUrl.indexOf('http') === 0) { return collectorUrl; } @@ -767,7 +737,7 @@ export function Tracker( * Initialize new `pageViewId` if it shouldn't be preserved. * Should be called when `trackPageView` is invoked */ - function resetPageView(){ + function resetPageView() { if (!preservePageViewId || state.pageViewId == null) { state.pageViewId = uuid(); state.pageViewUrl = configCustomUrl || locationHrefAlias; @@ -778,7 +748,7 @@ export function Tracker( * Safe function to get `pageViewId`. * Generates it if it wasn't initialized by other tracker */ - function getPageViewId(){ + function getPageViewId() { if (shouldGenerateNewPageViewId()) { state.pageViewId = uuid(); state.pageViewUrl = configCustomUrl || locationHrefAlias; @@ -786,7 +756,7 @@ export function Tracker( return state.pageViewId!; } - function shouldGenerateNewPageViewId(){ + function shouldGenerateNewPageViewId() { // If pageViewId is not initialized, generate it if (state.pageViewId == null) { return true; @@ -821,7 +791,7 @@ export function Tracker( * Safe function to get `tabId`. * Generates it if it is not yet initialized. Shared between trackers. */ - function getTabId(){ + function getTabId() { if (configStateStorageStrategy === 'none' || configAnonymousTracking || !isWebPageContextAvailable) { return null; } @@ -839,7 +809,7 @@ export function Tracker( * * @returns web_page context */ - function getWebPagePlugin(){ + function getWebPagePlugin() { return { contexts: () => { return [ @@ -854,7 +824,7 @@ export function Tracker( }; } - function getBrowserContextPlugin(){ + function getBrowserContextPlugin() { return { contexts: () => { return [ @@ -874,7 +844,7 @@ export function Tracker( * Attaches common web fields to every request (resolution, url, referrer, etc.) * Also sets the required cookies. */ - function getBrowserDataPlugin(){ + function getBrowserDataPlugin() { const anonymizeOr = (value?: string | number | null) => (configAnonymousTracking ? null : value); const anonymizeSessionOr = (value?: string | number | null) => configAnonymousSessionTracking ? value : anonymizeOr(value); @@ -938,7 +908,7 @@ export function Tracker( const clientSession = clientSessionFromIdCookie( idCookie, configStateStorageStrategy, - configAnonymousTracking, + configAnonymousTracking ); if (configSessionContext && (!configAnonymousTracking || configAnonymousSessionTracking)) { addSessionContextToPayload(payloadBuilder, clientSession); @@ -964,7 +934,7 @@ export function Tracker( }; } - function addSessionContextToPayload(payloadBuilder: PayloadBuilder, clientSession: ClientSession){ + function addSessionContextToPayload(payloadBuilder: PayloadBuilder, clientSession: ClientSession) { let sessionContext: SelfDescribingJson = { schema: CLIENT_SESSION_SCHEMA, data: clientSession, @@ -975,7 +945,7 @@ export function Tracker( /** * Expires current session and starts a new session. */ - function newSession(){ + function newSession() { // If cookies are enabled, base visit count and session ID on the cookies let idCookie = loadDomainUserIdCookie(); @@ -1020,12 +990,12 @@ export function Tracker( */ function finalizeContexts( staticContexts?: Array | null, - contextCallback?: (() => Array) | null, - ){ + contextCallback?: (() => Array) | null + ) { return (staticContexts || []).concat(contextCallback ? contextCallback() : []); } - function logPageView({ title, context, timestamp, contextCallback }: PageViewEvent & CommonEventProperties){ + function logPageView({ title, context, timestamp, contextCallback }: PageViewEvent & CommonEventProperties) { refreshUrl(); if (lastSentPageViewId && lastSentPageViewId == getPageViewId()) { // Do not reset pageViewId if a page view was not tracked yet or a different page view ID was used (in order to support multiple trackers with shared state) @@ -1053,7 +1023,7 @@ export function Tracker( referrer: purify(customReferrer || configReferrerUrl), }), finalizeContexts(context, contextCallback), - timestamp, + timestamp ); // Send ping (to log that user has stayed on page) @@ -1066,22 +1036,20 @@ export function Tracker( // Add mousewheel event handler, detect passive event listeners for performance const detectPassiveEvents: { update: () => void; hasSupport?: boolean } = { - update: function update(){ + update: function update() { if (typeof window !== 'undefined' && typeof window.addEventListener === 'function') { let passive = false; const options = Object.defineProperty({}, 'passive', { - get: function get(){ + get: function get() { passive = true; }, - set: function set(){ - }, + set: function set() {}, }); // note: have to set and remove a no-op listener instead of null // (which was used previously), because Edge v15 throws an error // when providing a null callback. // https://github.com/rafrex/detect-passive-events/pull/3 - const noop = function noop(){ - }; + const noop = function noop() {}; window.addEventListener('testPassiveEventSupport', noop, options); window.removeEventListener('testPassiveEventSupport', noop, options); detectPassiveEvents.hasSupport = passive; @@ -1151,8 +1119,8 @@ export function Tracker( function scheduleActivityInterval( config: ActivityConfig, context?: Array | null, - contextCallback?: (() => Array) | null, - ){ + contextCallback?: (() => Array) | null + ) { const executePagePing = (cb: ActivityCallback, context: Array) => { refreshUrl(); cb({ context, pageViewId: getPageViewId(), minXOffset, minYOffset, maxXOffset, maxYOffset }); @@ -1192,8 +1160,8 @@ export function Tracker( * Configure the activity tracking and ensures integer values for min visit and heartbeat */ function configureActivityTracking( - configuration: ActivityTrackingConfiguration & ActivityTrackingConfigurationCallback, - ): ActivityConfig | undefined{ + configuration: ActivityTrackingConfiguration & ActivityTrackingConfigurationCallback + ): ActivityConfig | undefined { const { minimumVisitLength, heartbeatDelay, callback } = configuration; if (isInteger(minimumVisitLength) && isInteger(heartbeatDelay)) { return { @@ -1211,7 +1179,7 @@ export function Tracker( * Log that a user is still viewing a given page by sending a page ping. * Not part of the public API - only called from logPageView() above. */ - function logPagePing({ context, minXOffset, minYOffset, maxXOffset, maxYOffset }: ActivityCallbackData){ + function logPagePing({ context, minXOffset, minYOffset, maxXOffset, maxYOffset }: ActivityCallbackData) { const newDocumentTitle = document.title; if (newDocumentTitle !== lastDocumentTitle) { lastDocumentTitle = newDocumentTitle; @@ -1227,11 +1195,11 @@ export function Tracker( minYOffset: cleanOffset(minYOffset), maxYOffset: cleanOffset(maxYOffset), }), - context, + context ); } - function disableActivityTrackingAction(actionKey: keyof ActivityConfigurations){ + function disableActivityTrackingAction(actionKey: keyof ActivityConfigurations) { const callbackConfiguration = activityTrackingConfig.configurations[actionKey]; if (callbackConfiguration?.configMinimumVisitLength === 0) { window.clearTimeout(callbackConfiguration?.activityInterval); @@ -1243,7 +1211,7 @@ export function Tracker( } const apiMethods = { - getDomainSessionIndex: function(){ + getDomainSessionIndex: function () { return memorizedVisitCount; }, @@ -1253,60 +1221,60 @@ export function Tracker( newSession, - getCookieName: function(basename: string){ + getCookieName: function (basename: string) { return getSnowplowCookieName(basename); }, - getUserId: function(){ + getUserId: function () { return businessUserId; }, - getDomainUserId: function(){ + getDomainUserId: function () { return loadDomainUserIdCookie()[1]; }, - getDomainUserInfo: function(){ + getDomainUserInfo: function () { return loadDomainUserIdCookie(); }, - setReferrerUrl: function(url: string){ + setReferrerUrl: function (url: string) { customReferrer = url; }, - setCustomUrl: function(url: string){ + setCustomUrl: function (url: string) { refreshUrl(); configCustomUrl = resolveRelativeReference(locationHrefAlias, url); }, - setDocumentTitle: function(title: string){ + setDocumentTitle: function (title: string) { // So we know what document.title was at the time of trackPageView lastDocumentTitle = document.title; lastConfigTitle = title; lastConfigTitleFromTrackPageView = false; }, - discardHashTag: function(enableFilter: boolean){ + discardHashTag: function (enableFilter: boolean) { configDiscardHashTag = enableFilter; }, - discardBrace: function(enableFilter: boolean){ + discardBrace: function (enableFilter: boolean) { configDiscardBrace = enableFilter; }, - setCookiePath: function(path: string){ + setCookiePath: function (path: string) { configCookiePath = path; updateDomainHash(); }, - setVisitorCookieTimeout: function(timeout: number){ + setVisitorCookieTimeout: function (timeout: number) { configVisitorCookieTimeout = timeout; }, - crossDomainLinker: function(crossDomainLinkerCriterion: (elt: HTMLAnchorElement | HTMLAreaElement) => boolean){ + crossDomainLinker: function (crossDomainLinkerCriterion: (elt: HTMLAnchorElement | HTMLAreaElement) => boolean) { decorateLinks(crossDomainLinkerCriterion); }, - enableActivityTracking: function(configuration: ActivityTrackingConfiguration){ + enableActivityTracking: function (configuration: ActivityTrackingConfiguration) { if (!activityTrackingConfig.configurations.pagePing) { activityTrackingConfig.enabled = true; activityTrackingConfig.configurations.pagePing = configureActivityTracking({ @@ -1316,77 +1284,77 @@ export function Tracker( } }, - enableActivityTrackingCallback: function( - configuration: ActivityTrackingConfiguration & ActivityTrackingConfigurationCallback, - ){ + enableActivityTrackingCallback: function ( + configuration: ActivityTrackingConfiguration & ActivityTrackingConfigurationCallback + ) { if (!activityTrackingConfig.configurations.callback) { activityTrackingConfig.enabled = true; activityTrackingConfig.configurations.callback = configureActivityTracking(configuration); } }, - disableActivityTracking: function(){ + disableActivityTracking: function () { disableActivityTrackingAction('pagePing'); }, - disableActivityTrackingCallback: function(){ + disableActivityTrackingCallback: function () { disableActivityTrackingAction('callback'); }, - updatePageActivity: function(){ + updatePageActivity: function () { activityHandler(); }, - setOptOutCookie: function(name?: string | null){ + setOptOutCookie: function (name?: string | null) { configOptOutCookie = name; }, - setUserId: function(userId?: string | null){ + setUserId: function (userId?: string | null) { businessUserId = userId; }, - setUserIdFromLocation: function(querystringField: string){ + setUserIdFromLocation: function (querystringField: string) { refreshUrl(); businessUserId = fromQuerystring(querystringField, locationHrefAlias); }, - setUserIdFromReferrer: function(querystringField: string){ + setUserIdFromReferrer: function (querystringField: string) { refreshUrl(); businessUserId = fromQuerystring(querystringField, configReferrerUrl); }, - setUserIdFromCookie: function(cookieName: string){ + setUserIdFromCookie: function (cookieName: string) { businessUserId = cookieStorage.getCookie(cookieName); }, - setCollectorUrl: function(collectorUrl: string){ + setCollectorUrl: function (collectorUrl: string) { outQueue.setCollectorUrl(asCollectorUrl(collectorUrl)); }, - setBufferSize: function(newBufferSize: number){ + setBufferSize: function (newBufferSize: number) { outQueue.setBufferSize(newBufferSize); }, - flushBuffer: function(configuration: FlushBufferConfiguration = {}){ + flushBuffer: function (configuration: FlushBufferConfiguration = {}) { outQueue.executeQueue(); if (configuration.newBufferSize) { outQueue.setBufferSize(configuration.newBufferSize); } }, - trackPageView: function(event: PageViewEvent & CommonEventProperties = {}){ + trackPageView: function (event: PageViewEvent & CommonEventProperties = {}) { logPageView(event); }, - preservePageViewId: function(){ + preservePageViewId: function () { preservePageViewId = true; }, - preservePageViewIdForUrl: function(preserve: PreservePageViewIdForUrl){ + preservePageViewIdForUrl: function (preserve: PreservePageViewIdForUrl) { preservePageViewIdForUrl = preserve; }, - disableAnonymousTracking: function(configuration?: DisableAnonymousTrackingConfiguration){ + disableAnonymousTracking: function (configuration?: DisableAnonymousTrackingConfiguration) { trackerConfiguration.anonymousTracking = false; toggleAnonymousTracking(configuration); @@ -1396,7 +1364,7 @@ export function Tracker( outQueue.executeQueue(); // There might be some events in the queue we've been unable to send in anonymous mode }, - enableAnonymousTracking: function(configuration?: EnableAnonymousTrackingConfiguration){ + enableAnonymousTracking: function (configuration?: EnableAnonymousTrackingConfiguration) { trackerConfiguration.anonymousTracking = (configuration && configuration?.options) ?? true; toggleAnonymousTracking(configuration); From e624cad3d3f53777d12a372613f4b64d20af8e2f Mon Sep 17 00:00:00 2001 From: dsinitsyn Date: Wed, 21 Jan 2026 17:27:13 +0100 Subject: [PATCH 19/19] spaces markup fix --- libraries/browser-tracker-core/src/tracker/index.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/browser-tracker-core/src/tracker/index.ts b/libraries/browser-tracker-core/src/tracker/index.ts index d526cbbe5..dae0c3011 100755 --- a/libraries/browser-tracker-core/src/tracker/index.ts +++ b/libraries/browser-tracker-core/src/tracker/index.ts @@ -1063,8 +1063,8 @@ export function Tracker( 'onwheel' in document.createElement('div') ? 'wheel' // Modern browsers support "wheel" : (document as any).onmousewheel !== undefined - ? 'mousewheel' // Webkit and IE support at least "mousewheel" - : 'DOMMouseScroll'; // let's assume that remaining browsers are older Firefox + ? 'mousewheel' // Webkit and IE support at least "mousewheel" + : 'DOMMouseScroll'; // let's assume that remaining browsers are older Firefox if (Object.prototype.hasOwnProperty.call(detectPassiveEvents, 'hasSupport')) { addEventListener(document, wheelEvent, activityHandler, { passive: true }); @@ -1091,8 +1091,8 @@ export function Tracker( const windowHandlers = ['resize', 'focus', 'blur']; const listener = (_: Document | Window, handler = activityHandler) => - (ev: string) => - addEventListener(document, ev, handler); + (ev: string) => + addEventListener(document, ev, handler); documentHandlers.forEach(listener(document)); windowHandlers.forEach(listener(window));