@adobe/spacecat-shared-utils 1.28.3 → 1.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-utils-v1.29.0](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-utils-v1.28.4...@adobe/spacecat-shared-utils-v1.29.0) (2025-02-03)
2
+
3
+
4
+ ### Features
5
+
6
+ * add utility api for forms metrics ([#566](https://github.com/adobe/spacecat-shared/issues/566)) ([a7365b6](https://github.com/adobe/spacecat-shared/commit/a7365b6eaabf6178a7febd2f25a4083e60d0156b))
7
+
8
+ # [@adobe/spacecat-shared-utils-v1.28.4](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-utils-v1.28.3...@adobe/spacecat-shared-utils-v1.28.4) (2025-02-03)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * use Mozilla user agent ([7d95872](https://github.com/adobe/spacecat-shared/commit/7d95872a7e9c1a5ec6b0413ccca4b1d901dfdad4))
14
+
1
15
  # [@adobe/spacecat-shared-utils-v1.28.3](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-utils-v1.28.2...@adobe/spacecat-shared-utils-v1.28.3) (2025-02-01)
2
16
 
3
17
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adobe/spacecat-shared-utils",
3
- "version": "1.28.3",
3
+ "version": "1.29.0",
4
4
  "description": "Shared modules of the Spacecat Services - utils",
5
5
  "type": "module",
6
6
  "engines": {
@@ -0,0 +1,98 @@
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
+ const DAILY_PAGEVIEW_THRESHOLD = 200;
14
+ const CR_THRESHOLD_RATIO = 0.2;
15
+ const MOBILE = 'mobile';
16
+ const DESKTOP = 'desktop';
17
+
18
+ /**
19
+ * Aggregates the form vitals by device type.
20
+ *
21
+ * @param {*} formVitalsCollection - form vitals collection
22
+ * @returns {object} - aggregated form vitals by device type
23
+ */
24
+ function aggregateFormVitalsByDevice(formVitalsCollection) {
25
+ const resultMap = new Map();
26
+
27
+ formVitalsCollection.forEach((item) => {
28
+ const {
29
+ url, formview = {}, formengagement = {}, pageview = {}, formsubmit = {},
30
+ } = item;
31
+
32
+ const totals = {
33
+ formview: { total: 0, desktop: 0, mobile: 0 },
34
+ formengagement: { total: 0, desktop: 0, mobile: 0 },
35
+ pageview: { total: 0, desktop: 0, mobile: 0 },
36
+ formsubmit: { total: 0, desktop: 0, mobile: 0 },
37
+ };
38
+
39
+ const calculateSums = (metric, initialTarget) => {
40
+ const updatedTarget = { ...initialTarget }; // Create a new object to store the updated totals
41
+ Object.entries(metric).forEach(([key, value]) => {
42
+ updatedTarget.total += value;
43
+ if (key.startsWith(DESKTOP)) {
44
+ updatedTarget.desktop += value;
45
+ } else if (key.startsWith(MOBILE)) {
46
+ updatedTarget.mobile += value;
47
+ }
48
+ });
49
+ return updatedTarget; // Return the updated target
50
+ };
51
+
52
+ totals.formview = calculateSums(formview, totals.formview);
53
+ totals.formengagement = calculateSums(formengagement, totals.formengagement);
54
+ totals.pageview = calculateSums(pageview, totals.pageview);
55
+ totals.formsubmit = calculateSums(formsubmit, totals.formsubmit);
56
+
57
+ resultMap.set(url, totals);
58
+ });
59
+
60
+ return resultMap;
61
+ }
62
+
63
+ function hasHighPageViews(interval, pageViews) {
64
+ return pageViews > DAILY_PAGEVIEW_THRESHOLD * interval;
65
+ }
66
+
67
+ function hasLowerConversionRate(formSubmit, formViews) {
68
+ return formSubmit / formViews < CR_THRESHOLD_RATIO;
69
+ }
70
+
71
+ /**
72
+ * Returns the form urls with high form views and low conversion rate
73
+ *
74
+ * @param {*} formVitalsCollection - form vitals collection
75
+ * @returns {Array} - urls with high form views and low conversion rate
76
+ */
77
+ export function getHighFormViewsLowConversionMetrics(formVitalsCollection, interval) {
78
+ const resultMap = aggregateFormVitalsByDevice(formVitalsCollection);
79
+ const urls = [];
80
+ resultMap.forEach((metrics, url) => {
81
+ const pageViews = metrics.pageview.total;
82
+ // Default to pageViews if formViews are not available
83
+ const formViews = metrics.formview.total || pageViews;
84
+ const formEngagement = metrics.formengagement.total;
85
+ const formSubmit = metrics.formsubmit.total || formEngagement;
86
+
87
+ if (hasHighPageViews(interval, pageViews) && hasLowerConversionRate(formSubmit, formViews)) {
88
+ urls.push({
89
+ url,
90
+ pageViews,
91
+ formViews,
92
+ formEngagement,
93
+ formSubmit,
94
+ });
95
+ }
96
+ });
97
+ return urls;
98
+ }
package/src/index.d.ts CHANGED
@@ -163,7 +163,16 @@ declare function replacePlaceholders(content: string, placeholders: object): str
163
163
  * or null if an error occurs.
164
164
  */
165
165
  declare function getPrompt(placeholders: object, filename: string, log: object):
166
- Promise<string|null>;
166
+ Promise<string | null>;
167
+
168
+ /**
169
+ * Retrieves the high-form-view-low-form-conversion metrics from the provided array of form vitals.
170
+ * @param {Object[]} formVitals - An array of form vitals.
171
+ * @param {number} interval - The interval in days.
172
+ * @returns {Object[]} - An array of high-form-view-low-form-conversion metrics.
173
+ */
174
+ declare function getHighFormViewsLowConversionMetrics(formVitals: object[], interval: number):
175
+ object[];
167
176
 
168
177
  /**
169
178
  * Retrieves stored metrics from S3.
@@ -179,7 +188,7 @@ declare function getPrompt(placeholders: object, filename: string, log: object):
179
188
  * @returns {Promise<any|*[]>} - The stored metrics
180
189
  */
181
190
  export function getStoredMetrics(config: object, context: object):
182
- Promise<Array<object>>;
191
+ Promise<Array<object>>;
183
192
 
184
193
  /**
185
194
  * Stores metrics in S3.
@@ -198,8 +207,8 @@ export function getStoredMetrics(config: object, context: object):
198
207
  export function storeMetrics(content: object, config: object, context: object): Promise<string>;
199
208
 
200
209
  export function s3Wrapper(fn: (request: object, context: object) => Promise<Response>):
201
- (request: object, context: object) => Promise<Response>;
210
+ (request: object, context: object) => Promise<Response>;
202
211
 
203
- export function fetch(url: string|Request, options?: RequestOptions): Promise<Response>;
212
+ export function fetch(url: string | Request, options?: RequestOptions): Promise<Response>;
204
213
 
205
- export function tracingFetch(url: string|Request, options?: RequestOptions): Promise<Response>;
214
+ export function tracingFetch(url: string | Request, options?: RequestOptions): Promise<Response>;
package/src/index.js CHANGED
@@ -61,3 +61,4 @@ export { s3Wrapper } from './s3.js';
61
61
 
62
62
  export { fetch } from './adobe-fetch.js';
63
63
  export { tracingFetch } from './tracing-fetch.js';
64
+ export { getHighFormViewsLowConversionMetrics } from './formcalc.js';
@@ -88,7 +88,7 @@ async function composeAuditURL(url) {
88
88
  const resp = await fetch(urlWithScheme, {
89
89
  method: 'GET',
90
90
  headers: {
91
- 'User-Agent': 'curl/7.88.1',
91
+ 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36',
92
92
  },
93
93
  });
94
94
  const finalUrl = resp.url.split('://')[1];