@schibsted/pulse-sdk 8.0.0-rc.0 → 8.0.0-rc.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. package/dist/cjs/Tracker.js +201 -209
  2. package/dist/cjs/Tracker.test-d.js +2 -2
  3. package/dist/cjs/builders/actor.js +3 -3
  4. package/dist/cjs/builders/consents.js +2 -2
  5. package/dist/cjs/builders/events-anonymous-node.js +1 -1
  6. package/dist/cjs/builders/events-node.js +3 -3
  7. package/dist/cjs/builders/experiments.js +1 -0
  8. package/dist/cjs/builders/tracker-node.js +2 -5
  9. package/dist/cjs/identity/actor.js +18 -6
  10. package/dist/cjs/identity/cis.js +4 -6
  11. package/dist/cjs/leaveTrackingTpaas/constants.js +2 -7
  12. package/dist/cjs/leaveTrackingTpaas/index.js +52 -40
  13. package/dist/cjs/tracker-proxy/consts.js +12 -6
  14. package/dist/cjs/tracker-proxy/proxy-provider.js +6 -0
  15. package/dist/cjs/version.js +5 -0
  16. package/dist/cjs/visibility-observer/index.js +9 -7
  17. package/dist/cjs/wrapper/types/event.js +5 -0
  18. package/dist/ejs/Tracker.js +203 -211
  19. package/dist/ejs/Tracker.test-d.js +2 -2
  20. package/dist/ejs/builders/actor.js +2 -2
  21. package/dist/ejs/builders/consents.js +2 -2
  22. package/dist/ejs/builders/events-anonymous-node.js +1 -1
  23. package/dist/ejs/builders/events-node.js +3 -3
  24. package/dist/ejs/builders/experiments.js +1 -1
  25. package/dist/ejs/builders/tracker-node.js +2 -2
  26. package/dist/ejs/identity/actor.js +19 -7
  27. package/dist/ejs/identity/cis.js +4 -3
  28. package/dist/ejs/leaveTrackingTpaas/constants.js +1 -5
  29. package/dist/ejs/leaveTrackingTpaas/index.js +53 -41
  30. package/dist/ejs/tracker-proxy/consts.js +12 -6
  31. package/dist/ejs/tracker-proxy/proxy-provider.js +6 -0
  32. package/dist/ejs/version.js +2 -0
  33. package/dist/ejs/visibility-observer/index.js +9 -7
  34. package/dist/ejs/wrapper/types/event.js +4 -1
  35. package/dist/types/Tracker.d.ts +92 -87
  36. package/dist/types/builders/actor.d.ts +3 -2
  37. package/dist/types/builders/consents.d.ts +2 -2
  38. package/dist/types/builders/experiments.d.ts +1 -0
  39. package/dist/types/builders/index.d.ts +1 -1
  40. package/dist/types/identity/actor.d.ts +2 -2
  41. package/dist/types/identity/cis.d.ts +1 -1
  42. package/dist/types/index.d.ts +1 -2
  43. package/dist/types/leaveTrackingTpaas/constants.d.ts +1 -5
  44. package/dist/types/leaveTrackingTpaas/index.d.ts +6 -6
  45. package/dist/types/leaveTrackingTpaas/types.d.ts +1 -9
  46. package/dist/types/tracker-proxy/consts.d.ts +1 -1
  47. package/dist/types/types.d.ts +19 -13
  48. package/dist/types/version.d.ts +1 -0
  49. package/dist/types/visibility-observer/index.d.ts +6 -1
  50. package/dist/types/wrapper/engagement/ui-element/types.d.ts +1 -0
  51. package/dist/types/wrapper/types/event.d.ts +3 -1
  52. package/dist/types/wrapper/view/ui-element/types.d.ts +1 -0
  53. package/node_modules/@schibsted/pulse-cis-sync/dist/cjs/cis-browser.d.ts +1 -0
  54. package/node_modules/@schibsted/pulse-cis-sync/dist/cjs/cis-browser.js +17 -3
  55. package/node_modules/@schibsted/pulse-cis-sync/dist/cjs/cis-browser.js.map +1 -1
  56. package/node_modules/@schibsted/pulse-cis-sync/dist/cjs/storage/pulseStorage.d.ts +4 -0
  57. package/node_modules/@schibsted/pulse-cis-sync/dist/cjs/storage/pulseStorage.js +15 -5
  58. package/node_modules/@schibsted/pulse-cis-sync/dist/cjs/storage/pulseStorage.js.map +1 -1
  59. package/node_modules/@schibsted/pulse-cis-sync/dist/cjs/storage/storage.d.ts +1 -0
  60. package/node_modules/@schibsted/pulse-cis-sync/dist/cjs/storage/storage.js +5 -0
  61. package/node_modules/@schibsted/pulse-cis-sync/dist/cjs/storage/storage.js.map +1 -1
  62. package/node_modules/@schibsted/pulse-cis-sync/dist/ejs/cis-browser.d.ts +1 -0
  63. package/node_modules/@schibsted/pulse-cis-sync/dist/ejs/cis-browser.js +16 -2
  64. package/node_modules/@schibsted/pulse-cis-sync/dist/ejs/cis-browser.js.map +1 -1
  65. package/node_modules/@schibsted/pulse-cis-sync/dist/ejs/storage/pulseStorage.d.ts +4 -0
  66. package/node_modules/@schibsted/pulse-cis-sync/dist/ejs/storage/pulseStorage.js +12 -3
  67. package/node_modules/@schibsted/pulse-cis-sync/dist/ejs/storage/pulseStorage.js.map +1 -1
  68. package/node_modules/@schibsted/pulse-cis-sync/dist/ejs/storage/storage.d.ts +1 -0
  69. package/node_modules/@schibsted/pulse-cis-sync/dist/ejs/storage/storage.js +5 -0
  70. package/node_modules/@schibsted/pulse-cis-sync/dist/ejs/storage/storage.js.map +1 -1
  71. package/node_modules/@schibsted/pulse-cis-sync/dist/types/cis-browser.d.ts +1 -0
  72. package/node_modules/@schibsted/pulse-cis-sync/dist/types/storage/pulseStorage.d.ts +4 -0
  73. package/node_modules/@schibsted/pulse-cis-sync/dist/types/storage/storage.d.ts +1 -0
  74. package/node_modules/@schibsted/pulse-utils/package.json +1 -1
  75. package/node_modules/@schibsted/tpaas-event-builder/dist/cjs/builders/catalogVersion.js +9 -0
  76. package/node_modules/@schibsted/tpaas-event-builder/dist/cjs/builders/device.js +8 -2
  77. package/node_modules/@schibsted/tpaas-event-builder/dist/cjs/builders/provider.js +3 -1
  78. package/node_modules/@schibsted/tpaas-event-builder/dist/cjs/builders/session.js +19 -6
  79. package/node_modules/@schibsted/tpaas-event-builder/dist/cjs/builders.js +75 -76
  80. package/node_modules/@schibsted/tpaas-event-builder/dist/cjs/guards.js +7 -0
  81. package/node_modules/@schibsted/tpaas-event-builder/dist/cjs/index.js +3 -0
  82. package/node_modules/@schibsted/tpaas-event-builder/dist/ejs/builders/catalogVersion.js +6 -0
  83. package/node_modules/@schibsted/tpaas-event-builder/dist/ejs/builders/device.js +8 -2
  84. package/node_modules/@schibsted/tpaas-event-builder/dist/ejs/builders/provider.js +3 -1
  85. package/node_modules/@schibsted/tpaas-event-builder/dist/ejs/builders/session.js +19 -6
  86. package/node_modules/@schibsted/tpaas-event-builder/dist/ejs/builders.js +74 -71
  87. package/node_modules/@schibsted/tpaas-event-builder/dist/ejs/guards.js +4 -0
  88. package/node_modules/@schibsted/tpaas-event-builder/dist/ejs/index.js +1 -0
  89. package/node_modules/@schibsted/tpaas-event-builder/dist/index.d.ts +1730 -1127
  90. package/node_modules/@schibsted/tpaas-event-builder/package.json +3 -2
  91. package/node_modules/@schibsted/tpaas-schemas/dist/cjs/constants.js +136 -103
  92. package/node_modules/@schibsted/tpaas-schemas/dist/cjs/index.js +1 -0
  93. package/node_modules/@schibsted/tpaas-schemas/dist/cjs/types.js +2 -0
  94. package/node_modules/@schibsted/tpaas-schemas/dist/ejs/constants.js +134 -101
  95. package/node_modules/@schibsted/tpaas-schemas/dist/ejs/index.js +1 -0
  96. package/node_modules/@schibsted/tpaas-schemas/dist/ejs/types.js +1 -0
  97. package/node_modules/@schibsted/tpaas-schemas/dist/index.d.ts +1940 -1266
  98. package/node_modules/@schibsted/tpaas-schemas/package.json +1 -1
  99. package/package.json +7 -6
  100. package/dist/cjs/version.json +0 -1
  101. package/dist/ejs/version.json +0 -1
