From 2bcb587d2f578425f907e83ad01187b0a517ead7 Mon Sep 17 00:00:00 2001
From: Peter Perlepes
Date: Fri, 12 Jan 2024 13:52:09 +0200
Subject: [PATCH] Extend cross-domain linking with more user/session
information and optional configuration
#1278
---
.bundlemonrc.json | 8 +-
.../browser-tracker/browser-tracker.api.md | 13 +
...omain-linking-entity_2023-08-28-10-46.json | 10 +
...omain-linking-entity_2023-08-28-10-46.json | 10 +
.../rush/browser-approved-packages.json | 4 +
common/config/rush/pnpm-lock.yaml | 369 ++++++++++++++++++
common/config/rush/repo-state.json | 2 +-
libraries/browser-tracker-core/package.json | 3 +-
.../src/helpers/cross_domain.ts | 72 ++++
.../browser-tracker-core/src/helpers/index.ts | 1 +
.../browser-tracker-core/src/tracker/index.ts | 83 ++--
.../browser-tracker-core/src/tracker/types.ts | 19 +
.../test/tracker/cross_domain.test.ts | 147 +++++++
trackers/browser-tracker/src/api.ts | 6 +-
14 files changed, 698 insertions(+), 49 deletions(-)
create mode 100644 common/changes/@snowplow/browser-tracker-core/feature-extend-cross-domain-linking-entity_2023-08-28-10-46.json
create mode 100644 common/changes/@snowplow/browser-tracker/feature-extend-cross-domain-linking-entity_2023-08-28-10-46.json
create mode 100644 libraries/browser-tracker-core/src/helpers/cross_domain.ts
create mode 100644 libraries/browser-tracker-core/test/tracker/cross_domain.test.ts
diff --git a/.bundlemonrc.json b/.bundlemonrc.json
index 502cd2cd3..466289b96 100644
--- a/.bundlemonrc.json
+++ b/.bundlemonrc.json
@@ -7,22 +7,22 @@
},
{
"path": "./trackers/browser-tracker/dist/index.umd.min.js",
- "maxSize": "15kb",
+ "maxSize": "15.5kb",
"maxPercentIncrease": 10
},
{
"path": "./trackers/javascript-tracker/dist/sp.js",
- "maxSize": "25kb",
+ "maxSize": "25.5kb",
"maxPercentIncrease": 10
},
{
"path": "./trackers/javascript-tracker/dist/sp.lite.js",
- "maxSize": "15.5kb",
+ "maxSize": "16kb",
"maxPercentIncrease": 10
},
{
"path": "./libraries/browser-tracker-core/dist/index.module.js",
- "maxSize": "26kb",
+ "maxSize": "27kb",
"maxPercentIncrease": 10
},
{
diff --git a/api-docs/docs/browser-tracker/browser-tracker.api.md b/api-docs/docs/browser-tracker/browser-tracker.api.md
index a449cbe07..2936496d3 100644
--- a/api-docs/docs/browser-tracker/browser-tracker.api.md
+++ b/api-docs/docs/browser-tracker/browser-tracker.api.md
@@ -221,6 +221,18 @@ export type EventBatch = GetBatch | PostBatch;
// @public (undocumented)
export type EventMethod = "post" | "get" | "beacon";
+// @public (undocumented)
+export type ExtendedCrossDomainLinkerAttributes = {
+ userId?: boolean;
+ sessionId?: boolean;
+ sourceId?: boolean;
+ sourcePlatform?: boolean;
+ reason?: boolean | ((evt: Event) => string);
+};
+
+// @public (undocumented)
+export type ExtendedCrossDomainLinkerOptions = boolean | ExtendedCrossDomainLinkerAttributes;
+
// @public
export type FilterProvider = [
ContextFilter,
@@ -371,6 +383,7 @@ export type TrackerConfiguration = {
useStm?: boolean;
bufferSize?: number;
crossDomainLinker?: (elt: HTMLAnchorElement | HTMLAreaElement) => boolean;
+ useExtendedCrossDomainLinker?: ExtendedCrossDomainLinkerOptions;
maxPostBytes?: number;
maxGetBytes?: number;
discoverRootDomain?: boolean;
diff --git a/common/changes/@snowplow/browser-tracker-core/feature-extend-cross-domain-linking-entity_2023-08-28-10-46.json b/common/changes/@snowplow/browser-tracker-core/feature-extend-cross-domain-linking-entity_2023-08-28-10-46.json
new file mode 100644
index 000000000..cf3ab3d98
--- /dev/null
+++ b/common/changes/@snowplow/browser-tracker-core/feature-extend-cross-domain-linking-entity_2023-08-28-10-46.json
@@ -0,0 +1,10 @@
+{
+ "changes": [
+ {
+ "packageName": "@snowplow/browser-tracker-core",
+ "comment": "Allow for extended cross domain linking information using the useExtendedCrossDomainLinker option",
+ "type": "none"
+ }
+ ],
+ "packageName": "@snowplow/browser-tracker-core"
+}
\ No newline at end of file
diff --git a/common/changes/@snowplow/browser-tracker/feature-extend-cross-domain-linking-entity_2023-08-28-10-46.json b/common/changes/@snowplow/browser-tracker/feature-extend-cross-domain-linking-entity_2023-08-28-10-46.json
new file mode 100644
index 000000000..508727e2d
--- /dev/null
+++ b/common/changes/@snowplow/browser-tracker/feature-extend-cross-domain-linking-entity_2023-08-28-10-46.json
@@ -0,0 +1,10 @@
+{
+ "changes": [
+ {
+ "packageName": "@snowplow/browser-tracker",
+ "comment": "Add new useExtendedCrossDomainLinker option",
+ "type": "none"
+ }
+ ],
+ "packageName": "@snowplow/browser-tracker"
+}
\ No newline at end of file
diff --git a/common/config/rush/browser-approved-packages.json b/common/config/rush/browser-approved-packages.json
index 376c8af07..95e14afe7 100644
--- a/common/config/rush/browser-approved-packages.json
+++ b/common/config/rush/browser-approved-packages.json
@@ -138,6 +138,10 @@
"name": "@snowplow/tracker-core",
"allowedCategories": [ "libraries", "plugins", "trackers" ]
},
+ {
+ "name": "@testing-library/dom",
+ "allowedCategories": [ "libraries" ]
+ },
{
"name": "@types/dockerode",
"allowedCategories": [ "trackers" ]
diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml
index 1f09a82f2..b85e817fe 100644
--- a/common/config/rush/pnpm-lock.yaml
+++ b/common/config/rush/pnpm-lock.yaml
@@ -11,6 +11,7 @@ importers:
'@rollup/plugin-commonjs': ~21.0.2
'@rollup/plugin-node-resolve': ~13.1.3
'@snowplow/tracker-core': workspace:*
+ '@testing-library/dom': ~9.3.1
'@types/jest': ~27.4.1
'@types/jsdom': ~16.2.14
'@types/sha1': ~1.1.3
@@ -41,6 +42,7 @@ importers:
'@ampproject/rollup-plugin-closure-compiler': 0.27.0_rollup@2.70.1
'@rollup/plugin-commonjs': 21.0.2_rollup@2.70.1
'@rollup/plugin-node-resolve': 13.1.3_rollup@2.70.1
+ '@testing-library/dom': 9.3.4
'@types/jest': 27.4.1
'@types/jsdom': 16.2.14
'@types/sha1': 1.1.3
@@ -2846,6 +2848,20 @@ packages:
resolution: {integrity: sha512-kIhULpw9TrGYnHp/8VfdcneIcxKnLixmADtukQRtJUmsVlMg0niMkwV0xZmi8hqa57xqilIHjWFA0GKvEjVU5g==}
dev: true
+ /@testing-library/dom/9.3.4:
+ resolution: {integrity: sha512-FlS4ZWlp97iiNWig0Muq8p+3rVDjRiYE+YKGbAqXOu9nwJFFOdL00kFpz42M+4huzYi86vAK1sOOfyOG45muIQ==}
+ engines: {node: '>=14'}
+ dependencies:
+ '@babel/code-frame': 7.22.13
+ '@babel/runtime': 7.18.9
+ '@types/aria-query': 5.0.4
+ aria-query: 5.1.3
+ chalk: 4.1.2
+ dom-accessibility-api: 0.5.16
+ lz-string: 1.5.0
+ pretty-format: 27.5.1
+ dev: true
+
/@tootallnate/once/1.1.2:
resolution: {integrity: sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==}
engines: {node: '>= 6'}
@@ -2871,6 +2887,10 @@ packages:
resolution: {integrity: sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==}
dev: true
+ /@types/aria-query/5.0.4:
+ resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==}
+ dev: true
+
/@types/babel__core/7.1.18:
resolution: {integrity: sha512-S7unDjm/C7z2A2R9NzfKCK1I+BAALDtxEmsJBwlB3EzNfb929ykjL++1CK9LO++EIp2fQrC8O+BwjKvz6UeDyQ==}
dependencies:
@@ -3780,6 +3800,19 @@ packages:
engines: {node: '>=6.0'}
dev: true
+ /aria-query/5.1.3:
+ resolution: {integrity: sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==}
+ dependencies:
+ deep-equal: 2.2.3
+ dev: true
+
+ /array-buffer-byte-length/1.0.0:
+ resolution: {integrity: sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==}
+ dependencies:
+ call-bind: 1.0.5
+ is-array-buffer: 3.0.2
+ dev: true
+
/array-find-index/1.0.2:
resolution: {integrity: sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=}
engines: {node: '>=0.10.0'}
@@ -3894,6 +3927,11 @@ packages:
- supports-color
dev: true
+ /available-typed-arrays/1.0.5:
+ resolution: {integrity: sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==}
+ engines: {node: '>= 0.4'}
+ dev: true
+
/aws-sign2/0.7.0:
resolution: {integrity: sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==}
dev: true
@@ -4313,6 +4351,14 @@ packages:
get-intrinsic: 1.0.2
dev: true
+ /call-bind/1.0.5:
+ resolution: {integrity: sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==}
+ dependencies:
+ function-bind: 1.1.2
+ get-intrinsic: 1.2.2
+ set-function-length: 1.1.1
+ dev: true
+
/callsites/3.1.0:
resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
engines: {node: '>=6'}
@@ -5067,6 +5113,30 @@ packages:
resolution: {integrity: sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==}
dev: true
+ /deep-equal/2.2.3:
+ resolution: {integrity: sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ array-buffer-byte-length: 1.0.0
+ call-bind: 1.0.5
+ es-get-iterator: 1.1.3
+ get-intrinsic: 1.2.2
+ is-arguments: 1.1.1
+ is-array-buffer: 3.0.2
+ is-date-object: 1.0.5
+ is-regex: 1.1.4
+ is-shared-array-buffer: 1.0.2
+ isarray: 2.0.5
+ object-is: 1.1.5
+ object-keys: 1.1.1
+ object.assign: 4.1.5
+ regexp.prototype.flags: 1.5.1
+ side-channel: 1.0.4
+ which-boxed-primitive: 1.0.2
+ which-collection: 1.0.1
+ which-typed-array: 1.1.13
+ dev: true
+
/deep-is/0.1.3:
resolution: {integrity: sha512-GtxAN4HvBachZzm4OnWqc45ESpUCMwkYcsjnsPs23FwJbsO+k4t0k9bQCgOmzIlpHO28+WPK/KRbRk0DDHuuDw==}
dev: true
@@ -5091,6 +5161,15 @@ packages:
resolution: {integrity: sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==}
engines: {node: '>=10'}
+ /define-data-property/1.1.1:
+ resolution: {integrity: sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ get-intrinsic: 1.2.2
+ gopd: 1.0.1
+ has-property-descriptors: 1.0.1
+ dev: true
+
/define-properties/1.1.3:
resolution: {integrity: sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==}
engines: {node: '>= 0.4'}
@@ -5098,6 +5177,15 @@ packages:
object-keys: 1.1.1
dev: true
+ /define-properties/1.2.1:
+ resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ define-data-property: 1.1.1
+ has-property-descriptors: 1.0.1
+ object-keys: 1.1.1
+ dev: true
+
/degenerator/5.0.1:
resolution: {integrity: sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==}
engines: {node: '>= 14'}
@@ -5213,6 +5301,10 @@ packages:
esutils: 2.0.3
dev: true
+ /dom-accessibility-api/0.5.16:
+ resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==}
+ dev: true
+
/domexception/2.0.1:
resolution: {integrity: sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==}
engines: {node: '>=8'}
@@ -5401,6 +5493,20 @@ packages:
string.prototype.trimstart: 1.0.3
dev: true
+ /es-get-iterator/1.1.3:
+ resolution: {integrity: sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==}
+ dependencies:
+ call-bind: 1.0.5
+ get-intrinsic: 1.2.2
+ has-symbols: 1.0.3
+ is-arguments: 1.1.1
+ is-map: 2.0.2
+ is-set: 2.0.2
+ is-string: 1.0.7
+ isarray: 2.0.5
+ stop-iteration-iterator: 1.0.0
+ dev: true
+
/es-to-primitive/1.2.1:
resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==}
engines: {node: '>= 0.4'}
@@ -6039,6 +6145,12 @@ packages:
optional: true
dev: true
+ /for-each/0.3.3:
+ resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==}
+ dependencies:
+ is-callable: 1.2.3
+ dev: true
+
/foreground-child/3.1.1:
resolution: {integrity: sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==}
engines: {node: '>=14'}
@@ -6171,10 +6283,18 @@ packages:
resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==}
dev: true
+ /function-bind/1.1.2:
+ resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
+ dev: true
+
/functional-red-black-tree/1.0.1:
resolution: {integrity: sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=}
dev: true
+ /functions-have-names/1.2.3:
+ resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==}
+ dev: true
+
/gauge/2.7.4:
resolution: {integrity: sha512-14x4kjc6lkD3ltw589k0NrPD6cCNTD6CWoVUNpB85+DrtONoZn+Rug6xZU5RvSC4+TZPxA5AnBibQYAvZn41Hg==}
dependencies:
@@ -6231,6 +6351,15 @@ packages:
has-symbols: 1.0.1
dev: true
+ /get-intrinsic/1.2.2:
+ resolution: {integrity: sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==}
+ dependencies:
+ function-bind: 1.1.2
+ has-proto: 1.0.1
+ has-symbols: 1.0.3
+ hasown: 2.0.0
+ dev: true
+
/get-package-type/0.1.0:
resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==}
engines: {node: '>=8.0.0'}
@@ -6446,6 +6575,12 @@ packages:
google-closure-compiler-windows: 20210808.0.0
dev: true
+ /gopd/1.0.1:
+ resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==}
+ dependencies:
+ get-intrinsic: 1.2.2
+ dev: true
+
/got/11.8.5:
resolution: {integrity: sha512-o0Je4NvQObAuZPHLFoRSkdG2lTgtcynqymzg2Vupdx6PorhaT5MCbIyXG6d4D94kk8ZG57QeosgdiqfJWhEhlQ==}
engines: {node: '>=10.19.0'}
@@ -6577,6 +6712,10 @@ packages:
ansi-regex: 2.1.1
dev: true
+ /has-bigints/1.0.2:
+ resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==}
+ dev: true
+
/has-flag/3.0.0:
resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==}
engines: {node: '>=4'}
@@ -6587,6 +6726,17 @@ packages:
engines: {node: '>=8'}
dev: true
+ /has-property-descriptors/1.0.1:
+ resolution: {integrity: sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==}
+ dependencies:
+ get-intrinsic: 1.2.2
+ dev: true
+
+ /has-proto/1.0.1:
+ resolution: {integrity: sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==}
+ engines: {node: '>= 0.4'}
+ dev: true
+
/has-symbol-support-x/1.4.2:
resolution: {integrity: sha512-3ToOva++HaW+eCpgqZrCfN51IPB+7bJNVT6CUATzueB5Heb8o6Nam0V3HG5dlDvZU1Gn5QLcbahiKw/XVk5JJw==}
dev: true
@@ -6596,12 +6746,24 @@ packages:
engines: {node: '>= 0.4'}
dev: true
+ /has-symbols/1.0.3:
+ resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==}
+ engines: {node: '>= 0.4'}
+ dev: true
+
/has-to-string-tag-x/1.4.1:
resolution: {integrity: sha512-vdbKfmw+3LoOYVr+mtxHaX5a96+0f3DljYd8JOqvOLsf5mw2Otda2qCDT9qRqLAhrjyQ0h7ual5nOiASpsGNFw==}
dependencies:
has-symbol-support-x: 1.4.2
dev: true
+ /has-tostringtag/1.0.0:
+ resolution: {integrity: sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ has-symbols: 1.0.3
+ dev: true
+
/has-unicode/2.0.1:
resolution: {integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==}
dev: true
@@ -6620,6 +6782,13 @@ packages:
minimalistic-assert: 1.0.1
dev: true
+ /hasown/2.0.0:
+ resolution: {integrity: sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ function-bind: 1.1.2
+ dev: true
+
/header-case/2.0.4:
resolution: {integrity: sha512-H/vuk5TEEVZwrR0lp2zed9OCo1uAILMlx0JEMgC26rzyJJ3N1v6XkwHHXJQdR2doSjcGPM6OKPYoJgf0plJ11Q==}
dependencies:
@@ -6899,6 +7068,15 @@ packages:
wrap-ansi: 6.2.0
dev: true
+ /internal-slot/1.0.6:
+ resolution: {integrity: sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ get-intrinsic: 1.2.2
+ hasown: 2.0.0
+ side-channel: 1.0.4
+ dev: true
+
/into-stream/3.1.0:
resolution: {integrity: sha512-TcdjPibTksa1NQximqep2r17ISRiNE9fwlfbg3F8ANdvP5/yrFTew86VcO//jk4QTaMlbjypPBq76HN2zaKfZQ==}
engines: {node: '>=4'}
@@ -6930,10 +7108,32 @@ packages:
engines: {node: '>=8'}
dev: true
+ /is-arguments/1.1.1:
+ resolution: {integrity: sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ call-bind: 1.0.5
+ has-tostringtag: 1.0.0
+ dev: true
+
+ /is-array-buffer/3.0.2:
+ resolution: {integrity: sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==}
+ dependencies:
+ call-bind: 1.0.5
+ get-intrinsic: 1.2.2
+ is-typed-array: 1.1.12
+ dev: true
+
/is-arrayish/0.2.1:
resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==}
dev: true
+ /is-bigint/1.0.4:
+ resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==}
+ dependencies:
+ has-bigints: 1.0.2
+ dev: true
+
/is-binary-path/2.1.0:
resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
engines: {node: '>=8'}
@@ -6941,6 +7141,14 @@ packages:
binary-extensions: 2.1.0
dev: true
+ /is-boolean-object/1.1.2:
+ resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ call-bind: 1.0.5
+ has-tostringtag: 1.0.0
+ dev: true
+
/is-callable/1.2.3:
resolution: {integrity: sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ==}
engines: {node: '>= 0.4'}
@@ -6970,6 +7178,13 @@ packages:
engines: {node: '>= 0.4'}
dev: true
+ /is-date-object/1.0.5:
+ resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ has-tostringtag: 1.0.0
+ dev: true
+
/is-error/2.2.2:
resolution: {integrity: sha512-IOQqts/aHWbiisY5DuPJQ0gcbvaLFCa7fBa9xoLfxBZvQ+ZI/Zh9xoI7Gk+G64N0FdK4AbibytHht2tWgpJWLg==}
dev: true
@@ -7017,6 +7232,10 @@ packages:
resolution: {integrity: sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==}
dev: true
+ /is-map/2.0.2:
+ resolution: {integrity: sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==}
+ dev: true
+
/is-module/1.0.0:
resolution: {integrity: sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE=}
dev: true
@@ -7030,6 +7249,13 @@ packages:
engines: {node: '>= 0.4'}
dev: true
+ /is-number-object/1.0.7:
+ resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ has-tostringtag: 1.0.0
+ dev: true
+
/is-number/7.0.0:
resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
engines: {node: '>=0.12.0'}
@@ -7086,11 +7312,29 @@ packages:
has-symbols: 1.0.1
dev: true
+ /is-regex/1.1.4:
+ resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ call-bind: 1.0.5
+ has-tostringtag: 1.0.0
+ dev: true
+
/is-retry-allowed/1.2.0:
resolution: {integrity: sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg==}
engines: {node: '>=0.10.0'}
dev: true
+ /is-set/2.0.2:
+ resolution: {integrity: sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==}
+ dev: true
+
+ /is-shared-array-buffer/1.0.2:
+ resolution: {integrity: sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==}
+ dependencies:
+ call-bind: 1.0.5
+ dev: true
+
/is-stream/1.1.0:
resolution: {integrity: sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==}
engines: {node: '>=0.10.0'}
@@ -7106,6 +7350,13 @@ packages:
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
dev: true
+ /is-string/1.0.7:
+ resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ has-tostringtag: 1.0.0
+ dev: true
+
/is-symbol/1.0.3:
resolution: {integrity: sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==}
engines: {node: '>= 0.4'}
@@ -7113,6 +7364,13 @@ packages:
has-symbols: 1.0.1
dev: true
+ /is-typed-array/1.1.12:
+ resolution: {integrity: sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ which-typed-array: 1.1.13
+ dev: true
+
/is-typedarray/1.0.0:
resolution: {integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==}
dev: true
@@ -7140,6 +7398,17 @@ packages:
resolution: {integrity: sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q==}
dev: true
+ /is-weakmap/2.0.1:
+ resolution: {integrity: sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==}
+ dev: true
+
+ /is-weakset/2.0.2:
+ resolution: {integrity: sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==}
+ dependencies:
+ call-bind: 1.0.5
+ get-intrinsic: 1.2.2
+ dev: true
+
/is2/2.0.6:
resolution: {integrity: sha512-+Z62OHOjA6k2sUDOKXoZI3EXv7Fb1K52jpTBLbkfx62bcUeSsrTBLhEquCRDKTx0XE5XbHcG/S2vrtE3lnEDsQ==}
engines: {node: '>=v0.10.0'}
@@ -7157,6 +7426,10 @@ packages:
resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==}
dev: true
+ /isarray/2.0.5:
+ resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==}
+ dev: true
+
/isbot/3.3.4:
resolution: {integrity: sha512-a6o/e6nBMoRGvoovg5NT2r/N7S4398yCDXc6HgEOILdBAjYv05SX1MBhgc8SHnEJdRyLfOpAPqc10ezLWkj7rQ==}
engines: {node: '>=12'}
@@ -8323,6 +8596,11 @@ packages:
engines: {node: '>=12'}
dev: true
+ /lz-string/1.5.0:
+ resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==}
+ hasBin: true
+ dev: true
+
/magic-string/0.25.7:
resolution: {integrity: sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==}
dependencies:
@@ -8969,6 +9247,14 @@ packages:
resolution: {integrity: sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw==}
dev: true
+ /object-is/1.1.5:
+ resolution: {integrity: sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ call-bind: 1.0.5
+ define-properties: 1.1.3
+ dev: true
+
/object-keys/1.1.1:
resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==}
engines: {node: '>= 0.4'}
@@ -8989,6 +9275,16 @@ packages:
object-keys: 1.1.1
dev: true
+ /object.assign/4.1.5:
+ resolution: {integrity: sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ call-bind: 1.0.5
+ define-properties: 1.2.1
+ has-symbols: 1.0.3
+ object-keys: 1.1.1
+ dev: true
+
/on-finished/2.3.0:
resolution: {integrity: sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==}
engines: {node: '>= 0.8'}
@@ -9861,6 +10157,15 @@ packages:
resolution: {integrity: sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==}
dev: true
+ /regexp.prototype.flags/1.5.1:
+ resolution: {integrity: sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ call-bind: 1.0.5
+ define-properties: 1.2.1
+ set-function-name: 2.0.1
+ dev: true
+
/regexpp/3.2.0:
resolution: {integrity: sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==}
engines: {node: '>=8'}
@@ -10286,6 +10591,25 @@ packages:
resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==}
dev: true
+ /set-function-length/1.1.1:
+ resolution: {integrity: sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ define-data-property: 1.1.1
+ get-intrinsic: 1.2.2
+ gopd: 1.0.1
+ has-property-descriptors: 1.0.1
+ dev: true
+
+ /set-function-name/2.0.1:
+ resolution: {integrity: sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ define-data-property: 1.1.1
+ functions-have-names: 1.2.3
+ has-property-descriptors: 1.0.1
+ dev: true
+
/setimmediate/1.0.5:
resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==}
dev: true
@@ -10329,6 +10653,14 @@ packages:
resolution: {integrity: sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg==}
dev: true
+ /side-channel/1.0.4:
+ resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==}
+ dependencies:
+ call-bind: 1.0.5
+ get-intrinsic: 1.2.2
+ object-inspect: 1.12.0
+ dev: true
+
/signal-exit/3.0.7:
resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==}
dev: true
@@ -10586,6 +10918,13 @@ packages:
engines: {node: '>= 0.6'}
dev: true
+ /stop-iteration-iterator/1.0.0:
+ resolution: {integrity: sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ internal-slot: 1.0.6
+ dev: true
+
/stream-buffers/3.0.2:
resolution: {integrity: sha512-DQi1h8VEBA/lURbSwFtEHnSTb9s2/pwLEaFuNhXwy1Dx3Sa0lOuYT2yNUr4/j2fs8oCAMANtrZ5OrPZtyVs3MQ==}
engines: {node: '>= 0.10.0'}
@@ -11608,6 +11947,36 @@ packages:
webidl-conversions: 6.1.0
dev: true
+ /which-boxed-primitive/1.0.2:
+ resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==}
+ dependencies:
+ is-bigint: 1.0.4
+ is-boolean-object: 1.1.2
+ is-number-object: 1.0.7
+ is-string: 1.0.7
+ is-symbol: 1.0.3
+ dev: true
+
+ /which-collection/1.0.1:
+ resolution: {integrity: sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==}
+ dependencies:
+ is-map: 2.0.2
+ is-set: 2.0.2
+ is-weakmap: 2.0.1
+ is-weakset: 2.0.2
+ dev: true
+
+ /which-typed-array/1.1.13:
+ resolution: {integrity: sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ available-typed-arrays: 1.0.5
+ call-bind: 1.0.5
+ for-each: 0.3.3
+ gopd: 1.0.1
+ has-tostringtag: 1.0.0
+ dev: true
+
/which/1.3.1:
resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==}
hasBin: true
diff --git a/common/config/rush/repo-state.json b/common/config/rush/repo-state.json
index d5834052d..4d226ab04 100644
--- a/common/config/rush/repo-state.json
+++ b/common/config/rush/repo-state.json
@@ -1,5 +1,5 @@
// DO NOT MODIFY THIS FILE MANUALLY BUT DO COMMIT IT. It is generated and used by Rush.
{
- "pnpmShrinkwrapHash": "f22225c05d2747a95a11b5d6c6ee8827084cf800",
+ "pnpmShrinkwrapHash": "76374ca64642c7cf6282ba6e6c9417787e019665",
"preferredVersionsHash": "bf21a9e8fbc5a3846fb05b4fa0859e0917b2202f"
}
diff --git a/libraries/browser-tracker-core/package.json b/libraries/browser-tracker-core/package.json
index 9bda3600e..6f2bf998d 100644
--- a/libraries/browser-tracker-core/package.json
+++ b/libraries/browser-tracker-core/package.json
@@ -48,6 +48,7 @@
"rollup-plugin-terser": "~7.0.2",
"rollup-plugin-ts": "~2.0.5",
"ts-jest": "~27.1.3",
- "typescript": "~4.6.2"
+ "typescript": "~4.6.2",
+ "@testing-library/dom": "~9.3.1"
}
}
diff --git a/libraries/browser-tracker-core/src/helpers/cross_domain.ts b/libraries/browser-tracker-core/src/helpers/cross_domain.ts
new file mode 100644
index 000000000..75fc8c088
--- /dev/null
+++ b/libraries/browser-tracker-core/src/helpers/cross_domain.ts
@@ -0,0 +1,72 @@
+import { ExtendedCrossDomainLinkerAttributes, Platform } from '../tracker/types';
+
+const DEFAULT_CROSS_DOMAIN_LINKER_PARAMS: ExtendedCrossDomainLinkerAttributes = {
+ sessionId: true,
+ sourceId: true,
+ sourcePlatform: false,
+ userId: false,
+ reason: false,
+};
+
+interface ExtendedCrossDomainLinkerValues {
+ domainUserId?: string;
+ /* Timestamp of the cross-domain link click. */
+ timestamp?: number;
+ /* Current user ID as set with setUserId(). */
+ userId?: string;
+ /* Visitor ID. */
+ sessionId?: string;
+ /* The app ID. */
+ sourceId?: string;
+ sourcePlatform?: Platform;
+ /* Link text of the cross-domain link. */
+ reason?: string;
+}
+
+export function createCrossDomainParameterValue(
+ isExtendedFormat: boolean,
+ attributeConfiguration: ExtendedCrossDomainLinkerAttributes | undefined,
+ attributeValues: ExtendedCrossDomainLinkerValues & {
+ /* As `reason` might be a callback, we also need to pass the event to calculate the reason value. */
+ event: Event;
+ }
+): string {
+ let crossDomainParameterValue;
+ const timestamp = new Date().getTime();
+ const config = { ...DEFAULT_CROSS_DOMAIN_LINKER_PARAMS, ...attributeConfiguration };
+ const { domainUserId, userId, sessionId, sourceId, sourcePlatform, event } = attributeValues;
+
+ const eventTarget = event.currentTarget as HTMLAnchorElement | HTMLAreaElement | null;
+ const reason = typeof config.reason === 'function' ? config.reason(event) : eventTarget?.textContent?.trim();
+
+ if (isExtendedFormat) {
+ /* Index is used by Enrich, so it should not be changed. */
+ crossDomainParameterValue = [
+ domainUserId,
+ timestamp,
+ config.sessionId && sessionId,
+ config.userId && urlSafeBase64Encode(userId || ''),
+ config.sourceId && urlSafeBase64Encode(sourceId || ''),
+ config.sourcePlatform && sourcePlatform,
+ config.reason && urlSafeBase64Encode(reason || ''),
+ ]
+ .map((attribute) => attribute || '')
+ .join('.')
+ // Remove trailing dots
+ .replace(/([.]*$)/, '');
+ } else {
+ crossDomainParameterValue = attributeValues.domainUserId + '.' + timestamp;
+ }
+
+ return crossDomainParameterValue;
+}
+
+/**
+ *
+ * The url-safe variation emits - and _ instead of + and / characters. Also removes = sign padding.
+ * @param {string} str The string to encode in a URL safe manner
+ * @return {string}
+ */
+export function urlSafeBase64Encode(str: string) {
+ return btoa(str).replace(/\+/g, '-').replace(/\//g, '_').replace(/\=+$/, '');
+}
diff --git a/libraries/browser-tracker-core/src/helpers/index.ts b/libraries/browser-tracker-core/src/helpers/index.ts
index b160157d4..817780a86 100755
--- a/libraries/browser-tracker-core/src/helpers/index.ts
+++ b/libraries/browser-tracker-core/src/helpers/index.ts
@@ -29,6 +29,7 @@
*/
export * from './storage';
+export * from './cross_domain';
declare global {
interface EventTarget {
diff --git a/libraries/browser-tracker-core/src/tracker/index.ts b/libraries/browser-tracker-core/src/tracker/index.ts
index e79d30484..3754376aa 100755
--- a/libraries/browser-tracker-core/src/tracker/index.ts
+++ b/libraries/browser-tracker-core/src/tracker/index.ts
@@ -1,33 +1,3 @@
-/*
- * Copyright (c) 2022 Snowplow Analytics Ltd, 2010 Anthon Pang
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * 3. Neither the name of the copyright holder nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * 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.
- */
-
import {
trackerCore,
buildPagePing,
@@ -56,6 +26,7 @@ import {
isInteger,
attemptGetSessionStorage,
attemptWriteSessionStorage,
+ createCrossDomainParameterValue,
} from '../helpers';
import { BrowserPlugin } from '../plugins';
import { OutQueueManager } from './out_queue';
@@ -75,6 +46,7 @@ import {
BrowserPluginConfiguration,
ClearUserDataConfiguration,
ClientSession,
+ ExtendedCrossDomainLinkerOptions,
} from './types';
import {
parseIdCookie,
@@ -186,7 +158,17 @@ export function Tracker(
},
getAnonymousTracking = (config: TrackerConfiguration) => !!config.anonymousTracking,
isBrowserContextAvailable = trackerConfiguration?.contexts?.browser ?? false,
- isWebPageContextAvailable = trackerConfiguration?.contexts?.webPage ?? true;
+ isWebPageContextAvailable = trackerConfiguration?.contexts?.webPage ?? true,
+ getExtendedCrossDomainTrackingConfiguration = (crossDomainTrackingConfig: ExtendedCrossDomainLinkerOptions) => {
+ if (typeof crossDomainTrackingConfig === 'boolean') {
+ return { useExtendedCrossDomainLinker: crossDomainTrackingConfig };
+ }
+
+ return {
+ useExtendedCrossDomainLinker: true,
+ collectCrossDomainAttributes: crossDomainTrackingConfig,
+ };
+ };
// Get all injected plugins
browserPlugins.push(getBrowserDataPlugin());
@@ -321,7 +303,10 @@ export function Tracker(
configSessionContext = trackerConfiguration.contexts?.session ?? false,
toOptoutByCookie: string | boolean,
onSessionUpdateCallback = trackerConfiguration.onSessionUpdateCallback,
- manualSessionUpdateCalled = false;
+ manualSessionUpdateCalled = false,
+ { useExtendedCrossDomainLinker, collectCrossDomainAttributes } = getExtendedCrossDomainTrackingConfiguration(
+ trackerConfiguration.useExtendedCrossDomainLinker || false
+ );
if (trackerConfiguration.hasOwnProperty('discoverRootDomain') && trackerConfiguration.discoverRootDomain) {
configCookieDomain = findRootDomain(configCookieSameSite, configCookieSecure);
@@ -369,31 +354,45 @@ export function Tracker(
}
/**
- * Decorate the querystring of a single link
+ * Create link handler to decorate the querystring of a link (onClick/onMouseDown)
*
* @param event - e The event targeting the link
*/
- function linkDecorationHandler(evt: Event) {
- const timestamp = new Date().getTime();
- const elt = evt.currentTarget as HTMLAnchorElement | HTMLAreaElement | null;
- if (elt?.href) {
- elt.href = decorateQuerystring(elt.href, '_sp', domainUserId + '.' + timestamp);
- }
+ function addLinkDecorationHandler(extended: boolean): (evt: Event) => void {
+ const CROSS_DOMAIN_PARAMETER_NAME = '_sp';
+
+ return (evt) => {
+ const elt = evt.currentTarget as HTMLAnchorElement | HTMLAreaElement | null;
+
+ const crossDomainParameterValue = createCrossDomainParameterValue(extended, collectCrossDomainAttributes, {
+ domainUserId,
+ userId: businessUserId || undefined,
+ sessionId: memorizedSessionId,
+ sourceId: configTrackerSiteId,
+ sourcePlatform: configPlatform,
+ event: evt,
+ });
+
+ if (elt?.href) {
+ elt.href = decorateQuerystring(elt.href, CROSS_DOMAIN_PARAMETER_NAME, crossDomainParameterValue);
+ }
+ };
}
/**
- * Enable querystring decoration for links pasing a filter
+ * Enable querystring decoration for links passing a filter
* Whenever such a link is clicked on or navigated to via the keyboard,
* add "_sp={{duid}}.{{timestamp}}" to its querystring
*
* @param crossDomainLinker - Function used to determine which links to decorate
*/
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];
if (!(elt as any).spDecorationEnabled && crossDomainLinker(elt)) {
- addEventListener(elt, 'click', linkDecorationHandler, true);
- addEventListener(elt, 'mousedown', linkDecorationHandler, true);
+ elt.addEventListener('click', crossDomainLinkHandler, true);
+ elt.addEventListener('mousedown', crossDomainLinkHandler, true);
// Don't add event listeners more than once
(elt as any).spDecorationEnabled = true;
diff --git a/libraries/browser-tracker-core/src/tracker/types.ts b/libraries/browser-tracker-core/src/tracker/types.ts
index fa2105500..922e6af90 100755
--- a/libraries/browser-tracker-core/src/tracker/types.ts
+++ b/libraries/browser-tracker-core/src/tracker/types.ts
@@ -32,6 +32,20 @@ export type CookieSameSite = 'None' | 'Lax' | 'Strict';
/* The supported methods which events can be sent with */
export type EventMethod = 'post' | 'get' | 'beacon';
+/* Available configuration for the extended cross domain linker */
+export type ExtendedCrossDomainLinkerAttributes = {
+ userId?: boolean;
+ sessionId?: boolean;
+ sourceId?: boolean;
+ sourcePlatform?: boolean;
+ /**
+ * Allow for the collection of the link text when a cross-domain link is clicked. Can also accept a callback for customizing information collection.
+ */
+ reason?: boolean | ((evt: Event) => string);
+};
+
+export type ExtendedCrossDomainLinkerOptions = boolean | ExtendedCrossDomainLinkerAttributes;
+
/**
* The configuration object for initialising the tracker
* @example
@@ -126,6 +140,11 @@ export type TrackerConfiguration = {
* links on the callback
*/
crossDomainLinker?: (elt: HTMLAnchorElement | HTMLAreaElement) => boolean;
+ /**
+ * Configure the cross domain linker to use the extended format, allowing for
+ * more user/session information to pass to the cross domain navigation.
+ */
+ useExtendedCrossDomainLinker?: ExtendedCrossDomainLinkerOptions;
/**
* The max size a POST request can be before the tracker will force send it
* @defaultValue 40000
diff --git a/libraries/browser-tracker-core/test/tracker/cross_domain.test.ts b/libraries/browser-tracker-core/test/tracker/cross_domain.test.ts
new file mode 100644
index 000000000..cf7748e9f
--- /dev/null
+++ b/libraries/browser-tracker-core/test/tracker/cross_domain.test.ts
@@ -0,0 +1,147 @@
+import * as uuid from 'uuid';
+jest.mock('uuid');
+const MOCK_UUID = '123456789';
+jest.spyOn(uuid, 'v4').mockReturnValue(MOCK_UUID);
+
+import { createTracker } from '../helpers';
+import { getByText, queryByText, waitFor } from '@testing-library/dom';
+import { urlSafeBase64Encode } from '../../src';
+
+function getCrossDomainURLParam(url: string) {
+ const CROSS_DOMAIN_PARAMETER_NAME = '_sp';
+ const urlParams = new URLSearchParams(new URL(url).search);
+ return urlParams.get(CROSS_DOMAIN_PARAMETER_NAME);
+}
+
+function decodeExtendedCrossDomainLinkParam(crossDomainLinkValue: string) {
+ return crossDomainLinkValue.split('.');
+}
+
+describe('Cross-domain linking: ', () => {
+ const CROSS_DOMAIN_LINK_PARAMETERS_LENGTH = 7;
+ const standardDate = new Date(2023, 1, 1);
+
+ beforeAll(() => {
+ jest.useFakeTimers('modern');
+ jest.setSystemTime(standardDate);
+ });
+
+ afterEach(() => {
+ jest.clearAllMocks();
+ });
+
+ afterAll(() => {
+ jest.useRealTimers();
+ });
+
+ const CROSS_DOMAIN_LINK_TEXT = 'Cross-domain link';
+ const appId = 'my-app';
+ const userId = 'U1234';
+ function getLinkDom() {
+ const div = document.createElement('div');
+ div.innerHTML = `
+ ${CROSS_DOMAIN_LINK_TEXT}
+ `;
+ return div;
+ }
+
+ it('Adds the correct link decoration', async () => {
+ const container = getLinkDom();
+
+ const elem = getByText(container, CROSS_DOMAIN_LINK_TEXT);
+ // @ts-ignore
+ jest.spyOn(document, 'links', 'get').mockReturnValue([elem]);
+ createTracker({ crossDomainLinker: () => true });
+ elem.click();
+ await waitFor(() => {
+ const clickedLink = queryByText(container, CROSS_DOMAIN_LINK_TEXT)?.getAttribute('href') as string;
+ const crossDomainLinkParam = getCrossDomainURLParam(clickedLink);
+ const crossDomainParamParts = crossDomainLinkParam?.split('.') as string[];
+ expect(crossDomainParamParts?.length).toEqual(2);
+ expect(crossDomainParamParts[0]).toEqual(MOCK_UUID);
+ expect(crossDomainParamParts[1]).toEqual(String(standardDate.getTime()));
+ });
+ });
+
+ it('Adds the correct link decoration with default extended format', async () => {
+ const container = getLinkDom();
+ const elem = getByText(container, CROSS_DOMAIN_LINK_TEXT);
+ // @ts-ignore
+ jest.spyOn(document, 'links', 'get').mockReturnValue([elem]);
+ const t = createTracker({ appId, crossDomainLinker: () => true, useExtendedCrossDomainLinker: true });
+ t?.setUserId(userId);
+ elem.click();
+ await waitFor(() => {
+ const clickedLink = queryByText(container, CROSS_DOMAIN_LINK_TEXT)?.getAttribute('href') as string;
+ const crossDomainLinkParam = getCrossDomainURLParam(clickedLink) as string;
+ const decodedCrossDomainLinkParam = decodeExtendedCrossDomainLinkParam(crossDomainLinkParam);
+ expect(decodedCrossDomainLinkParam.length).toBe(CROSS_DOMAIN_LINK_PARAMETERS_LENGTH - 2);
+ expect(decodedCrossDomainLinkParam).toStrictEqual([
+ MOCK_UUID,
+ String(standardDate.getTime()),
+ MOCK_UUID,
+ '',
+ urlSafeBase64Encode(appId),
+ ]);
+ });
+ });
+
+ it('Adds the correct link decoration with configurable extended format and link text collection', async () => {
+ const container = getLinkDom();
+
+ const elem = getByText(container, CROSS_DOMAIN_LINK_TEXT);
+ // @ts-ignore
+ jest.spyOn(document, 'links', 'get').mockReturnValue([elem]);
+ const t = createTracker({
+ appId,
+ crossDomainLinker: () => true,
+ useExtendedCrossDomainLinker: { userId: true, reason: true, sourcePlatform: true },
+ });
+ t?.setUserId(userId);
+ elem.click();
+ await waitFor(() => {
+ const clickedLink = queryByText(container, CROSS_DOMAIN_LINK_TEXT)?.getAttribute('href') as string;
+ const crossDomainLinkParam = getCrossDomainURLParam(clickedLink) as string;
+ const decodedCrossDomainLinkParam = decodeExtendedCrossDomainLinkParam(crossDomainLinkParam);
+ expect(decodedCrossDomainLinkParam.length).toBe(CROSS_DOMAIN_LINK_PARAMETERS_LENGTH);
+ expect(decodedCrossDomainLinkParam).toStrictEqual([
+ MOCK_UUID,
+ String(standardDate.getTime()),
+ MOCK_UUID,
+ urlSafeBase64Encode(userId),
+ urlSafeBase64Encode(appId),
+ 'web',
+ urlSafeBase64Encode(CROSS_DOMAIN_LINK_TEXT),
+ ]);
+ });
+ });
+
+ it('Adds the correct link decoration with configurable extended format and event callback', async () => {
+ const container = getLinkDom();
+
+ const elem = getByText(container, CROSS_DOMAIN_LINK_TEXT);
+ // @ts-ignore
+ jest.spyOn(document, 'links', 'get').mockReturnValue([elem]);
+ createTracker({
+ appId,
+ crossDomainLinker: () => true,
+ useExtendedCrossDomainLinker: { reason: (evt) => evt.type },
+ });
+ elem.click();
+ await waitFor(() => {
+ const clickedLink = queryByText(container, CROSS_DOMAIN_LINK_TEXT)?.getAttribute('href') as string;
+ const crossDomainLinkParam = getCrossDomainURLParam(clickedLink) as string;
+ const decodedCrossDomainLinkParam = decodeExtendedCrossDomainLinkParam(crossDomainLinkParam);
+ expect(decodedCrossDomainLinkParam.length).toBe(CROSS_DOMAIN_LINK_PARAMETERS_LENGTH);
+ expect(decodedCrossDomainLinkParam).toStrictEqual([
+ MOCK_UUID,
+ String(standardDate.getTime()),
+ MOCK_UUID,
+ '',
+ urlSafeBase64Encode(appId),
+ '',
+ urlSafeBase64Encode('click'),
+ ]);
+ });
+ });
+});
diff --git a/trackers/browser-tracker/src/api.ts b/trackers/browser-tracker/src/api.ts
index e8eba06b6..1367ed841 100644
--- a/trackers/browser-tracker/src/api.ts
+++ b/trackers/browser-tracker/src/api.ts
@@ -43,6 +43,8 @@ import {
FlushBufferConfiguration,
PageViewEvent,
ClearUserDataConfiguration,
+ ExtendedCrossDomainLinkerOptions,
+ ExtendedCrossDomainLinkerAttributes,
} from '@snowplow/browser-tracker-core';
import {
buildSelfDescribingEvent,
@@ -87,6 +89,8 @@ export {
ContextEvent,
ContextFilter,
RuleSet,
+ ExtendedCrossDomainLinkerOptions,
+ ExtendedCrossDomainLinkerAttributes,
};
/**
@@ -185,7 +189,7 @@ export function setVisitorCookieTimeout(timeout: number, trackers?: Array