@guardian/commercial-core 0.27.0 → 0.31.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.
- package/dist/cjs/EventTimer.d.ts +2 -2
- package/dist/cjs/EventTimer.js +10 -7
- package/dist/cjs/ad-targeting-youtube.js +2 -2
- package/dist/cjs/targeting/content.d.ts +189 -0
- package/dist/cjs/targeting/content.js +90 -0
- package/dist/cjs/targeting/personalised.d.ts +78 -0
- package/dist/cjs/targeting/personalised.js +148 -0
- package/dist/cjs/targeting/session.d.ts +98 -0
- package/dist/cjs/targeting/session.js +57 -0
- package/dist/cjs/types.d.ts +5 -5
- package/dist/esm/EventTimer.d.ts +2 -2
- package/dist/esm/EventTimer.js +10 -7
- package/dist/esm/ad-targeting-youtube.js +1 -1
- package/dist/esm/targeting/content.d.ts +189 -0
- package/dist/esm/targeting/content.js +86 -0
- package/dist/esm/targeting/personalised.d.ts +78 -0
- package/dist/esm/targeting/personalised.js +145 -0
- package/dist/esm/targeting/session.d.ts +98 -0
- package/dist/esm/targeting/session.js +53 -0
- package/dist/esm/types.d.ts +5 -5
- package/package.json +23 -21
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getSessionTargeting = void 0;
|
|
4
|
+
const libs_1 = require("@guardian/libs");
|
|
5
|
+
/* -- Types -- */
|
|
6
|
+
const referrers = [
|
|
7
|
+
{
|
|
8
|
+
id: 'facebook',
|
|
9
|
+
match: 'facebook.com',
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
id: 'google',
|
|
13
|
+
match: 'www.google',
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
id: 'twitter',
|
|
17
|
+
match: '/t.co/',
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
id: 'reddit',
|
|
21
|
+
match: 'reddit.com',
|
|
22
|
+
},
|
|
23
|
+
];
|
|
24
|
+
/* -- Methods -- */
|
|
25
|
+
const getReferrer = (referrer) => {
|
|
26
|
+
if (referrer === '')
|
|
27
|
+
return null;
|
|
28
|
+
const matchedRef = referrers.find((referrerType) => referrer.includes(referrerType.match)) ?? null;
|
|
29
|
+
return matchedRef ? matchedRef.id : null;
|
|
30
|
+
};
|
|
31
|
+
const experimentsTargeting = ({ clientSideParticipations, serverSideParticipations, }) => {
|
|
32
|
+
const testToParams = (testName, variant) => {
|
|
33
|
+
if (variant === 'notintest')
|
|
34
|
+
return null;
|
|
35
|
+
// GAM key-value pairs accept value strings up to 40 characters long
|
|
36
|
+
return `${testName}-${variant}`.substring(0, 40);
|
|
37
|
+
};
|
|
38
|
+
const clientSideExperiment = Object.entries(clientSideParticipations)
|
|
39
|
+
.map((test) => {
|
|
40
|
+
const [name, variant] = test;
|
|
41
|
+
return testToParams(name, variant.variant);
|
|
42
|
+
})
|
|
43
|
+
.filter(libs_1.isString);
|
|
44
|
+
const serverSideExperiments = Object.entries(serverSideParticipations)
|
|
45
|
+
.map((test) => testToParams(...test))
|
|
46
|
+
.filter(libs_1.isString);
|
|
47
|
+
if (clientSideExperiment.length + serverSideExperiments.length === 0)
|
|
48
|
+
return null;
|
|
49
|
+
return [...clientSideExperiment, ...serverSideExperiments];
|
|
50
|
+
};
|
|
51
|
+
/* -- Targeting -- */
|
|
52
|
+
const getSessionTargeting = (referrer, participations, targeting) => ({
|
|
53
|
+
ref: getReferrer(referrer),
|
|
54
|
+
ab: experimentsTargeting(participations),
|
|
55
|
+
...targeting,
|
|
56
|
+
});
|
|
57
|
+
exports.getSessionTargeting = getSessionTargeting;
|
package/dist/cjs/types.d.ts
CHANGED
|
@@ -1,13 +1,10 @@
|
|
|
1
|
-
export declare type
|
|
1
|
+
export declare type TagAttribute = {
|
|
2
2
|
name: string;
|
|
3
3
|
value: string;
|
|
4
4
|
};
|
|
5
|
-
export declare type GetThirdPartyTag = (arg0: {
|
|
6
|
-
shouldRun: boolean;
|
|
7
|
-
}) => ThirdPartyTag;
|
|
8
5
|
export declare type ThirdPartyTag = {
|
|
9
6
|
async?: boolean;
|
|
10
|
-
attrs?:
|
|
7
|
+
attrs?: TagAttribute[];
|
|
11
8
|
beforeLoad?: () => void;
|
|
12
9
|
insertSnippet?: () => void;
|
|
13
10
|
loaded?: boolean;
|
|
@@ -17,6 +14,9 @@ export declare type ThirdPartyTag = {
|
|
|
17
14
|
url?: string;
|
|
18
15
|
useImage?: boolean;
|
|
19
16
|
};
|
|
17
|
+
export declare type GetThirdPartyTag = (arg0: {
|
|
18
|
+
shouldRun: boolean;
|
|
19
|
+
}) => ThirdPartyTag;
|
|
20
20
|
export declare type GuardianAnalyticsConfig = {
|
|
21
21
|
trackers: Record<string, string>;
|
|
22
22
|
};
|
package/dist/esm/EventTimer.d.ts
CHANGED
|
@@ -37,10 +37,10 @@ export declare class EventTimer {
|
|
|
37
37
|
effectiveType?: string;
|
|
38
38
|
};
|
|
39
39
|
/**
|
|
40
|
-
*
|
|
40
|
+
* Initialise the EventTimer class on page.
|
|
41
41
|
* Returns the singleton instance of the EventTimer class and binds
|
|
42
42
|
* to window.guardian.commercialTimer. If it's been previously
|
|
43
|
-
*
|
|
43
|
+
* initialised and bound it returns the original instance
|
|
44
44
|
* Note: We save to window.guardian.commercialTimer because
|
|
45
45
|
* different bundles (DCR / DCP) can use commercial core, and we want
|
|
46
46
|
* all timer events saved to a single instance per-page
|
package/dist/esm/EventTimer.js
CHANGED
|
@@ -63,10 +63,10 @@ export class EventTimer {
|
|
|
63
63
|
: {};
|
|
64
64
|
}
|
|
65
65
|
/**
|
|
66
|
-
*
|
|
66
|
+
* Initialise the EventTimer class on page.
|
|
67
67
|
* Returns the singleton instance of the EventTimer class and binds
|
|
68
68
|
* to window.guardian.commercialTimer. If it's been previously
|
|
69
|
-
*
|
|
69
|
+
* initialised and bound it returns the original instance
|
|
70
70
|
* Note: We save to window.guardian.commercialTimer because
|
|
71
71
|
* different bundles (DCR / DCP) can use commercial core, and we want
|
|
72
72
|
* all timer events saved to a single instance per-page
|
|
@@ -122,7 +122,7 @@ export class EventTimer {
|
|
|
122
122
|
* @param {origin} [origin=page] - Either 'page' (default) or the name of the slot
|
|
123
123
|
*/
|
|
124
124
|
trigger(eventName, origin = 'page') {
|
|
125
|
-
const
|
|
125
|
+
const TRACKED_SLOT_NAME = 'top-above-nav';
|
|
126
126
|
if (origin === 'page' &&
|
|
127
127
|
!this.triggers.page[eventName]) {
|
|
128
128
|
this.mark(eventName);
|
|
@@ -136,12 +136,12 @@ export class EventTimer {
|
|
|
136
136
|
this.trackInGA(eventName, trackLabel);
|
|
137
137
|
this.triggers.first[eventName] = true;
|
|
138
138
|
}
|
|
139
|
-
if (origin ===
|
|
140
|
-
if (!this.triggers[
|
|
141
|
-
const trackLabel = `${
|
|
139
|
+
if (origin === TRACKED_SLOT_NAME) {
|
|
140
|
+
if (!this.triggers[TRACKED_SLOT_NAME][eventName]) {
|
|
141
|
+
const trackLabel = `${TRACKED_SLOT_NAME}-${eventName}`;
|
|
142
142
|
this.mark(trackLabel);
|
|
143
143
|
this.trackInGA(eventName, trackLabel);
|
|
144
|
-
this.triggers[
|
|
144
|
+
this.triggers[TRACKED_SLOT_NAME][eventName] = true;
|
|
145
145
|
}
|
|
146
146
|
}
|
|
147
147
|
}
|
|
@@ -155,9 +155,12 @@ export class EventTimer {
|
|
|
155
155
|
}
|
|
156
156
|
EventTimer._externallyDefinedEventNames = [
|
|
157
157
|
'cmp-tcfv2-init',
|
|
158
|
+
'cmp-tcfv2-ui-displayed',
|
|
158
159
|
'cmp-tcfv2-got-consent',
|
|
159
160
|
'cmp-ccpa-init',
|
|
161
|
+
'cmp-ccpa-ui-displayed',
|
|
160
162
|
'cmp-ccpa-got-consent',
|
|
161
163
|
'cmp-aus-init',
|
|
164
|
+
'cmp-aus-ui-displayed',
|
|
162
165
|
'cmp-aus-got-consent',
|
|
163
166
|
];
|
|
@@ -2,6 +2,7 @@ import { getCookie } from '@guardian/libs';
|
|
|
2
2
|
import { canUseDom } from './lib/can-use-dom';
|
|
3
3
|
import { constructQuery } from './lib/construct-query';
|
|
4
4
|
import { getPermutivePFPSegments } from './permutive';
|
|
5
|
+
const disabledAds = { disableAds: true };
|
|
5
6
|
const buildCustomParamsFromCookies = () => canUseDom()
|
|
6
7
|
? {
|
|
7
8
|
permutive: getPermutivePFPSegments(),
|
|
@@ -51,7 +52,6 @@ const buildAdsConfig = (cmpConsent, adUnit, customParams) => {
|
|
|
51
52
|
// Shouldn't happen but handle if no matching framework
|
|
52
53
|
return disabledAds;
|
|
53
54
|
};
|
|
54
|
-
const disabledAds = { disableAds: true };
|
|
55
55
|
const buildAdsConfigWithConsent = (isAdFreeUser, adUnit, customParamsToMerge, consentState) => {
|
|
56
56
|
if (isAdFreeUser) {
|
|
57
57
|
return disabledAds;
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import type { False, True } from '../types';
|
|
2
|
+
declare const brands: {
|
|
3
|
+
readonly Foundation: "f";
|
|
4
|
+
readonly Paid: "p";
|
|
5
|
+
readonly Sponsored: "s";
|
|
6
|
+
};
|
|
7
|
+
declare const editions: {
|
|
8
|
+
readonly UnitedKingdom: "uk";
|
|
9
|
+
readonly UnitedStates: "us";
|
|
10
|
+
readonly Australia: "au";
|
|
11
|
+
readonly International: "int";
|
|
12
|
+
};
|
|
13
|
+
declare const videoLengths: readonly ["25", "30", "60", "90", "120", "150", "180", "210", "240", "270", "300"];
|
|
14
|
+
declare const surges: {
|
|
15
|
+
readonly 0: "0";
|
|
16
|
+
readonly 50: "5";
|
|
17
|
+
readonly 100: "4";
|
|
18
|
+
readonly 200: "3";
|
|
19
|
+
readonly 300: "2";
|
|
20
|
+
readonly 400: "1";
|
|
21
|
+
};
|
|
22
|
+
declare const platforms: {
|
|
23
|
+
readonly R2: "r2";
|
|
24
|
+
readonly NextGen: "ng";
|
|
25
|
+
readonly MobileApp: "app";
|
|
26
|
+
readonly AcceleratedMobilePages: "amp";
|
|
27
|
+
};
|
|
28
|
+
declare const contentTypes: readonly ["article", "audio", "crossword", "gallery", "interactive", "liveblog", "network-front", "section", "tag", "video"];
|
|
29
|
+
/**
|
|
30
|
+
* Content Targeting comes from the server
|
|
31
|
+
*
|
|
32
|
+
* For a specific URL, it will only change on
|
|
33
|
+
* - a Composer/CAPI update
|
|
34
|
+
* - a rendering platform capability update
|
|
35
|
+
* - a main media update
|
|
36
|
+
* - a series tag update
|
|
37
|
+
* - a surge in page views per minute
|
|
38
|
+
*
|
|
39
|
+
*/
|
|
40
|
+
export declare type ContentTargeting = {
|
|
41
|
+
/**
|
|
42
|
+
* **Bl**og tags – [see on Ad Manager][gam]
|
|
43
|
+
*
|
|
44
|
+
* Type: _Dynamic_
|
|
45
|
+
*
|
|
46
|
+
* [gam]: https://admanager.google.com/59666047#inventory/custom_targeting/detail/custom_key_id=186687
|
|
47
|
+
*/
|
|
48
|
+
bl: string[];
|
|
49
|
+
/**
|
|
50
|
+
* **Br**anding - [see on Ad Manager][gam]
|
|
51
|
+
*
|
|
52
|
+
* Type: _Predefined_
|
|
53
|
+
*
|
|
54
|
+
* [gam]: https://admanager.google.com/59666047#inventory/custom_targeting/detail/custom_key_id=259767
|
|
55
|
+
*/
|
|
56
|
+
br: typeof brands[keyof typeof brands] | null;
|
|
57
|
+
/**
|
|
58
|
+
* **Co**ntributor - [see on Ad Manager][gam]
|
|
59
|
+
*
|
|
60
|
+
* Array of all contributors to the content on the page
|
|
61
|
+
*
|
|
62
|
+
* Type: _Dynamic_
|
|
63
|
+
*
|
|
64
|
+
* [gam]: https://admanager.google.com/59666047#inventory/custom_targeting/detail/custom_key_id=186207
|
|
65
|
+
*/
|
|
66
|
+
co: string[];
|
|
67
|
+
/**
|
|
68
|
+
* **C**ontent **T**ype - [see on Ad Manager][gam]
|
|
69
|
+
*
|
|
70
|
+
* Type: _Predefined_
|
|
71
|
+
*
|
|
72
|
+
* [gam]: https://admanager.google.com/59666047#inventory/custom_targeting/detail/custom_key_id=177807
|
|
73
|
+
*/
|
|
74
|
+
ct: typeof contentTypes[number];
|
|
75
|
+
/**
|
|
76
|
+
* **D**ot**c**om-**r**endering **E**ligible - [see on Ad Manager][gam]
|
|
77
|
+
*
|
|
78
|
+
* Type: _Predefined_
|
|
79
|
+
*
|
|
80
|
+
* [gam]: https://admanager.google.com/59666047#inventory/custom_targeting/detail/custom_key_id=11958028
|
|
81
|
+
*/
|
|
82
|
+
dcre: True | False;
|
|
83
|
+
/**
|
|
84
|
+
* **Edition** - [see on Ad Manager][gam]
|
|
85
|
+
*
|
|
86
|
+
* Type: _Predefined_
|
|
87
|
+
*
|
|
88
|
+
* [gam]: https://admanager.google.com/59666047#inventory/custom_targeting/detail/custom_key_id=174207
|
|
89
|
+
*/
|
|
90
|
+
edition: typeof editions[keyof typeof editions];
|
|
91
|
+
/**
|
|
92
|
+
* **K**eywords - [see on Ad Manager][gam]
|
|
93
|
+
*
|
|
94
|
+
* Type: _Dynamic_
|
|
95
|
+
*
|
|
96
|
+
* [gam]: https://admanager.google.com/59666047#inventory/custom_targeting/detail/custom_key_id=177687
|
|
97
|
+
*/
|
|
98
|
+
k: string[];
|
|
99
|
+
/**
|
|
100
|
+
* **Ob**server Content - [see on Ad Manager][gam]
|
|
101
|
+
*
|
|
102
|
+
* [gam]: https://admanager.google.com/59666047#inventory/custom_targeting/detail/custom_key_id=256887
|
|
103
|
+
*/
|
|
104
|
+
ob: 't' | null;
|
|
105
|
+
/**
|
|
106
|
+
* **P**latform - [see on Ad Manager][gam]
|
|
107
|
+
*
|
|
108
|
+
* [gam]: https://admanager.google.com/59666047#inventory/custom_targeting/detail/custom_key_id=180207
|
|
109
|
+
*/
|
|
110
|
+
p: typeof platforms[keyof typeof platforms];
|
|
111
|
+
/**
|
|
112
|
+
* Rendering Platform - [see on Ad Manager][gam]
|
|
113
|
+
*
|
|
114
|
+
* [gam]: https://admanager.google.com/59666047#inventory/custom_targeting/detail/custom_key_id=11881005
|
|
115
|
+
*/
|
|
116
|
+
rp: 'dotcom-rendering' | 'dotcom-platform';
|
|
117
|
+
/**
|
|
118
|
+
* Site **S**ection - [see on Ad Manager][gam]
|
|
119
|
+
*
|
|
120
|
+
* [gam]: https://admanager.google.com/59666047#inventory/custom_targeting/detail/custom_key_id=173967
|
|
121
|
+
*/
|
|
122
|
+
s: string;
|
|
123
|
+
/**
|
|
124
|
+
* **Se**ries - [see on Ad Manager][gam]
|
|
125
|
+
*
|
|
126
|
+
* [gam]: https://admanager.google.com/59666047#inventory/custom_targeting/detail/custom_key_id=180447
|
|
127
|
+
*/
|
|
128
|
+
se: string[];
|
|
129
|
+
/**
|
|
130
|
+
* **Sens**itive - [see on Ad Manager][gam]
|
|
131
|
+
*
|
|
132
|
+
* [gam]: https://admanager.google.com/59666047#inventory/custom_targeting/detail/custom_key_id=11654206
|
|
133
|
+
*/
|
|
134
|
+
sens: True | False;
|
|
135
|
+
/**
|
|
136
|
+
* **Su**rging Article - [see on Ad Manager][gam]
|
|
137
|
+
*
|
|
138
|
+
* Type: _Predefined_
|
|
139
|
+
*
|
|
140
|
+
* [gam]: https://admanager.google.com/59666047#inventory/custom_targeting/detail/custom_key_id=185007
|
|
141
|
+
*/
|
|
142
|
+
su: Array<typeof surges[keyof typeof surges]>;
|
|
143
|
+
/**
|
|
144
|
+
* **T**o**n**es - [see on Ad Manager][gam]
|
|
145
|
+
*
|
|
146
|
+
* [gam]: https://admanager.google.com/59666047#inventory/custom_targeting/detail/custom_key_id=191487
|
|
147
|
+
*/
|
|
148
|
+
tn: string[];
|
|
149
|
+
/**
|
|
150
|
+
* **U**niform **R**esource **L**ocator - [see on Ad Manager][gam]
|
|
151
|
+
*
|
|
152
|
+
* Relative to `www.theguardian.com`, starts with `/`
|
|
153
|
+
*
|
|
154
|
+
* Type: _Dynamic_
|
|
155
|
+
*
|
|
156
|
+
* [gam]: https://admanager.google.com/59666047#inventory/custom_targeting/detail/custom_key_id=174327
|
|
157
|
+
*/
|
|
158
|
+
url: `/${string}`;
|
|
159
|
+
/**
|
|
160
|
+
* URL Keywords - [see on Ad Manager][gam]
|
|
161
|
+
*
|
|
162
|
+
* Type: _Dynamic_
|
|
163
|
+
*
|
|
164
|
+
* [gam]: https://admanager.google.com/59666047#inventory/custom_targeting/detail/custom_key_id=12058265
|
|
165
|
+
*/
|
|
166
|
+
urlkw: string[];
|
|
167
|
+
/**
|
|
168
|
+
* **V**ideo **L**ength - [see on Ad Manager][gam]
|
|
169
|
+
*
|
|
170
|
+
* Video.JS only (?)
|
|
171
|
+
*
|
|
172
|
+
* Type: _Predefined_
|
|
173
|
+
*
|
|
174
|
+
* [gam]: https://admanager.google.com/59666047#inventory/custom_targeting/detail/custom_key_id=195087
|
|
175
|
+
*/
|
|
176
|
+
vl: null | typeof videoLengths[number];
|
|
177
|
+
};
|
|
178
|
+
export declare const getContentTargeting: ({ branding, contentType, contributors, platform, sensitive, tones, path, videoLength, surging, }: {
|
|
179
|
+
branding?: "Foundation" | "Paid" | "Sponsored" | undefined;
|
|
180
|
+
contentType: typeof contentTypes[number];
|
|
181
|
+
contributors: string[];
|
|
182
|
+
platform: keyof typeof platforms;
|
|
183
|
+
sensitive: boolean;
|
|
184
|
+
tones: ContentTargeting['tn'];
|
|
185
|
+
path: ContentTargeting['url'];
|
|
186
|
+
videoLength?: number | undefined;
|
|
187
|
+
surging: number;
|
|
188
|
+
}, targeting: Omit<ContentTargeting, 'br' | 'ct' | 'co' | 'p' | 'sens' | 'tn' | 'url' | 'urlkw' | 'vl' | 'su'>) => ContentTargeting;
|
|
189
|
+
export {};
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { isString } from '@guardian/libs';
|
|
2
|
+
/* -- Types -- */
|
|
3
|
+
const brands = {
|
|
4
|
+
Foundation: 'f',
|
|
5
|
+
Paid: 'p',
|
|
6
|
+
Sponsored: 's',
|
|
7
|
+
};
|
|
8
|
+
const editions = {
|
|
9
|
+
UnitedKingdom: 'uk',
|
|
10
|
+
UnitedStates: 'us',
|
|
11
|
+
Australia: 'au',
|
|
12
|
+
International: 'int',
|
|
13
|
+
};
|
|
14
|
+
const videoLengths = [
|
|
15
|
+
'25',
|
|
16
|
+
'30',
|
|
17
|
+
'60',
|
|
18
|
+
'90',
|
|
19
|
+
'120',
|
|
20
|
+
'150',
|
|
21
|
+
'180',
|
|
22
|
+
'210',
|
|
23
|
+
'240',
|
|
24
|
+
'270',
|
|
25
|
+
'300',
|
|
26
|
+
];
|
|
27
|
+
const surges = {
|
|
28
|
+
0: '0',
|
|
29
|
+
50: '5',
|
|
30
|
+
100: '4',
|
|
31
|
+
200: '3',
|
|
32
|
+
300: '2',
|
|
33
|
+
400: '1',
|
|
34
|
+
};
|
|
35
|
+
const platforms = {
|
|
36
|
+
R2: 'r2',
|
|
37
|
+
NextGen: 'ng',
|
|
38
|
+
MobileApp: 'app',
|
|
39
|
+
AcceleratedMobilePages: 'amp',
|
|
40
|
+
};
|
|
41
|
+
const contentTypes = [
|
|
42
|
+
'article',
|
|
43
|
+
'audio',
|
|
44
|
+
'crossword',
|
|
45
|
+
'gallery',
|
|
46
|
+
'interactive',
|
|
47
|
+
'liveblog',
|
|
48
|
+
'network-front',
|
|
49
|
+
'section',
|
|
50
|
+
'tag',
|
|
51
|
+
'video',
|
|
52
|
+
];
|
|
53
|
+
/* -- Methods -- */
|
|
54
|
+
const getVideoLength = (videoLength) => {
|
|
55
|
+
const index = Math.min(Math.ceil(videoLength / 30), 10);
|
|
56
|
+
return videoLengths[index] ?? null;
|
|
57
|
+
};
|
|
58
|
+
const getUrlKeywords = (url) => {
|
|
59
|
+
const lastSegment = url
|
|
60
|
+
.split('/')
|
|
61
|
+
.filter(Boolean) // This handles a trailing slash
|
|
62
|
+
.slice(-1)[0];
|
|
63
|
+
return isString(lastSegment) ? lastSegment.split('-').filter(Boolean) : [];
|
|
64
|
+
};
|
|
65
|
+
const getSurgingParam = (surging) => {
|
|
66
|
+
if (surging < 50 || isNaN(surging))
|
|
67
|
+
return ['0'];
|
|
68
|
+
const thresholds = [400, 300, 200, 100, 50];
|
|
69
|
+
return thresholds.filter((n) => n <= surging).map((s) => surges[s]);
|
|
70
|
+
};
|
|
71
|
+
/* -- Targeting -- */
|
|
72
|
+
export const getContentTargeting = ({ branding, contentType, contributors, platform, sensitive, tones, path, videoLength, surging, }, targeting) => {
|
|
73
|
+
return {
|
|
74
|
+
...targeting,
|
|
75
|
+
br: branding ? brands[branding] : null,
|
|
76
|
+
co: contributors,
|
|
77
|
+
ct: contentType,
|
|
78
|
+
p: platforms[platform],
|
|
79
|
+
sens: sensitive ? 't' : 'f',
|
|
80
|
+
tn: tones,
|
|
81
|
+
su: getSurgingParam(surging),
|
|
82
|
+
url: path,
|
|
83
|
+
urlkw: getUrlKeywords(path),
|
|
84
|
+
vl: videoLength ? getVideoLength(videoLength) : null,
|
|
85
|
+
};
|
|
86
|
+
};
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import type { ConsentState } from '@guardian/consent-management-platform/dist/types';
|
|
2
|
+
import type { TCEventStatusCode } from '@guardian/consent-management-platform/dist/types/tcfv2';
|
|
3
|
+
import type { False, NotApplicable, True } from '../types';
|
|
4
|
+
declare const frequency: readonly ["0", "1", "2", "3", "4", "5", "6-9", "10-15", "16-19", "20-29", "30plus"];
|
|
5
|
+
declare const adManagerGroups: readonly ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12"];
|
|
6
|
+
declare type AdManagerGroup = typeof adManagerGroups[number];
|
|
7
|
+
/**
|
|
8
|
+
* Personalised Targeting requires user consent
|
|
9
|
+
*
|
|
10
|
+
* It allows or prevents personalised advertising, restrict data processing
|
|
11
|
+
* and handles access to cookies and local storage
|
|
12
|
+
*/
|
|
13
|
+
export declare type PersonalisedTargeting = {
|
|
14
|
+
/**
|
|
15
|
+
* **A**d **M**anager **T**argeting **Gr**ou**p** – [see on Ad Manager][gam]
|
|
16
|
+
*
|
|
17
|
+
* Type: _Predefined_
|
|
18
|
+
*
|
|
19
|
+
* Sample values:
|
|
20
|
+
* -
|
|
21
|
+
*
|
|
22
|
+
* [gam]: https://admanager.google.com/59666047#inventory/custom_targeting/detail/custom_key_id=12318099
|
|
23
|
+
* */
|
|
24
|
+
amtgrp: AdManagerGroup | null;
|
|
25
|
+
/**
|
|
26
|
+
* Interaction with TCFv2 banner – [see on Ad Manager][gam]
|
|
27
|
+
*
|
|
28
|
+
* Type: _Predefined_
|
|
29
|
+
*
|
|
30
|
+
* [gam]: https://admanager.google.com/59666047#inventory/custom_targeting/detail/custom_key_id=12083384
|
|
31
|
+
*/
|
|
32
|
+
cmp_interaction?: TCEventStatusCode | NotApplicable;
|
|
33
|
+
/**
|
|
34
|
+
* **TCFv2 Consent** to [all purposes] – [see on Ad Manager][gam]
|
|
35
|
+
*
|
|
36
|
+
* Type: _Predefined_
|
|
37
|
+
*
|
|
38
|
+
* [all purposes]: https://vendor-list.consensu.org/v2/vendor-list.json
|
|
39
|
+
* [gam]: https://admanager.google.com/59666047#inventory/custom_targeting/detail/custom_key_id=12080297
|
|
40
|
+
* */
|
|
41
|
+
consent_tcfv2: True | False | NotApplicable;
|
|
42
|
+
/**
|
|
43
|
+
* **Fr**equency – [see on Ad Manager][gam]
|
|
44
|
+
*
|
|
45
|
+
* Type: _Predefined_
|
|
46
|
+
*
|
|
47
|
+
* [gam]: https://admanager.google.com/59666047#inventory/custom_targeting/detail/custom_key_id=214647
|
|
48
|
+
*/
|
|
49
|
+
fr: typeof frequency[number];
|
|
50
|
+
/**
|
|
51
|
+
* **P**ersonalised **A**ds Consent – [see on Ad Manager][gam]
|
|
52
|
+
*
|
|
53
|
+
* Type: _Predefined_
|
|
54
|
+
*
|
|
55
|
+
* [gam]: https://admanager.google.com/59666047#inventory/custom_targeting/detail/custom_key_id=11701767
|
|
56
|
+
*/
|
|
57
|
+
pa: True | False;
|
|
58
|
+
/**
|
|
59
|
+
* **Permutive** user segments – [see on Ad Manager][gam]
|
|
60
|
+
*
|
|
61
|
+
* Type: _Predefined_
|
|
62
|
+
*
|
|
63
|
+
* Values: 900+ number IDs
|
|
64
|
+
*
|
|
65
|
+
* [gam]: https://admanager.google.com/59666047#inventory/custom_targeting/detail/custom_key_id=11958727
|
|
66
|
+
*/
|
|
67
|
+
permutive: string[];
|
|
68
|
+
/**
|
|
69
|
+
* **R**estrict **D**ata **P**rocessing Flag – [see on Ad Manager][gam]
|
|
70
|
+
*
|
|
71
|
+
* Type: _Predefined_
|
|
72
|
+
*
|
|
73
|
+
* [gam]: https://admanager.google.com/59666047#inventory/custom_targeting/detail/custom_key_id=11701767
|
|
74
|
+
*/
|
|
75
|
+
rdp: True | False | NotApplicable;
|
|
76
|
+
};
|
|
77
|
+
declare const getPersonalisedTargeting: (state: ConsentState) => PersonalisedTargeting;
|
|
78
|
+
export { getPersonalisedTargeting };
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { storage } from '@guardian/libs';
|
|
2
|
+
import { clearPermutiveSegments, getPermutiveSegments } from '../permutive';
|
|
3
|
+
/* -- Types -- */
|
|
4
|
+
const frequency = [
|
|
5
|
+
'0',
|
|
6
|
+
'1',
|
|
7
|
+
'2',
|
|
8
|
+
'3',
|
|
9
|
+
'4',
|
|
10
|
+
'5',
|
|
11
|
+
'6-9',
|
|
12
|
+
'10-15',
|
|
13
|
+
'16-19',
|
|
14
|
+
'20-29',
|
|
15
|
+
'30plus',
|
|
16
|
+
];
|
|
17
|
+
const AMTGRP_STORAGE_KEY = 'gu.adManagerGroup';
|
|
18
|
+
const adManagerGroups = [
|
|
19
|
+
'1',
|
|
20
|
+
'2',
|
|
21
|
+
'3',
|
|
22
|
+
'4',
|
|
23
|
+
'5',
|
|
24
|
+
'6',
|
|
25
|
+
'7',
|
|
26
|
+
'8',
|
|
27
|
+
'9',
|
|
28
|
+
'10',
|
|
29
|
+
'11',
|
|
30
|
+
'12',
|
|
31
|
+
];
|
|
32
|
+
/* -- Methods -- */
|
|
33
|
+
const getRawWithConsent = (key, state) => {
|
|
34
|
+
if (state.tcfv2) {
|
|
35
|
+
if (state.tcfv2.consents['1'])
|
|
36
|
+
return storage.local.getRaw(key);
|
|
37
|
+
}
|
|
38
|
+
if (state.ccpa) {
|
|
39
|
+
if (!state.ccpa.doNotSell)
|
|
40
|
+
return storage.local.getRaw(key);
|
|
41
|
+
}
|
|
42
|
+
if (state.aus) {
|
|
43
|
+
if (state.aus.personalisedAdvertising)
|
|
44
|
+
return storage.local.getRaw(key);
|
|
45
|
+
}
|
|
46
|
+
return null;
|
|
47
|
+
};
|
|
48
|
+
const getFrequencyValue = (state) => {
|
|
49
|
+
const rawValue = getRawWithConsent('gu.alreadyVisited', state);
|
|
50
|
+
if (!rawValue)
|
|
51
|
+
return '0';
|
|
52
|
+
const visitCount = parseInt(rawValue, 10);
|
|
53
|
+
if (visitCount <= 5) {
|
|
54
|
+
return frequency[visitCount] ?? '0';
|
|
55
|
+
}
|
|
56
|
+
else if (visitCount >= 6 && visitCount <= 9) {
|
|
57
|
+
return '6-9';
|
|
58
|
+
}
|
|
59
|
+
else if (visitCount >= 10 && visitCount <= 15) {
|
|
60
|
+
return '10-15';
|
|
61
|
+
}
|
|
62
|
+
else if (visitCount >= 16 && visitCount <= 19) {
|
|
63
|
+
return '16-19';
|
|
64
|
+
}
|
|
65
|
+
else if (visitCount >= 20 && visitCount <= 29) {
|
|
66
|
+
return '20-29';
|
|
67
|
+
}
|
|
68
|
+
else if (visitCount >= 30) {
|
|
69
|
+
return '30plus';
|
|
70
|
+
}
|
|
71
|
+
return '0';
|
|
72
|
+
};
|
|
73
|
+
const tcfv2AllPurposesConsented = (consents) => Object.keys(consents).length > 0 && Object.values(consents).every(Boolean);
|
|
74
|
+
const personalisedAdvertising = (state) => {
|
|
75
|
+
if (state.tcfv2)
|
|
76
|
+
return tcfv2AllPurposesConsented(state.tcfv2.consents);
|
|
77
|
+
if (state.ccpa)
|
|
78
|
+
return !state.ccpa.doNotSell;
|
|
79
|
+
if (state.aus)
|
|
80
|
+
return state.aus.personalisedAdvertising;
|
|
81
|
+
return false;
|
|
82
|
+
};
|
|
83
|
+
const getCMPTargeting = (state) => {
|
|
84
|
+
if (state.tcfv2) {
|
|
85
|
+
return {
|
|
86
|
+
cmp_interaction: state.tcfv2.eventStatus,
|
|
87
|
+
pa: tcfv2AllPurposesConsented(state.tcfv2.consents) ? 't' : 'f',
|
|
88
|
+
consent_tcfv2: tcfv2AllPurposesConsented(state.tcfv2.consents)
|
|
89
|
+
? 't'
|
|
90
|
+
: 'f',
|
|
91
|
+
rdp: 'na',
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
if (state.ccpa) {
|
|
95
|
+
return {
|
|
96
|
+
consent_tcfv2: 'na',
|
|
97
|
+
rdp: state.ccpa.doNotSell ? 't' : 'f',
|
|
98
|
+
pa: state.ccpa.doNotSell ? 'f' : 't',
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
if (state.aus) {
|
|
102
|
+
return {
|
|
103
|
+
consent_tcfv2: 'na',
|
|
104
|
+
rdp: 'na',
|
|
105
|
+
pa: state.aus.personalisedAdvertising ? 't' : 'f',
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
return {
|
|
109
|
+
cmp_interaction: 'na',
|
|
110
|
+
consent_tcfv2: 'na',
|
|
111
|
+
rdp: 'na',
|
|
112
|
+
pa: 'f',
|
|
113
|
+
};
|
|
114
|
+
};
|
|
115
|
+
const isAdManagerGroup = (s) => adManagerGroups.some((g) => g === s);
|
|
116
|
+
const createAdManagerGroup = () => {
|
|
117
|
+
const index = Math.floor(Math.random() * adManagerGroups.length);
|
|
118
|
+
const group = adManagerGroups[index] ?? '12';
|
|
119
|
+
storage.local.setRaw(AMTGRP_STORAGE_KEY, group);
|
|
120
|
+
return group;
|
|
121
|
+
};
|
|
122
|
+
const getAdManagerGroup = (state) => {
|
|
123
|
+
if (!personalisedAdvertising(state)) {
|
|
124
|
+
storage.local.remove(AMTGRP_STORAGE_KEY);
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
const existingGroup = storage.local.getRaw(AMTGRP_STORAGE_KEY);
|
|
128
|
+
return isAdManagerGroup(existingGroup)
|
|
129
|
+
? existingGroup
|
|
130
|
+
: createAdManagerGroup();
|
|
131
|
+
};
|
|
132
|
+
const getPermutiveWithState = (state) => {
|
|
133
|
+
if (personalisedAdvertising(state))
|
|
134
|
+
return getPermutiveSegments();
|
|
135
|
+
clearPermutiveSegments();
|
|
136
|
+
return [];
|
|
137
|
+
};
|
|
138
|
+
/* -- Targeting -- */
|
|
139
|
+
const getPersonalisedTargeting = (state) => ({
|
|
140
|
+
amtgrp: getAdManagerGroup(state),
|
|
141
|
+
fr: getFrequencyValue(state),
|
|
142
|
+
permutive: getPermutiveWithState(state),
|
|
143
|
+
...getCMPTargeting(state),
|
|
144
|
+
});
|
|
145
|
+
export { getPersonalisedTargeting };
|