From 7f0f14ad3880a30cf7d820e06aa6053c82006205 Mon Sep 17 00:00:00 2001
From: Peter Perlepes
Date: Mon, 28 Aug 2023 13:27:55 +0300
Subject: [PATCH] Extend cross-domain linking with more user/session
information
---
...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 | 320 ++++++++++++++++++
common/config/rush/repo-state.json | 2 +-
libraries/browser-tracker-core/package.json | 3 +-
.../browser-tracker-core/src/tracker/index.ts | 90 ++---
.../browser-tracker-core/src/tracker/types.ts | 29 ++
.../test/tracker/cross_domain.test.ts | 105 ++++++
.../docs/browser-tracker.api.md | 6 +
trackers/browser-tracker/src/api.ts | 4 +-
11 files changed, 538 insertions(+), 45 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/test/tracker/cross_domain.test.ts
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 fd359cee8..f06a0010f 100644
--- a/common/config/rush/browser-approved-packages.json
+++ b/common/config/rush/browser-approved-packages.json
@@ -130,6 +130,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 261787db4..064e03e43 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.1
'@types/jest': 27.4.1
'@types/jsdom': 16.2.14
'@types/sha1': 1.1.3
@@ -2728,6 +2730,20 @@ packages:
resolution: {integrity: sha512-g697J3WxV/Zytemz8aTuKjTGYtta9+02kva3C1xc7KXB8GdbfE1akGJIsZLyY/FSh2QrnE+fiB7vmWU3XNcb6A==}
dev: true
+ /@testing-library/dom/9.3.1:
+ resolution: {integrity: sha512-0DGPd9AR3+iDTjGoMpxIkAsUihHZ3Ai6CneU6bRRrffXMgzCdlNk43jTrD2/5LT6CBb3MWTP8v510JzYtahD2w==}
+ engines: {node: '>=14'}
+ dependencies:
+ '@babel/code-frame': 7.16.7
+ '@babel/runtime': 7.18.9
+ '@types/aria-query': 5.0.1
+ 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'}
@@ -2754,6 +2770,10 @@ packages:
resolution: {integrity: sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==}
dev: true
+ /@types/aria-query/5.0.1:
+ resolution: {integrity: sha512-XTIieEY+gvJ39ChLcB4If5zHtPxt3Syj5rgZR+e1ctpmK8NjPf0zFqsz4JpLJT0xla9GFDKjy8Cpu331nrmE1Q==}
+ dev: true
+
/@types/babel__core/7.1.18:
resolution: {integrity: sha512-S7unDjm/C7z2A2R9NzfKCK1I+BAALDtxEmsJBwlB3EzNfb929ykjL++1CK9LO++EIp2fQrC8O+BwjKvz6UeDyQ==}
dependencies:
@@ -3718,6 +3738,19 @@ packages:
engines: {node: '>=6.0'}
dev: true
+ /aria-query/5.1.3:
+ resolution: {integrity: sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==}
+ dependencies:
+ deep-equal: 2.2.2
+ dev: true
+
+ /array-buffer-byte-length/1.0.0:
+ resolution: {integrity: sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==}
+ dependencies:
+ call-bind: 1.0.2
+ is-array-buffer: 3.0.2
+ dev: true
+
/array-find-index/1.0.2:
resolution: {integrity: sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=}
engines: {node: '>=0.10.0'}
@@ -3829,6 +3862,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
@@ -5022,6 +5060,29 @@ packages:
resolution: {integrity: sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==}
dev: true
+ /deep-equal/2.2.2:
+ resolution: {integrity: sha512-xjVyBf0w5vH0I42jdAZzOKVldmPgSulmiyPRywoyq7HXC9qdgo17kxJE+rdnif5Tz6+pIrpJI8dCpMNLIGkUiA==}
+ dependencies:
+ array-buffer-byte-length: 1.0.0
+ call-bind: 1.0.2
+ es-get-iterator: 1.1.3
+ get-intrinsic: 1.2.1
+ 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.4
+ regexp.prototype.flags: 1.5.0
+ side-channel: 1.0.4
+ which-boxed-primitive: 1.0.2
+ which-collection: 1.0.1
+ which-typed-array: 1.1.11
+ dev: true
+
/deep-is/0.1.3:
resolution: {integrity: sha512-GtxAN4HvBachZzm4OnWqc45ESpUCMwkYcsjnsPs23FwJbsO+k4t0k9bQCgOmzIlpHO28+WPK/KRbRk0DDHuuDw==}
dev: true
@@ -5063,6 +5124,14 @@ packages:
object-keys: 1.1.1
dev: true
+ /define-properties/1.2.0:
+ resolution: {integrity: sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ has-property-descriptors: 1.0.0
+ object-keys: 1.1.1
+ dev: true
+
/del/6.0.0:
resolution: {integrity: sha512-1shh9DQ23L16oXSZKB2JxpL7iMy2E0S9d517ptA1P8iw0alkPtQcrKH7ru31rYtKwF499HkTu+DRzq3TCKDFRQ==}
engines: {node: '>=10'}
@@ -5234,6 +5303,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'}
@@ -5418,6 +5491,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.2
+ get-intrinsic: 1.2.1
+ 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'}
@@ -6058,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'}
@@ -6176,6 +6269,10 @@ packages:
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:
@@ -6214,6 +6311,15 @@ packages:
has-symbols: 1.0.1
dev: true
+ /get-intrinsic/1.2.1:
+ resolution: {integrity: sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==}
+ dependencies:
+ function-bind: 1.1.1
+ has: 1.0.3
+ has-proto: 1.0.1
+ has-symbols: 1.0.3
+ dev: true
+
/get-package-type/0.1.0:
resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==}
engines: {node: '>=8.0.0'}
@@ -6408,6 +6514,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.1
+ dev: true
+
/got/11.8.5:
resolution: {integrity: sha512-o0Je4NvQObAuZPHLFoRSkdG2lTgtcynqymzg2Vupdx6PorhaT5MCbIyXG6d4D94kk8ZG57QeosgdiqfJWhEhlQ==}
engines: {node: '>=10.19.0'}
@@ -6521,6 +6633,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'}
@@ -6531,6 +6647,17 @@ packages:
engines: {node: '>=8'}
dev: true
+ /has-property-descriptors/1.0.0:
+ resolution: {integrity: sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==}
+ dependencies:
+ get-intrinsic: 1.2.1
+ 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
@@ -6540,12 +6667,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
@@ -6840,6 +6979,15 @@ packages:
wrap-ansi: 8.1.0
dev: true
+ /internal-slot/1.0.5:
+ resolution: {integrity: sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ get-intrinsic: 1.2.1
+ has: 1.0.3
+ side-channel: 1.0.4
+ dev: true
+
/into-stream/3.1.0:
resolution: {integrity: sha512-TcdjPibTksa1NQximqep2r17ISRiNE9fwlfbg3F8ANdvP5/yrFTew86VcO//jk4QTaMlbjypPBq76HN2zaKfZQ==}
engines: {node: '>=4'}
@@ -6871,10 +7019,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.2
+ 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.2
+ get-intrinsic: 1.2.1
+ 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'}
@@ -6882,6 +7052,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.2
+ has-tostringtag: 1.0.0
+ dev: true
+
/is-callable/1.2.3:
resolution: {integrity: sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ==}
engines: {node: '>= 0.4'}
@@ -6911,6 +7089,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-docker/2.1.1:
resolution: {integrity: sha512-ZOoqiXfEwtGknTiuDEy8pN2CfE3TxMHprvNer1mXiqwkOT77Rw3YVrUQ52EqAOU3QAWDQ+bQdx7HJzrv7LS2Hw==}
engines: {node: '>=8'}
@@ -6964,6 +7149,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
@@ -6977,6 +7166,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'}
@@ -7033,11 +7229,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.2
+ 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.2
+ dev: true
+
/is-stream/1.1.0:
resolution: {integrity: sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==}
engines: {node: '>=0.10.0'}
@@ -7053,6 +7267,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'}
@@ -7060,6 +7281,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.11
+ dev: true
+
/is-typedarray/1.0.0:
resolution: {integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==}
dev: true
@@ -7082,6 +7310,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.2
+ get-intrinsic: 1.2.1
+ dev: true
+
/is-wsl/2.2.0:
resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==}
engines: {node: '>=8'}
@@ -7106,6 +7345,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'}
@@ -8324,6 +8567,11 @@ packages:
yallist: 4.0.0
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:
@@ -9009,6 +9257,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.2
+ define-properties: 1.1.3
+ dev: true
+
/object-keys/1.1.1:
resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==}
engines: {node: '>= 0.4'}
@@ -9029,6 +9285,16 @@ packages:
object-keys: 1.1.1
dev: true
+ /object.assign/4.1.4:
+ resolution: {integrity: sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ call-bind: 1.0.2
+ define-properties: 1.2.0
+ 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'}
@@ -9869,6 +10135,15 @@ packages:
resolution: {integrity: sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==}
dev: true
+ /regexp.prototype.flags/1.5.0:
+ resolution: {integrity: sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ call-bind: 1.0.2
+ define-properties: 1.2.0
+ functions-have-names: 1.2.3
+ dev: true
+
/regexpp/3.2.0:
resolution: {integrity: sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==}
engines: {node: '>=8'}
@@ -10322,6 +10597,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.2
+ get-intrinsic: 1.2.1
+ object-inspect: 1.12.0
+ dev: true
+
/signal-exit/3.0.7:
resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==}
dev: true
@@ -10560,6 +10843,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.5
+ dev: true
+
/stream-buffers/3.0.2:
resolution: {integrity: sha512-DQi1h8VEBA/lURbSwFtEHnSTb9s2/pwLEaFuNhXwy1Dx3Sa0lOuYT2yNUr4/j2fs8oCAMANtrZ5OrPZtyVs3MQ==}
engines: {node: '>= 0.10.0'}
@@ -11604,6 +11894,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.11:
+ resolution: {integrity: sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ available-typed-arrays: 1.0.5
+ call-bind: 1.0.2
+ 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 fc69b38e4..e3e7764b6 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": "a8b7bdefbe86b839e0c359de02cae9c4ccc90039",
+ "pnpmShrinkwrapHash": "ad647267d9c76c711740c0114479544268b0c9fa",
"preferredVersionsHash": "bf21a9e8fbc5a3846fb05b4fa0859e0917b2202f"
}
diff --git a/libraries/browser-tracker-core/package.json b/libraries/browser-tracker-core/package.json
index 370920f23..689bd72d3 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/tracker/index.ts b/libraries/browser-tracker-core/src/tracker/index.ts
index a6443a932..a9c3f3a89 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,
@@ -75,6 +45,8 @@ import {
BrowserPluginConfiguration,
ClearUserDataConfiguration,
ClientSession,
+ ExtendedCrossDomainLinker,
+ 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,
+ collectCrossDomainLinkText: crossDomainTrackingConfig.enableLinkText,
+ };
+ };
// Get all injected plugins
browserPlugins.push(getBrowserDataPlugin());
@@ -318,7 +300,10 @@ export function Tracker(
configSessionContext = trackerConfiguration.contexts?.session ?? false,
toOptoutByCookie: string | boolean,
onSessionUpdateCallback = trackerConfiguration.onSessionUpdateCallback,
- manualSessionUpdateCalled = false;
+ manualSessionUpdateCalled = false,
+ { useExtendedCrossDomainLinker, collectCrossDomainLinkText } = getExtendedCrossDomainTrackingConfiguration(
+ trackerConfiguration.useExtendedCrossDomainLinker || false
+ );
if (trackerConfiguration.hasOwnProperty('discoverRootDomain') && trackerConfiguration.discoverRootDomain) {
configCookieDomain = findRootDomain(configCookieSameSite, configCookieSecure);
@@ -366,20 +351,40 @@ 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;
+ let crossDomainParameterValue;
+ const timestamp = new Date().getTime();
+ if (extended) {
+ const extendedLinkData: ExtendedCrossDomainLinker = {
+ domainUserId,
+ userId: businessUserId || undefined,
+ sessionId: memorizedSessionId,
+ sourceId: configTrackerSiteId,
+ sourcePlatform: configPlatform,
+ timestamp,
+ reason: collectCrossDomainLinkText ? elt?.textContent?.trim() : undefined,
+ };
+ crossDomainParameterValue = encodeURIComponent(btoa(JSON.stringify(extendedLinkData)));
+ } else {
+ crossDomainParameterValue = domainUserId + '.' + timestamp;
+ }
+
+ 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
*
@@ -389,8 +394,9 @@ export function Tracker(
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);
+ const crossDomainLinkHandler = addLinkDecorationHandler(useExtendedCrossDomainLinker);
+ 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 0f5801c9d..a05865d8d 100755
--- a/libraries/browser-tracker-core/src/tracker/types.ts
+++ b/libraries/browser-tracker-core/src/tracker/types.ts
@@ -61,6 +61,15 @@ export type Platform = 'web' | 'mob' | 'pc' | 'srv' | 'app' | 'tv' | 'cnsl' | 'i
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 ExtendedCrossDomainLinkerOptions =
+ | boolean
+ | {
+ /**
+ * Allow for the collection of the link text when a cross-domain link is clicked.
+ */
+ enableLinkText?: boolean;
+ };
/**
* The configuration object for initialising the tracker
@@ -156,6 +165,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
@@ -648,3 +662,18 @@ export interface ClientSession extends Record {
*/
firstEventTimestamp: string | null;
}
+
+export interface ExtendedCrossDomainLinker {
+ 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;
+}
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..7a51183c0
--- /dev/null
+++ b/libraries/browser-tracker-core/test/tracker/cross_domain.test.ts
@@ -0,0 +1,105 @@
+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';
+
+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 JSON.parse(atob(decodeURIComponent(crossDomainLinkValue)));
+}
+
+describe('Cross-domain linking: ', () => {
+ 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';
+ 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 extended format', async () => {
+ const container = getLinkDom();
+ const elem = getByText(container, CROSS_DOMAIN_LINK_TEXT);
+ // @ts-ignore
+ jest.spyOn(document, 'links', 'get').mockReturnValue([elem]);
+ createTracker({ crossDomainLinker: () => true, useExtendedCrossDomainLinker: true });
+ 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).toStrictEqual({
+ domainUserId: MOCK_UUID,
+ sessionId: MOCK_UUID,
+ sourceId: '',
+ sourcePlatform: 'web',
+ timestamp: standardDate.getTime(),
+ });
+ });
+ });
+
+ it('Adds the correct link decoration with 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]);
+ createTracker({ crossDomainLinker: () => true, useExtendedCrossDomainLinker: { enableLinkText: true } });
+ 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).toStrictEqual({
+ domainUserId: MOCK_UUID,
+ sessionId: MOCK_UUID,
+ sourceId: '',
+ sourcePlatform: 'web',
+ timestamp: standardDate.getTime(),
+ reason: CROSS_DOMAIN_LINK_TEXT,
+ });
+ });
+ });
+});
diff --git a/trackers/browser-tracker/docs/browser-tracker.api.md b/trackers/browser-tracker/docs/browser-tracker.api.md
index 70aabae95..61205b664 100644
--- a/trackers/browser-tracker/docs/browser-tracker.api.md
+++ b/trackers/browser-tracker/docs/browser-tracker.api.md
@@ -218,6 +218,11 @@ export interface EnableAnonymousTrackingConfiguration {
// @public (undocumented)
export type EventMethod = "post" | "get" | "beacon";
+// @public (undocumented)
+export type ExtendedCrossDomainLinkerOptions = boolean | {
+ enableLinkText?: boolean;
+};
+
// @public
export type FilterProvider = [
ContextFilter,
@@ -354,6 +359,7 @@ export type TrackerConfiguration = {
useStm?: boolean;
bufferSize?: number;
crossDomainLinker?: (elt: HTMLAnchorElement | HTMLAreaElement) => boolean;
+ useExtendedCrossDomainLinker?: ExtendedCrossDomainLinkerOptions;
maxPostBytes?: number;
maxGetBytes?: number;
discoverRootDomain?: boolean;
diff --git a/trackers/browser-tracker/src/api.ts b/trackers/browser-tracker/src/api.ts
index e8eba06b6..ea6080b5e 100644
--- a/trackers/browser-tracker/src/api.ts
+++ b/trackers/browser-tracker/src/api.ts
@@ -43,6 +43,7 @@ import {
FlushBufferConfiguration,
PageViewEvent,
ClearUserDataConfiguration,
+ ExtendedCrossDomainLinkerOptions,
} from '@snowplow/browser-tracker-core';
import {
buildSelfDescribingEvent,
@@ -87,6 +88,7 @@ export {
ContextEvent,
ContextFilter,
RuleSet,
+ ExtendedCrossDomainLinkerOptions,
};
/**
@@ -185,7 +187,7 @@ export function setVisitorCookieTimeout(timeout: number, trackers?: Array