@guardian/commercial-core 31.0.0 → 32.0.0

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 (34) hide show
  1. package/dist/cjs/ad-sizes.d.ts +1 -1
  2. package/dist/cjs/ad-sizes.js +1 -1
  3. package/dist/cjs/constants/index.d.ts +3 -3
  4. package/dist/cjs/constants/index.js +3 -3
  5. package/dist/cjs/email-hash.d.ts +1 -1
  6. package/dist/cjs/email-hash.js +1 -0
  7. package/dist/cjs/geo/geo-utils.js +1 -1
  8. package/dist/cjs/global-ad-events.d.ts +8 -0
  9. package/dist/cjs/global-ad-events.js +11 -0
  10. package/dist/cjs/global.d.ts +1 -1
  11. package/dist/cjs/index.d.ts +15 -14
  12. package/dist/cjs/index.js +14 -12
  13. package/dist/cjs/send-commercial-metrics.d.ts +1 -1
  14. package/dist/cjs/send-commercial-metrics.js +1 -1
  15. package/dist/cjs/targeting/build-page-targeting.d.ts +6 -7
  16. package/dist/cjs/targeting/build-page-targeting.js +9 -13
  17. package/dist/cjs/targeting/session.d.ts +4 -17
  18. package/dist/cjs/targeting/session.js +6 -22
  19. package/dist/cjs/targeting/youtube-ima.d.ts +3 -4
  20. package/dist/cjs/targeting/youtube-ima.js +5 -5
  21. package/dist/cjs/types.d.ts +2 -2
  22. package/dist/esm/email-hash.d.ts +1 -1
  23. package/dist/esm/email-hash.js +1 -0
  24. package/dist/esm/global-ad-events.d.ts +8 -0
  25. package/dist/esm/global-ad-events.js +8 -0
  26. package/dist/esm/index.d.ts +1 -0
  27. package/dist/esm/index.js +1 -0
  28. package/dist/esm/targeting/build-page-targeting.d.ts +2 -3
  29. package/dist/esm/targeting/build-page-targeting.js +2 -6
  30. package/dist/esm/targeting/session.d.ts +4 -17
  31. package/dist/esm/targeting/session.js +6 -22
  32. package/dist/esm/targeting/youtube-ima.d.ts +2 -3
  33. package/dist/esm/targeting/youtube-ima.js +4 -4
  34. package/package.json +4 -6
@@ -1,4 +1,4 @@
1
- import type { Breakpoint } from './breakpoint';
1
+ import type { Breakpoint } from './breakpoint.js';
2
2
  type AdSizeString = 'fluid' | `${number},${number}`;
