@adobe/spacecat-shared-rum-api-client 2.28.0 → 2.29.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
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
# [@adobe/spacecat-shared-rum-api-client-v2.29.0](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-rum-api-client-v2.28.1...@adobe/spacecat-shared-rum-api-client-v2.29.0) (2025-06-20)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* high organic low ctr detection using traffic instead of thresholds ([#815](https://github.com/adobe/spacecat-shared/issues/815)) ([54569fb](https://github.com/adobe/spacecat-shared/commit/54569fb5d9b5fbe8400da0aadbae148380866fda))
|
|
7
|
+
|
|
8
|
+
# [@adobe/spacecat-shared-rum-api-client-v2.28.1](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-rum-api-client-v2.28.0...@adobe/spacecat-shared-rum-api-client-v2.28.1) (2025-05-31)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* **deps:** update external fixes ([#779](https://github.com/adobe/spacecat-shared/issues/779)) ([07f8cce](https://github.com/adobe/spacecat-shared/commit/07f8cce73e33bfb9c61fe14f2ef28012b872437d))
|
|
14
|
+
|
|
1
15
|
# [@adobe/spacecat-shared-rum-api-client-v2.28.0](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-rum-api-client-v2.27.2...@adobe/spacecat-shared-rum-api-client-v2.28.0) (2025-05-30)
|
|
2
16
|
|
|
3
17
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adobe/spacecat-shared-rum-api-client",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.29.0",
|
|
4
4
|
"description": "Shared modules of the Spacecat Services - Rum API client",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"devDependencies": {
|
|
47
47
|
"chai": "5.2.0",
|
|
48
48
|
"chai-as-promised": "8.0.1",
|
|
49
|
-
"nock": "14.0.
|
|
49
|
+
"nock": "14.0.5",
|
|
50
50
|
"sinon": "20.0.0",
|
|
51
51
|
"sinon-chai": "4.0.0",
|
|
52
52
|
"typescript": "5.8.3"
|
|
@@ -13,16 +13,15 @@
|
|
|
13
13
|
import trafficAcquisition from '../traffic-acquisition.js';
|
|
14
14
|
import { getCTRByUrlAndVendor, getSiteAvgCTR } from '../../common/aggregateFns.js';
|
|
15
15
|
|
|
16
|
-
const DAILY_EARNED_THRESHOLD = 1000;
|
|
17
|
-
const CTR_THRESHOLD_RATIO = 0.95;
|
|
18
|
-
const DAILY_PAGEVIEW_THRESHOLD = 1000;
|
|
19
16
|
const VENDORS_TO_CONSIDER = 5;
|
|
17
|
+
const MAX_OPPORTUNITIES = 10;
|
|
20
18
|
|
|
21
19
|
const MAIN_TYPES = ['paid', 'earned', 'owned'];
|
|
22
20
|
|
|
23
21
|
function convertToOpportunity(traffic) {
|
|
24
22
|
const {
|
|
25
|
-
url, total, ctr, paid, owned, earned,
|
|
23
|
+
url, total, ctr, paid, percentileScore, owned, earned,
|
|
24
|
+
sources, siteAvgCTR, ctrByUrlAndVendor, pageOnTime,
|
|
26
25
|
} = traffic;
|
|
27
26
|
|
|
28
27
|
const vendors = sources.reduce((acc, { type, views }) => {
|
|
@@ -51,6 +50,7 @@ function convertToOpportunity(traffic) {
|
|
|
51
50
|
trackedKPISiteAverage: siteAvgCTR,
|
|
52
51
|
pageViews: total,
|
|
53
52
|
samples: total, // todo: get the actual number of samples
|
|
53
|
+
percentileScore,
|
|
54
54
|
metrics: [{
|
|
55
55
|
type: 'traffic',
|
|
56
56
|
vendor: '*',
|
|
@@ -106,33 +106,48 @@ function convertToOpportunity(traffic) {
|
|
|
106
106
|
return opportunity;
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
}
|
|
109
|
+
/**
|
|
110
|
+
* Sort pages by earned AND overall traffic using percentile scoring.
|
|
111
|
+
* @param {Array} pages - List of { url, total, earned }
|
|
112
|
+
* @returns {Array} List of pages sorted by joint strength
|
|
113
|
+
*/
|
|
114
|
+
function sortPagesByEarnedAndOverallTraffic(pages) {
|
|
115
|
+
if (!Array.isArray(pages) || pages.length === 0) return [];
|
|
113
116
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
+
const sortedOverall = [...pages].sort((a, b) => a.total - b.total);
|
|
118
|
+
const sortedEarned = [...pages].sort((a, b) => {
|
|
119
|
+
if (a.earned === b.earned) {
|
|
120
|
+
return a.total - b.total;
|
|
121
|
+
}
|
|
122
|
+
return a.earned - b.earned;
|
|
123
|
+
});
|
|
124
|
+
const n = pages.length;
|
|
125
|
+
|
|
126
|
+
const percentiles = pages.map((p) => {
|
|
127
|
+
const totalPercentile = sortedOverall.findIndex((x) => x.url === p.url) / (n - 1);
|
|
128
|
+
const earnedPercentile = sortedEarned.findIndex((x) => x.url === p.url) / (n - 1);
|
|
129
|
+
const percentileScore = totalPercentile * earnedPercentile;
|
|
130
|
+
return { ...p, percentileScore };
|
|
131
|
+
});
|
|
117
132
|
|
|
118
|
-
|
|
119
|
-
|
|
133
|
+
return percentiles.sort((a, b) => b.percentileScore - a.percentileScore);
|
|
134
|
+
}
|
|
120
135
|
|
|
136
|
+
function handler(bundles) {
|
|
121
137
|
const trafficByUrl = trafficAcquisition.handler(bundles);
|
|
122
138
|
const ctrByUrlAndVendor = getCTRByUrlAndVendor(bundles);
|
|
123
139
|
const siteAvgCTR = getSiteAvgCTR(bundles);
|
|
140
|
+
const pagesSortedByEarnedAndOverallTraffic = sortPagesByEarnedAndOverallTraffic(
|
|
141
|
+
trafficByUrl,
|
|
142
|
+
).slice(0, MAX_OPPORTUNITIES);
|
|
124
143
|
|
|
125
|
-
return
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
ctrByUrlAndVendor: ctrByUrlAndVendor[traffic.url].vendors,
|
|
133
|
-
pageOnTime: traffic.maxTimeDelta,
|
|
134
|
-
}))
|
|
135
|
-
.map(convertToOpportunity);
|
|
144
|
+
return pagesSortedByEarnedAndOverallTraffic.map((traffic) => ({
|
|
145
|
+
...traffic,
|
|
146
|
+
ctr: ctrByUrlAndVendor[traffic.url].value,
|
|
147
|
+
siteAvgCTR,
|
|
148
|
+
ctrByUrlAndVendor: ctrByUrlAndVendor[traffic.url].vendors,
|
|
149
|
+
pageOnTime: traffic.maxTimeDelta,
|
|
150
|
+
})).map(convertToOpportunity);
|
|
136
151
|
}
|
|
137
152
|
|
|
138
153
|
export default {
|
|
@@ -47,7 +47,7 @@ function formatTraffic(row) {
|
|
|
47
47
|
url, weight, type, category, vendor, events = [],
|
|
48
48
|
} = row;
|
|
49
49
|
|
|
50
|
-
const maxTimeDelta = events.reduce((max, e) => Math.max(max, e.timeDelta), 0);
|
|
50
|
+
const maxTimeDelta = events.reduce((max, e) => Math.max(max, e.timeDelta || 0), 0);
|
|
51
51
|
|
|
52
52
|
return {
|
|
53
53
|
url,
|