@adobe/spacecat-shared-rum-api-client 2.24.3 → 2.25.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/CHANGELOG.md +7 -0
- package/package.json +1 -1
- package/src/common/constants.js +92 -0
- package/src/common/page.js +48 -0
- package/src/functions/traffic-metrics.js +234 -0
- package/src/index.js +2 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
# [@adobe/spacecat-shared-rum-api-client-v2.25.0](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-rum-api-client-v2.24.3...@adobe/spacecat-shared-rum-api-client-v2.25.0) (2025-05-16)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* optel traffic group metrics ([#741](https://github.com/adobe/spacecat-shared/issues/741)) ([cdfef6a](https://github.com/adobe/spacecat-shared/commit/cdfef6a42731bf5fb5056619df605e9db67e724e))
|
|
7
|
+
|
|
1
8
|
# [@adobe/spacecat-shared-rum-api-client-v2.24.3](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-rum-api-client-v2.24.2...@adobe/spacecat-shared-rum-api-client-v2.24.3) (2025-05-15)
|
|
2
9
|
|
|
3
10
|
|
package/package.json
CHANGED
package/src/common/constants.js
CHANGED
|
@@ -14,3 +14,95 @@ export const GRANULARITY = {
|
|
|
14
14
|
HOURLY: 'HOURLY',
|
|
15
15
|
DAILY: 'DAILY',
|
|
16
16
|
};
|
|
17
|
+
|
|
18
|
+
export const COOKIE_CONSENT_SELECTORS = [
|
|
19
|
+
// OneTrust
|
|
20
|
+
'onetrust',
|
|
21
|
+
'.onetrust-banner',
|
|
22
|
+
'#onetrust-consent-sdk',
|
|
23
|
+
'#onetrust-banner-sdk',
|
|
24
|
+
'#ot-sdk-cookie-policy',
|
|
25
|
+
'.ot-sdk-container',
|
|
26
|
+
'.ot-sdk-row',
|
|
27
|
+
'.ot-sdk-column',
|
|
28
|
+
'.ot-sdk-show-settings',
|
|
29
|
+
'#ot-',
|
|
30
|
+
'.ot-',
|
|
31
|
+
|
|
32
|
+
// Cookiebot / Usercentrics
|
|
33
|
+
'#CybotCookiebotDialog',
|
|
34
|
+
'.CybotCookiebotDialog',
|
|
35
|
+
'#CookiebotWidget',
|
|
36
|
+
'.CookiebotWidget',
|
|
37
|
+
'.cookie-consent__banner',
|
|
38
|
+
'.cookie-consent__dialog',
|
|
39
|
+
'.cookie-consent__button',
|
|
40
|
+
|
|
41
|
+
// TrustArc
|
|
42
|
+
'truste-',
|
|
43
|
+
'#truste-',
|
|
44
|
+
'#truste-consent-banner',
|
|
45
|
+
'.truste-consent-banner',
|
|
46
|
+
'#truste-consent-button',
|
|
47
|
+
'.truste-consent-button',
|
|
48
|
+
'.truste-consent-text',
|
|
49
|
+
'.truste-consent-close',
|
|
50
|
+
|
|
51
|
+
// CookieYes
|
|
52
|
+
'#cookieyes-banner',
|
|
53
|
+
'.cky-consent',
|
|
54
|
+
'.cky-banner',
|
|
55
|
+
'.cky-btn',
|
|
56
|
+
'.cky-btn-accept',
|
|
57
|
+
'.cky-btn-reject',
|
|
58
|
+
'.cky-btn-settings',
|
|
59
|
+
|
|
60
|
+
// Termly
|
|
61
|
+
'#termly-consent-banner',
|
|
62
|
+
'.termly-consent-banner',
|
|
63
|
+
'.termly-consent-button',
|
|
64
|
+
'.termly-consent-text',
|
|
65
|
+
'.termly-consent-close',
|
|
66
|
+
|
|
67
|
+
// CookieScript
|
|
68
|
+
'#cookiescript_injected',
|
|
69
|
+
'.cookiescript-consent',
|
|
70
|
+
'.cookiescript-banner',
|
|
71
|
+
'.cookiescript-btn',
|
|
72
|
+
'.cookiescript-btn-accept',
|
|
73
|
+
'.cookiescript-btn-reject',
|
|
74
|
+
'.cookiescript-btn-settings',
|
|
75
|
+
|
|
76
|
+
// CookieFirst
|
|
77
|
+
'#cookiefirst-root',
|
|
78
|
+
'.cookiefirst-banner',
|
|
79
|
+
'.cookiefirst-button',
|
|
80
|
+
'.cookiefirst-text',
|
|
81
|
+
'.cookiefirst-close',
|
|
82
|
+
|
|
83
|
+
// Quantcast Choice
|
|
84
|
+
'#qc-cmp2-ui',
|
|
85
|
+
'.qc-cmp2-ui',
|
|
86
|
+
'.qc-cmp2-summary',
|
|
87
|
+
'.qc-cmp2-footer',
|
|
88
|
+
'.qc-cmp2-header',
|
|
89
|
+
'.qc-cmp2-buttons',
|
|
90
|
+
'.qc-cmp2-button',
|
|
91
|
+
'.qc-cmp2-button-accept',
|
|
92
|
+
'.qc-cmp2-button-reject',
|
|
93
|
+
|
|
94
|
+
// Crownpeak (Evidon)
|
|
95
|
+
'#evidon-banner',
|
|
96
|
+
'.evidon-banner',
|
|
97
|
+
'.evidon-button',
|
|
98
|
+
'.evidon-text',
|
|
99
|
+
'.evidon-close',
|
|
100
|
+
|
|
101
|
+
// Popupsmart
|
|
102
|
+
'#popupsmart-consent',
|
|
103
|
+
'.popupsmart-consent',
|
|
104
|
+
'.popupsmart-banner',
|
|
105
|
+
'.popupsmart-button',
|
|
106
|
+
'.popupsmart-text',
|
|
107
|
+
'.popupsmart-close',
|
|
108
|
+
];
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2025 Adobe. All rights reserved.
|
|
3
|
+
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
* you may not use this file except in compliance with the License. You may obtain a copy
|
|
5
|
+
* of the License at http://www.apache.org/licenses/LICENSE-2.0
|
|
6
|
+
*
|
|
7
|
+
* Unless required by applicable law or agreed to in writing, software distributed under
|
|
8
|
+
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
|
|
9
|
+
* OF ANY KIND, either express or implied. See the License for the specific language
|
|
10
|
+
* governing permissions and limitations under the License.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { hasText, isNonEmptyObject } from '@adobe/spacecat-shared-utils';
|
|
14
|
+
import { COOKIE_CONSENT_SELECTORS } from './constants.js';
|
|
15
|
+
|
|
16
|
+
const uncategorized = 'uncategorized';
|
|
17
|
+
|
|
18
|
+
export function getPageType(bundle, pageTypes) {
|
|
19
|
+
if (!isNonEmptyObject(pageTypes)) {
|
|
20
|
+
return uncategorized;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const pageTypeEntries = Object.entries(pageTypes);
|
|
24
|
+
|
|
25
|
+
const classify = ([, regEx]) => {
|
|
26
|
+
if (regEx instanceof RegExp) {
|
|
27
|
+
return regEx.test(bundle.url);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return new RegExp(regEx).test(bundle.url);
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const entry = pageTypeEntries.find(classify);
|
|
34
|
+
|
|
35
|
+
if (!hasText(entry?.[0]) || entry?.[0] === 'other | Other Pages') {
|
|
36
|
+
return uncategorized;
|
|
37
|
+
}
|
|
38
|
+
return entry?.[0] ?? uncategorized;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function isConsentClick(source) {
|
|
42
|
+
if (typeof source !== 'string' || !source) {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const sourceLower = source.toLowerCase();
|
|
47
|
+
return COOKIE_CONSENT_SELECTORS.some((keyword) => sourceLower.includes(keyword.toLowerCase()));
|
|
48
|
+
}
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2025 Adobe. All rights reserved.
|
|
3
|
+
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
* you may not use this file except in compliance with the License. You may obtain a copy
|
|
5
|
+
* of the License at http://www.apache.org/licenses/LICENSE-2.0
|
|
6
|
+
*
|
|
7
|
+
* Unless required by applicable law or agreed to in writing, software distributed under
|
|
8
|
+
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
|
|
9
|
+
* OF ANY KIND, either express or implied. See the License for the specific language
|
|
10
|
+
* governing permissions and limitations under the License.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { DataChunks } from '@adobe/rum-distiller';
|
|
14
|
+
import { DELIMITER, generateKey, loadBundles } from '../utils.js';
|
|
15
|
+
import { classifyTraffic } from '../common/traffic.js';
|
|
16
|
+
import { getPageType, isConsentClick } from '../common/page.js';
|
|
17
|
+
|
|
18
|
+
function getTrafficSource(bundle) {
|
|
19
|
+
return classifyTraffic(bundle).type;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function getDeviceType(bundle) {
|
|
23
|
+
return bundle.userAgent.split(':')?.[0];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function addPageTypeFacet(dataChunks, pageTypes) {
|
|
27
|
+
dataChunks.addFacet('pageType', (bundle) => getPageType(bundle, pageTypes));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function addPageTypeDeviceTypeFacet(dataChunks, pageTypes) {
|
|
31
|
+
dataChunks.addFacet('pageTypeDeviceTypes', (bundle) => {
|
|
32
|
+
const deviceType = getDeviceType(bundle);
|
|
33
|
+
const pageType = getPageType(bundle, pageTypes);
|
|
34
|
+
return generateKey(pageType, deviceType);
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function addPageTypeTrafficSourceDeviceTypes(dataChunks, pageTypes) {
|
|
39
|
+
dataChunks.addFacet('pageTrafficDeviceTypes', (bundle) => {
|
|
40
|
+
const deviceType = getDeviceType(bundle);
|
|
41
|
+
const pageType = getPageType(bundle, pageTypes);
|
|
42
|
+
return generateKey(pageType, getTrafficSource(bundle), deviceType);
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function addPageTypeTrafficSourceFacet(dataChunks, pageTypes) {
|
|
47
|
+
dataChunks.addFacet('pageTypeTrafficSources', (bundle) => {
|
|
48
|
+
const pageType = getPageType(bundle, pageTypes);
|
|
49
|
+
return generateKey(pageType, getTrafficSource(bundle));
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function handler(bundles, options = { pageTypes: null }) {
|
|
54
|
+
const dataChunks = new DataChunks();
|
|
55
|
+
const { pageTypes: pageTypeOpt } = options;
|
|
56
|
+
|
|
57
|
+
loadBundles(bundles, dataChunks);
|
|
58
|
+
|
|
59
|
+
const metricFilter = (metrics) => {
|
|
60
|
+
const { ctr, enters, sumOfAllClicks } = metrics;
|
|
61
|
+
return {
|
|
62
|
+
ctr: ctr.sum / ctr.weight,
|
|
63
|
+
clickedSessions: ctr.sum,
|
|
64
|
+
totalSessions: ctr.weight,
|
|
65
|
+
sessionsWithEnter: enters.sum,
|
|
66
|
+
clicksOverViews: ctr.weight ? ctr.sum / ctr.weight : 0,
|
|
67
|
+
bounceRate: ctr.weight ? (1 - (ctr.sum / ctr.weight)) : 0,
|
|
68
|
+
totalNumClicks: sumOfAllClicks.sum,
|
|
69
|
+
avgClicksPerSession: ctr.sum ? sumOfAllClicks.sum / ctr.sum : 0,
|
|
70
|
+
};
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
dataChunks.addFacet('urls', (bundle) => bundle.url);
|
|
74
|
+
|
|
75
|
+
dataChunks.addFacet('trafficSources', (bundle) => getTrafficSource(bundle));
|
|
76
|
+
|
|
77
|
+
dataChunks.addFacet('urlTrafficSources', (bundle) => generateKey(bundle.url, getTrafficSource(bundle)));
|
|
78
|
+
|
|
79
|
+
dataChunks.addFacet('urlDeviceTypes', (bundle) => generateKey(bundle.url, getDeviceType(bundle)));
|
|
80
|
+
|
|
81
|
+
dataChunks.addFacet('deviceTypes', (bundle) => getDeviceType(bundle));
|
|
82
|
+
|
|
83
|
+
dataChunks.addFacet('urlTrafficSourceDeviceTypes', (bundle) => generateKey(bundle.url, getTrafficSource(bundle), getDeviceType(bundle)));
|
|
84
|
+
|
|
85
|
+
dataChunks.addFacet('deviceTypeTrafficSources', (bundle) => generateKey(getDeviceType(bundle), getTrafficSource(bundle)));
|
|
86
|
+
|
|
87
|
+
addPageTypeFacet(dataChunks, pageTypeOpt);
|
|
88
|
+
|
|
89
|
+
addPageTypeTrafficSourceFacet(dataChunks, pageTypeOpt);
|
|
90
|
+
|
|
91
|
+
addPageTypeDeviceTypeFacet(dataChunks, pageTypeOpt);
|
|
92
|
+
|
|
93
|
+
addPageTypeTrafficSourceDeviceTypes(dataChunks, pageTypeOpt);
|
|
94
|
+
|
|
95
|
+
dataChunks.addSeries('ctr', (bundle) => {
|
|
96
|
+
const isClicked = bundle.events.some((e) => e.checkpoint === 'click');
|
|
97
|
+
return isClicked ? bundle.weight : 0;
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
dataChunks.addSeries('sumOfAllClicks', (bundle) => {
|
|
101
|
+
const nonConsentClicks = bundle.events
|
|
102
|
+
.filter((e) => e.checkpoint === 'click' && !isConsentClick(e.source))
|
|
103
|
+
.map(() => bundle.weight)
|
|
104
|
+
.reduce((sum, weight) => sum + weight, 0);
|
|
105
|
+
|
|
106
|
+
return nonConsentClicks;
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
dataChunks.addSeries('enters', (bundle) => {
|
|
110
|
+
const containsEnter = bundle.events.some((e) => e.checkpoint === 'enter');
|
|
111
|
+
return containsEnter ? bundle.weight : 0;
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
const urls = dataChunks.facets.urls.map((facet) => {
|
|
115
|
+
const url = facet.value;
|
|
116
|
+
return {
|
|
117
|
+
...metricFilter(facet.metrics),
|
|
118
|
+
url,
|
|
119
|
+
};
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
const pageType = dataChunks.facets.pageType.map((facet) => {
|
|
123
|
+
const type = facet.value;
|
|
124
|
+
return {
|
|
125
|
+
...metricFilter(facet.metrics),
|
|
126
|
+
type,
|
|
127
|
+
};
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
const deviceTypes = dataChunks.facets.deviceTypes.map((facet) => {
|
|
131
|
+
const deviceType = facet.value;
|
|
132
|
+
return {
|
|
133
|
+
...metricFilter(facet.metrics),
|
|
134
|
+
deviceType,
|
|
135
|
+
};
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
const urlDeviceTypes = dataChunks.facets.urlDeviceTypes.map((facet) => {
|
|
139
|
+
const [url, deviceType] = facet.value.split(DELIMITER);
|
|
140
|
+
return {
|
|
141
|
+
...metricFilter(facet.metrics),
|
|
142
|
+
url,
|
|
143
|
+
deviceType,
|
|
144
|
+
};
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
const trafficSources = dataChunks.facets.trafficSources.map((facet) => {
|
|
148
|
+
const source = facet.value;
|
|
149
|
+
return {
|
|
150
|
+
...metricFilter(facet.metrics),
|
|
151
|
+
source,
|
|
152
|
+
};
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
const urlTrafficSources = dataChunks.facets.urlTrafficSources.map((facet) => {
|
|
156
|
+
const [url, source] = facet.value.split(DELIMITER);
|
|
157
|
+
return {
|
|
158
|
+
...metricFilter(facet.metrics),
|
|
159
|
+
url,
|
|
160
|
+
source,
|
|
161
|
+
};
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
const urlTrafficSourceDeviceTypes = dataChunks.facets.urlTrafficSourceDeviceTypes.map((facet) => {
|
|
165
|
+
const [url, source, deviceType] = facet.value.split(DELIMITER);
|
|
166
|
+
return {
|
|
167
|
+
...metricFilter(facet.metrics),
|
|
168
|
+
url,
|
|
169
|
+
source,
|
|
170
|
+
deviceType,
|
|
171
|
+
};
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
const pageTypeTrafficSources = dataChunks.facets.pageTypeTrafficSources.map((facet) => {
|
|
175
|
+
const [type, source] = facet.value.split(DELIMITER);
|
|
176
|
+
return {
|
|
177
|
+
...metricFilter(facet.metrics),
|
|
178
|
+
type,
|
|
179
|
+
source,
|
|
180
|
+
};
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
const pageTypeDeviceTypes = dataChunks.facets.pageTypeDeviceTypes.map((facet) => {
|
|
184
|
+
const [type, deviceType] = facet.value.split(DELIMITER);
|
|
185
|
+
return {
|
|
186
|
+
...metricFilter(facet.metrics),
|
|
187
|
+
type,
|
|
188
|
+
deviceType,
|
|
189
|
+
};
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
const deviceTypeTrafficSources = dataChunks.facets.deviceTypeTrafficSources.map((facet) => {
|
|
193
|
+
const [deviceType, source] = facet.value.split(DELIMITER);
|
|
194
|
+
return {
|
|
195
|
+
...metricFilter(facet.metrics),
|
|
196
|
+
deviceType,
|
|
197
|
+
source,
|
|
198
|
+
};
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
const pageTrafficDeviceTypes = dataChunks.facets.pageTrafficDeviceTypes.map((facet) => {
|
|
202
|
+
const [type, source, deviceType] = facet.value.split(DELIMITER);
|
|
203
|
+
return {
|
|
204
|
+
...metricFilter(facet.metrics),
|
|
205
|
+
type,
|
|
206
|
+
source,
|
|
207
|
+
deviceType,
|
|
208
|
+
};
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
const metrics = {
|
|
212
|
+
url: urls,
|
|
213
|
+
urlTrafficSource: urlTrafficSources,
|
|
214
|
+
urlDeviceType: urlDeviceTypes,
|
|
215
|
+
urlTrafficSourceDeviceType: urlTrafficSourceDeviceTypes,
|
|
216
|
+
pageType,
|
|
217
|
+
pageTypeTrafficSource: pageTypeTrafficSources,
|
|
218
|
+
pageTypeDeviceType: pageTypeDeviceTypes,
|
|
219
|
+
pageTypeTrafficSourceDeviceType: pageTrafficDeviceTypes,
|
|
220
|
+
deviceType: deviceTypes,
|
|
221
|
+
deviceTypeTrafficSource: deviceTypeTrafficSources,
|
|
222
|
+
trafficSource: trafficSources,
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
return Object.entries(metrics)
|
|
226
|
+
.map(([key, value]) => ({
|
|
227
|
+
key,
|
|
228
|
+
value,
|
|
229
|
+
}));
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
export default {
|
|
233
|
+
handler,
|
|
234
|
+
};
|
package/src/index.js
CHANGED
|
@@ -20,6 +20,7 @@ import trafficAcquisition from './functions/traffic-acquisition.js';
|
|
|
20
20
|
import totalMetrics from './functions/total-metrics.js';
|
|
21
21
|
import variant from './functions/variant.js';
|
|
22
22
|
import pageviews from './functions/pageviews.js';
|
|
23
|
+
import trafficMetrics from './functions/traffic-metrics.js';
|
|
23
24
|
import rageclick from './functions/opportunities/rageclick.js';
|
|
24
25
|
import highInorganicHighBounceRate from './functions/opportunities/high-inorganic-high-bounce-rate.js';
|
|
25
26
|
import highOrganicLowCtr from './functions/opportunities/high-organic-low-ctr.js';
|
|
@@ -40,6 +41,7 @@ const HANDLERS = {
|
|
|
40
41
|
'high-inorganic-high-bounce-rate': highInorganicHighBounceRate,
|
|
41
42
|
'high-organic-low-ctr': highOrganicLowCtr,
|
|
42
43
|
pageviews,
|
|
44
|
+
trafficMetrics,
|
|
43
45
|
};
|
|
44
46
|
|
|
45
47
|
function sanitize(opts) {
|