@guardian/commercial-core 3.4.0 → 4.1.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.
@@ -1,30 +1,45 @@
1
1
  declare type AdSizeString = 'fluid' | `${number},${number}`;
2
- declare type AdSize = Readonly<{
3
- width: number;
4
- height: number;
5
- toString: () => AdSizeString;
6
- }>;
7
- declare type SizeKeys = '160x600' | '300x1050' | '300x250' | '300x600' | '728x90' | '970x250' | 'billboard' | 'empty' | 'fabric' | 'fluid' | 'googleCard' | 'halfPage' | 'inlineMerchandising' | 'leaderboard' | 'merchandising' | 'merchandisingHigh' | 'merchandisingHighAdFeature' | 'mobilesticky' | 'mpu' | 'outOfPage' | 'outstreamDesktop' | 'outstreamGoogleDesktop' | 'outstreamMobile' | 'portrait' | 'skyscraper';
8
- interface SizeMapping {
9
- mobile?: AdSize[];
10
- desktop?: AdSize[];
11
- phablet?: AdSize[];
12
- tablet?: AdSize[];
13
- }
14
- interface SizeMappings {
15
- right: SizeMapping;
16
- comments: SizeMapping;
17
- 'top-above-nav': SizeMapping;
18
- mostpop: SizeMapping;
19
- 'merchandising-high': SizeMapping;
20
- merchandising: SizeMapping;
21
- survey: SizeMapping;
2
+ /**
3
+ * Store ad sizes in a way that is compatible with google-tag but also accessible via
4
+ * more semantic `width`/`height` properties and keep things readonly.
5
+ *
6
+ * example:
7
+ * const size = new AdSize([300, 250]);
8
+ *
9
+ * size.width === 300; // true
10
+ * size[0] === 300; // true
11
+ *
12
+ * size.height === 250; // true
13
+ * size[1] === 250; // true
14
+ *
15
+ * size[0] = 200; // throws error
16
+ * size.width = 200; // throws error
17
+ *
18
+ */
19
+ declare class AdSize extends Array<number> {
20
+ readonly [0]: number;
21
+ readonly [1]: number;
22
+ constructor([width, height]: [number, number]);
23
+ toString(): AdSizeString;
24
+ get width(): number;
25
+ get height(): number;
22
26
  }
27
+ declare type SizeKeys = '160x600' | '300x1050' | '300x250' | '300x600' | '728x90' | '970x250' | 'billboard' | 'empty' | 'fabric' | 'fluid' | 'googleCard' | 'halfPage' | 'inlineMerchandising' | 'leaderboard' | 'merchandising' | 'merchandisingHigh' | 'merchandisingHighAdFeature' | 'mobilesticky' | 'mpu' | 'outOfPage' | 'outstreamDesktop' | 'outstreamGoogleDesktop' | 'outstreamMobile' | 'portrait' | 'skyscraper';
28
+ declare type SlotName = 'right' | 'comments' | 'top-above-nav' | 'mostpop' | 'merchandising' | 'merchandising-high' | 'survey';
29
+ declare type Breakpoint = 'mobile' | 'desktop' | 'phablet' | 'tablet';
30
+ declare type SizeMapping = Partial<Record<Breakpoint, AdSize[]>>;
31
+ declare type SlotSizeMappings = Record<SlotName, SizeMapping>;
32
+ declare const createAdSize: (width: number, height: number) => AdSize;
23
33
  declare const adSizes: Record<SizeKeys, AdSize>;
24
- declare const sizeMappings: SizeMappings;
34
+ /**
35
+ * mark: 432b3a46-90c1-4573-90d3-2400b51af8d0
36
+ * Some of these may or may not need to be synced for with the sizes in ./create-ad-slot.ts
37
+ * these were originally from DCR, create-ad-slot.ts ones were in frontend.
38
+ **/
39
+ declare const slotSizeMappings: SlotSizeMappings;
25
40
  declare const getAdSize: (size: SizeKeys) => AdSize;
26
41
  export declare const _: {
27
42
  createAdSize: (width: number, height: number) => AdSize;
28
43
  };
29
- export type { AdSizeString, AdSize, SizeKeys };
30
- export { adSizes, getAdSize, sizeMappings };
44
+ export type { AdSizeString, AdSize, SizeKeys, SizeMapping, SlotSizeMappings, SlotName, };
45
+ export { adSizes, getAdSize, slotSizeMappings, createAdSize };
@@ -1,14 +1,44 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.sizeMappings = exports.getAdSize = exports.adSizes = exports._ = void 0;
3
+ exports.createAdSize = exports.slotSizeMappings = exports.getAdSize = exports.adSizes = exports._ = void 0;
4
+ /**
5
+ * Store ad sizes in a way that is compatible with google-tag but also accessible via
6
+ * more semantic `width`/`height` properties and keep things readonly.
7
+ *
8
+ * example:
9
+ * const size = new AdSize([300, 250]);
10
+ *
11
+ * size.width === 300; // true
12
+ * size[0] === 300; // true
13
+ *
14
+ * size.height === 250; // true
15
+ * size[1] === 250; // true
16
+ *
17
+ * size[0] = 200; // throws error
18
+ * size.width = 200; // throws error
19
+ *
20
+ */
21
+ class AdSize extends Array {
22
+ constructor([width, height]) {
23
+ super();
24
+ this.push(width, height);
25
+ }
26
+ toString() {
27
+ return this.width === 0 && this.height === 0
28
+ ? 'fluid'
29
+ : `${this.width},${this.height}`;
30
+ }
31
+ get width() {
32
+ return this[0];
33
+ }
34
+ get height() {
35
+ return this[1];
36
+ }
37
+ }
4
38
  const createAdSize = (width, height) => {
5
- const toString = () => width === 0 && height === 0 ? 'fluid' : `${width},${height}`;
6
- return Object.freeze({
7
- width,
8
- height,
9
- toString,
10
- });
39
+ return new AdSize([width, height]);
11
40
  };
41
+ exports.createAdSize = createAdSize;
12
42
  const adSizesPartial = {
13
43
  // standard ad sizes
14
44
  billboard: createAdSize(970, 250),
@@ -43,7 +73,12 @@ const adSizes = {
43
73
  '160x600': adSizesPartial.skyscraper,
44
74
  };
45
75
  exports.adSizes = adSizes;
46
- const sizeMappings = {
76
+ /**
77
+ * mark: 432b3a46-90c1-4573-90d3-2400b51af8d0
78
+ * Some of these may or may not need to be synced for with the sizes in ./create-ad-slot.ts
79
+ * these were originally from DCR, create-ad-slot.ts ones were in frontend.
80
+ **/
81
+ const slotSizeMappings = {
47
82
  right: {
48
83
  mobile: [
49
84
  adSizes.outOfPage,
@@ -156,7 +191,7 @@ const sizeMappings = {
156
191
  desktop: [adSizes.outOfPage],
157
192
  },
158
193
  };
159
- exports.sizeMappings = sizeMappings;
194
+ exports.slotSizeMappings = slotSizeMappings;
160
195
  const getAdSize = (size) => adSizes[size];
161
196
  exports.getAdSize = getAdSize;
162
197
  // Export for testing
@@ -0,0 +1,9 @@
1
+ import type { AdSize } from './ad-sizes';
2
+ declare type SlotName = 'im' | 'high-merch' | 'high-merch-lucky' | 'high-merch-paid' | 'inline' | 'mostpop' | 'comments' | 'top-above-nav' | 'carrot' | 'epic' | 'mobile-sticky';
3
+ declare type CreateSlotOptions = {
4
+ classes?: string;
5
+ name?: string;
6
+ sizes?: Record<string, AdSize[]>;
7
+ };
8
+ export declare const createAdSlot: (name: SlotName, options?: CreateSlotOptions) => HTMLElement;
9
+ export {};
@@ -0,0 +1,222 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createAdSlot = void 0;
4
+ const ad_sizes_1 = require("./ad-sizes");
5
+ const adSlotIdPrefix = 'dfp-ad--';
6
+ const commonSizeMappings = {
7
+ mobile: [
8
+ ad_sizes_1.adSizes.outOfPage,
9
+ ad_sizes_1.adSizes.empty,
10
+ ad_sizes_1.adSizes.outstreamMobile,
11
+ ad_sizes_1.adSizes.mpu,
12
+ ad_sizes_1.adSizes.googleCard,
13
+ ad_sizes_1.adSizes.fluid,
14
+ ],
15
+ phablet: [
16
+ ad_sizes_1.adSizes.outOfPage,
17
+ ad_sizes_1.adSizes.empty,
18
+ ad_sizes_1.adSizes.outstreamMobile,
19
+ ad_sizes_1.adSizes.mpu,
20
+ ad_sizes_1.adSizes.googleCard,
21
+ ad_sizes_1.adSizes.fluid,
22
+ ],
23
+ desktop: [
24
+ ad_sizes_1.adSizes.outOfPage,
25
+ ad_sizes_1.adSizes.empty,
26
+ ad_sizes_1.adSizes.mpu,
27
+ ad_sizes_1.adSizes.googleCard,
28
+ ad_sizes_1.adSizes.fluid,
29
+ ],
30
+ };
31
+ /**
32
+ * mark: 432b3a46-90c1-4573-90d3-2400b51af8d0
33
+ * Some of these may or may not need to be synced for with the sizes in ./ad-sizes.ts
34
+ * these were originally from frontend, ad-sizes.ts ones were in DCR.
35
+ **/
36
+ const adSlotConfigs = {
37
+ im: {
38
+ label: false,
39
+ refresh: false,
40
+ sizeMappings: {
41
+ mobile: [
42
+ ad_sizes_1.adSizes.outOfPage,
43
+ ad_sizes_1.adSizes.empty,
44
+ ad_sizes_1.adSizes.inlineMerchandising,
45
+ ad_sizes_1.adSizes.fluid,
46
+ ],
47
+ },
48
+ },
49
+ 'high-merch': {
50
+ label: false,
51
+ refresh: false,
52
+ name: 'merchandising-high',
53
+ sizeMappings: {
54
+ mobile: [
55
+ ad_sizes_1.adSizes.outOfPage,
56
+ ad_sizes_1.adSizes.empty,
57
+ ad_sizes_1.adSizes.merchandisingHigh,
58
+ ad_sizes_1.adSizes.fluid,
59
+ ],
60
+ },
61
+ },
62
+ 'high-merch-lucky': {
63
+ label: false,
64
+ refresh: false,
65
+ name: 'merchandising-high-lucky',
66
+ sizeMappings: {
67
+ mobile: [ad_sizes_1.adSizes.outOfPage, ad_sizes_1.adSizes.empty, ad_sizes_1.adSizes.fluid],
68
+ },
69
+ },
70
+ 'high-merch-paid': {
71
+ label: false,
72
+ refresh: false,
73
+ name: 'merchandising-high',
74
+ sizeMappings: {
75
+ mobile: [
76
+ ad_sizes_1.adSizes.outOfPage,
77
+ ad_sizes_1.adSizes.empty,
78
+ ad_sizes_1.adSizes.merchandisingHighAdFeature,
79
+ ad_sizes_1.adSizes.fluid,
80
+ ],
81
+ },
82
+ },
83
+ inline: {
84
+ sizeMappings: commonSizeMappings,
85
+ },
86
+ mostpop: {
87
+ sizeMappings: commonSizeMappings,
88
+ },
89
+ comments: {
90
+ sizeMappings: commonSizeMappings,
91
+ },
92
+ 'top-above-nav': {
93
+ sizeMappings: {
94
+ mobile: [
95
+ ad_sizes_1.adSizes.outOfPage,
96
+ ad_sizes_1.adSizes.empty,
97
+ ad_sizes_1.adSizes.fabric,
98
+ ad_sizes_1.adSizes.outstreamMobile,
99
+ ad_sizes_1.adSizes.mpu,
100
+ ad_sizes_1.adSizes.fluid,
101
+ ],
102
+ },
103
+ },
104
+ carrot: {
105
+ label: false,
106
+ refresh: false,
107
+ name: 'carrot',
108
+ sizeMappings: {
109
+ mobile: [ad_sizes_1.adSizes.fluid],
110
+ },
111
+ },
112
+ epic: {
113
+ label: false,
114
+ refresh: false,
115
+ name: 'epic',
116
+ sizeMappings: {
117
+ mobile: [ad_sizes_1.adSizes.fluid],
118
+ },
119
+ },
120
+ 'mobile-sticky': {
121
+ label: true,
122
+ refresh: true,
123
+ name: 'mobile-sticky',
124
+ sizeMappings: {
125
+ mobile: [ad_sizes_1.adSizes.mobilesticky],
126
+ },
127
+ },
128
+ };
129
+ /**
130
+ Returns an adSlot HTMLElement which is the main DFP slot.
131
+
132
+ Insert that element as siblings at the place you want adverts to appear.
133
+
134
+ Note that for the DFP slot to be filled by GTP, you'll have to
135
+ use addSlot from add-slot.js
136
+ */
137
+ const createAdSlotElement = (name, attrs, classes) => {
138
+ const id = `${adSlotIdPrefix}${name}`;
139
+ // 3562dc07-78e9-4507-b922-78b979d4c5cb
140
+ if (window.guardian.config?.isDotcomRendering && name === 'top-above-nav') {
141
+ // This is to prevent a problem that appeared with DCR.
142
+ // We are simply making sure that if we are about to
143
+ // introduce dfp-ad--top-above-nav then there isn't already one.
144
+ const node = document.getElementById(id);
145
+ if (node?.parentNode) {
146
+ const pnode = node.parentNode;
147
+ console.log(`warning: cleaning up dom node id: dfp-ad--${name}`);
148
+ pnode.removeChild(node);
149
+ }
150
+ }
151
+ // The 'main' adSlot
152
+ const adSlot = document.createElement('div');
153
+ adSlot.id = id;
154
+ adSlot.className = `js-ad-slot ad-slot ${classes.join(' ')}`;
155
+ adSlot.setAttribute('data-link-name', `ad slot ${name}`);
156
+ adSlot.setAttribute('data-name', name);
157
+ adSlot.setAttribute('aria-hidden', 'true');
158
+ Object.entries(attrs).forEach(([k, v]) => adSlot.setAttribute(k, v));
159
+ return adSlot;
160
+ };
161
+ /**
162
+ * Split class names and prefix all with ad-slot--${className}
163
+ */
164
+ const createClasses = (slotName, classes) => [...(classes?.split(' ') ?? []), slotName].map((className) => `ad-slot--${className}`);
165
+ /**
166
+ * Given default size mappings and additional size mappings from
167
+ * the createAdSlot options parameter.
168
+ *
169
+ * 1. Check that the options size mappings use known device names
170
+ * 2. If so concat the size mappings
171
+ *
172
+ */
173
+ const concatSizeMappings = (defaultSizeMappings, optionSizeMappings) => {
174
+ if (!optionSizeMappings)
175
+ return defaultSizeMappings;
176
+ const concatenatedSizeMappings = { ...defaultSizeMappings };
177
+ const optionDevices = Object.keys(optionSizeMappings); // ['mobile', 'desktop']
178
+ for (let i = 0; i < optionDevices.length; i++) {
179
+ const device = optionDevices[i];
180
+ if (optionSizeMappings[device] && defaultSizeMappings[device]) {
181
+ const optionSizeMappingsForDevice = optionSizeMappings[device];
182
+ const defaultSizeMappingsForDevice = defaultSizeMappings[device];
183
+ if (defaultSizeMappingsForDevice && optionSizeMappingsForDevice) {
184
+ // TODO can we do concatenatedSizeMappings[device]?.concat ?
185
+ concatenatedSizeMappings[device] = (concatenatedSizeMappings[device] ?? []).concat(optionSizeMappingsForDevice);
186
+ }
187
+ }
188
+ }
189
+ return concatenatedSizeMappings;
190
+ };
191
+ /**
192
+ * Convert size mappings to a string that will be added to the ad slot
193
+ * data attributes.
194
+ *
195
+ * e.g. { desktop: [[320,250], [320, 280]] } => { desktop: '320,250|320,280' }
196
+ *
197
+ */
198
+ const covertSizeMappingsToStrings = (sizeMappings) => Object.entries(sizeMappings).reduce((result, [device, sizes]) => {
199
+ result[device] = sizes.join('|');
200
+ return result;
201
+ }, {});
202
+ /**
203
+ * Prefix all object keys with data-${key}
204
+ */
205
+ const createDataAttributes = (attrs) => Object.entries(attrs).reduce((result, [key, value]) => {
206
+ result[`data-${key}`] = value;
207
+ return result;
208
+ }, {});
209
+ const createAdSlot = (name, options = {}) => {
210
+ const adSlotConfig = adSlotConfigs[name];
211
+ const slotName = options.name ?? adSlotConfig.name ?? name;
212
+ const sizeMappings = concatSizeMappings(adSlotConfig.sizeMappings, options.sizes);
213
+ const attributes = covertSizeMappingsToStrings(sizeMappings);
214
+ if (adSlotConfig.label === false) {
215
+ attributes.label = 'false';
216
+ }
217
+ if (adSlotConfig.refresh === false) {
218
+ attributes.refresh = 'false';
219
+ }
220
+ return createAdSlotElement(slotName, createDataAttributes(attributes), createClasses(slotName, options.classes));
221
+ };
222
+ exports.createAdSlot = createAdSlot;
@@ -30,6 +30,7 @@ interface EventTimerProperties {
30
30
  adSlotsInline?: number;
31
31
  adSlotsTotal?: number;
32
32
  pageHeightVH?: number;
33
+ gpcSignal?: number;
33
34
  lazyLoadMarginPercent?: number;
34
35
  }
35
36
  export declare class EventTimer {
@@ -71,7 +72,7 @@ export declare class EventTimer {
71
72
  * @param {string} name - the property's name
72
73
  * @param {value} number - the property's value
73
74
  */
74
- setProperty(name: 'adSlotsInline' | 'adSlotsTotal' | 'pageHeightVH' | 'lazyLoadMarginPercent', value: number): void;
75
+ setProperty(name: 'adSlotsInline' | 'adSlotsTotal' | 'pageHeightVH' | 'gpcSignal' | 'lazyLoadMarginPercent', value: number): void;
75
76
  /**
76
77
  * Creates a new performance mark
77
78
  * For slot events also ensures each TYPE of event event is marked only once for 'first'
@@ -7,11 +7,12 @@ export { remarketing } from './third-party-tags/remarketing';
7
7
  export { EventTimer } from './event-timer';
8
8
  export { bypassCommercialMetricsSampling, initCommercialMetrics, } from './send-commercial-metrics';
9
9
  export type { ThirdPartyTag } from './types';
10
- export { adSizes, getAdSize, sizeMappings } from './ad-sizes';
11
- export type { SizeKeys, AdSizeString, AdSize } from './ad-sizes';
10
+ export { adSizes, getAdSize, slotSizeMappings, createAdSize } from './ad-sizes';
11
+ export type { SizeKeys, AdSizeString, AdSize, SizeMapping, SlotSizeMappings, SlotName, } from './ad-sizes';
12
12
  export { isAdBlockInUse } from './detect-ad-blocker';
13
13
  export { clearPermutiveSegments, getPermutiveSegments, getPermutivePFPSegments, } from './permutive';
14
14
  export { initTrackScrollDepth } from './track-scroll-depth';
15
+ export { initTrackGpcSignal } from './track-gpc-signal';
15
16
  export { buildAdsConfigWithConsent, disabledAds } from './ad-targeting-youtube';
16
17
  export type { AdsConfig, AdsConfigBasic, AdsConfigDisabled, AdTargetingBuilder, CustomParams, } from './types';
17
18
  export * as constants from './constants';
package/dist/cjs/index.js CHANGED
@@ -2,7 +2,11 @@
2
2
  /* istanbul ignore file -- there's no point check this for test coverage */
3
3
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
4
  if (k2 === undefined) k2 = k;
5
- Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
5
+ var desc = Object.getOwnPropertyDescriptor(m, k);
6
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
+ desc = { enumerable: true, get: function() { return m[k]; } };
8
+ }
9
+ Object.defineProperty(o, k2, desc);
6
10
  }) : (function(o, m, k, k2) {
7
11
  if (k2 === undefined) k2 = k;
8
12
  o[k2] = m[k];
@@ -20,7 +24,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
20
24
  return result;
21
25
  };
22
26
  Object.defineProperty(exports, "__esModule", { value: true });
23
- exports.pickTargetingValues = exports.getViewportTargeting = exports.getSharedTargeting = exports.getSessionTargeting = exports.getPersonalisedTargeting = exports.getContentTargeting = exports.constants = exports.disabledAds = exports.buildAdsConfigWithConsent = exports.initTrackScrollDepth = exports.getPermutivePFPSegments = exports.getPermutiveSegments = exports.clearPermutiveSegments = exports.isAdBlockInUse = exports.sizeMappings = exports.getAdSize = exports.adSizes = exports.initCommercialMetrics = exports.bypassCommercialMetricsSampling = exports.EventTimer = exports.remarketing = exports.inizio = exports.twitter = exports.fbPixel = exports.permutive = exports.ias = void 0;
27
+ exports.pickTargetingValues = exports.getViewportTargeting = exports.getSharedTargeting = exports.getSessionTargeting = exports.getPersonalisedTargeting = exports.getContentTargeting = exports.constants = exports.disabledAds = exports.buildAdsConfigWithConsent = exports.initTrackGpcSignal = exports.initTrackScrollDepth = exports.getPermutivePFPSegments = exports.getPermutiveSegments = exports.clearPermutiveSegments = exports.isAdBlockInUse = exports.createAdSize = exports.slotSizeMappings = exports.getAdSize = exports.adSizes = exports.initCommercialMetrics = exports.bypassCommercialMetricsSampling = exports.EventTimer = exports.remarketing = exports.inizio = exports.twitter = exports.fbPixel = exports.permutive = exports.ias = void 0;
24
28
  var ias_1 = require("./third-party-tags/ias");
25
29
  Object.defineProperty(exports, "ias", { enumerable: true, get: function () { return ias_1.ias; } });
26
30
  var permutive_1 = require("./third-party-tags/permutive");
@@ -41,7 +45,8 @@ Object.defineProperty(exports, "initCommercialMetrics", { enumerable: true, get:
41
45
  var ad_sizes_1 = require("./ad-sizes");
42
46
  Object.defineProperty(exports, "adSizes", { enumerable: true, get: function () { return ad_sizes_1.adSizes; } });
43
47
  Object.defineProperty(exports, "getAdSize", { enumerable: true, get: function () { return ad_sizes_1.getAdSize; } });
44
- Object.defineProperty(exports, "sizeMappings", { enumerable: true, get: function () { return ad_sizes_1.sizeMappings; } });
48
+ Object.defineProperty(exports, "slotSizeMappings", { enumerable: true, get: function () { return ad_sizes_1.slotSizeMappings; } });
49
+ Object.defineProperty(exports, "createAdSize", { enumerable: true, get: function () { return ad_sizes_1.createAdSize; } });
45
50
  var detect_ad_blocker_1 = require("./detect-ad-blocker");
46
51
  Object.defineProperty(exports, "isAdBlockInUse", { enumerable: true, get: function () { return detect_ad_blocker_1.isAdBlockInUse; } });
47
52
  var permutive_2 = require("./permutive");
@@ -50,6 +55,8 @@ Object.defineProperty(exports, "getPermutiveSegments", { enumerable: true, get:
50
55
  Object.defineProperty(exports, "getPermutivePFPSegments", { enumerable: true, get: function () { return permutive_2.getPermutivePFPSegments; } });
51
56
  var track_scroll_depth_1 = require("./track-scroll-depth");
52
57
  Object.defineProperty(exports, "initTrackScrollDepth", { enumerable: true, get: function () { return track_scroll_depth_1.initTrackScrollDepth; } });
58
+ var track_gpc_signal_1 = require("./track-gpc-signal");
59
+ Object.defineProperty(exports, "initTrackGpcSignal", { enumerable: true, get: function () { return track_gpc_signal_1.initTrackGpcSignal; } });
53
60
  var ad_targeting_youtube_1 = require("./ad-targeting-youtube");
54
61
  Object.defineProperty(exports, "buildAdsConfigWithConsent", { enumerable: true, get: function () { return ad_targeting_youtube_1.buildAdsConfigWithConsent; } });
55
62
  Object.defineProperty(exports, "disabledAds", { enumerable: true, get: function () { return ad_targeting_youtube_1.disabledAds; } });
@@ -0,0 +1,7 @@
1
+ import type { ConsentState } from '@guardian/consent-management-platform/dist/types';
2
+ /**
3
+ * Collect metrics on gpcSignal presence and value
4
+ * https://globalprivacycontrol.github.io/gpc-spec/
5
+ */
6
+ declare const initTrackGpcSignal: (consentState: ConsentState) => void;
7
+ export { initTrackGpcSignal };
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.initTrackGpcSignal = void 0;
4
+ const libs_1 = require("@guardian/libs");
5
+ const event_timer_1 = require("./event-timer");
6
+ /**
7
+ * Collect metrics on gpcSignal presence and value
8
+ * https://globalprivacycontrol.github.io/gpc-spec/
9
+ */
10
+ const initTrackGpcSignal = (consentState) => {
11
+ // If undefined we set the property value to -1, false is 0, true is 1
12
+ const gpcSignal = consentState.gpcSignal === undefined ? -1 : +consentState.gpcSignal;
13
+ const eventTimer = event_timer_1.EventTimer.get();
14
+ (0, libs_1.log)('commercial', `gpcSignal ${gpcSignal}`);
15
+ eventTimer.setProperty('gpcSignal', gpcSignal);
16
+ };
17
+ exports.initTrackGpcSignal = initTrackGpcSignal;
@@ -23,6 +23,7 @@ export declare type GuardianAnalyticsConfig = {
23
23
  };
24
24
  export declare type GuardianWindowConfig = {
25
25
  googleAnalytics?: GuardianAnalyticsConfig;
26
+ isDotcomRendering?: boolean;
26
27
  };
27
28
  export declare type GoogleTagParams = unknown;
28
29
  export declare type GoogleTrackConversionObject = {
@@ -1,30 +1,45 @@
1
1
  declare type AdSizeString = 'fluid' | `${number},${number}`;
2
- declare type AdSize = Readonly<{
3
- width: number;
4
- height: number;
5
- toString: () => AdSizeString;
6
- }>;
7
- declare type SizeKeys = '160x600' | '300x1050' | '300x250' | '300x600' | '728x90' | '970x250' | 'billboard' | 'empty' | 'fabric' | 'fluid' | 'googleCard' | 'halfPage' | 'inlineMerchandising' | 'leaderboard' | 'merchandising' | 'merchandisingHigh' | 'merchandisingHighAdFeature' | 'mobilesticky' | 'mpu' | 'outOfPage' | 'outstreamDesktop' | 'outstreamGoogleDesktop' | 'outstreamMobile' | 'portrait' | 'skyscraper';
8
- interface SizeMapping {
9
- mobile?: AdSize[];
10
- desktop?: AdSize[];
11
- phablet?: AdSize[];
12
- tablet?: AdSize[];
13
- }
14
- interface SizeMappings {
15
- right: SizeMapping;
16
- comments: SizeMapping;
17
- 'top-above-nav': SizeMapping;
18
- mostpop: SizeMapping;
19
- 'merchandising-high': SizeMapping;
20
- merchandising: SizeMapping;
21
- survey: SizeMapping;
2
+ /**
3
+ * Store ad sizes in a way that is compatible with google-tag but also accessible via
4
+ * more semantic `width`/`height` properties and keep things readonly.
5
+ *
6
+ * example:
7
+ * const size = new AdSize([300, 250]);
8
+ *
9
+ * size.width === 300; // true
10
+ * size[0] === 300; // true
11
+ *
12
+ * size.height === 250; // true
13
+ * size[1] === 250; // true
14
+ *
15
+ * size[0] = 200; // throws error
16
+ * size.width = 200; // throws error
17
+ *
18
+ */
19
+ declare class AdSize extends Array<number> {
20
+ readonly [0]: number;
21
+ readonly [1]: number;
22
+ constructor([width, height]: [number, number]);
23
+ toString(): AdSizeString;
24
+ get width(): number;
25
+ get height(): number;
22
26
  }
27
+ declare type SizeKeys = '160x600' | '300x1050' | '300x250' | '300x600' | '728x90' | '970x250' | 'billboard' | 'empty' | 'fabric' | 'fluid' | 'googleCard' | 'halfPage' | 'inlineMerchandising' | 'leaderboard' | 'merchandising' | 'merchandisingHigh' | 'merchandisingHighAdFeature' | 'mobilesticky' | 'mpu' | 'outOfPage' | 'outstreamDesktop' | 'outstreamGoogleDesktop' | 'outstreamMobile' | 'portrait' | 'skyscraper';
28
+ declare type SlotName = 'right' | 'comments' | 'top-above-nav' | 'mostpop' | 'merchandising' | 'merchandising-high' | 'survey';
29
+ declare type Breakpoint = 'mobile' | 'desktop' | 'phablet' | 'tablet';
30
+ declare type SizeMapping = Partial<Record<Breakpoint, AdSize[]>>;
31
+ declare type SlotSizeMappings = Record<SlotName, SizeMapping>;
32
+ declare const createAdSize: (width: number, height: number) => AdSize;
23
33
  declare const adSizes: Record<SizeKeys, AdSize>;
24
- declare const sizeMappings: SizeMappings;
34
+ /**
35
+ * mark: 432b3a46-90c1-4573-90d3-2400b51af8d0
36
+ * Some of these may or may not need to be synced for with the sizes in ./create-ad-slot.ts
37
+ * these were originally from DCR, create-ad-slot.ts ones were in frontend.
38
+ **/
39
+ declare const slotSizeMappings: SlotSizeMappings;
25
40
  declare const getAdSize: (size: SizeKeys) => AdSize;
26
41
  export declare const _: {
27
42
  createAdSize: (width: number, height: number) => AdSize;
28
43
  };
29
- export type { AdSizeString, AdSize, SizeKeys };
30
- export { adSizes, getAdSize, sizeMappings };
44
+ export type { AdSizeString, AdSize, SizeKeys, SizeMapping, SlotSizeMappings, SlotName, };
45
+ export { adSizes, getAdSize, slotSizeMappings, createAdSize };
@@ -1,10 +1,39 @@
1
+ /**
2
+ * Store ad sizes in a way that is compatible with google-tag but also accessible via
3
+ * more semantic `width`/`height` properties and keep things readonly.
4
+ *
5
+ * example:
6
+ * const size = new AdSize([300, 250]);
7
+ *
8
+ * size.width === 300; // true
9
+ * size[0] === 300; // true
10
+ *
11
+ * size.height === 250; // true
12
+ * size[1] === 250; // true
13
+ *
14
+ * size[0] = 200; // throws error
15
+ * size.width = 200; // throws error
16
+ *
17
+ */
18
+ class AdSize extends Array {
19
+ constructor([width, height]) {
20
+ super();
21
+ this.push(width, height);
22
+ }
23
+ toString() {
24
+ return this.width === 0 && this.height === 0
25
+ ? 'fluid'
26
+ : `${this.width},${this.height}`;
27
+ }
28
+ get width() {
29
+ return this[0];
30
+ }
31
+ get height() {
32
+ return this[1];
33
+ }
34
+ }
1
35
  const createAdSize = (width, height) => {
2
- const toString = () => width === 0 && height === 0 ? 'fluid' : `${width},${height}`;
3
- return Object.freeze({
4
- width,
5
- height,
6
- toString,
7
- });
36
+ return new AdSize([width, height]);
8
37
  };
9
38
  const adSizesPartial = {
10
39
  // standard ad sizes
@@ -39,7 +68,12 @@ const adSizes = {
39
68
  '300x1050': adSizesPartial.portrait,
40
69
  '160x600': adSizesPartial.skyscraper,
41
70
  };
42
- const sizeMappings = {
71
+ /**
72
+ * mark: 432b3a46-90c1-4573-90d3-2400b51af8d0
73
+ * Some of these may or may not need to be synced for with the sizes in ./create-ad-slot.ts
74
+ * these were originally from DCR, create-ad-slot.ts ones were in frontend.
75
+ **/
76
+ const slotSizeMappings = {
43
77
  right: {
44
78
  mobile: [
45
79
  adSizes.outOfPage,
@@ -155,4 +189,4 @@ const sizeMappings = {
155
189
  const getAdSize = (size) => adSizes[size];
156
190
  // Export for testing
157
191
  export const _ = { createAdSize };
158
- export { adSizes, getAdSize, sizeMappings };
192
+ export { adSizes, getAdSize, slotSizeMappings, createAdSize };
@@ -0,0 +1,9 @@
1
+ import type { AdSize } from './ad-sizes';
2
+ declare type SlotName = 'im' | 'high-merch' | 'high-merch-lucky' | 'high-merch-paid' | 'inline' | 'mostpop' | 'comments' | 'top-above-nav' | 'carrot' | 'epic' | 'mobile-sticky';
3
+ declare type CreateSlotOptions = {
4
+ classes?: string;
5
+ name?: string;
6
+ sizes?: Record<string, AdSize[]>;
7
+ };
8
+ export declare const createAdSlot: (name: SlotName, options?: CreateSlotOptions) => HTMLElement;
9
+ export {};
@@ -0,0 +1,218 @@
1
+ import { adSizes } from './ad-sizes';
2
+ const adSlotIdPrefix = 'dfp-ad--';
3
+ const commonSizeMappings = {
4
+ mobile: [
5
+ adSizes.outOfPage,
6
+ adSizes.empty,
7
+ adSizes.outstreamMobile,
8
+ adSizes.mpu,
9
+ adSizes.googleCard,
10
+ adSizes.fluid,
11
+ ],
12
+ phablet: [
13
+ adSizes.outOfPage,
14
+ adSizes.empty,
15
+ adSizes.outstreamMobile,
16
+ adSizes.mpu,
17
+ adSizes.googleCard,
18
+ adSizes.fluid,
19
+ ],
20
+ desktop: [
21
+ adSizes.outOfPage,
22
+ adSizes.empty,
23
+ adSizes.mpu,
24
+ adSizes.googleCard,
25
+ adSizes.fluid,
26
+ ],
27
+ };
28
+ /**
29
+ * mark: 432b3a46-90c1-4573-90d3-2400b51af8d0
30
+ * Some of these may or may not need to be synced for with the sizes in ./ad-sizes.ts
31
+ * these were originally from frontend, ad-sizes.ts ones were in DCR.
32
+ **/
33
+ const adSlotConfigs = {
34
+ im: {
35
+ label: false,
36
+ refresh: false,
37
+ sizeMappings: {
38
+ mobile: [
39
+ adSizes.outOfPage,
40
+ adSizes.empty,
41
+ adSizes.inlineMerchandising,
42
+ adSizes.fluid,
43
+ ],
44
+ },
45
+ },
46
+ 'high-merch': {
47
+ label: false,
48
+ refresh: false,
49
+ name: 'merchandising-high',
50
+ sizeMappings: {
51
+ mobile: [
52
+ adSizes.outOfPage,
53
+ adSizes.empty,
54
+ adSizes.merchandisingHigh,
55
+ adSizes.fluid,
56
+ ],
57
+ },
58
+ },
59
+ 'high-merch-lucky': {
60
+ label: false,
61
+ refresh: false,
62
+ name: 'merchandising-high-lucky',
63
+ sizeMappings: {
64
+ mobile: [adSizes.outOfPage, adSizes.empty, adSizes.fluid],
65
+ },
66
+ },
67
+ 'high-merch-paid': {
68
+ label: false,
69
+ refresh: false,
70
+ name: 'merchandising-high',
71
+ sizeMappings: {
72
+ mobile: [
73
+ adSizes.outOfPage,
74
+ adSizes.empty,
75
+ adSizes.merchandisingHighAdFeature,
76
+ adSizes.fluid,
77
+ ],
78
+ },
79
+ },
80
+ inline: {
81
+ sizeMappings: commonSizeMappings,
82
+ },
83
+ mostpop: {
84
+ sizeMappings: commonSizeMappings,
85
+ },
86
+ comments: {
87
+ sizeMappings: commonSizeMappings,
88
+ },
89
+ 'top-above-nav': {
90
+ sizeMappings: {
91
+ mobile: [
92
+ adSizes.outOfPage,
93
+ adSizes.empty,
94
+ adSizes.fabric,
95
+ adSizes.outstreamMobile,
96
+ adSizes.mpu,
97
+ adSizes.fluid,
98
+ ],
99
+ },
100
+ },
101
+ carrot: {
102
+ label: false,
103
+ refresh: false,
104
+ name: 'carrot',
105
+ sizeMappings: {
106
+ mobile: [adSizes.fluid],
107
+ },
108
+ },
109
+ epic: {
110
+ label: false,
111
+ refresh: false,
112
+ name: 'epic',
113
+ sizeMappings: {
114
+ mobile: [adSizes.fluid],
115
+ },
116
+ },
117
+ 'mobile-sticky': {
118
+ label: true,
119
+ refresh: true,
120
+ name: 'mobile-sticky',
121
+ sizeMappings: {
122
+ mobile: [adSizes.mobilesticky],
123
+ },
124
+ },
125
+ };
126
+ /**
127
+ Returns an adSlot HTMLElement which is the main DFP slot.
128
+
129
+ Insert that element as siblings at the place you want adverts to appear.
130
+
131
+ Note that for the DFP slot to be filled by GTP, you'll have to
132
+ use addSlot from add-slot.js
133
+ */
134
+ const createAdSlotElement = (name, attrs, classes) => {
135
+ const id = `${adSlotIdPrefix}${name}`;
136
+ // 3562dc07-78e9-4507-b922-78b979d4c5cb
137
+ if (window.guardian.config?.isDotcomRendering && name === 'top-above-nav') {
138
+ // This is to prevent a problem that appeared with DCR.
139
+ // We are simply making sure that if we are about to
140
+ // introduce dfp-ad--top-above-nav then there isn't already one.
141
+ const node = document.getElementById(id);
142
+ if (node?.parentNode) {
143
+ const pnode = node.parentNode;
144
+ console.log(`warning: cleaning up dom node id: dfp-ad--${name}`);
145
+ pnode.removeChild(node);
146
+ }
147
+ }
148
+ // The 'main' adSlot
149
+ const adSlot = document.createElement('div');
150
+ adSlot.id = id;
151
+ adSlot.className = `js-ad-slot ad-slot ${classes.join(' ')}`;
152
+ adSlot.setAttribute('data-link-name', `ad slot ${name}`);
153
+ adSlot.setAttribute('data-name', name);
154
+ adSlot.setAttribute('aria-hidden', 'true');
155
+ Object.entries(attrs).forEach(([k, v]) => adSlot.setAttribute(k, v));
156
+ return adSlot;
157
+ };
158
+ /**
159
+ * Split class names and prefix all with ad-slot--${className}
160
+ */
161
+ const createClasses = (slotName, classes) => [...(classes?.split(' ') ?? []), slotName].map((className) => `ad-slot--${className}`);
162
+ /**
163
+ * Given default size mappings and additional size mappings from
164
+ * the createAdSlot options parameter.
165
+ *
166
+ * 1. Check that the options size mappings use known device names
167
+ * 2. If so concat the size mappings
168
+ *
169
+ */
170
+ const concatSizeMappings = (defaultSizeMappings, optionSizeMappings) => {
171
+ if (!optionSizeMappings)
172
+ return defaultSizeMappings;
173
+ const concatenatedSizeMappings = { ...defaultSizeMappings };
174
+ const optionDevices = Object.keys(optionSizeMappings); // ['mobile', 'desktop']
175
+ for (let i = 0; i < optionDevices.length; i++) {
176
+ const device = optionDevices[i];
177
+ if (optionSizeMappings[device] && defaultSizeMappings[device]) {
178
+ const optionSizeMappingsForDevice = optionSizeMappings[device];
179
+ const defaultSizeMappingsForDevice = defaultSizeMappings[device];
180
+ if (defaultSizeMappingsForDevice && optionSizeMappingsForDevice) {
181
+ // TODO can we do concatenatedSizeMappings[device]?.concat ?
182
+ concatenatedSizeMappings[device] = (concatenatedSizeMappings[device] ?? []).concat(optionSizeMappingsForDevice);
183
+ }
184
+ }
185
+ }
186
+ return concatenatedSizeMappings;
187
+ };
188
+ /**
189
+ * Convert size mappings to a string that will be added to the ad slot
190
+ * data attributes.
191
+ *
192
+ * e.g. { desktop: [[320,250], [320, 280]] } => { desktop: '320,250|320,280' }
193
+ *
194
+ */
195
+ const covertSizeMappingsToStrings = (sizeMappings) => Object.entries(sizeMappings).reduce((result, [device, sizes]) => {
196
+ result[device] = sizes.join('|');
197
+ return result;
198
+ }, {});
199
+ /**
200
+ * Prefix all object keys with data-${key}
201
+ */
202
+ const createDataAttributes = (attrs) => Object.entries(attrs).reduce((result, [key, value]) => {
203
+ result[`data-${key}`] = value;
204
+ return result;
205
+ }, {});
206
+ export const createAdSlot = (name, options = {}) => {
207
+ const adSlotConfig = adSlotConfigs[name];
208
+ const slotName = options.name ?? adSlotConfig.name ?? name;
209
+ const sizeMappings = concatSizeMappings(adSlotConfig.sizeMappings, options.sizes);
210
+ const attributes = covertSizeMappingsToStrings(sizeMappings);
211
+ if (adSlotConfig.label === false) {
212
+ attributes.label = 'false';
213
+ }
214
+ if (adSlotConfig.refresh === false) {
215
+ attributes.refresh = 'false';
216
+ }
217
+ return createAdSlotElement(slotName, createDataAttributes(attributes), createClasses(slotName, options.classes));
218
+ };
@@ -30,6 +30,7 @@ interface EventTimerProperties {
30
30
  adSlotsInline?: number;
31
31
  adSlotsTotal?: number;
32
32
  pageHeightVH?: number;
33
+ gpcSignal?: number;
33
34
  lazyLoadMarginPercent?: number;
34
35
  }
35
36
  export declare class EventTimer {
@@ -71,7 +72,7 @@ export declare class EventTimer {
71
72
  * @param {string} name - the property's name
72
73
  * @param {value} number - the property's value
73
74
  */
74
- setProperty(name: 'adSlotsInline' | 'adSlotsTotal' | 'pageHeightVH' | 'lazyLoadMarginPercent', value: number): void;
75
+ setProperty(name: 'adSlotsInline' | 'adSlotsTotal' | 'pageHeightVH' | 'gpcSignal' | 'lazyLoadMarginPercent', value: number): void;
75
76
  /**
76
77
  * Creates a new performance mark
77
78
  * For slot events also ensures each TYPE of event event is marked only once for 'first'
@@ -7,11 +7,12 @@ export { remarketing } from './third-party-tags/remarketing';
7
7
  export { EventTimer } from './event-timer';
8
8
  export { bypassCommercialMetricsSampling, initCommercialMetrics, } from './send-commercial-metrics';
9
9
  export type { ThirdPartyTag } from './types';
10
- export { adSizes, getAdSize, sizeMappings } from './ad-sizes';
11
- export type { SizeKeys, AdSizeString, AdSize } from './ad-sizes';
10
+ export { adSizes, getAdSize, slotSizeMappings, createAdSize } from './ad-sizes';
11
+ export type { SizeKeys, AdSizeString, AdSize, SizeMapping, SlotSizeMappings, SlotName, } from './ad-sizes';
12
12
  export { isAdBlockInUse } from './detect-ad-blocker';
13
13
  export { clearPermutiveSegments, getPermutiveSegments, getPermutivePFPSegments, } from './permutive';
14
14
  export { initTrackScrollDepth } from './track-scroll-depth';
15
+ export { initTrackGpcSignal } from './track-gpc-signal';
15
16
  export { buildAdsConfigWithConsent, disabledAds } from './ad-targeting-youtube';
16
17
  export type { AdsConfig, AdsConfigBasic, AdsConfigDisabled, AdTargetingBuilder, CustomParams, } from './types';
17
18
  export * as constants from './constants';
package/dist/esm/index.js CHANGED
@@ -7,10 +7,11 @@ export { inizio } from './third-party-tags/inizio';
7
7
  export { remarketing } from './third-party-tags/remarketing';
8
8
  export { EventTimer } from './event-timer';
9
9
  export { bypassCommercialMetricsSampling, initCommercialMetrics, } from './send-commercial-metrics';
10
- export { adSizes, getAdSize, sizeMappings } from './ad-sizes';
10
+ export { adSizes, getAdSize, slotSizeMappings, createAdSize } from './ad-sizes';
11
11
  export { isAdBlockInUse } from './detect-ad-blocker';
12
12
  export { clearPermutiveSegments, getPermutiveSegments, getPermutivePFPSegments, } from './permutive';
13
13
  export { initTrackScrollDepth } from './track-scroll-depth';
14
+ export { initTrackGpcSignal } from './track-gpc-signal';
14
15
  export { buildAdsConfigWithConsent, disabledAds } from './ad-targeting-youtube';
15
16
  import * as constants_1 from './constants';
16
17
  export { constants_1 as constants };
@@ -0,0 +1,7 @@
1
+ import type { ConsentState } from '@guardian/consent-management-platform/dist/types';
2
+ /**
3
+ * Collect metrics on gpcSignal presence and value
4
+ * https://globalprivacycontrol.github.io/gpc-spec/
5
+ */
6
+ declare const initTrackGpcSignal: (consentState: ConsentState) => void;
7
+ export { initTrackGpcSignal };
@@ -0,0 +1,14 @@
1
+ import { log } from '@guardian/libs';
2
+ import { EventTimer } from './event-timer';
3
+ /**
4
+ * Collect metrics on gpcSignal presence and value
5
+ * https://globalprivacycontrol.github.io/gpc-spec/
6
+ */
7
+ const initTrackGpcSignal = (consentState) => {
8
+ // If undefined we set the property value to -1, false is 0, true is 1
9
+ const gpcSignal = consentState.gpcSignal === undefined ? -1 : +consentState.gpcSignal;
10
+ const eventTimer = EventTimer.get();
11
+ log('commercial', `gpcSignal ${gpcSignal}`);
12
+ eventTimer.setProperty('gpcSignal', gpcSignal);
13
+ };
14
+ export { initTrackGpcSignal };
@@ -23,6 +23,7 @@ export declare type GuardianAnalyticsConfig = {
23
23
  };
24
24
  export declare type GuardianWindowConfig = {
25
25
  googleAnalytics?: GuardianAnalyticsConfig;
26
+ isDotcomRendering?: boolean;
26
27
  };
27
28
  export declare type GoogleTagParams = unknown;
28
29
  export declare type GoogleTrackConversionObject = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@guardian/commercial-core",
3
- "version": "3.4.0",
3
+ "version": "4.1.0",
4
4
  "description": "Guardian advertising business logic",
5
5
  "homepage": "https://github.com/guardian/commercial-core#readme",
6
6
  "bugs": {
@@ -43,16 +43,16 @@
43
43
  "prettier": "@guardian/prettier",
44
44
  "devDependencies": {
45
45
  "@commitlint/cli": "^16.1.0",
46
- "@commitlint/config-conventional": "^16.0.0",
46
+ "@commitlint/config-conventional": "^17.0.0",
47
47
  "@guardian/ab-core": "^2.0.0",
48
- "@guardian/consent-management-platform": "^10.5.0",
48
+ "@guardian/consent-management-platform": "^10.6.0",
49
49
  "@guardian/eslint-config-typescript": "^1.0.0",
50
- "@guardian/libs": "3.6.1",
51
- "@guardian/prettier": "^0.7.0",
50
+ "@guardian/libs": "5.1.0",
51
+ "@guardian/prettier": "^1.0.0",
52
52
  "@octokit/core": "^3.5.1",
53
53
  "@semantic-release/github": "^8.0.2",
54
54
  "@types/google.analytics": "^0.0.42",
55
- "@types/googletag": "^1.1.4",
55
+ "@types/googletag": "^2.0.0",
56
56
  "@types/jest": "^27.0.3",
57
57
  "@typescript-eslint/eslint-plugin": "^5.5.0",
58
58
  "@typescript-eslint/parser": "^5.5.0",
@@ -63,15 +63,15 @@
63
63
  "eslint-config-prettier": "^8.3.0",
64
64
  "eslint-plugin-eslint-comments": "^3.2.0",
65
65
  "eslint-plugin-import": "^2.25.3",
66
- "eslint-plugin-jest": "^25.3.0",
66
+ "eslint-plugin-jest": "^26.1.5",
67
67
  "eslint-plugin-prettier": "^4.0.0",
68
- "husky": "^7.0.4",
68
+ "husky": "^8.0.1",
69
69
  "jest": "^27.4.1",
70
70
  "lint-staged": "^12.1.2",
71
71
  "mockdate": "^3.0.5",
72
72
  "npm-run-all": "^4.1.5",
73
73
  "prettier": "^2.5.0",
74
- "semantic-release": "^18.0.1",
74
+ "semantic-release": "^19.0.2",
75
75
  "ts-jest": "^27.0.7",
76
76
  "type-fest": "^2.8.0",
77
77
  "typescript": "^4.5.2",