3
3
  /**
4
4
  * Store ad sizes in a way that is compatible with google-tag but also accessible via
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.findAppliedSizesForBreakpoint = exports.createAdSize = exports.slotSizeMappings = exports.getAdSize = exports.outstreamSizes = exports.standardAdSizes = exports.adSizes = exports.AdSize = exports._ = void 0;
4
- const breakpoint_1 = require("./breakpoint");
4
+ const breakpoint_1 = require("./breakpoint.js");
5
5
  /**
6
6
  * Store ad sizes in a way that is compatible with google-tag but also accessible via
7
7
  * more semantic `width`/`height` properties and keep things readonly.
@@ -1,3 +1,3 @@
1
- export { AD_LABEL_HEIGHT } from './ad-label-height';
2
- export { PREBID_TIMEOUT } from './prebid-timeout';
3
- export { TOP_ABOVE_NAV_HEIGHT } from './top-above-nav-height';
1
+ export { AD_LABEL_HEIGHT } from './ad-label-height.js';
2
+ export { PREBID_TIMEOUT } from './prebid-timeout.js';
3
+ export { TOP_ABOVE_NAV_HEIGHT } from './top-above-nav-height.js';
@@ -1,9 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.TOP_ABOVE_NAV_HEIGHT = exports.PREBID_TIMEOUT = exports.AD_LABEL_HEIGHT = void 0;
4
- var ad_label_height_1 = require("./ad-label-height");
4
+ var ad_label_height_1 = require("./ad-label-height.js");
5
5
  Object.defineProperty(exports, "AD_LABEL_HEIGHT", { enumerable: true, get: function () { return ad_label_height_1.AD_LABEL_HEIGHT; } });
6
- var prebid_timeout_1 = require("./prebid-timeout");
6
+ var prebid_timeout_1 = require("./prebid-timeout.js");
7
7
  Object.defineProperty(exports, "PREBID_TIMEOUT", { enumerable: true, get: function () { return prebid_timeout_1.PREBID_TIMEOUT; } });
8
- var top_above_nav_height_1 = require("./top-above-nav-height");
8
+ var top_above_nav_height_1 = require("./top-above-nav-height.js");
9
9
  Object.defineProperty(exports, "TOP_ABOVE_NAV_HEIGHT", { enumerable: true, get: function () { return top_above_nav_height_1.TOP_ABOVE_NAV_HEIGHT; } });
@@ -1,4 +1,4 @@
1
- type HashClient = 'euid' | 'id5' | 'liveramp' | 'uid2';
1
+ type HashClient = 'euid' | 'id5' | 'liveramp' | 'uid2' | 'permutive';
2
2
  type Email = `${string}@${string}`;
3
3
  declare function normaliseEmail(email: string): Email;
4
4
  declare function hashEmailForClient(email: string, client: HashClient): Promise<string>;
@@ -33,6 +33,7 @@ async function hashEmailForClient(email, client) {
33
33
  return toBase64(hashBuffer);
34
34
  case 'id5':
35
35
  case 'liveramp':
36
+ case 'permutive':
36
37
  return toHex(hashBuffer);
37
38
  }
38
39
  }
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports._ = exports.isInRow = exports.isInAuOrNz = exports.isInUsOrCa = exports.isInNewZealand = exports.isInAustralia = exports.isInCanada = exports.isInUsa = exports.isInUk = void 0;
4
- const country_code_1 = require("./country-code");
4
+ const country_code_1 = require("./country-code.js");
5
5
  // cache the users location so we only have to look it up once
6
6
  let geo;
7
7
  const currentGeoLocation = () => {
@@ -0,0 +1,8 @@
1
+ /**
2
+ * `globalAdEvents` acts as an event bus that broadcasts ad lifecycle
3
+ * events, allowing consumers to react to ad status changes without
4
+ * needing direct access to `Advert` instances.
5
+ * @see /docs/global-ad-events.md
6
+ */
7
+ declare const globalAdEvents: EventTarget;
8
+ export { globalAdEvents };
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.globalAdEvents = void 0;
4
+ /**
5
+ * `globalAdEvents` acts as an event bus that broadcasts ad lifecycle
6
+ * events, allowing consumers to react to ad status changes without
7
+ * needing direct access to `Advert` instances.
8
+ * @see /docs/global-ad-events.md
9
+ */
10
+ const globalAdEvents = new EventTarget();
11
+ exports.globalAdEvents = globalAdEvents;
@@ -1,4 +1,4 @@
1
- import type { CoreGuardian } from './types';
1
+ import type { CoreGuardian } from './types.js';
2
2
  declare global {
3
3
  interface Window {
4
4
  guardian: CoreGuardian;
@@ -1,14 +1,15 @@
1
- export { isAdBlockInUse } from './detect-ad-blocker';
2
- export { EventTimer } from './event-timer';
3
- export { adSizes } from './ad-sizes';
4
- export * as constants from './constants';
5
- export { bypassCommercialMetricsSampling, initCommercialMetrics, } from './send-commercial-metrics';
6
- export { buildPageTargeting } from './targeting/build-page-targeting';
7
- export { postMessage } from './messenger/post-message';
8
- export { buildImaAdTagUrl } from './targeting/youtube-ima';
9
- export { getPermutivePFPSegments } from './permutive';
10
- export { isEligibleForTeads } from './targeting/teads-eligibility';
11
- export { hashEmailForClient } from './email-hash';
12
- export type { AdSize, SizeMapping, SlotName } from './ad-sizes';
13
- export type { PageTargeting } from './targeting/build-page-targeting';
14
- export type { AdsConfigDisabled, AdsConfigUSNATorAus, AdsConfigTCFV2, } from './types';
1
+ export { isAdBlockInUse } from './detect-ad-blocker.js';
2
+ export { EventTimer } from './event-timer.js';
3
+ export { adSizes } from './ad-sizes.js';
4
+ export * as constants from './constants/index.js';
5
+ export { bypassCommercialMetricsSampling, initCommercialMetrics, } from './send-commercial-metrics.js';
6
+ export { buildPageTargeting } from './targeting/build-page-targeting.js';
7
+ export { postMessage } from './messenger/post-message.js';
8
+ export { buildImaAdTagUrl } from './targeting/youtube-ima.js';
9
+ export { getPermutivePFPSegments } from './permutive.js';
10
+ export { isEligibleForTeads } from './targeting/teads-eligibility.js';
11
+ export { hashEmailForClient } from './email-hash.js';
12
+ export { globalAdEvents } from './global-ad-events.js';
13
+ export type { AdSize, SizeMapping, SlotName } from './ad-sizes.js';
14
+ export type { PageTargeting } from './targeting/build-page-targeting.js';
15
+ export type { AdsConfigDisabled, AdsConfigUSNATorAus, AdsConfigTCFV2, } from './types.js';
package/dist/cjs/index.js CHANGED
@@ -33,26 +33,28 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.hashEmailForClient = exports.isEligibleForTeads = exports.getPermutivePFPSegments = exports.buildImaAdTagUrl = exports.buildPageTargeting = exports.initCommercialMetrics = exports.bypassCommercialMetricsSampling = exports.constants = exports.adSizes = exports.EventTimer = exports.isAdBlockInUse = void 0;
37
- var detect_ad_blocker_1 = require("./detect-ad-blocker");
36
+ exports.globalAdEvents = exports.hashEmailForClient = exports.isEligibleForTeads = exports.getPermutivePFPSegments = exports.buildImaAdTagUrl = exports.buildPageTargeting = exports.initCommercialMetrics = exports.bypassCommercialMetricsSampling = exports.constants = exports.adSizes = exports.EventTimer = exports.isAdBlockInUse = void 0;
37
+ var detect_ad_blocker_1 = require("./detect-ad-blocker.js");
38
38
  Object.defineProperty(exports, "isAdBlockInUse", { enumerable: true, get: function () { return detect_ad_blocker_1.isAdBlockInUse; } });
39
- var event_timer_1 = require("./event-timer");
39
+ var event_timer_1 = require("./event-timer.js");
40
40
  Object.defineProperty(exports, "EventTimer", { enumerable: true, get: function () { return event_timer_1.EventTimer; } });
41
- var ad_sizes_1 = require("./ad-sizes");
41
+ var ad_sizes_1 = require("./ad-sizes.js");
42
42
  Object.defineProperty(exports, "adSizes", { enumerable: true, get: function () { return ad_sizes_1.adSizes; } });
43
- exports.constants = __importStar(require("./constants"));
44
- var send_commercial_metrics_1 = require("./send-commercial-metrics");
43
+ exports.constants = __importStar(require("./constants/index.js"));
44
+ var send_commercial_metrics_1 = require("./send-commercial-metrics.js");
45
45
  Object.defineProperty(exports, "bypassCommercialMetricsSampling", { enumerable: true, get: function () { return send_commercial_metrics_1.bypassCommercialMetricsSampling; } });
46
46
  Object.defineProperty(exports, "initCommercialMetrics", { enumerable: true, get: function () { return send_commercial_metrics_1.initCommercialMetrics; } });
47
- var build_page_targeting_1 = require("./targeting/build-page-targeting");
47
+ var build_page_targeting_1 = require("./targeting/build-page-targeting.js");
48
48
  Object.defineProperty(exports, "buildPageTargeting", { enumerable: true, get: function () { return build_page_targeting_1.buildPageTargeting; } });
49
- var post_message_1 = require("./messenger/post-message");
49
+ var post_message_1 = require("./messenger/post-message.js");
50
50
  Object.defineProperty(exports, "postMessage", { enumerable: true, get: function () { return post_message_1.postMessage; } });
51
- var youtube_ima_1 = require("./targeting/youtube-ima");
51
+ var youtube_ima_1 = require("./targeting/youtube-ima.js");
52
52
  Object.defineProperty(exports, "buildImaAdTagUrl", { enumerable: true, get: function () { return youtube_ima_1.buildImaAdTagUrl; } });
53
- var permutive_1 = require("./permutive");
53
+ var permutive_1 = require("./permutive.js");
54
54
  Object.defineProperty(exports, "getPermutivePFPSegments", { enumerable: true, get: function () { return permutive_1.getPermutivePFPSegments; } });
55
- var teads_eligibility_1 = require("./targeting/teads-eligibility");
55
+ var teads_eligibility_1 = require("./targeting/teads-eligibility.js");
56
56
  Object.defineProperty(exports, "isEligibleForTeads", { enumerable: true, get: function () { return teads_eligibility_1.isEligibleForTeads; } });
57
- var email_hash_1 = require("./email-hash");
57
+ var email_hash_1 = require("./email-hash.js");
58
58
  Object.defineProperty(exports, "hashEmailForClient", { enumerable: true, get: function () { return email_hash_1.hashEmailForClient; } });
59
+ var global_ad_events_1 = require("./global-ad-events.js");
60
+ Object.defineProperty(exports, "globalAdEvents", { enumerable: true, get: function () { return global_ad_events_1.globalAdEvents; } });
@@ -1,4 +1,4 @@
1
- import type { ConnectionType } from './types';
1
+ import type { ConnectionType } from './types.js';
2
2
  type Metric = {
3
3
  name: string;
4
4
  value: number;
@@ -4,7 +4,7 @@ exports.checkConsent = exports._ = void 0;
4
4
  exports.bypassCommercialMetricsSampling = bypassCommercialMetricsSampling;
5
5
  exports.initCommercialMetrics = initCommercialMetrics;
6
6
  const libs_1 = require("@guardian/libs");
7
- const event_timer_1 = require("./event-timer");
7
+ const event_timer_1 = require("./event-timer.js");
8
8
  var Endpoints;
9
9
  (function (Endpoints) {
10
10
  Endpoints["CODE"] = "//performance-events.code.dev-guardianapis.com/commercial-metrics";
@@ -1,9 +1,8 @@
1
- import type { Participations } from '@guardian/ab-core';
2
1
  import type { ConsentState, CountryCode } from '@guardian/libs';
3
- import type { AdManagerGroup, Frequency } from './personalised';
4
- import type { SharedTargeting } from './shared';
5
- import { getLocalHour } from './shared';
6
- import type { TrueOrFalse } from './types';
2
+ import type { AdManagerGroup, Frequency } from './personalised.js';
3
+ import type { SharedTargeting } from './shared.js';
4
+ import { getLocalHour } from './shared.js';
5
+ import type { TrueOrFalse } from './types.js';
7
6
  type PartialWithNulls<T> = {
8
7
  [P in keyof T]?: T[P] | null;
9
8
  };
@@ -50,12 +49,12 @@ type UserId = {
50
49
  };
51
50
  type BuildPageTargetingParams = {
52
51
  adFree: boolean;
53
- clientSideParticipations: Participations;
52
+ abTestParticipations: Record<string, string>;
54
53
  consentState: ConsentState;
55
54
  isSignedIn?: boolean;
56
55
  youtube?: boolean;
57
56
  idProviders?: UserId[];
58
57
  };
59
- declare const buildPageTargeting: ({ adFree, clientSideParticipations, consentState, isSignedIn, youtube, idProviders, }: BuildPageTargetingParams) => Record<string, string | string[]>;
58
+ declare const buildPageTargeting: ({ adFree, abTestParticipations, consentState, isSignedIn, youtube, idProviders, }: BuildPageTargetingParams) => Record<string, string | string[]>;
60
59
  export { buildPageTargeting, filterValues, getLocalHour };
61
60
  export type { UserId, PageTargeting };
@@ -2,14 +2,14 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getLocalHour = exports.filterValues = exports.buildPageTargeting = void 0;
4
4
  const libs_1 = require("@guardian/libs");
5
- const event_timer_1 = require("../event-timer");
6
- const get_locale_1 = require("../geo/get-locale");
7
- const content_1 = require("./content");
8
- const personalised_1 = require("./personalised");
9
- const session_1 = require("./session");
10
- const shared_1 = require("./shared");
5
+ const event_timer_1 = require("../event-timer.js");
6
+ const get_locale_1 = require("../geo/get-locale.js");
7
+ const content_1 = require("./content.js");
8
+ const personalised_1 = require("./personalised.js");
9
+ const session_1 = require("./session.js");
10
+ const shared_1 = require("./shared.js");
11
11
  Object.defineProperty(exports, "getLocalHour", { enumerable: true, get: function () { return shared_1.getLocalHour; } });
12
- const viewport_1 = require("./viewport");
12
+ const viewport_1 = require("./viewport.js");
13
13
  const filterValues = (pageTargets) => {
14
14
  const filtered = {};
15
15
  for (const key in pageTargets) {
@@ -49,7 +49,7 @@ const isFirstVisit = (referrer) => {
49
49
  }
50
50
  return !referrerMatchesHost(referrer);
51
51
  };
52
- const buildPageTargeting = ({ adFree, clientSideParticipations, consentState, isSignedIn = false, youtube = false, idProviders = [], }) => {
52
+ const buildPageTargeting = ({ adFree, abTestParticipations, consentState, isSignedIn = false, youtube = false, idProviders = [], }) => {
53
53
  const { page, isDotcomRendering } = window.guardian.config;
54
54
  const adFreeTargeting = adFree ? { af: 't' } : {};
55
55
  const sharedAdTargeting = page.sharedAdTargeting
@@ -74,11 +74,7 @@ const buildPageTargeting = ({ adFree, clientSideParticipations, consentState, is
74
74
  localHour: (0, shared_1.getLocalHour)(),
75
75
  isSignedIn,
76
76
  pageViewId: window.guardian.config.ophan.pageViewId,
77
- participations: {
78
- clientSideParticipations,
79
- serverSideParticipations: window.guardian.config.tests ?? {},
80
- betaAbTestParticipations: window.guardian.modules.abTests?.getParticipations() ?? {},
81
- },
77
+ participations: abTestParticipations,
82
78
  referrer,
83
79
  idProviders,
84
80
  });
@@ -1,4 +1,3 @@
1
- import type { Participations } from '@guardian/ab-core';
2
1
  import type { CountryCode } from '@guardian/libs';
3
2
  import type { UserId } from '../targeting/build-page-targeting.js';
4
3
  import type { False, True } from './types.js';
@@ -107,29 +106,17 @@ type SessionTargeting = {
107
106
  */
108
107
  idp: string[] | null;
109
108
  };
110
- type AllParticipations = {
111
- clientSideParticipations: Participations;
112
- serverSideParticipations: {
113
- [key: `${string}Control`]: 'control';
114
- [key: `${string}Variant`]: 'variant';
115
- };
116
- betaAbTestParticipations: Record<string, string>;
117
- };
118
- /**
119
- * @todo drop old client/server side participations and rename to just `abTestsParticipations` once
120
- * all tests have been migrated to the new AB testing platform
121
- */
122
- declare const experimentsTargeting: ({ clientSideParticipations, serverSideParticipations, betaAbTestParticipations, }: AllParticipations) => SessionTargeting["ab"];
109
+ declare const abTestingTargeting: (abTestParticipations: Record<string, string>) => SessionTargeting["ab"];
123
110
  type Session = {
124
111
  adTest: SessionTargeting['at'];
125
112
  countryCode: CountryCode;
126
113
  localHour: string;
127
114
  isSignedIn: boolean;
128
115
  pageViewId: SessionTargeting['pv'];
129
- participations: AllParticipations;
116
+ participations: Record<string, string>;
130
117
  referrer: string;
131
118
  idProviders: UserId[];
132
119
  };
133
120
  declare const getSessionTargeting: ({ adTest, countryCode, localHour, isSignedIn, pageViewId, participations, referrer, idProviders, }: Session) => SessionTargeting;
134
- export type { SessionTargeting, AllParticipations };
135
- export { getSessionTargeting, experimentsTargeting };
121
+ export type { SessionTargeting };
122
+ export { getSessionTargeting, abTestingTargeting as experimentsTargeting };
@@ -28,46 +28,30 @@ const getReferrer = (referrer) => {
28
28
  const matchedRef = referrers.find((referrerType) => referrer.includes(referrerType.match)) ?? null;
29
29
  return matchedRef ? matchedRef.id : null;
30
30
  };
31
- /**
32
- * @todo drop old client/server side participations and rename to just `abTestsParticipations` once
33
- * all tests have been migrated to the new AB testing platform
34
- */
35
- const experimentsTargeting = ({ clientSideParticipations, serverSideParticipations, betaAbTestParticipations, }) => {
31
+ const abTestingTargeting = (abTestParticipations) => {
36
32
  const testToParams = (testName, variant) => {
37
33
  if (variant === 'notintest')
38
34
  return null;
39
35
  // GAM key-value pairs accept value strings up to 40 characters long
40
36
  return `${testName}-${variant}`.substring(0, 40);
41
37
  };
42
- const clientSideExperiment = Object.entries(clientSideParticipations)
43
- .map((test) => {
44
- const [name, variant] = test;
45
- return testToParams(name, variant.variant);
46
- })
47
- .filter(libs_1.isString);
48
- const serverSideExperiments = Object.entries(serverSideParticipations)
49
- .map((test) => testToParams(...test))
50
- .filter(libs_1.isString);
51
- const betaAbTests = Object.entries(betaAbTestParticipations)
38
+ const abTests = Object.entries(abTestParticipations)
52
39
  .map((test) => {
53
40
  const [name, variant] = test;
54
41
  return testToParams(name, variant);
55
42
  })
56
43
  .filter(libs_1.isString);
57
- if (clientSideExperiment.length +
58
- serverSideExperiments.length +
59
- betaAbTests.length ===
60
- 0) {
44
+ if (abTests.length === 0) {
61
45
  return null;
62
46
  }
63
- return [...clientSideExperiment, ...serverSideExperiments, ...betaAbTests];
47
+ return abTests;
64
48
  };
65
- exports.experimentsTargeting = experimentsTargeting;
49
+ exports.experimentsTargeting = abTestingTargeting;
66
50
  const getIdProviders = (userIds) => {
67
51
  return userIds.map((id) => id.name);
68
52
  };
69
53
  const getSessionTargeting = ({ adTest, countryCode, localHour, isSignedIn, pageViewId, participations, referrer, idProviders, }) => ({
70
- ab: experimentsTargeting(participations),
54
+ ab: abTestingTargeting(participations),
71
55
  at: adTest,
72
56
  cc: countryCode,
73
57
  lh: localHour,
@@ -1,12 +1,11 @@
1
- import type { Participations } from '@guardian/ab-core';
2
1
  import type { ConsentState } from '@guardian/libs';
3
- import type { CustomParams } from './types';
2
+ import type { CustomParams } from './types.js';
4
3
  type BuildImaAdTagUrl = {
5
4
  adUnit: string;
6
5
  customParams: CustomParams;
7
6
  consentState: ConsentState;
8
- clientSideParticipations: Participations;
7
+ abTestParticipations: Record<string, string>;
9
8
  isSignedIn: boolean;
10
9
  };
11
- declare const buildImaAdTagUrl: ({ adUnit, clientSideParticipations, consentState, customParams, isSignedIn, }: BuildImaAdTagUrl) => string;
10
+ declare const buildImaAdTagUrl: ({ adUnit, abTestParticipations, consentState, customParams, isSignedIn, }: BuildImaAdTagUrl) => string;
12
11
  export { buildImaAdTagUrl };
@@ -2,7 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.buildImaAdTagUrl = void 0;
4
4
  const libs_1 = require("@guardian/libs");
5
- const build_page_targeting_1 = require("./build-page-targeting");
5
+ const build_page_targeting_1 = require("./build-page-targeting.js");
6
6
  /**
7
7
  * @param {Record<string, MaybeArray<string|number|boolean>>
8
8
  * Follows https://support.google.com/admanager/answer/1080597
@@ -18,12 +18,12 @@ const encodeCustomParams = (params) => {
18
18
  .join('&');
19
19
  return encodedParams;
20
20
  };
21
- const mergeCustomParamsWithTargeting = (customParams, consentState, clientSideParticipations, isSignedIn) => {
21
+ const mergeCustomParamsWithTargeting = (customParams, consentState, abTestParticipations, isSignedIn) => {
22
22
  let pageTargeting = {};
23
23
  try {
24
24
  pageTargeting = (0, build_page_targeting_1.buildPageTargeting)({
25
25
  adFree: false,
26
- clientSideParticipations,
26
+ abTestParticipations,
27
27
  consentState: consentState,
28
28
  isSignedIn: isSignedIn,
29
29
  });
@@ -43,8 +43,8 @@ const mergeCustomParamsWithTargeting = (customParams, consentState, clientSidePa
43
43
  };
44
44
  return mergedCustomParams;
45
45
  };
46
- const buildImaAdTagUrl = ({ adUnit, clientSideParticipations, consentState, customParams, isSignedIn, }) => {
47
- const mergedCustomParams = mergeCustomParamsWithTargeting(customParams, consentState, clientSideParticipations, isSignedIn);
46
+ const buildImaAdTagUrl = ({ adUnit, abTestParticipations, consentState, customParams, isSignedIn, }) => {
47
+ const mergedCustomParams = mergeCustomParamsWithTargeting(customParams, consentState, abTestParticipations, isSignedIn);
48
48
  const queryParams = {
49
49
  iu: adUnit,
50
50
  tfcd: '0',
@@ -1,6 +1,6 @@
1
1
  import type { EventPayload } from '@guardian/ophan-tracker-js';
2
- import type { EventTimer } from './event-timer';
3
- import type { PageTargeting } from './targeting/build-page-targeting';
2
+ import type { EventTimer } from './event-timer.js';
3
+ import type { PageTargeting } from './targeting/build-page-targeting.js';
4
4
  type ConnectionType = 'bluetooth' | 'cellular' | 'ethernet' | 'mixed' | 'none' | 'other' | 'unknown' | 'wifi';
5
5
  interface NetworkInformation extends EventTarget {
6
6
  readonly type?: ConnectionType;
@@ -1,4 +1,4 @@
1
- type HashClient = 'euid' | 'id5' | 'liveramp' | 'uid2';
1
+ type HashClient = 'euid' | 'id5' | 'liveramp' | 'uid2' | 'permutive';
2
2
  type Email = `${string}@${string}`;
3
3
  declare function normaliseEmail(email: string): Email;
4
4
  declare function hashEmailForClient(email: string, client: HashClient): Promise<string>;
@@ -29,6 +29,7 @@ async function hashEmailForClient(email, client) {
29
29
  return toBase64(hashBuffer);
30
30
  case 'id5':
31
31
  case 'liveramp':
32
+ case 'permutive':
32
33
  return toHex(hashBuffer);
33
34
  }
34
35
  }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * `globalAdEvents` acts as an event bus that broadcasts ad lifecycle
3
+ * events, allowing consumers to react to ad status changes without
4
+ * needing direct access to `Advert` instances.
5
+ * @see /docs/global-ad-events.md
6
+ */
7
+ declare const globalAdEvents: EventTarget;
8
+ export { globalAdEvents };
@@ -0,0 +1,8 @@
1
+ /**
2
+ * `globalAdEvents` acts as an event bus that broadcasts ad lifecycle
3
+ * events, allowing consumers to react to ad status changes without
4
+ * needing direct access to `Advert` instances.
5
+ * @see /docs/global-ad-events.md
6
+ */
7
+ const globalAdEvents = new EventTarget();
8
+ export { globalAdEvents };
@@ -9,6 +9,7 @@ export { buildImaAdTagUrl } from './targeting/youtube-ima.js';
9
9
  export { getPermutivePFPSegments } from './permutive.js';
10
10
  export { isEligibleForTeads } from './targeting/teads-eligibility.js';
11
11
  export { hashEmailForClient } from './email-hash.js';
12
+ export { globalAdEvents } from './global-ad-events.js';
12
13
  export type { AdSize, SizeMapping, SlotName } from './ad-sizes.js';
13
14
  export type { PageTargeting } from './targeting/build-page-targeting.js';
14
15
  export type { AdsConfigDisabled, AdsConfigUSNATorAus, AdsConfigTCFV2, } from './types.js';
package/dist/esm/index.js CHANGED
@@ -9,3 +9,4 @@ export { buildImaAdTagUrl } from './targeting/youtube-ima.js';
9
9
  export { getPermutivePFPSegments } from './permutive.js';
10
10
  export { isEligibleForTeads } from './targeting/teads-eligibility.js';
11
11
  export { hashEmailForClient } from './email-hash.js';
12
+ export { globalAdEvents } from './global-ad-events.js';
@@ -1,4 +1,3 @@
1
- import type { Participations } from '@guardian/ab-core';
2
1
  import type { ConsentState, CountryCode } from '@guardian/libs';
3
2
  import type { AdManagerGroup, Frequency } from './personalised.js';
4
3
  import type { SharedTargeting } from './shared.js';
@@ -50,12 +49,12 @@ type UserId = {
50
49
  };
51
50
  type BuildPageTargetingParams = {
52
51
  adFree: boolean;
53
- clientSideParticipations: Participations;
52
+ abTestParticipations: Record<string, string>;
54
53
  consentState: ConsentState;
55
54
  isSignedIn?: boolean;
56
55
  youtube?: boolean;
57
56
  idProviders?: UserId[];
58
57
  };
59
- declare const buildPageTargeting: ({ adFree, clientSideParticipations, consentState, isSignedIn, youtube, idProviders, }: BuildPageTargetingParams) => Record<string, string | string[]>;
58
+ declare const buildPageTargeting: ({ adFree, abTestParticipations, consentState, isSignedIn, youtube, idProviders, }: BuildPageTargetingParams) => Record<string, string | string[]>;
60
59
  export { buildPageTargeting, filterValues, getLocalHour };
61
60
  export type { UserId, PageTargeting };
@@ -44,7 +44,7 @@ const isFirstVisit = (referrer) => {
44
44
  }
45
45
  return !referrerMatchesHost(referrer);
46
46
  };
47
- const buildPageTargeting = ({ adFree, clientSideParticipations, consentState, isSignedIn = false, youtube = false, idProviders = [], }) => {
47
+ const buildPageTargeting = ({ adFree, abTestParticipations, consentState, isSignedIn = false, youtube = false, idProviders = [], }) => {
48
48
  const { page, isDotcomRendering } = window.guardian.config;
49
49
  const adFreeTargeting = adFree ? { af: 't' } : {};
50
50
  const sharedAdTargeting = page.sharedAdTargeting
@@ -69,11 +69,7 @@ const buildPageTargeting = ({ adFree, clientSideParticipations, consentState, is
69
69
  localHour: getLocalHour(),
70
70
  isSignedIn,
71
71
  pageViewId: window.guardian.config.ophan.pageViewId,
72
- participations: {
73
- clientSideParticipations,
74
- serverSideParticipations: window.guardian.config.tests ?? {},
75
- betaAbTestParticipations: window.guardian.modules.abTests?.getParticipations() ?? {},
76
- },
72
+ participations: abTestParticipations,
77
73
  referrer,
78
74
  idProviders,
79
75
  });
@@ -1,4 +1,3 @@
1
- import type { Participations } from '@guardian/ab-core';
2
1
  import type { CountryCode } from '@guardian/libs';
3
2
  import type { UserId } from '../targeting/build-page-targeting.js';
4
3
  import type { False, True } from './types.js';
@@ -107,29 +106,17 @@ type SessionTargeting = {
107
106
  */
108
107
  idp: string[] | null;
109
108
  };
110
- type AllParticipations = {
111
- clientSideParticipations: Participations;
112
- serverSideParticipations: {
113
- [key: `${string}Control`]: 'control';
114
- [key: `${string}Variant`]: 'variant';
115
- };
116
- betaAbTestParticipations: Record<string, string>;
117
- };
118
- /**
119
- * @todo drop old client/server side participations and rename to just `abTestsParticipations` once
120
- * all tests have been migrated to the new AB testing platform
121
- */
122
- declare const experimentsTargeting: ({ clientSideParticipations, serverSideParticipations, betaAbTestParticipations, }: AllParticipations) => SessionTargeting["ab"];
109
+ declare const abTestingTargeting: (abTestParticipations: Record<string, string>) => SessionTargeting["ab"];
123
110
  type Session = {
124
111
  adTest: SessionTargeting['at'];
125
112
  countryCode: CountryCode;
126
113
  localHour: string;
127
114
  isSignedIn: boolean;
128
115
  pageViewId: SessionTargeting['pv'];
129
- participations: AllParticipations;
116
+ participations: Record<string, string>;
130
117
  referrer: string;
131
118
  idProviders: UserId[];
132
119
  };
133
120
  declare const getSessionTargeting: ({ adTest, countryCode, localHour, isSignedIn, pageViewId, participations, referrer, idProviders, }: Session) => SessionTargeting;
134
- export type { SessionTargeting, AllParticipations };
135
- export { getSessionTargeting, experimentsTargeting };
121
+ export type { SessionTargeting };
122
+ export { getSessionTargeting, abTestingTargeting as experimentsTargeting };
@@ -25,45 +25,29 @@ const getReferrer = (referrer) => {
25
25
  const matchedRef = referrers.find((referrerType) => referrer.includes(referrerType.match)) ?? null;
26
26
  return matchedRef ? matchedRef.id : null;
27
27
  };
28
- /**
29
- * @todo drop old client/server side participations and rename to just `abTestsParticipations` once
30
- * all tests have been migrated to the new AB testing platform
31
- */
32
- const experimentsTargeting = ({ clientSideParticipations, serverSideParticipations, betaAbTestParticipations, }) => {
28
+ const abTestingTargeting = (abTestParticipations) => {
33
29
  const testToParams = (testName, variant) => {
34
30
  if (variant === 'notintest')
35
31
  return null;
36
32
  // GAM key-value pairs accept value strings up to 40 characters long
37
33
  return `${testName}-${variant}`.substring(0, 40);
38
34
  };
39
- const clientSideExperiment = Object.entries(clientSideParticipations)
40
- .map((test) => {
41
- const [name, variant] = test;
42
- return testToParams(name, variant.variant);
43
- })
44
- .filter(isString);
45
- const serverSideExperiments = Object.entries(serverSideParticipations)
46
- .map((test) => testToParams(...test))
47
- .filter(isString);
48
- const betaAbTests = Object.entries(betaAbTestParticipations)
35
+ const abTests = Object.entries(abTestParticipations)
49
36
  .map((test) => {
50
37
  const [name, variant] = test;
51
38
  return testToParams(name, variant);
52
39
  })
53
40
  .filter(isString);
54
- if (clientSideExperiment.length +
55
- serverSideExperiments.length +
56
- betaAbTests.length ===
57
- 0) {
41
+ if (abTests.length === 0) {
58
42
  return null;
59
43
  }
60
- return [...clientSideExperiment, ...serverSideExperiments, ...betaAbTests];
44
+ return abTests;
61
45
  };
62
46
  const getIdProviders = (userIds) => {
63
47
  return userIds.map((id) => id.name);
64
48
  };
65
49
  const getSessionTargeting = ({ adTest, countryCode, localHour, isSignedIn, pageViewId, participations, referrer, idProviders, }) => ({
66
- ab: experimentsTargeting(participations),
50
+ ab: abTestingTargeting(participations),
67
51
  at: adTest,
68
52
  cc: countryCode,
69
53
  lh: localHour,
@@ -72,4 +56,4 @@ const getSessionTargeting = ({ adTest, countryCode, localHour, isSignedIn, pageV
72
56
  si: isSignedIn ? 't' : 'f',
73
57
  idp: getIdProviders(idProviders),
74
58
  });
75
- export { getSessionTargeting, experimentsTargeting };
59
+ export { getSessionTargeting, abTestingTargeting as experimentsTargeting };
@@ -1,12 +1,11 @@
1
- import type { Participations } from '@guardian/ab-core';
2
1
  import type { ConsentState } from '@guardian/libs';
3
2
  import type { CustomParams } from './types.js';
4
3
  type BuildImaAdTagUrl = {
5
4
  adUnit: string;
6
5
  customParams: CustomParams;
7
6
  consentState: ConsentState;
8
- clientSideParticipations: Participations;
7
+ abTestParticipations: Record<string, string>;
9
8
  isSignedIn: boolean;
10
9
  };
11
- declare const buildImaAdTagUrl: ({ adUnit, clientSideParticipations, consentState, customParams, isSignedIn, }: BuildImaAdTagUrl) => string;
10
+ declare const buildImaAdTagUrl: ({ adUnit, abTestParticipations, consentState, customParams, isSignedIn, }: BuildImaAdTagUrl) => string;
12
11
  export { buildImaAdTagUrl };
@@ -15,12 +15,12 @@ const encodeCustomParams = (params) => {
15
15
  .join('&');
16
16
  return encodedParams;
17
17
  };
18
- const mergeCustomParamsWithTargeting = (customParams, consentState, clientSideParticipations, isSignedIn) => {
18
+ const mergeCustomParamsWithTargeting = (customParams, consentState, abTestParticipations, isSignedIn) => {
19
19
  let pageTargeting = {};
20
20
  try {
21
21
  pageTargeting = buildPageTargeting({
22
22
  adFree: false,
23
- clientSideParticipations,
23
+ abTestParticipations,
24
24
  consentState: consentState,
25
25
  isSignedIn: isSignedIn,
26
26
  });
@@ -40,8 +40,8 @@ const mergeCustomParamsWithTargeting = (customParams, consentState, clientSidePa
40
40
  };
41
41
  return mergedCustomParams;
42
42
  };
43
- const buildImaAdTagUrl = ({ adUnit, clientSideParticipations, consentState, customParams, isSignedIn, }) => {
44
- const mergedCustomParams = mergeCustomParamsWithTargeting(customParams, consentState, clientSideParticipations, isSignedIn);
43
+ const buildImaAdTagUrl = ({ adUnit, abTestParticipations, consentState, customParams, isSignedIn, }) => {
44
+ const mergedCustomParams = mergeCustomParamsWithTargeting(customParams, consentState, abTestParticipations, isSignedIn);
45
45
  const queryParams = {
46
46
  iu: adUnit,
47
47
  tfcd: '0',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@guardian/commercial-core",
3
- "version": "31.0.0",
3
+ "version": "32.0.0",
4
4
  "description": "Guardian advertising business logic",
5
5
  "homepage": "https://github.com/guardian/commercial#readme",
6
6
  "bugs": {
@@ -32,19 +32,17 @@
32
32
  "./package.json": "./package.json"
33
33
  },
34
34
  "peerDependencies": {
35
- "@guardian/ab-core": "^9.0.0",
36
35
  "@guardian/libs": "^30.1.0"
37
36
  },
38
37
  "dependencies": {
39
- "@guardian/ab-core": "9.0.0",
40
38
  "@guardian/libs": "30.1.0"
41
39
  },
42
40
  "devDependencies": {
43
41
  "@guardian/ophan-tracker-js": "2.8.0",
44
42
  "@types/jest": "30.0.0",
45
- "@types/node": "25.2.0",
46
- "jest": "^30.2.0",
47
- "jest-environment-jsdom": "^30.2.0",
43
+ "@types/node": "25.4.0",
44
+ "jest": "^30.3.0",
45
+ "jest-environment-jsdom": "^30.3.0",
48
46
  "jest-environment-jsdom-global": "~4.0.0",
49
47
  "ts-jest": "^29.4.6",
50
48
  "tsc-alias": "1.8.16",