@guardian/commercial-core 0.33.0 → 0.36.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.
@@ -4,8 +4,10 @@ export declare type AdSize = Readonly<{
4
4
  height: number;
5
5
  toString: () => AdSizeString;
6
6
  }>;
7
+ export declare type AdSizeTuple = [width: number, height: number];
7
8
  export declare type SizeKeys = 'billboard' | 'leaderboard' | 'mpu' | 'halfPage' | 'portrait' | 'skyscraper' | 'mobilesticky' | 'fluid' | 'outOfPage' | 'googleCard' | 'video' | 'outstreamDesktop' | 'outstreamGoogleDesktop' | 'outstreamMobile' | 'merchandisingHighAdFeature' | 'merchandisingHigh' | 'merchandising' | 'inlineMerchandising' | 'fabric' | 'empty' | '970x250' | '728x90' | '300x250' | '300x600' | '300x1050' | '160x600';
8
9
  export declare const adSizes: Record<SizeKeys, AdSize>;
10
+ export declare const getTuple: (size: SizeKeys) => AdSizeTuple;
9
11
  export declare const _: {
10
12
  getAdSize: (width: number, height: number) => AdSize;
11
13
  };
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports._ = exports.adSizes = void 0;
3
+ exports._ = exports.getTuple = exports.adSizes = void 0;
4
4
  const getAdSize = (width, height) => {
5
5
  const toString = () => width === 0 && height === 0 ? 'fluid' : `${width},${height}`;
6
6
  return Object.freeze({
@@ -43,5 +43,11 @@ exports.adSizes = {
43
43
  '300x1050': adSizesPartial.portrait,
44
44
  '160x600': adSizesPartial.skyscraper,
45
45
  };
46
+ const getTuple = (size) => {
47
+ const { width, height } = exports.adSizes[size];
48
+ const tuple = [width, height];
49
+ return tuple;
50
+ };
51
+ exports.getTuple = getTuple;
46
52
  // Export for testing
47
53
  exports._ = { getAdSize };
@@ -1,31 +1,6 @@
1
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
- };
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
- 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;
61
+ declare type Content = {
62
+ eligibleForDCR: boolean;
63
+ path: SharedTargeting['url'];
64
+ renderingPlatform: ContentTargeting['rp'];
65
+ section: ContentTargeting['s'];
183
66
  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;
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 getSurgingParam = (surging) => {
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
- ...targeting,
78
- br: branding ? brands[branding] : null,
79
- co: contributors,
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
  };
@@ -1,8 +1,5 @@
1
- declare type ValidTargeting<T, K> = '' extends T ? never : [] extends T ? never : [''] extends T ? never : T extends boolean ? never : T extends NonNullable<T> ? K : never;
2
- declare type DefinedKeys<T> = {
3
- [K in keyof T]-?: ValidTargeting<T[K], K>;
4
- }[keyof T];
5
- declare type ObjectWithDefinedValues<T> = Pick<T, DefinedKeys<T>>;
1
+ import type { ConditionalExcept } from 'type-fest';
2
+ declare type ValidTargetingObject<Base> = ConditionalExcept<Base, null | undefined | '' | readonly [] | readonly [''] | boolean | number>;
6
3
  /**
7
4
  * Picks only keys with targeting values from an object.
8
5
  * A targeting values is defined as either:
@@ -24,5 +21,5 @@ declare type ObjectWithDefinedValues<T> = Pick<T, DefinedKeys<T>>;
24
21
  * clean.invalid
25
22
  * ```
26
23
  */
27
- export declare const pickTargetingValues: <T extends Record<string, string | readonly string[] | undefined>>(obj: T) => ObjectWithDefinedValues<T>;
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]>>;
28
25
  export {};
@@ -37,7 +37,9 @@ const pickTargetingValues = (obj) => {
37
37
  return Object.entries(obj).reduce((valid, [key, value]) => {
38
38
  if (isValidTargeting(value)) {
39
39
  // @ts-expect-error -- isValidTargeting checks this
40
- valid[key] = value;
40
+ valid[key] = Array.isArray(value)
41
+ ? value.filter(isTargetingString)
42
+ : value;
41
43
  }
42
44
  return valid;
43
45
  }, initialValue);
@@ -92,7 +92,18 @@ export declare type SessionTargeting = {
92
92
  };
93
93
  export declare type AllParticipations = {
94
94
  clientSideParticipations: Participations;
95
- serverSideParticipations: Record<string, 'control' | 'variant'>;
95
+ serverSideParticipations: {
96
+ [key: `${string}Control`]: 'control';
97
+ [key: `${string}Variant`]: 'variant';
98
+ };
96
99
  };
97
- export declare const getSessionTargeting: (referrer: string, participations: AllParticipations, targeting: Omit<SessionTargeting, 'ab' | 'ref'>) => SessionTargeting;
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 {};
@@ -49,10 +49,12 @@ const experimentsTargeting = ({ clientSideParticipations, serverSideParticipatio
49
49
  }
50
50
  return [...clientSideExperiment, ...serverSideExperiments];
51
51
  };
52
- /* -- Targeting -- */
53
- const getSessionTargeting = (referrer, participations, targeting) => ({
54
- ref: getReferrer(referrer),
52
+ const getSessionTargeting = ({ adTest, countryCode, isSignedIn, pageViewId, participations, referrer, }) => ({
55
53
  ab: experimentsTargeting(participations),
56
- ...targeting,
54
+ at: adTest,
55
+ cc: countryCode,
56
+ pv: pageViewId,
57
+ ref: getReferrer(referrer),
58
+ si: isSignedIn ? 't' : 'f',
57
59
  });
58
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
+ };
@@ -4,8 +4,10 @@ export declare type AdSize = Readonly<{
4
4
  height: number;
5
5
  toString: () => AdSizeString;
6
6
  }>;
7
+ export declare type AdSizeTuple = [width: number, height: number];
7
8
  export declare type SizeKeys = 'billboard' | 'leaderboard' | 'mpu' | 'halfPage' | 'portrait' | 'skyscraper' | 'mobilesticky' | 'fluid' | 'outOfPage' | 'googleCard' | 'video' | 'outstreamDesktop' | 'outstreamGoogleDesktop' | 'outstreamMobile' | 'merchandisingHighAdFeature' | 'merchandisingHigh' | 'merchandising' | 'inlineMerchandising' | 'fabric' | 'empty' | '970x250' | '728x90' | '300x250' | '300x600' | '300x1050' | '160x600';
8
9
  export declare const adSizes: Record<SizeKeys, AdSize>;
10
+ export declare const getTuple: (size: SizeKeys) => AdSizeTuple;
9
11
  export declare const _: {
10
12
  getAdSize: (width: number, height: number) => AdSize;
11
13
  };
@@ -40,5 +40,10 @@ export const adSizes = {
40
40
  '300x1050': adSizesPartial.portrait,
41
41
  '160x600': adSizesPartial.skyscraper,
42
42
  };
43
+ export const getTuple = (size) => {
44
+ const { width, height } = adSizes[size];
45
+ const tuple = [width, height];
46
+ return tuple;
47
+ };
43
48
  // Export for testing
44
49
  export const _ = { getAdSize };
@@ -1,31 +1,6 @@
1
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
- };
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
- 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;
61
+ declare type Content = {
62
+ eligibleForDCR: boolean;
63
+ path: SharedTargeting['url'];
64
+ renderingPlatform: ContentTargeting['rp'];
65
+ section: ContentTargeting['s'];
183
66
  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;
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 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) => {
28
+ export const getContentTargeting = ({ eligibleForDCR, path, renderingPlatform, section, sensitive, videoLength, }) => {
73
29
  return {
74
- ...targeting,
75
- br: branding ? brands[branding] : null,
76
- co: contributors,
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
  };
@@ -1,8 +1,5 @@
1
- declare type ValidTargeting<T, K> = '' extends T ? never : [] extends T ? never : [''] extends T ? never : T extends boolean ? never : T extends NonNullable<T> ? K : never;
2
- declare type DefinedKeys<T> = {
3
- [K in keyof T]-?: ValidTargeting<T[K], K>;
4
- }[keyof T];
5
- declare type ObjectWithDefinedValues<T> = Pick<T, DefinedKeys<T>>;
1
+ import type { ConditionalExcept } from 'type-fest';
2
+ declare type ValidTargetingObject<Base> = ConditionalExcept<Base, null | undefined | '' | readonly [] | readonly [''] | boolean | number>;
6
3
  /**
7
4
  * Picks only keys with targeting values from an object.
8
5
  * A targeting values is defined as either:
@@ -24,5 +21,5 @@ declare type ObjectWithDefinedValues<T> = Pick<T, DefinedKeys<T>>;
24
21
  * clean.invalid
25
22
  * ```
26
23
  */
27
- export declare const pickTargetingValues: <T extends Record<string, string | readonly string[] | undefined>>(obj: T) => ObjectWithDefinedValues<T>;
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]>>;
28
25
  export {};
@@ -34,7 +34,9 @@ export const pickTargetingValues = (obj) => {
34
34
  return Object.entries(obj).reduce((valid, [key, value]) => {
35
35
  if (isValidTargeting(value)) {
36
36
  // @ts-expect-error -- isValidTargeting checks this
37
- valid[key] = value;
37
+ valid[key] = Array.isArray(value)
38
+ ? value.filter(isTargetingString)
39
+ : value;
38
40
  }
39
41
  return valid;
40
42
  }, initialValue);
@@ -92,7 +92,18 @@ export declare type SessionTargeting = {
92
92
  };
93
93
  export declare type AllParticipations = {
94
94
  clientSideParticipations: Participations;
95
- serverSideParticipations: Record<string, 'control' | 'variant'>;
95
+ serverSideParticipations: {
96
+ [key: `${string}Control`]: 'control';
97
+ [key: `${string}Variant`]: 'variant';
98
+ };
96
99
  };
97
- export declare const getSessionTargeting: (referrer: string, participations: AllParticipations, targeting: Omit<SessionTargeting, 'ab' | 'ref'>) => SessionTargeting;
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 {};
@@ -46,9 +46,11 @@ const experimentsTargeting = ({ clientSideParticipations, serverSideParticipatio
46
46
  }
47
47
  return [...clientSideExperiment, ...serverSideExperiments];
48
48
  };
49
- /* -- Targeting -- */
50
- export const getSessionTargeting = (referrer, participations, targeting) => ({
51
- ref: getReferrer(referrer),
49
+ export const getSessionTargeting = ({ adTest, countryCode, isSignedIn, pageViewId, participations, referrer, }) => ({
52
50
  ab: experimentsTargeting(participations),
53
- ...targeting,
51
+ at: adTest,
52
+ cc: countryCode,
53
+ pv: pageViewId,
54
+ ref: getReferrer(referrer),
55
+ si: isSignedIn ? 't' : 'f',
54
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.33.0",
3
+ "version": "0.36.0",
4
4
  "description": "Guardian advertising business logic",
5
5
  "homepage": "https://github.com/guardian/commercial-core#readme",
6
6
  "bugs": {
@@ -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
  },