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