@adobe/spacecat-shared-rum-api-client 2.36.4 → 2.37.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 +14 -0
- package/README.md +7 -1
- package/package.json +2 -2
- package/src/functions/opportunities/high-organic-low-ctr.js +3 -3
- package/src/functions/reports/optimization/graph.js +97 -0
- package/src/functions/reports/optimization/metrics.js +30 -0
- package/src/functions/reports/optimization/utils.js +201 -0
- package/src/index.js +4 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
# [@adobe/spacecat-shared-rum-api-client-v2.37.0](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-rum-api-client-v2.36.5...@adobe/spacecat-shared-rum-api-client-v2.37.0) (2025-08-28)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* added metrics and graph data functions for optimization-report ([#919](https://github.com/adobe/spacecat-shared/issues/919)) ([29461b2](https://github.com/adobe/spacecat-shared/commit/29461b246b74423dcd39b0af4e1d140f1a3d3af1))
|
|
7
|
+
|
|
8
|
+
# [@adobe/spacecat-shared-rum-api-client-v2.36.5](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-rum-api-client-v2.36.4...@adobe/spacecat-shared-rum-api-client-v2.36.5) (2025-08-25)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* make max opportunities configurable ([#930](https://github.com/adobe/spacecat-shared/issues/930)) ([2b18c25](https://github.com/adobe/spacecat-shared/commit/2b18c25e8dc3303af0e1a09ebcbe830672368d13))
|
|
14
|
+
|
|
1
15
|
# [@adobe/spacecat-shared-rum-api-client-v2.36.4](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-rum-api-client-v2.36.3...@adobe/spacecat-shared-rum-api-client-v2.36.4) (2025-08-15)
|
|
2
16
|
|
|
3
17
|
|
package/README.md
CHANGED
|
@@ -335,7 +335,13 @@ Calculates the amount of inorganic traffic and the bounce rate for each page. Id
|
|
|
335
335
|
|
|
336
336
|
### high-organic-low-ctr (Experimentation Opportunity)
|
|
337
337
|
|
|
338
|
-
Calculates the amount of non-inorganic (earned and owned) traffic and the click-through rate for each page and vendor. Identifies pages with high non-inorganic traffic and low click-through rates, which can be targeted for future experimentation opportunities.
|
|
338
|
+
Calculates the amount of non-inorganic (earned and owned) traffic and the click-through rate for each page and vendor. Identifies pages with high non-inorganic traffic and low click-through rates, which can be targeted for future experimentation opportunities.
|
|
339
|
+
|
|
340
|
+
#### options supported
|
|
341
|
+
- **maxOpportunities** - No. of maximum opportunities to return, defaults to 10
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
An example payload is provided below:
|
|
339
345
|
|
|
340
346
|
```json
|
|
341
347
|
[
|
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.37.0",
|
|
4
4
|
"description": "Shared modules of the Spacecat Services - Rum API client",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -38,8 +38,8 @@
|
|
|
38
38
|
"@adobe/fetch": "4.2.2",
|
|
39
39
|
"@adobe/helix-shared-wrap": "2.0.2",
|
|
40
40
|
"@adobe/helix-universal": "5.2.2",
|
|
41
|
-
"@adobe/spacecat-shared-utils": "1.26.4",
|
|
42
41
|
"@adobe/rum-distiller": "1.17.0",
|
|
42
|
+
"@adobe/spacecat-shared-utils": "1.48.0",
|
|
43
43
|
"aws4": "1.13.2",
|
|
44
44
|
"urijs": "1.19.11"
|
|
45
45
|
},
|
|
@@ -14,7 +14,7 @@ import trafficAcquisition from '../traffic-acquisition.js';
|
|
|
14
14
|
import { getCTRByUrlAndVendor, getSiteAvgCTR } from '../../common/aggregateFns.js';
|
|
15
15
|
|
|
16
16
|
const VENDORS_TO_CONSIDER = 5;
|
|
17
|
-
const
|
|
17
|
+
const DEFAULT_MAX_OPPORTUNITIES = 10;
|
|
18
18
|
|
|
19
19
|
const MAIN_TYPES = ['paid', 'earned', 'owned'];
|
|
20
20
|
|
|
@@ -133,13 +133,13 @@ function sortPagesByEarnedAndOverallTraffic(pages) {
|
|
|
133
133
|
return percentiles.sort((a, b) => b.percentileScore - a.percentileScore);
|
|
134
134
|
}
|
|
135
135
|
|
|
136
|
-
function handler(bundles) {
|
|
136
|
+
function handler(bundles, opts) {
|
|
137
137
|
const trafficByUrl = trafficAcquisition.handler(bundles);
|
|
138
138
|
const ctrByUrlAndVendor = getCTRByUrlAndVendor(bundles);
|
|
139
139
|
const siteAvgCTR = getSiteAvgCTR(bundles);
|
|
140
140
|
const pagesSortedByEarnedAndOverallTraffic = sortPagesByEarnedAndOverallTraffic(
|
|
141
141
|
trafficByUrl,
|
|
142
|
-
).slice(0,
|
|
142
|
+
).slice(0, opts?.maxOpportunities || DEFAULT_MAX_OPPORTUNITIES);
|
|
143
143
|
|
|
144
144
|
return pagesSortedByEarnedAndOverallTraffic.map((traffic) => ({
|
|
145
145
|
...traffic,
|
|
@@ -0,0 +1,97 @@
|
|
|
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
|
+
import {
|
|
13
|
+
filterBundles,
|
|
14
|
+
initializeDataChunks,
|
|
15
|
+
extractMetrics,
|
|
16
|
+
createTimeSeriesData,
|
|
17
|
+
calculateTotals,
|
|
18
|
+
validateDateRange,
|
|
19
|
+
} from './utils.js';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Process URL-specific data
|
|
23
|
+
* @param {Object} urlFacet - URL facet from DataChunks
|
|
24
|
+
* @returns {Object} URL-specific data with totals and time series
|
|
25
|
+
*/
|
|
26
|
+
function processUrlData(urlFacet) {
|
|
27
|
+
const urlBundles = urlFacet.entries;
|
|
28
|
+
const urlDataChunks = initializeDataChunks(urlBundles, { includeDateFacet: true });
|
|
29
|
+
|
|
30
|
+
return {
|
|
31
|
+
total: extractMetrics(urlFacet),
|
|
32
|
+
timeSeries: createTimeSeriesData(urlDataChunks.facets.date),
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Process bundles into aggregated graph data
|
|
38
|
+
* @param {Object[]} bundles - Array of RUM bundles
|
|
39
|
+
* @returns {Object} Aggregated traffic data
|
|
40
|
+
*/
|
|
41
|
+
function processBundles(bundles) {
|
|
42
|
+
const dataChunks = initializeDataChunks(bundles, {
|
|
43
|
+
includeUrlFacet: true,
|
|
44
|
+
includeDateFacet: true,
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// Process URL-specific data
|
|
48
|
+
const byUrl = {};
|
|
49
|
+
if (dataChunks.facets.url) {
|
|
50
|
+
dataChunks.facets.url.forEach((urlFacet) => {
|
|
51
|
+
byUrl[urlFacet.value] = processUrlData(urlFacet);
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Process overall traffic data
|
|
56
|
+
const trafficData = createTimeSeriesData(dataChunks.facets.date);
|
|
57
|
+
|
|
58
|
+
return { trafficData, byUrl };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Main handler function to generate graph data
|
|
63
|
+
* @param {Object[]} bundles - Array of RUM bundles
|
|
64
|
+
* @param {Object} opts - Options object
|
|
65
|
+
* @returns {Object}
|
|
66
|
+
*/
|
|
67
|
+
function handler(bundles, opts) {
|
|
68
|
+
if (!opts) {
|
|
69
|
+
return {
|
|
70
|
+
trafficData: [],
|
|
71
|
+
byUrl: {},
|
|
72
|
+
totals: {},
|
|
73
|
+
urlsFiltered: [],
|
|
74
|
+
granularity: 'DAILY',
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const {
|
|
79
|
+
startTime, endTime, urls = [],
|
|
80
|
+
} = opts;
|
|
81
|
+
|
|
82
|
+
validateDateRange(startTime, endTime);
|
|
83
|
+
|
|
84
|
+
const filteredBundles = filterBundles(bundles, opts);
|
|
85
|
+
const result = processBundles(filteredBundles);
|
|
86
|
+
|
|
87
|
+
return {
|
|
88
|
+
...result,
|
|
89
|
+
totals: calculateTotals(result.trafficData),
|
|
90
|
+
urlsFiltered: urls,
|
|
91
|
+
granularity: opts.granularity || 'DAILY',
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export default {
|
|
96
|
+
handler,
|
|
97
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
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 { initializeDataChunks, calculateMetrics, filterBundles } from './utils.js';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Handler for optimization reports metrics
|
|
17
|
+
* @param {Object[]} bundles - Array of RUM bundles
|
|
18
|
+
* @param {Object} opts - Options object
|
|
19
|
+
* @returns {Object} Calculated metrics
|
|
20
|
+
*/
|
|
21
|
+
function handler(bundles, opts) {
|
|
22
|
+
const options = opts || {};
|
|
23
|
+
const filteredBundles = filterBundles(bundles, options);
|
|
24
|
+
const dataChunks = initializeDataChunks(filteredBundles);
|
|
25
|
+
return calculateMetrics(dataChunks);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export default {
|
|
29
|
+
handler,
|
|
30
|
+
};
|
|
@@ -0,0 +1,201 @@
|
|
|
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
|
+
import { DataChunks, series, facets } from '@adobe/rum-distiller';
|
|
13
|
+
import { computeConversionRate } from '@adobe/rum-distiller/utils.js';
|
|
14
|
+
import { urlMatchesFilter } from '@adobe/spacecat-shared-utils';
|
|
15
|
+
import { loadBundles } from '../../../utils.js';
|
|
16
|
+
|
|
17
|
+
// Constants
|
|
18
|
+
const METRIC_NAMES = ['organic', 'visits', 'pageViews', 'bounces', 'conversions', 'engagement'];
|
|
19
|
+
const CONVERSION_SPEC = { checkpoint: ['click'] };
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Create date facet function for YYYY-MM-DD format
|
|
23
|
+
* @returns {Function} Date facet function
|
|
24
|
+
*/
|
|
25
|
+
function createDateFacet(bundle) {
|
|
26
|
+
const date = new Date(bundle.time);
|
|
27
|
+
return date.toISOString().split('T')[0];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Create conversion series function
|
|
32
|
+
* @param {DataChunks} dataChunks - DataChunks instance
|
|
33
|
+
* @returns {Function} Conversion series function
|
|
34
|
+
*/
|
|
35
|
+
function createConversionSeries(dataChunks) {
|
|
36
|
+
return (bundle) => (bundle
|
|
37
|
+
&& dataChunks.hasConversion(bundle, CONVERSION_SPEC) ? bundle.weight : 0);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Initialize DataChunks with common configuration
|
|
42
|
+
* @param {Object[]} bundles - Array of RUM bundles
|
|
43
|
+
* @param {Object} options - Configuration options
|
|
44
|
+
* @param {boolean} options.includeUrlFacet - Whether to include URL facet aggregation
|
|
45
|
+
* @param {boolean} options.includeDateFacet - Whether to include date facet aggregation
|
|
46
|
+
* @returns {DataChunks} Configured DataChunks instance
|
|
47
|
+
*/
|
|
48
|
+
export function initializeDataChunks(bundles, options = {}) {
|
|
49
|
+
const { includeUrlFacet = false, includeDateFacet = false } = options;
|
|
50
|
+
|
|
51
|
+
const dataChunks = new DataChunks();
|
|
52
|
+
|
|
53
|
+
// Handle null/undefined bundles
|
|
54
|
+
const validBundles = (!bundles || !Array.isArray(bundles)) ? [] : bundles;
|
|
55
|
+
|
|
56
|
+
// Filter out bundles with missing or invalid URLs
|
|
57
|
+
const processedBundles = validBundles.filter((bundle) => bundle && bundle.url);
|
|
58
|
+
|
|
59
|
+
loadBundles(processedBundles, dataChunks);
|
|
60
|
+
|
|
61
|
+
// Add checkpoint facet for conversion detection
|
|
62
|
+
dataChunks.addFacet('checkpoint', facets.checkpoint, 'every', 'none');
|
|
63
|
+
|
|
64
|
+
// Add URL facet if requested
|
|
65
|
+
if (includeUrlFacet) {
|
|
66
|
+
dataChunks.addFacet('url', facets.url, 'some', 'none');
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Add date facet if requested
|
|
70
|
+
if (includeDateFacet) {
|
|
71
|
+
dataChunks.addFacet('date', createDateFacet);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Add metrics series
|
|
75
|
+
dataChunks.addSeries('pageViews', series.pageViews);
|
|
76
|
+
dataChunks.addSeries('engagement', series.engagement);
|
|
77
|
+
dataChunks.addSeries('bounces', series.bounces);
|
|
78
|
+
dataChunks.addSeries('organic', series.organic);
|
|
79
|
+
dataChunks.addSeries('visits', series.visits);
|
|
80
|
+
dataChunks.addSeries('conversions', createConversionSeries(dataChunks));
|
|
81
|
+
|
|
82
|
+
return dataChunks;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Extract metrics from a facet
|
|
87
|
+
* @param {Object} facet - DataChunks facet
|
|
88
|
+
* @returns {Object} Metrics object
|
|
89
|
+
*/
|
|
90
|
+
export function extractMetrics(facet) {
|
|
91
|
+
return METRIC_NAMES.reduce((acc, metric) => {
|
|
92
|
+
acc[metric] = facet.metrics[metric]?.sum || 0;
|
|
93
|
+
return acc;
|
|
94
|
+
}, {});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Create time series data from date facets
|
|
99
|
+
* @param {Object[]} dateFacets - Array of date facets
|
|
100
|
+
* @returns {Object[]} Sorted time series data
|
|
101
|
+
*/
|
|
102
|
+
export function createTimeSeriesData(dateFacets) {
|
|
103
|
+
return dateFacets
|
|
104
|
+
.map((facet) => ({
|
|
105
|
+
date: facet.value,
|
|
106
|
+
...extractMetrics(facet),
|
|
107
|
+
}))
|
|
108
|
+
.sort((a, b) => new Date(a.date) - new Date(b.date));
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Calculate metrics from a DataChunks instance
|
|
113
|
+
* @param {DataChunks} chunk - DataChunks instance
|
|
114
|
+
* @returns {Object} Calculated metrics
|
|
115
|
+
*/
|
|
116
|
+
export function calculateMetrics(chunk) {
|
|
117
|
+
const {
|
|
118
|
+
totals,
|
|
119
|
+
} = chunk;
|
|
120
|
+
return {
|
|
121
|
+
pageViews: { total: totals.pageViews?.sum || 0 },
|
|
122
|
+
visits: { total: totals.visits?.sum || 0 },
|
|
123
|
+
organicTraffic: { total: totals.organic?.sum || 0 },
|
|
124
|
+
bounces: {
|
|
125
|
+
total: totals.bounces?.sum || 0,
|
|
126
|
+
rate: computeConversionRate(totals.bounces?.sum || 0, totals.visits?.sum || 0) || 0,
|
|
127
|
+
},
|
|
128
|
+
engagement: {
|
|
129
|
+
total: totals.engagement?.sum || 0,
|
|
130
|
+
rate: computeConversionRate(totals.conversions?.sum || 0, totals.engagement?.sum || 0) || 0,
|
|
131
|
+
},
|
|
132
|
+
conversions: {
|
|
133
|
+
total: totals.conversions?.sum || 0,
|
|
134
|
+
rate: computeConversionRate(totals.conversions?.sum || 0, totals.pageViews?.sum || 0) || 0,
|
|
135
|
+
},
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Calculate totals from time series data
|
|
141
|
+
* @param {Object[]} timeSeriesData - Array of time series data points
|
|
142
|
+
* @returns {Object} Totals object
|
|
143
|
+
*/
|
|
144
|
+
export function calculateTotals(timeSeriesData) {
|
|
145
|
+
return timeSeriesData.reduce((acc, data) => {
|
|
146
|
+
METRIC_NAMES.forEach((metric) => {
|
|
147
|
+
acc[metric] += (data[metric] || 0);
|
|
148
|
+
});
|
|
149
|
+
return acc;
|
|
150
|
+
}, METRIC_NAMES.reduce((acc, metric) => {
|
|
151
|
+
acc[metric] = 0;
|
|
152
|
+
return acc;
|
|
153
|
+
}, {}));
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Filter bundles based on the outlierUrls and urls
|
|
158
|
+
* @param {Object[]} bundles - Array of RUM bundles
|
|
159
|
+
* @param {Object} opts - Options object
|
|
160
|
+
* @param {string[]} opts.outlierUrls - URLs to exclude
|
|
161
|
+
* @param {string[]} opts.urls - URLs to include
|
|
162
|
+
* @returns {Object[]} Filtered bundles
|
|
163
|
+
*/
|
|
164
|
+
export function filterBundles(bundles, opts) {
|
|
165
|
+
// Handle null/undefined opts
|
|
166
|
+
const options = opts || {};
|
|
167
|
+
|
|
168
|
+
const {
|
|
169
|
+
outlierUrls,
|
|
170
|
+
urls,
|
|
171
|
+
} = options;
|
|
172
|
+
|
|
173
|
+
if (!bundles || !Array.isArray(bundles)) {
|
|
174
|
+
return [];
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Filter bundles by outlier URLs if provided
|
|
178
|
+
let filteredBundles = bundles;
|
|
179
|
+
if (outlierUrls && outlierUrls.length > 0) {
|
|
180
|
+
filteredBundles = filteredBundles
|
|
181
|
+
.filter((item) => item && item.url && !urlMatchesFilter(item.url, outlierUrls));
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// If urls filter is provided, keep only those URLs
|
|
185
|
+
if (urls && urls.length > 0) {
|
|
186
|
+
filteredBundles = filteredBundles
|
|
187
|
+
.filter((item) => item && item.url && urlMatchesFilter(item.url, urls));
|
|
188
|
+
}
|
|
189
|
+
return filteredBundles;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Validate date range
|
|
194
|
+
* @param {string} startTime
|
|
195
|
+
* @param {string} endTime
|
|
196
|
+
*/
|
|
197
|
+
export function validateDateRange(startTime, endTime) {
|
|
198
|
+
if (startTime && endTime && new Date(startTime) > new Date(endTime)) {
|
|
199
|
+
throw new Error('Start time must be before end time');
|
|
200
|
+
}
|
|
201
|
+
}
|
package/src/index.js
CHANGED
|
@@ -25,6 +25,8 @@ import rageclick from './functions/opportunities/rageclick.js';
|
|
|
25
25
|
import highInorganicHighBounceRate from './functions/opportunities/high-inorganic-high-bounce-rate.js';
|
|
26
26
|
import highOrganicLowCtr from './functions/opportunities/high-organic-low-ctr.js';
|
|
27
27
|
import trafficAnalysis from './functions/traffic-analysis.js';
|
|
28
|
+
import optimizationReportMetrics from './functions/reports/optimization/metrics.js';
|
|
29
|
+
import optimizationReportGraph from './functions/reports/optimization/graph.js';
|
|
28
30
|
|
|
29
31
|
// exported for tests
|
|
30
32
|
export const RUM_BUNDLER_API_HOST = 'https://bundles.aem.page';
|
|
@@ -44,6 +46,8 @@ const HANDLERS = {
|
|
|
44
46
|
pageviews,
|
|
45
47
|
trafficMetrics,
|
|
46
48
|
'traffic-analysis': trafficAnalysis,
|
|
49
|
+
'optimization-report-metrics': optimizationReportMetrics,
|
|
50
|
+
'optimization-report-graph': optimizationReportGraph,
|
|
47
51
|
};
|
|
48
52
|
|
|
49
53
|
function sanitize(opts) {
|