@guardian/commercial-core 0.32.0 → 0.35.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/detectAdBlocker.js +2 -1
- package/dist/cjs/targeting/content.d.ts +9 -128
- package/dist/cjs/targeting/content.js +4 -53
- package/dist/cjs/targeting/pick-targeting-values.d.ts +25 -0
- package/dist/cjs/targeting/pick-targeting-values.js +47 -0
- package/dist/cjs/targeting/session.d.ts +13 -2
- package/dist/cjs/targeting/session.js +8 -5
- package/dist/cjs/targeting/shared.d.ts +142 -0
- package/dist/cjs/targeting/shared.js +58 -0
- package/dist/esm/detectAdBlocker.js +2 -1
- package/dist/esm/targeting/content.d.ts +9 -128
- package/dist/esm/targeting/content.js +4 -53
- package/dist/esm/targeting/pick-targeting-values.d.ts +25 -0
- package/dist/esm/targeting/pick-targeting-values.js +43 -0
- package/dist/esm/targeting/session.d.ts +13 -2
- package/dist/esm/targeting/session.js +8 -5
- package/dist/esm/targeting/shared.d.ts +142 -0
- package/dist/esm/targeting/shared.js +54 -0
- package/package.json +3 -2
|
@@ -16,8 +16,9 @@ function adElementBlocked(ad) {
|
|
|
16
16
|
ad.offsetTop === 0 ||
|
|
17
17
|
ad.offsetWidth === 0 ||
|
|
18
18
|
ad.clientHeight === 0 ||
|
|
19
|
-
ad.clientWidth === 0)
|
|
19
|
+
ad.clientWidth === 0) {
|
|
20
20
|
return true;
|
|
21
|
+
}
|
|
21
22
|
const adStyles = window.getComputedStyle(ad);
|
|
22
23
|
if (adStyles.getPropertyValue('display') === 'none')
|
|
23
24
|
return true;
|
|
@@ -1,31 +1,6 @@
|
|
|
1
1
|
import type { False, True } from '../types';
|
|
2
|
-
|
|
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
|
-
};
|
|
2
|
+
import type { SharedTargeting } from './shared';
|
|
13
3
|
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
4
|
/**
|
|
30
5
|
* Content Targeting comes from the server
|
|
31
6
|
*
|
|
@@ -38,40 +13,6 @@ declare const contentTypes: readonly ["article", "audio", "crossword", "gallery"
|
|
|
38
13
|
*
|
|
39
14
|
*/
|
|
40
15
|
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
16
|
/**
|
|
76
17
|
* **D**ot**c**om-**r**endering **E**ligible - [see on Ad Manager][gam]
|
|
77
18
|
*
|
|
@@ -80,34 +21,6 @@ export declare type ContentTargeting = {
|
|
|
80
21
|
* [gam]: https://admanager.google.com/59666047#inventory/custom_targeting/detail/custom_key_id=11958028
|
|
81
22
|
*/
|
|
82
23
|
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
24
|
/**
|
|
112
25
|
* Rendering Platform - [see on Ad Manager][gam]
|
|
113
26
|
*
|
|
@@ -120,42 +33,12 @@ export declare type ContentTargeting = {
|
|
|
120
33
|
* [gam]: https://admanager.google.com/59666047#inventory/custom_targeting/detail/custom_key_id=173967
|
|
121
34
|
*/
|
|
122
35
|
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
36
|
/**
|
|
130
37
|
* **Sens**itive - [see on Ad Manager][gam]
|
|
131
38
|
*
|
|
132
39
|
* [gam]: https://admanager.google.com/59666047#inventory/custom_targeting/detail/custom_key_id=11654206
|
|
133
40
|
*/
|
|
134
41
|
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
42
|
/**
|
|
160
43
|
* URL Keywords - [see on Ad Manager][gam]
|
|
161
44
|
*
|
|
@@ -175,15 +58,13 @@ export declare type ContentTargeting = {
|
|
|
175
58
|
*/
|
|
176
59
|
vl: null | typeof videoLengths[number];
|
|
177
60
|
};
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
61
|
+
declare type Content = {
|
|
62
|
+
eligibleForDCR: boolean;
|
|
63
|
+
path: SharedTargeting['url'];
|
|
64
|
+
renderingPlatform: ContentTargeting['rp'];
|
|
65
|
+
section: ContentTargeting['s'];
|
|
183
66
|
sensitive: boolean;
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
surging: number;
|
|
188
|
-
}, targeting: Omit<ContentTargeting, 'br' | 'ct' | 'co' | 'p' | 'sens' | 'tn' | 'url' | 'urlkw' | 'vl' | 'su'>) => ContentTargeting;
|
|
67
|
+
videoLength?: number;
|
|
68
|
+
};
|
|
69
|
+
export declare const getContentTargeting: ({ eligibleForDCR, path, renderingPlatform, section, sensitive, videoLength, }: Content) => ContentTargeting;
|
|
189
70
|
export {};
|
|
@@ -3,17 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.getContentTargeting = void 0;
|
|
4
4
|
const libs_1 = require("@guardian/libs");
|
|
5
5
|
/* -- Types -- */
|
|
6
|
-
const brands = {
|
|
7
|
-
Foundation: 'f',
|
|
8
|
-
Paid: 'p',
|
|
9
|
-
Sponsored: 's',
|
|
10
|
-
};
|
|
11
|
-
const editions = {
|
|
12
|
-
UnitedKingdom: 'uk',
|
|
13
|
-
UnitedStates: 'us',
|
|
14
|
-
Australia: 'au',
|
|
15
|
-
International: 'int',
|
|
16
|
-
};
|
|
17
6
|
const videoLengths = [
|
|
18
7
|
'25',
|
|
19
8
|
'30',
|
|
@@ -27,32 +16,6 @@ const videoLengths = [
|
|
|
27
16
|
'270',
|
|
28
17
|
'300',
|
|
29
18
|
];
|
|
30
|
-
const surges = {
|
|
31
|
-
0: '0',
|
|
32
|
-
50: '5',
|
|
33
|
-
100: '4',
|
|
34
|
-
200: '3',
|
|
35
|
-
300: '2',
|
|
36
|
-
400: '1',
|
|
37
|
-
};
|
|
38
|
-
const platforms = {
|
|
39
|
-
R2: 'r2',
|
|
40
|
-
NextGen: 'ng',
|
|
41
|
-
MobileApp: 'app',
|
|
42
|
-
AcceleratedMobilePages: 'amp',
|
|
43
|
-
};
|
|
44
|
-
const contentTypes = [
|
|
45
|
-
'article',
|
|
46
|
-
'audio',
|
|
47
|
-
'crossword',
|
|
48
|
-
'gallery',
|
|
49
|
-
'interactive',
|
|
50
|
-
'liveblog',
|
|
51
|
-
'network-front',
|
|
52
|
-
'section',
|
|
53
|
-
'tag',
|
|
54
|
-
'video',
|
|
55
|
-
];
|
|
56
19
|
/* -- Methods -- */
|
|
57
20
|
const getVideoLength = (videoLength) => {
|
|
58
21
|
const index = Math.min(Math.ceil(videoLength / 30), 10);
|
|
@@ -65,24 +28,12 @@ const getUrlKeywords = (url) => {
|
|
|
65
28
|
.slice(-1)[0];
|
|
66
29
|
return (0, libs_1.isString)(lastSegment) ? lastSegment.split('-').filter(Boolean) : [];
|
|
67
30
|
};
|
|
68
|
-
const
|
|
69
|
-
if (surging < 50 || isNaN(surging))
|
|
70
|
-
return ['0'];
|
|
71
|
-
const thresholds = [400, 300, 200, 100, 50];
|
|
72
|
-
return thresholds.filter((n) => n <= surging).map((s) => surges[s]);
|
|
73
|
-
};
|
|
74
|
-
/* -- Targeting -- */
|
|
75
|
-
const getContentTargeting = ({ branding, contentType, contributors, platform, sensitive, tones, path, videoLength, surging, }, targeting) => {
|
|
31
|
+
const getContentTargeting = ({ eligibleForDCR, path, renderingPlatform, section, sensitive, videoLength, }) => {
|
|
76
32
|
return {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
ct: contentType,
|
|
81
|
-
p: platforms[platform],
|
|
33
|
+
dcre: eligibleForDCR ? 't' : 'f',
|
|
34
|
+
rp: renderingPlatform,
|
|
35
|
+
s: section,
|
|
82
36
|
sens: sensitive ? 't' : 'f',
|
|
83
|
-
tn: tones,
|
|
84
|
-
su: getSurgingParam(surging),
|
|
85
|
-
url: path,
|
|
86
37
|
urlkw: getUrlKeywords(path),
|
|
87
38
|
vl: videoLength ? getVideoLength(videoLength) : null,
|
|
88
39
|
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { ConditionalExcept } from 'type-fest';
|
|
2
|
+
declare type ValidTargetingObject<Base> = ConditionalExcept<Base, null | undefined | '' | readonly [] | readonly [''] | boolean | number>;
|
|
3
|
+
/**
|
|
4
|
+
* Picks only keys with targeting values from an object.
|
|
5
|
+
* A targeting values is defined as either:
|
|
6
|
+
* - a non-empty string
|
|
7
|
+
* - an array of non-empty strings
|
|
8
|
+
*
|
|
9
|
+
* If you object is read-only, you can safely access properties on the result.
|
|
10
|
+
* For example:
|
|
11
|
+
*
|
|
12
|
+
* ```ts
|
|
13
|
+
* dirty = {
|
|
14
|
+
* valid: 'real',
|
|
15
|
+
* invalid: undefined,
|
|
16
|
+
* } as const;
|
|
17
|
+
*
|
|
18
|
+
* clean = pickDefinedValues(dirty);
|
|
19
|
+
*
|
|
20
|
+
* // @ts-expect-error -- you can’t access this property
|
|
21
|
+
* clean.invalid
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export declare const pickTargetingValues: <T extends Record<string, string | readonly string[] | undefined>>(obj: T) => import("type-fest").Except<T, NonNullable<{ [Key in keyof T]: T[Key] extends number | boolean | "" | readonly [] | readonly [""] | null | undefined ? Key : never; }[keyof T]>>;
|
|
25
|
+
export {};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.pickTargetingValues = void 0;
|
|
4
|
+
const libs_1 = require("@guardian/libs");
|
|
5
|
+
const isTargetingString = (string) => (0, libs_1.isString)(string) && string !== '';
|
|
6
|
+
const isTargetingArray = (array) => Array.isArray(array) && array.filter(isTargetingString).length > 0;
|
|
7
|
+
const isValidTargeting = (value) => {
|
|
8
|
+
if (isTargetingString(value))
|
|
9
|
+
return true;
|
|
10
|
+
if (isTargetingArray(value))
|
|
11
|
+
return true;
|
|
12
|
+
return false;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Picks only keys with targeting values from an object.
|
|
16
|
+
* A targeting values is defined as either:
|
|
17
|
+
* - a non-empty string
|
|
18
|
+
* - an array of non-empty strings
|
|
19
|
+
*
|
|
20
|
+
* If you object is read-only, you can safely access properties on the result.
|
|
21
|
+
* For example:
|
|
22
|
+
*
|
|
23
|
+
* ```ts
|
|
24
|
+
* dirty = {
|
|
25
|
+
* valid: 'real',
|
|
26
|
+
* invalid: undefined,
|
|
27
|
+
* } as const;
|
|
28
|
+
*
|
|
29
|
+
* clean = pickDefinedValues(dirty);
|
|
30
|
+
*
|
|
31
|
+
* // @ts-expect-error -- you can’t access this property
|
|
32
|
+
* clean.invalid
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
const pickTargetingValues = (obj) => {
|
|
36
|
+
const initialValue = {};
|
|
37
|
+
return Object.entries(obj).reduce((valid, [key, value]) => {
|
|
38
|
+
if (isValidTargeting(value)) {
|
|
39
|
+
// @ts-expect-error -- isValidTargeting checks this
|
|
40
|
+
valid[key] = Array.isArray(value)
|
|
41
|
+
? value.filter(isTargetingString)
|
|
42
|
+
: value;
|
|
43
|
+
}
|
|
44
|
+
return valid;
|
|
45
|
+
}, initialValue);
|
|
46
|
+
};
|
|
47
|
+
exports.pickTargetingValues = pickTargetingValues;
|
|
@@ -92,7 +92,18 @@ export declare type SessionTargeting = {
|
|
|
92
92
|
};
|
|
93
93
|
export declare type AllParticipations = {
|
|
94
94
|
clientSideParticipations: Participations;
|
|
95
|
-
serverSideParticipations:
|
|
95
|
+
serverSideParticipations: {
|
|
96
|
+
[key: `${string}Control`]: 'control';
|
|
97
|
+
[key: `${string}Variant`]: 'variant';
|
|
98
|
+
};
|
|
96
99
|
};
|
|
97
|
-
|
|
100
|
+
declare type Session = {
|
|
101
|
+
adTest: SessionTargeting['at'];
|
|
102
|
+
countryCode: CountryCode;
|
|
103
|
+
isSignedIn: boolean;
|
|
104
|
+
pageViewId: SessionTargeting['pv'];
|
|
105
|
+
participations: AllParticipations;
|
|
106
|
+
referrer: string;
|
|
107
|
+
};
|
|
108
|
+
export declare const getSessionTargeting: ({ adTest, countryCode, isSignedIn, pageViewId, participations, referrer, }: Session) => SessionTargeting;
|
|
98
109
|
export {};
|
|
@@ -44,14 +44,17 @@ const experimentsTargeting = ({ clientSideParticipations, serverSideParticipatio
|
|
|
44
44
|
const serverSideExperiments = Object.entries(serverSideParticipations)
|
|
45
45
|
.map((test) => testToParams(...test))
|
|
46
46
|
.filter(libs_1.isString);
|
|
47
|
-
if (clientSideExperiment.length + serverSideExperiments.length === 0)
|
|
47
|
+
if (clientSideExperiment.length + serverSideExperiments.length === 0) {
|
|
48
48
|
return null;
|
|
49
|
+
}
|
|
49
50
|
return [...clientSideExperiment, ...serverSideExperiments];
|
|
50
51
|
};
|
|
51
|
-
|
|
52
|
-
const getSessionTargeting = (referrer, participations, targeting) => ({
|
|
53
|
-
ref: getReferrer(referrer),
|
|
52
|
+
const getSessionTargeting = ({ adTest, countryCode, isSignedIn, pageViewId, participations, referrer, }) => ({
|
|
54
53
|
ab: experimentsTargeting(participations),
|
|
55
|
-
|
|
54
|
+
at: adTest,
|
|
55
|
+
cc: countryCode,
|
|
56
|
+
pv: pageViewId,
|
|
57
|
+
ref: getReferrer(referrer),
|
|
58
|
+
si: isSignedIn ? 't' : 'f',
|
|
56
59
|
});
|
|
57
60
|
exports.getSessionTargeting = getSessionTargeting;
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
declare const brands: {
|
|
2
|
+
readonly Foundation: "f";
|
|
3
|
+
readonly Paid: "p";
|
|
4
|
+
readonly Sponsored: "s";
|
|
5
|
+
};
|
|
6
|
+
declare const contentTypes: readonly ["article", "audio", "crossword", "gallery", "interactive", "liveblog", "network-front", "section", "tag", "video"];
|
|
7
|
+
declare const editions: {
|
|
8
|
+
readonly UnitedKingdom: "uk";
|
|
9
|
+
readonly UnitedStates: "us";
|
|
10
|
+
readonly Australia: "au";
|
|
11
|
+
readonly International: "int";
|
|
12
|
+
};
|
|
13
|
+
declare const platforms: {
|
|
14
|
+
readonly R2: "r2";
|
|
15
|
+
readonly NextGen: "ng";
|
|
16
|
+
readonly MobileApp: "app";
|
|
17
|
+
readonly AcceleratedMobilePages: "amp";
|
|
18
|
+
};
|
|
19
|
+
declare const surges: {
|
|
20
|
+
readonly 0: "0";
|
|
21
|
+
readonly 50: "5";
|
|
22
|
+
readonly 100: "4";
|
|
23
|
+
readonly 200: "3";
|
|
24
|
+
readonly 300: "2";
|
|
25
|
+
readonly 400: "1";
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Shared Targeting is passed by `frontend` https://git.io/JDJ6W
|
|
29
|
+
*
|
|
30
|
+
* It is generated in `commercial-shared` https://git.io/JDJ62
|
|
31
|
+
*
|
|
32
|
+
*
|
|
33
|
+
*
|
|
34
|
+
*/
|
|
35
|
+
export declare type SharedTargeting = {
|
|
36
|
+
/**
|
|
37
|
+
* **Bl**og tags – [see on Ad Manager][gam]
|
|
38
|
+
*
|
|
39
|
+
* Type: _Dynamic_
|
|
40
|
+
*
|
|
41
|
+
* [gam]: https://admanager.google.com/59666047#inventory/custom_targeting/detail/custom_key_id=186687
|
|
42
|
+
*/
|
|
43
|
+
bl: string[];
|
|
44
|
+
/**
|
|
45
|
+
* **Br**anding - [see on Ad Manager][gam]
|
|
46
|
+
*
|
|
47
|
+
* Type: _Predefined_
|
|
48
|
+
*
|
|
49
|
+
* [gam]: https://admanager.google.com/59666047#inventory/custom_targeting/detail/custom_key_id=259767
|
|
50
|
+
*/
|
|
51
|
+
br: typeof brands[keyof typeof brands];
|
|
52
|
+
/**
|
|
53
|
+
* **Co**ntributors and Authors - [see on Ad Manager][gam]
|
|
54
|
+
*
|
|
55
|
+
* Array of all contributors to the content on the page
|
|
56
|
+
*
|
|
57
|
+
* Type: _Dynamic_
|
|
58
|
+
*
|
|
59
|
+
* [gam]: https://admanager.google.com/59666047#inventory/custom_targeting/detail/custom_key_id=186207
|
|
60
|
+
*/
|
|
61
|
+
co: string[];
|
|
62
|
+
/**
|
|
63
|
+
* **C**ontent **T**ype - [see on Ad Manager][gam]
|
|
64
|
+
*
|
|
65
|
+
* Type: _Predefined_
|
|
66
|
+
*
|
|
67
|
+
* [gam]: https://admanager.google.com/59666047#inventory/custom_targeting/detail/custom_key_id=177807
|
|
68
|
+
*/
|
|
69
|
+
ct: typeof contentTypes[number];
|
|
70
|
+
/**
|
|
71
|
+
* **Edition** - [see on Ad Manager][gam]
|
|
72
|
+
*
|
|
73
|
+
* Type: _Predefined_
|
|
74
|
+
*
|
|
75
|
+
* [gam]: https://admanager.google.com/59666047#inventory/custom_targeting/detail/custom_key_id=174207
|
|
76
|
+
*/
|
|
77
|
+
edition: typeof editions[keyof typeof editions];
|
|
78
|
+
/**
|
|
79
|
+
* **K**eywords - [see on Ad Manager][gam]
|
|
80
|
+
*
|
|
81
|
+
* Type: _Dynamic_
|
|
82
|
+
*
|
|
83
|
+
* [gam]: https://admanager.google.com/59666047#inventory/custom_targeting/detail/custom_key_id=177687
|
|
84
|
+
*/
|
|
85
|
+
k: string[];
|
|
86
|
+
/**
|
|
87
|
+
* **Ob**server Content - [see on Ad Manager][gam]
|
|
88
|
+
*
|
|
89
|
+
* [gam]: https://admanager.google.com/59666047#inventory/custom_targeting/detail/custom_key_id=256887
|
|
90
|
+
*/
|
|
91
|
+
ob: 't';
|
|
92
|
+
/**
|
|
93
|
+
* **P**latform - [see on Ad Manager][gam]
|
|
94
|
+
*
|
|
95
|
+
* [gam]: https://admanager.google.com/59666047#inventory/custom_targeting/detail/custom_key_id=180207
|
|
96
|
+
*/
|
|
97
|
+
p: typeof platforms[keyof typeof platforms];
|
|
98
|
+
/**
|
|
99
|
+
* **Se**ries - [see on Ad Manager][gam]
|
|
100
|
+
*
|
|
101
|
+
* [gam]: https://admanager.google.com/59666047#inventory/custom_targeting/detail/custom_key_id=180447
|
|
102
|
+
*/
|
|
103
|
+
se: string[];
|
|
104
|
+
/**
|
|
105
|
+
* **Sh**ort URL - [see on Ad Manager][gam]
|
|
106
|
+
*
|
|
107
|
+
* [gam]: https://admanager.google.com/59666047#inventory/custom_targeting/detail/custom_key_id=286047
|
|
108
|
+
*/
|
|
109
|
+
sh: `https://www.theguardian.com/p/${string}`;
|
|
110
|
+
/**
|
|
111
|
+
* **Su**rging Article - [see on Ad Manager][gam]
|
|
112
|
+
*
|
|
113
|
+
* Type: _Predefined_
|
|
114
|
+
*
|
|
115
|
+
* [gam]: https://admanager.google.com/59666047#inventory/custom_targeting/detail/custom_key_id=185007
|
|
116
|
+
*/
|
|
117
|
+
su: Array<typeof surges[keyof typeof surges]>;
|
|
118
|
+
/**
|
|
119
|
+
* **T**o**n**es - [see on Ad Manager][gam]
|
|
120
|
+
*
|
|
121
|
+
* [gam]: https://admanager.google.com/59666047#inventory/custom_targeting/detail/custom_key_id=191487
|
|
122
|
+
*/
|
|
123
|
+
tn: string[];
|
|
124
|
+
/**
|
|
125
|
+
* **U**niform **R**esource **L**ocator - [see on Ad Manager][gam]
|
|
126
|
+
*
|
|
127
|
+
* Relative to `www.theguardian.com`, starts with `/`
|
|
128
|
+
*
|
|
129
|
+
* Type: _Dynamic_
|
|
130
|
+
*
|
|
131
|
+
* [gam]: https://admanager.google.com/59666047#inventory/custom_targeting/detail/custom_key_id=174327
|
|
132
|
+
*/
|
|
133
|
+
url: `/${string}`;
|
|
134
|
+
};
|
|
135
|
+
/**
|
|
136
|
+
* What goes in comes out
|
|
137
|
+
*/
|
|
138
|
+
export declare const getSharedTargeting: (shared: Partial<SharedTargeting>) => Partial<SharedTargeting>;
|
|
139
|
+
export declare const _: {
|
|
140
|
+
getSurgingParam: (surging: number) => SharedTargeting['su'];
|
|
141
|
+
};
|
|
142
|
+
export {};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports._ = exports.getSharedTargeting = void 0;
|
|
4
|
+
const pick_targeting_values_1 = require("./pick-targeting-values");
|
|
5
|
+
/* -- Types -- */
|
|
6
|
+
const brands = {
|
|
7
|
+
Foundation: 'f',
|
|
8
|
+
Paid: 'p',
|
|
9
|
+
Sponsored: 's',
|
|
10
|
+
};
|
|
11
|
+
const contentTypes = [
|
|
12
|
+
'article',
|
|
13
|
+
'audio',
|
|
14
|
+
'crossword',
|
|
15
|
+
'gallery',
|
|
16
|
+
'interactive',
|
|
17
|
+
'liveblog',
|
|
18
|
+
'network-front',
|
|
19
|
+
'section',
|
|
20
|
+
'tag',
|
|
21
|
+
'video',
|
|
22
|
+
];
|
|
23
|
+
const editions = {
|
|
24
|
+
UnitedKingdom: 'uk',
|
|
25
|
+
UnitedStates: 'us',
|
|
26
|
+
Australia: 'au',
|
|
27
|
+
International: 'int',
|
|
28
|
+
};
|
|
29
|
+
const platforms = {
|
|
30
|
+
R2: 'r2',
|
|
31
|
+
NextGen: 'ng',
|
|
32
|
+
MobileApp: 'app',
|
|
33
|
+
AcceleratedMobilePages: 'amp',
|
|
34
|
+
};
|
|
35
|
+
const surges = {
|
|
36
|
+
0: '0',
|
|
37
|
+
50: '5',
|
|
38
|
+
100: '4',
|
|
39
|
+
200: '3',
|
|
40
|
+
300: '2',
|
|
41
|
+
400: '1',
|
|
42
|
+
};
|
|
43
|
+
/* -- Methods -- */
|
|
44
|
+
const getSurgingParam = (surging) => {
|
|
45
|
+
if (surging < 50 || isNaN(surging))
|
|
46
|
+
return ['0'];
|
|
47
|
+
const thresholds = [400, 300, 200, 100, 50];
|
|
48
|
+
return thresholds.filter((n) => n <= surging).map((s) => surges[s]);
|
|
49
|
+
};
|
|
50
|
+
/* -- Targeting -- */
|
|
51
|
+
/**
|
|
52
|
+
* What goes in comes out
|
|
53
|
+
*/
|
|
54
|
+
const getSharedTargeting = (shared) => (0, pick_targeting_values_1.pickTargetingValues)(shared);
|
|
55
|
+
exports.getSharedTargeting = getSharedTargeting;
|
|
56
|
+
exports._ = {
|
|
57
|
+
getSurgingParam,
|
|
58
|
+
};
|
|
@@ -13,8 +13,9 @@ function adElementBlocked(ad) {
|
|
|
13
13
|
ad.offsetTop === 0 ||
|
|
14
14
|
ad.offsetWidth === 0 ||
|
|
15
15
|
ad.clientHeight === 0 ||
|
|
16
|
-
ad.clientWidth === 0)
|
|
16
|
+
ad.clientWidth === 0) {
|
|
17
17
|
return true;
|
|
18
|
+
}
|
|
18
19
|
const adStyles = window.getComputedStyle(ad);
|
|
19
20
|
if (adStyles.getPropertyValue('display') === 'none')
|
|
20
21
|
return true;
|
|
@@ -1,31 +1,6 @@
|
|
|
1
1
|
import type { False, True } from '../types';
|
|
2
|
-
|
|
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
|
-
};
|
|
2
|
+
import type { SharedTargeting } from './shared';
|
|
13
3
|
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
4
|
/**
|
|
30
5
|
* Content Targeting comes from the server
|
|
31
6
|
*
|
|
@@ -38,40 +13,6 @@ declare const contentTypes: readonly ["article", "audio", "crossword", "gallery"
|
|
|
38
13
|
*
|
|
39
14
|
*/
|
|
40
15
|
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
16
|
/**
|
|
76
17
|
* **D**ot**c**om-**r**endering **E**ligible - [see on Ad Manager][gam]
|
|
77
18
|
*
|
|
@@ -80,34 +21,6 @@ export declare type ContentTargeting = {
|
|
|
80
21
|
* [gam]: https://admanager.google.com/59666047#inventory/custom_targeting/detail/custom_key_id=11958028
|
|
81
22
|
*/
|
|
82
23
|
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
24
|
/**
|
|
112
25
|
* Rendering Platform - [see on Ad Manager][gam]
|
|
113
26
|
*
|
|
@@ -120,42 +33,12 @@ export declare type ContentTargeting = {
|
|
|
120
33
|
* [gam]: https://admanager.google.com/59666047#inventory/custom_targeting/detail/custom_key_id=173967
|
|
121
34
|
*/
|
|
122
35
|
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
36
|
/**
|
|
130
37
|
* **Sens**itive - [see on Ad Manager][gam]
|
|
131
38
|
*
|
|
132
39
|
* [gam]: https://admanager.google.com/59666047#inventory/custom_targeting/detail/custom_key_id=11654206
|
|
133
40
|
*/
|
|
134
41
|
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
42
|
/**
|
|
160
43
|
* URL Keywords - [see on Ad Manager][gam]
|
|
161
44
|
*
|
|
@@ -175,15 +58,13 @@ export declare type ContentTargeting = {
|
|
|
175
58
|
*/
|
|
176
59
|
vl: null | typeof videoLengths[number];
|
|
177
60
|
};
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
61
|
+
declare type Content = {
|
|
62
|
+
eligibleForDCR: boolean;
|
|
63
|
+
path: SharedTargeting['url'];
|
|
64
|
+
renderingPlatform: ContentTargeting['rp'];
|
|
65
|
+
section: ContentTargeting['s'];
|
|
183
66
|
sensitive: boolean;
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
surging: number;
|
|
188
|
-
}, targeting: Omit<ContentTargeting, 'br' | 'ct' | 'co' | 'p' | 'sens' | 'tn' | 'url' | 'urlkw' | 'vl' | 'su'>) => ContentTargeting;
|
|
67
|
+
videoLength?: number;
|
|
68
|
+
};
|
|
69
|
+
export declare const getContentTargeting: ({ eligibleForDCR, path, renderingPlatform, section, sensitive, videoLength, }: Content) => ContentTargeting;
|
|
189
70
|
export {};
|
|
@@ -1,16 +1,5 @@
|
|
|
1
1
|
import { isString } from '@guardian/libs';
|
|
2
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
3
|
const videoLengths = [
|
|
15
4
|
'25',
|
|
16
5
|
'30',
|
|
@@ -24,32 +13,6 @@ const videoLengths = [
|
|
|
24
13
|
'270',
|
|
25
14
|
'300',
|
|
26
15
|
];
|
|
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
16
|
/* -- Methods -- */
|
|
54
17
|
const getVideoLength = (videoLength) => {
|
|
55
18
|
const index = Math.min(Math.ceil(videoLength / 30), 10);
|
|
@@ -62,24 +25,12 @@ const getUrlKeywords = (url) => {
|
|
|
62
25
|
.slice(-1)[0];
|
|
63
26
|
return isString(lastSegment) ? lastSegment.split('-').filter(Boolean) : [];
|
|
64
27
|
};
|
|
65
|
-
const
|
|
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) => {
|
|
28
|
+
export const getContentTargeting = ({ eligibleForDCR, path, renderingPlatform, section, sensitive, videoLength, }) => {
|
|
73
29
|
return {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
ct: contentType,
|
|
78
|
-
p: platforms[platform],
|
|
30
|
+
dcre: eligibleForDCR ? 't' : 'f',
|
|
31
|
+
rp: renderingPlatform,
|
|
32
|
+
s: section,
|
|
79
33
|
sens: sensitive ? 't' : 'f',
|
|
80
|
-
tn: tones,
|
|
81
|
-
su: getSurgingParam(surging),
|
|
82
|
-
url: path,
|
|
83
34
|
urlkw: getUrlKeywords(path),
|
|
84
35
|
vl: videoLength ? getVideoLength(videoLength) : null,
|
|
85
36
|
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { ConditionalExcept } from 'type-fest';
|
|
2
|
+
declare type ValidTargetingObject<Base> = ConditionalExcept<Base, null | undefined | '' | readonly [] | readonly [''] | boolean | number>;
|
|
3
|
+
/**
|
|
4
|
+
* Picks only keys with targeting values from an object.
|
|
5
|
+
* A targeting values is defined as either:
|
|
6
|
+
* - a non-empty string
|
|
7
|
+
* - an array of non-empty strings
|
|
8
|
+
*
|
|
9
|
+
* If you object is read-only, you can safely access properties on the result.
|
|
10
|
+
* For example:
|
|
11
|
+
*
|
|
12
|
+
* ```ts
|
|
13
|
+
* dirty = {
|
|
14
|
+
* valid: 'real',
|
|
15
|
+
* invalid: undefined,
|
|
16
|
+
* } as const;
|
|
17
|
+
*
|
|
18
|
+
* clean = pickDefinedValues(dirty);
|
|
19
|
+
*
|
|
20
|
+
* // @ts-expect-error -- you can’t access this property
|
|
21
|
+
* clean.invalid
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export declare const pickTargetingValues: <T extends Record<string, string | readonly string[] | undefined>>(obj: T) => import("type-fest").Except<T, NonNullable<{ [Key in keyof T]: T[Key] extends number | boolean | "" | readonly [] | readonly [""] | null | undefined ? Key : never; }[keyof T]>>;
|
|
25
|
+
export {};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { isString } from '@guardian/libs';
|
|
2
|
+
const isTargetingString = (string) => isString(string) && string !== '';
|
|
3
|
+
const isTargetingArray = (array) => Array.isArray(array) && array.filter(isTargetingString).length > 0;
|
|
4
|
+
const isValidTargeting = (value) => {
|
|
5
|
+
if (isTargetingString(value))
|
|
6
|
+
return true;
|
|
7
|
+
if (isTargetingArray(value))
|
|
8
|
+
return true;
|
|
9
|
+
return false;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Picks only keys with targeting values from an object.
|
|
13
|
+
* A targeting values is defined as either:
|
|
14
|
+
* - a non-empty string
|
|
15
|
+
* - an array of non-empty strings
|
|
16
|
+
*
|
|
17
|
+
* If you object is read-only, you can safely access properties on the result.
|
|
18
|
+
* For example:
|
|
19
|
+
*
|
|
20
|
+
* ```ts
|
|
21
|
+
* dirty = {
|
|
22
|
+
* valid: 'real',
|
|
23
|
+
* invalid: undefined,
|
|
24
|
+
* } as const;
|
|
25
|
+
*
|
|
26
|
+
* clean = pickDefinedValues(dirty);
|
|
27
|
+
*
|
|
28
|
+
* // @ts-expect-error -- you can’t access this property
|
|
29
|
+
* clean.invalid
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export const pickTargetingValues = (obj) => {
|
|
33
|
+
const initialValue = {};
|
|
34
|
+
return Object.entries(obj).reduce((valid, [key, value]) => {
|
|
35
|
+
if (isValidTargeting(value)) {
|
|
36
|
+
// @ts-expect-error -- isValidTargeting checks this
|
|
37
|
+
valid[key] = Array.isArray(value)
|
|
38
|
+
? value.filter(isTargetingString)
|
|
39
|
+
: value;
|
|
40
|
+
}
|
|
41
|
+
return valid;
|
|
42
|
+
}, initialValue);
|
|
43
|
+
};
|
|
@@ -92,7 +92,18 @@ export declare type SessionTargeting = {
|
|
|
92
92
|
};
|
|
93
93
|
export declare type AllParticipations = {
|
|
94
94
|
clientSideParticipations: Participations;
|
|
95
|
-
serverSideParticipations:
|
|
95
|
+
serverSideParticipations: {
|
|
96
|
+
[key: `${string}Control`]: 'control';
|
|
97
|
+
[key: `${string}Variant`]: 'variant';
|
|
98
|
+
};
|
|
96
99
|
};
|
|
97
|
-
|
|
100
|
+
declare type Session = {
|
|
101
|
+
adTest: SessionTargeting['at'];
|
|
102
|
+
countryCode: CountryCode;
|
|
103
|
+
isSignedIn: boolean;
|
|
104
|
+
pageViewId: SessionTargeting['pv'];
|
|
105
|
+
participations: AllParticipations;
|
|
106
|
+
referrer: string;
|
|
107
|
+
};
|
|
108
|
+
export declare const getSessionTargeting: ({ adTest, countryCode, isSignedIn, pageViewId, participations, referrer, }: Session) => SessionTargeting;
|
|
98
109
|
export {};
|
|
@@ -41,13 +41,16 @@ const experimentsTargeting = ({ clientSideParticipations, serverSideParticipatio
|
|
|
41
41
|
const serverSideExperiments = Object.entries(serverSideParticipations)
|
|
42
42
|
.map((test) => testToParams(...test))
|
|
43
43
|
.filter(isString);
|
|
44
|
-
if (clientSideExperiment.length + serverSideExperiments.length === 0)
|
|
44
|
+
if (clientSideExperiment.length + serverSideExperiments.length === 0) {
|
|
45
45
|
return null;
|
|
46
|
+
}
|
|
46
47
|
return [...clientSideExperiment, ...serverSideExperiments];
|
|
47
48
|
};
|
|
48
|
-
|
|
49
|
-
export const getSessionTargeting = (referrer, participations, targeting) => ({
|
|
50
|
-
ref: getReferrer(referrer),
|
|
49
|
+
export const getSessionTargeting = ({ adTest, countryCode, isSignedIn, pageViewId, participations, referrer, }) => ({
|
|
51
50
|
ab: experimentsTargeting(participations),
|
|
52
|
-
|
|
51
|
+
at: adTest,
|
|
52
|
+
cc: countryCode,
|
|
53
|
+
pv: pageViewId,
|
|
54
|
+
ref: getReferrer(referrer),
|
|
55
|
+
si: isSignedIn ? 't' : 'f',
|
|
53
56
|
});
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
declare const brands: {
|
|
2
|
+
readonly Foundation: "f";
|
|
3
|
+
readonly Paid: "p";
|
|
4
|
+
readonly Sponsored: "s";
|
|
5
|
+
};
|
|
6
|
+
declare const contentTypes: readonly ["article", "audio", "crossword", "gallery", "interactive", "liveblog", "network-front", "section", "tag", "video"];
|
|
7
|
+
declare const editions: {
|
|
8
|
+
readonly UnitedKingdom: "uk";
|
|
9
|
+
readonly UnitedStates: "us";
|
|
10
|
+
readonly Australia: "au";
|
|
11
|
+
readonly International: "int";
|
|
12
|
+
};
|
|
13
|
+
declare const platforms: {
|
|
14
|
+
readonly R2: "r2";
|
|
15
|
+
readonly NextGen: "ng";
|
|
16
|
+
readonly MobileApp: "app";
|
|
17
|
+
readonly AcceleratedMobilePages: "amp";
|
|
18
|
+
};
|
|
19
|
+
declare const surges: {
|
|
20
|
+
readonly 0: "0";
|
|
21
|
+
readonly 50: "5";
|
|
22
|
+
readonly 100: "4";
|
|
23
|
+
readonly 200: "3";
|
|
24
|
+
readonly 300: "2";
|
|
25
|
+
readonly 400: "1";
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Shared Targeting is passed by `frontend` https://git.io/JDJ6W
|
|
29
|
+
*
|
|
30
|
+
* It is generated in `commercial-shared` https://git.io/JDJ62
|
|
31
|
+
*
|
|
32
|
+
*
|
|
33
|
+
*
|
|
34
|
+
*/
|
|
35
|
+
export declare type SharedTargeting = {
|
|
36
|
+
/**
|
|
37
|
+
* **Bl**og tags – [see on Ad Manager][gam]
|
|
38
|
+
*
|
|
39
|
+
* Type: _Dynamic_
|
|
40
|
+
*
|
|
41
|
+
* [gam]: https://admanager.google.com/59666047#inventory/custom_targeting/detail/custom_key_id=186687
|
|
42
|
+
*/
|
|
43
|
+
bl: string[];
|
|
44
|
+
/**
|
|
45
|
+
* **Br**anding - [see on Ad Manager][gam]
|
|
46
|
+
*
|
|
47
|
+
* Type: _Predefined_
|
|
48
|
+
*
|
|
49
|
+
* [gam]: https://admanager.google.com/59666047#inventory/custom_targeting/detail/custom_key_id=259767
|
|
50
|
+
*/
|
|
51
|
+
br: typeof brands[keyof typeof brands];
|
|
52
|
+
/**
|
|
53
|
+
* **Co**ntributors and Authors - [see on Ad Manager][gam]
|
|
54
|
+
*
|
|
55
|
+
* Array of all contributors to the content on the page
|
|
56
|
+
*
|
|
57
|
+
* Type: _Dynamic_
|
|
58
|
+
*
|
|
59
|
+
* [gam]: https://admanager.google.com/59666047#inventory/custom_targeting/detail/custom_key_id=186207
|
|
60
|
+
*/
|
|
61
|
+
co: string[];
|
|
62
|
+
/**
|
|
63
|
+
* **C**ontent **T**ype - [see on Ad Manager][gam]
|
|
64
|
+
*
|
|
65
|
+
* Type: _Predefined_
|
|
66
|
+
*
|
|
67
|
+
* [gam]: https://admanager.google.com/59666047#inventory/custom_targeting/detail/custom_key_id=177807
|
|
68
|
+
*/
|
|
69
|
+
ct: typeof contentTypes[number];
|
|
70
|
+
/**
|
|
71
|
+
* **Edition** - [see on Ad Manager][gam]
|
|
72
|
+
*
|
|
73
|
+
* Type: _Predefined_
|
|
74
|
+
*
|
|
75
|
+
* [gam]: https://admanager.google.com/59666047#inventory/custom_targeting/detail/custom_key_id=174207
|
|
76
|
+
*/
|
|
77
|
+
edition: typeof editions[keyof typeof editions];
|
|
78
|
+
/**
|
|
79
|
+
* **K**eywords - [see on Ad Manager][gam]
|
|
80
|
+
*
|
|
81
|
+
* Type: _Dynamic_
|
|
82
|
+
*
|
|
83
|
+
* [gam]: https://admanager.google.com/59666047#inventory/custom_targeting/detail/custom_key_id=177687
|
|
84
|
+
*/
|
|
85
|
+
k: string[];
|
|
86
|
+
/**
|
|
87
|
+
* **Ob**server Content - [see on Ad Manager][gam]
|
|
88
|
+
*
|
|
89
|
+
* [gam]: https://admanager.google.com/59666047#inventory/custom_targeting/detail/custom_key_id=256887
|
|
90
|
+
*/
|
|
91
|
+
ob: 't';
|
|
92
|
+
/**
|
|
93
|
+
* **P**latform - [see on Ad Manager][gam]
|
|
94
|
+
*
|
|
95
|
+
* [gam]: https://admanager.google.com/59666047#inventory/custom_targeting/detail/custom_key_id=180207
|
|
96
|
+
*/
|
|
97
|
+
p: typeof platforms[keyof typeof platforms];
|
|
98
|
+
/**
|
|
99
|
+
* **Se**ries - [see on Ad Manager][gam]
|
|
100
|
+
*
|
|
101
|
+
* [gam]: https://admanager.google.com/59666047#inventory/custom_targeting/detail/custom_key_id=180447
|
|
102
|
+
*/
|
|
103
|
+
se: string[];
|
|
104
|
+
/**
|
|
105
|
+
* **Sh**ort URL - [see on Ad Manager][gam]
|
|
106
|
+
*
|
|
107
|
+
* [gam]: https://admanager.google.com/59666047#inventory/custom_targeting/detail/custom_key_id=286047
|
|
108
|
+
*/
|
|
109
|
+
sh: `https://www.theguardian.com/p/${string}`;
|
|
110
|
+
/**
|
|
111
|
+
* **Su**rging Article - [see on Ad Manager][gam]
|
|
112
|
+
*
|
|
113
|
+
* Type: _Predefined_
|
|
114
|
+
*
|
|
115
|
+
* [gam]: https://admanager.google.com/59666047#inventory/custom_targeting/detail/custom_key_id=185007
|
|
116
|
+
*/
|
|
117
|
+
su: Array<typeof surges[keyof typeof surges]>;
|
|
118
|
+
/**
|
|
119
|
+
* **T**o**n**es - [see on Ad Manager][gam]
|
|
120
|
+
*
|
|
121
|
+
* [gam]: https://admanager.google.com/59666047#inventory/custom_targeting/detail/custom_key_id=191487
|
|
122
|
+
*/
|
|
123
|
+
tn: string[];
|
|
124
|
+
/**
|
|
125
|
+
* **U**niform **R**esource **L**ocator - [see on Ad Manager][gam]
|
|
126
|
+
*
|
|
127
|
+
* Relative to `www.theguardian.com`, starts with `/`
|
|
128
|
+
*
|
|
129
|
+
* Type: _Dynamic_
|
|
130
|
+
*
|
|
131
|
+
* [gam]: https://admanager.google.com/59666047#inventory/custom_targeting/detail/custom_key_id=174327
|
|
132
|
+
*/
|
|
133
|
+
url: `/${string}`;
|
|
134
|
+
};
|
|
135
|
+
/**
|
|
136
|
+
* What goes in comes out
|
|
137
|
+
*/
|
|
138
|
+
export declare const getSharedTargeting: (shared: Partial<SharedTargeting>) => Partial<SharedTargeting>;
|
|
139
|
+
export declare const _: {
|
|
140
|
+
getSurgingParam: (surging: number) => SharedTargeting['su'];
|
|
141
|
+
};
|
|
142
|
+
export {};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { pickTargetingValues } from './pick-targeting-values';
|
|
2
|
+
/* -- Types -- */
|
|
3
|
+
const brands = {
|
|
4
|
+
Foundation: 'f',
|
|
5
|
+
Paid: 'p',
|
|
6
|
+
Sponsored: 's',
|
|
7
|
+
};
|
|
8
|
+
const contentTypes = [
|
|
9
|
+
'article',
|
|
10
|
+
'audio',
|
|
11
|
+
'crossword',
|
|
12
|
+
'gallery',
|
|
13
|
+
'interactive',
|
|
14
|
+
'liveblog',
|
|
15
|
+
'network-front',
|
|
16
|
+
'section',
|
|
17
|
+
'tag',
|
|
18
|
+
'video',
|
|
19
|
+
];
|
|
20
|
+
const editions = {
|
|
21
|
+
UnitedKingdom: 'uk',
|
|
22
|
+
UnitedStates: 'us',
|
|
23
|
+
Australia: 'au',
|
|
24
|
+
International: 'int',
|
|
25
|
+
};
|
|
26
|
+
const platforms = {
|
|
27
|
+
R2: 'r2',
|
|
28
|
+
NextGen: 'ng',
|
|
29
|
+
MobileApp: 'app',
|
|
30
|
+
AcceleratedMobilePages: 'amp',
|
|
31
|
+
};
|
|
32
|
+
const surges = {
|
|
33
|
+
0: '0',
|
|
34
|
+
50: '5',
|
|
35
|
+
100: '4',
|
|
36
|
+
200: '3',
|
|
37
|
+
300: '2',
|
|
38
|
+
400: '1',
|
|
39
|
+
};
|
|
40
|
+
/* -- Methods -- */
|
|
41
|
+
const getSurgingParam = (surging) => {
|
|
42
|
+
if (surging < 50 || isNaN(surging))
|
|
43
|
+
return ['0'];
|
|
44
|
+
const thresholds = [400, 300, 200, 100, 50];
|
|
45
|
+
return thresholds.filter((n) => n <= surging).map((s) => surges[s]);
|
|
46
|
+
};
|
|
47
|
+
/* -- Targeting -- */
|
|
48
|
+
/**
|
|
49
|
+
* What goes in comes out
|
|
50
|
+
*/
|
|
51
|
+
export const getSharedTargeting = (shared) => pickTargetingValues(shared);
|
|
52
|
+
export const _ = {
|
|
53
|
+
getSurgingParam,
|
|
54
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@guardian/commercial-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.35.0",
|
|
4
4
|
"description": "Guardian advertising business logic",
|
|
5
5
|
"homepage": "https://github.com/guardian/commercial-core#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
"@typescript-eslint/parser": "^5.5.0",
|
|
58
58
|
"commitizen": "^4.2.4",
|
|
59
59
|
"cz-conventional-changelog": "^3.3.0",
|
|
60
|
-
"eslint": "^8.
|
|
60
|
+
"eslint": "^8.4.1",
|
|
61
61
|
"eslint-config-prettier": "^8.3.0",
|
|
62
62
|
"eslint-plugin-eslint-comments": "^3.2.0",
|
|
63
63
|
"eslint-plugin-import": "^2.25.3",
|
|
@@ -71,6 +71,7 @@
|
|
|
71
71
|
"prettier": "^2.5.0",
|
|
72
72
|
"semantic-release": "^18.0.1",
|
|
73
73
|
"ts-jest": "^27.0.7",
|
|
74
|
+
"type-fest": "^2.8.0",
|
|
74
75
|
"typescript": "^4.5.2",
|
|
75
76
|
"web-vitals": "^2.1.2"
|
|
76
77
|
},
|