@@ -5,6 +5,8 @@ const ARTICLE_PAYLOAD = {
5
5
  {
6
6
  name: 'test-experiment',
7
7
  variant: 'test-variant',
8
+ platform: 'web',
9
+ experimentSdrn: 'sdrn:the-standard:experiment:test-experiment',
8
10
  },
9
11
  ],
10
12
  object: {
@@ -30,8 +32,6 @@ const ARTICLE_PAYLOAD = {
30
32
  },
31
33
  },
32
34
  provider: {
33
- component: 'test-component',
34
- componentVersion: '1.0.0',
35
35
  product: 'test-product',
36
36
  stage: 'Dev',
37
37
  version: '1.0.0',
@@ -12,11 +12,11 @@ export function generateActorSDRN(id, realm = '__REALM_NOT_SET__') {
12
12
  return `sdrn:${realm}:user:${outputId}`;
13
13
  }
14
14
  /**
15
- * Type guard to check if the input is an ActorInput
15
+ * Type guard to check if the input has an Actor Builder
16
16
  * @param input
17
17
  * @internal
18
18
  */
19
- export function isActorInput(input) {
19
+ export function hasActorBuilder(input) {
20
20
  // biome-ignore lint/suspicious/noPrototypeBuiltins: Object.hasOwn is not available in ES2020
21
21
  return isPlainObject(input) && Object.prototype.hasOwnProperty.call(input, 'actor');
22
22
  }
@@ -3,7 +3,7 @@ import { isPromise } from '@schibsted/pulse-utils';
3
3
  import warnOnce from '../warnOnce';
4
4
  import { getFallbackConsents } from './consent-manager';
5
5
  /**
6
- * @enum ConsentCategory
6
+ * @enum ConsentsCategory
7
7
  */
8
8
  export var ConsentsCategory;
9
9
  (function (ConsentsCategory) {
@@ -12,7 +12,7 @@ export var ConsentsCategory;
12
12
  ConsentsCategory["CMP_ADVERTISING"] = "CMP_ADVERTISING";
13
13
  ConsentsCategory["CMP_PERSONALIZATION"] = "CMP_PERSONALIZATION";
14
14
  })(ConsentsCategory || (ConsentsCategory = {}));
15
- export const ConsentsSourceValues = ['default', 'cmp', 'cache'];
15
+ export const ConsentsSourceValues = ['default', 'cmp', 'cache', 'url'];
16
16
  export const ConsentsPurposeCategoryStatusValues = ['accepted', 'rejected', 'unknown'];
17
17
  const isLegacyConsentsPurpose = (consentsPurposeCategory) => {
18
18
  return !consentsPurposeCategory?.status;
@@ -25,7 +25,7 @@ export function anonymousTrackerEvent({ eventInput, sdkConfig } = {}) {
25
25
  return Object.assign({}, omit(eventInput, ['type']), {
26
26
  '@type': eventInput?.type || 'View',
27
27
  schema: eventInput?.schema ||
28
- `http://${sdkConfig?.schemaLocation || DEFAULT_SCHEMA_LOCATION}/events/tracker-event.json/351.json`,
28
+ `http://${sdkConfig?.schemaLocation || DEFAULT_SCHEMA_LOCATION}/events/tracker-event.json/360.json`,
29
29
  consents: consentsBuilder(parentInput),
30
30
  experiments: experimentsBuilder(parentInput),
31
31
  experimentMetadata: experimentMetadataBuilder(parentInput),
@@ -22,7 +22,7 @@ function routableEventDefaults(eventInput) {
22
22
  export function routableEvent({ eventInput, sdkConfig } = {}) {
23
23
  return Object.assign({}, eventInput, {
24
24
  schema: eventInput?.schema ||
25
- `http://${sdkConfig?.schemaLocation || DEFAULT_SCHEMA_LOCATION}/events/base-routable-event.json/246.json#`,
25
+ `http://${sdkConfig?.schemaLocation || DEFAULT_SCHEMA_LOCATION}/events/base-routable-event.json/248.json#`,
26
26
  provider: providerBuilder(eventInput),
27
27
  actor: actorBuilder(eventInput),
28
28
  });
@@ -55,7 +55,7 @@ export function trackerEvent({ eventInput, sdkConfig } = {}) {
55
55
  '@type': eventInput?.type || 'View',
56
56
  pageViewId: pageViewIdInput || pageViewIdSdk,
57
57
  schema: eventInput?.schema ||
58
- `http://${sdkConfig?.schemaLocation || DEFAULT_SCHEMA_LOCATION}/events/tracker-event.json/359.json`,
58
+ `http://${sdkConfig?.schemaLocation || DEFAULT_SCHEMA_LOCATION}/events/tracker-event.json/361.json`,
59
59
  actor: actorBuilder(parentInput),
60
60
  consents: consentsBuilder(parentInput),
61
61
  experiments: experimentsBuilder(parentInput),
@@ -73,7 +73,7 @@ trackerEvent.defaults = trackerEventInput;
73
73
  export function engagementEvent({ eventInput, sdkConfig } = {}) {
74
74
  return Object.assign(trackerEvent({ eventInput, sdkConfig }), {
75
75
  schema: eventInput?.schema ||
76
- `http://${sdkConfig?.schemaLocation || DEFAULT_SCHEMA_LOCATION}/events/engagement-event.json/386.json`,
76
+ `http://${sdkConfig?.schemaLocation || DEFAULT_SCHEMA_LOCATION}/events/engagement-event.json/388.json`,
77
77
  '@type': 'Engagement',
78
78
  });
79
79
  }
@@ -1,4 +1,4 @@
1
- function generateExperimentSDRN(id, experimentationPlatform) {
1
+ export function generateExperimentSDRN(id, experimentationPlatform) {
2
2
  return `sdrn:${experimentationPlatform}:experiment:${id}`;
3
3
  }
4
4
  /**
@@ -1,4 +1,4 @@
1
- import version from '../version.json';
1
+ import { SDK_VERSION } from '../version';
2
2
  /**
3
3
  * Format tracker objects
4
4
  */
@@ -7,7 +7,7 @@ export default function tracker(input) {
7
7
  const base = {
8
8
  name: 'Pulse Node.js SDK',
9
9
  type: 'JS',
10
- version: version.SDK_VERSION,
10
+ version: SDK_VERSION,
11
11
  };
12
12
  return Object.assign({}, base, trackerInput);
13
13
  }
@@ -1,12 +1,10 @@
1
1
  import { evaluateAndFlatten } from '@schibsted/pulse-utils';
2
- import { isActorInput } from '../builders/actor';
2
+ import { hasActorBuilder } from '../builders/actor';
3
3
  import warnOnce from '../warnOnce';
4
4
  /**
5
5
  * @internal exported for testing
6
6
  */
7
- export const WARN_CIS_SYNC_LOGIN_UNDEFINED = 'Cis sync initiated with no actor builder and no event available. If you do not use a login system, please indicate' +
8
- ' this using tracker.update({ actor: undefined }), otherwise do tracker.update({ actor: somePromise }) with a' +
9
- ' promise that resolves to the login state.';
7
+ export const WARN_CIS_SYNC_LOGIN_UNDEFINED = 'No Actor Builder found in event input or tracker builders. Please provide an Actor Builder to ensure the SDK resolves logged-in and logged-out scenarios correctly.';
10
8
  /**
11
9
  * Get the actor id (userId) from the current browser. A promise is returned
12
10
  * because a call may be made to complete the login process.
@@ -25,6 +23,19 @@ const getUserId = async (builders) => {
25
23
  const getActor = async (builders) => {
26
24
  return await evaluateActor(builders);
27
25
  };
26
+ /**
27
+ * Pinpoints the Actor Builder from the Event payload or the available Builders
28
+ * @param eventInput - The event input which may contain an `actor` property
29
+ * @param builders - The builders managed by the SDK.
30
+ */
31
+ function getActorSource(eventInput = {}, builders = {}) {
32
+ if (hasActorBuilder(eventInput)) {
33
+ return eventInput.actor;
34
+ }
35
+ if (hasActorBuilder(builders)) {
36
+ return builders.actor;
37
+ }
38
+ }
28
39
  const userIsLoggedIn = async (builders) => {
29
40
  const actor = await getActor(builders);
30
41
  return !!actor?.id;
@@ -36,8 +47,9 @@ const userIsLoggedIn = async (builders) => {
36
47
  * @private
37
48
  */
38
49
  const evaluateActor = async (eventInput = {}, builders = {}) => {
39
- if (isActorInput(eventInput) || isActorInput(builders)) {
40
- const evaluatedActor = await evaluateAndFlatten(eventInput.actor || builders.actor);
50
+ const actorSource = getActorSource(eventInput, builders);
51
+ if (actorSource) {
52
+ const evaluatedActor = await evaluateAndFlatten(actorSource);
41
53
  if (evaluatedActor) {
42
54
  return evaluatedActor;
43
55
  }
@@ -45,7 +57,7 @@ const evaluateActor = async (eventInput = {}, builders = {}) => {
45
57
  // We allow an empty actor object, because it is a sign that the application is not setting nor updating Actor correctly.
46
58
  // Inferring "id: undefined" as a fallback would mask that bug.
47
59
  // Hence, the warning below.
48
- warnOnce('no-actor-builder-and-no-event-present', WARN_CIS_SYNC_LOGIN_UNDEFINED);
60
+ warnOnce('no-actor-builder', WARN_CIS_SYNC_LOGIN_UNDEFINED);
49
61
  return {};
50
62
  };
51
63
  export { getUserId, getActor, userIsLoggedIn, evaluateActor };
@@ -1,5 +1,5 @@
1
1
  import { clearCookieJweIfStale, resetRefreshAfter, syncWithCis } from '@schibsted/pulse-cis-sync';
2
- import version from '../version.json';
2
+ import { SDK_VERSION } from '../version';
3
3
  import { evaluateActor } from './actor';
4
4
  const requireCisSyncCallForAdvertisingIds = () => {
5
5
  resetRefreshAfter(new Date());
@@ -40,14 +40,15 @@ const cisSync = async (data, builders, config) => {
40
40
  // _pulse2data cookie, or just not do the sync.
41
41
  //
42
42
  const actorPromise = evaluateActor(data, builders);
43
- const { cisBaseUrl, cisEnabled, includeAdvertising: includeAdvertisingPromise, isHybrid, nativeJwe, providerId, trackerId, vendors, } = config || {};
43
+ const { adChoicesEnabled, cisBaseUrl, cisEnabled, includeAdvertising: includeAdvertisingPromise, isHybrid, nativeJwe, providerId, trackerId, vendors, } = config || {};
44
44
  const [includeAdvertising, actor] = await Promise.all([includeAdvertisingPromise, actorPromise]);
45
45
  const { id, realm } = actor;
46
46
  if (cisEnabled) {
47
47
  // @ts-expect-error
48
48
  return syncWithCis(
49
49
  // @ts-expect-error
50
- id, nativeJwe, cisBaseUrl, realm, providerId, isHybrid ? 'Hybrid' : 'Web', version.SDK_VERSION, {
50
+ id, nativeJwe, cisBaseUrl, realm, providerId, isHybrid ? 'Hybrid' : 'Web', SDK_VERSION, {
51
+ adChoicesEnabled,
51
52
  includeAdvertising,
52
53
  vendors,
53
54
  trackerId,
@@ -1,8 +1,3 @@
1
- export const DEFAULT_ACTIVITY_DURATION = () => ({
2
- value: 0,
3
- interval: undefined,
4
- timeSinceLastActivity: 0,
5
- });
6
1
  export const DEFAULT_PAGE_LEAVE_EVENT = () => ({
7
2
  objectViewPercentage: 0,
8
3
  maxObjectViewPercentage: 0,
@@ -11,3 +6,4 @@ export const DEFAULT_PAGE_LEAVE_EVENT = () => ({
11
6
  pageScrollPosition: 0,
12
7
  maxPageScrollPosition: 0,
13
8
  });
9
+ export const ACTIVITY_DURATION_LIMIT_MS = 30000;
@@ -1,6 +1,6 @@
1
1
  import { isBrowser, isFunction, throttle } from '@schibsted/pulse-utils';
2
2
  import * as logger from 'loglevel';
3
- import { DEFAULT_ACTIVITY_DURATION, DEFAULT_PAGE_LEAVE_EVENT } from './constants';
3
+ import { ACTIVITY_DURATION_LIMIT_MS, DEFAULT_PAGE_LEAVE_EVENT } from './constants';
4
4
  import { BrowserEvents } from './types';
5
5
  let currentLeaveSchema;
6
6
  let currentTrackMethod;
@@ -9,19 +9,42 @@ let updateViewedContentListeners = [];
9
9
  let activityListeners = [];
10
10
  let resizeObserver = null;
11
11
  let throttledUpdateFn = throttle(() => undefined, 0);
12
- let startTime = 0;
13
- let activityDuration = DEFAULT_ACTIVITY_DURATION();
12
+ let durationStartTime = 0;
13
+ let activityStartTime = 0;
14
14
  let currentLeaveEvent = DEFAULT_PAGE_LEAVE_EVENT();
15
15
  let additionalHeightBuilder;
16
+ /**
17
+ * Resolves a DOM element from either a CSS selector string or an Element instance.
18
+ */
19
+ function resolveDomElement(element) {
20
+ try {
21
+ return typeof element === 'string' ? document.querySelector(element) : element || null;
22
+ }
23
+ catch (e) {
24
+ logger.error('Error while resolving DOM element', e);
25
+ return null;
26
+ }
27
+ }
16
28
  /*
17
29
  Add leave tracking
18
30
  */
19
31
  export function addTpaasLeaveTracking({ objectElement, pageElement, schema, objectResizable, heightBuilder, trackMethod, }) {
20
32
  function _addLeaveTracking() {
33
+ const objectDomElement = resolveDomElement(objectElement);
34
+ if (objectDomElement == null) {
35
+ logger.error('Could not find object element for leave tracking');
36
+ return;
37
+ }
38
+ let pageDomElement = resolveDomElement(pageElement);
39
+ if (pageDomElement == null) {
40
+ logger.warn('Could not find page element for leave tracking, defaulting to document.body');
41
+ pageDomElement = document.body;
42
+ }
21
43
  additionalHeightBuilder = heightBuilder;
22
44
  addTpaasLeaveListeners();
23
- addUpdateEventListeners(objectElement, pageElement || document.body, objectResizable);
24
- startTime = resetTime();
45
+ addUpdateEventListeners(objectDomElement, pageDomElement, objectResizable);
46
+ activityStartTime = resetTime();
47
+ durationStartTime = resetTime();
25
48
  currentLeaveSchema = schema;
26
49
  currentTrackMethod = trackMethod;
27
50
  logger.debug('addLeaveTracking:after:state', currentLeaveEvent);
@@ -113,12 +136,8 @@ function removeUpdateEventListeners() {
113
136
  */
114
137
  const resetEventDefaults = () => {
115
138
  currentLeaveEvent = DEFAULT_PAGE_LEAVE_EVENT();
116
- startTime = resetTime();
117
- activityDuration = resetActivityDuration();
118
- };
119
- const resetActivityDuration = () => {
120
- clearInterval(activityDuration.interval);
121
- return DEFAULT_ACTIVITY_DURATION();
139
+ durationStartTime = resetTime();
140
+ activityStartTime = resetTime();
122
141
  };
123
142
  export const resetTpaasViewedContent = () => {
124
143
  currentLeaveEvent = DEFAULT_PAGE_LEAVE_EVENT();
@@ -126,34 +145,41 @@ export const resetTpaasViewedContent = () => {
126
145
  const resetTime = () => Date.now();
127
146
  /*
128
147
  Send a Leave event only if Page Leave tracking is active in the SDK
129
- The event that is ultimately sent is a merge between the default event
130
- saved in this file and the event supplied by the brand, with preference going
131
- to the brand event. After the event has been sent, leave page tracking is removed
148
+ The event that is ultimately sent is the View event saved in the currentTrackMethod callback.
149
+ After the event has been sent, leave page tracking is removed
132
150
  and must be explicitly re-added if needed.
133
151
  */
134
- export function trackTpaasActiveLeave() {
152
+ export async function trackTpaasActiveLeave() {
135
153
  if (leaveTpaasTrackingIsActive()) {
136
- trackTpaasLeave();
154
+ await trackTpaasLeave();
137
155
  }
138
156
  }
139
157
  /*
140
158
  Send a Leave event regardless of whether Page Leave tracking is active in the SDK
141
- The event that is ultimately sent is a merge between the default event
142
- saved in this file and the event supplied by the brand, with preference going
143
- to the brand event. After the event has been sent, leave page tracking is removed
159
+ The event that is ultimately sent is the View event saved in the currentTrackMethod callback.
160
+ After the event has been sent, leave page tracking is removed
144
161
  and must be explicitly re-added if needed.
145
162
  */
146
- export function trackTpaasLeave() {
163
+ export async function trackTpaasLeave() {
147
164
  const leave = getTpaasLeaveObject();
148
- currentTrackMethod(leave);
165
+ // Capture currentTrackMethod and remove tracking synchronously before awaiting.
166
+ // If removal happened after the await, a new trackView* call arriving during the async
167
+ // gap would see tracking still active with the same schema and silently discard its
168
+ // new callback (e.g. a fresh leaveAudioBuilder), causing the next leave event to
169
+ // never fire or to use the wrong builder.
170
+ const trackMethod = currentTrackMethod;
149
171
  removeTpaasLeaveTracking();
172
+ await trackMethod(leave);
150
173
  }
151
174
  export function getTpaasLeaveObject() {
152
175
  const endTime = Date.now();
153
- const duration = endTime - startTime;
176
+ // TPaaS leave event requires duration/activity to be at least 1ms, otherwise the event is rejected
177
+ // also, in practice, it's almost impossible to have an activity or duration of 0ms
178
+ const durationMs = Math.max(endTime - durationStartTime, 1);
179
+ const activityDurationMs = Math.max(Math.min(endTime - activityStartTime, ACTIVITY_DURATION_LIMIT_MS), 1);
154
180
  const leave = {
155
- durationMs: duration,
156
- activityDurationMs: activityDuration.value,
181
+ durationMs,
182
+ activityDurationMs,
157
183
  ...currentLeaveEvent,
158
184
  };
159
185
  logger.debug('trackTpaasLeave: event', leave);
@@ -226,18 +252,10 @@ export const calculateScrollPosition = (element) => Math.abs(Math.min(element.ge
226
252
  'mousedown', 'mousemove', 'keydown', 'scroll' and/or 'touchstart' browser events.
227
253
  */
228
254
  function addUpdateActivityDurationListeners() {
229
- if (activityDuration.interval !== undefined) {
230
- // If the interval is already set, do not set it again
255
+ // if there are already listeners for activity, do not add more
256
+ if (activityListeners.length > 0) {
231
257
  return;
232
258
  }
233
- const interval = 1000;
234
- const activityTimeout = 30 * 1000;
235
- activityDuration.interval = window.setInterval(() => {
236
- activityDuration.timeSinceLastActivity += interval;
237
- if (activityDuration.timeSinceLastActivity < activityTimeout) {
238
- activityDuration.value += interval;
239
- }
240
- }, interval);
241
259
  const activityEvents = [
242
260
  BrowserEvents.SCROLL,
243
261
  BrowserEvents.MOUSEDOWN,
@@ -246,15 +264,9 @@ function addUpdateActivityDurationListeners() {
246
264
  BrowserEvents.TOUCHSTART,
247
265
  ];
248
266
  activityListeners = addEventListeners(activityEvents, () => {
249
- activityDuration = resetLastActivity(activityDuration);
267
+ activityStartTime = resetTime();
250
268
  }, { passive: true });
251
269
  }
252
- function resetLastActivity(activityDuration) {
253
- return {
254
- ...activityDuration,
255
- timeSinceLastActivity: 0,
256
- };
257
- }
258
270
  /*
259
271
  Add listeners to a set of browser events.
260
272
  */
@@ -33,15 +33,13 @@ export const TPAAS_ALLOWED_METHODS = [
33
33
  'trackLeaveLandingpage',
34
34
  'trackLeaveListing',
35
35
  'trackLeaveLockedArticle',
36
- 'trackLeaveLockedAudio',
37
36
  'trackLeaveLockedAudioPage',
38
- 'trackLeaveLockedVideo',
39
37
  'trackLeaveLockedVideoPage',
40
38
  'trackLeavePage',
39
+ 'trackLeaveSportsPage',
41
40
  'trackLeaveVideoPage',
42
41
  'trackLeaveWeather',
43
42
  'trackViewArticle',
44
- 'trackViewAudio',
45
43
  'trackViewAudioPage',
46
44
  'trackViewError',
47
45
  'trackViewFrontpage',
@@ -49,15 +47,23 @@ export const TPAAS_ALLOWED_METHODS = [
49
47
  'trackViewLandingpage',
50
48
  'trackViewListing',
51
49
  'trackViewLockedArticle',
52
- 'trackViewLockedAudio',
53
50
  'trackViewLockedAudioPage',
54
- 'trackViewLockedVideo',
55
51
  'trackViewLockedVideoPage',
56
52
  'trackViewPage',
53
+ 'trackViewSportsPage',
57
54
  'trackViewTitle',
58
- 'trackViewVideo',
59
55
  'trackViewVideoPage',
60
56
  'trackViewWeather',
57
+ // these are not TPaaS methods,
58
+ // but they're part of this list to not have to duplicate the tracker-tpaas-proxy.ts implementation in tracker-proxy.ts
59
+ 'trackLegacyViewArticle',
60
+ 'trackLegacyViewLoginPoster',
61
+ 'trackLegacyViewSalesPoster',
62
+ 'trackLegacyViewListing',
63
+ 'trackLegacyViewFrontpage',
64
+ 'trackLegacyViewPage',
65
+ 'trackLegacyViewUIElement',
66
+ 'trackLegacyClickUIElement',
61
67
  ];
62
68
  export function isPulseProxyMessage(message) {
63
69
  return (message !== null &&
@@ -7,6 +7,11 @@ export function startProxyProviderListener(tracker) {
7
7
  logger.debug('Proxy Provider - Not in browser environment, skipping message listener');
8
8
  return () => { };
9
9
  }
10
+ if (tracker.hasProxyListener) {
11
+ logger.debug('Proxy Provider - Message listener already registered for this tracker, skipping');
12
+ return () => { };
13
+ }
14
+ tracker.hasProxyListener = true;
10
15
  logger.debug('Proxy Provider - Added message listener for proxy provider');
11
16
  const listener = async (event) => {
12
17
  logger.debug('Proxy Provider - message received', event.data);
@@ -58,5 +63,6 @@ export function startProxyProviderListener(tracker) {
58
63
  window.addEventListener('message', listener);
59
64
  return () => {
60
65
  window.removeEventListener('message', listener);
66
+ tracker.hasProxyListener = false;
61
67
  };
62
68
  }
@@ -0,0 +1,2 @@
1
+ // This file is auto-generated by bin/generate-version.js. Do not edit manually.
2
+ export const SDK_VERSION = '8.0.0-rc.10';
@@ -13,14 +13,16 @@ export class VisibilityObserver {
13
13
  * Uses {@link https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver | MutationObserver} to monitor the DOM for dynamically loaded content.
14
14
  * Callback is invoked only once per element.
15
15
  * @param selector The CSS selector for elements to track.
16
+ * @param options {ObserverOptions} Optional configuration for the observer.
17
+ * @param options.threshold Which ratio triggers the Observer callback.
16
18
  * @throws Error if called more than once.
17
19
  */
18
- observe(selector) {
20
+ observe(selector, { threshold = 0.67 }) {
19
21
  if (this.selector) {
20
22
  throw new Error(`Cannot observe selector ${selector}. A visibility observer has already been set up for selector ${this.selector}.`);
21
23
  }
22
24
  this.selector = selector;
23
- this.setupObservers();
25
+ this.setupObservers({ threshold });
24
26
  // biome-ignore lint/suspicious/useIterableCallbackReturn: -
25
27
  this.elements().forEach((elementToTrack) => this.observeIntersection(elementToTrack));
26
28
  }
@@ -30,14 +32,14 @@ export class VisibilityObserver {
30
32
  elements() {
31
33
  return Array.from(document.querySelectorAll(this.selector));
32
34
  }
33
- // Initialize both observers and start watching for DOM changes
34
- setupObservers() {
35
- this.setupIntersectionObserver();
35
+ // Initialise both observers and start watching for DOM changes
36
+ setupObservers({ threshold }) {
37
+ this.setupIntersectionObserver({ threshold });
36
38
  this.setupMutationObserver();
37
39
  this.observeMutations();
38
40
  }
39
41
  // Create observer to detect when elements become visible in the viewport
40
- setupIntersectionObserver() {
42
+ setupIntersectionObserver({ threshold }) {
41
43
  this.intersectionObserver = new IntersectionObserver((entries) => {
42
44
  entries.forEach((entry) => {
43
45
  if (entry.isIntersecting) {
@@ -46,7 +48,7 @@ export class VisibilityObserver {
46
48
  this.intersectionObserver.unobserve(node);
47
49
  }
48
50
  });
49
- }, { threshold: 2 / 3 });
51
+ }, { threshold });
50
52
  }
51
53
  // Create observer to detect DOM changes that may add/remove tracked elements
52
54
  setupMutationObserver() {
@@ -1 +1,4 @@
1
- export {};
1
+ const LEGACY_PAGE_TYPE_VALUES = ['Article', 'Listing', 'Frontpage', 'SalesPoster', 'Page', 'LoginPoster'];
2
+ export function isLegacyPageType(value) {
3
+ return LEGACY_PAGE_TYPE_VALUES.includes(value);
4
+ }