@adobe/spacecat-shared-utils 1.59.4 → 1.61.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/package.json +1 -1
- package/src/cdn-helpers.js +4 -0
- package/src/constants.js +71 -0
- package/src/index.d.ts +22 -0
- package/src/index.js +5 -0
- package/src/url-extractors.js +183 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
# [@adobe/spacecat-shared-utils-v1.61.0](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-utils-v1.60.0...@adobe/spacecat-shared-utils-v1.61.0) (2025-10-23)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* SITES-36623 added url extraction for opportunities and suggestions ([#1038](https://github.com/adobe/spacecat-shared/issues/1038)) ([1e16802](https://github.com/adobe/spacecat-shared/commit/1e16802b8189f64cc71c9ecc8d0c100c350408f7))
|
|
7
|
+
|
|
8
|
+
# [@adobe/spacecat-shared-utils-v1.60.0](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-utils-v1.59.4...@adobe/spacecat-shared-utils-v1.60.0) (2025-10-21)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* add log format json ([#1035](https://github.com/adobe/spacecat-shared/issues/1035)) ([a5f82db](https://github.com/adobe/spacecat-shared/commit/a5f82dbfec42af783345058f63d48e1c2e8093c7))
|
|
14
|
+
|
|
1
15
|
# [@adobe/spacecat-shared-utils-v1.59.4](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-utils-v1.59.3...@adobe/spacecat-shared-utils-v1.59.4) (2025-10-10)
|
|
2
16
|
|
|
3
17
|
|
package/package.json
CHANGED
package/src/cdn-helpers.js
CHANGED
|
@@ -63,6 +63,7 @@ const CDN_TRANSFORMATIONS = {
|
|
|
63
63
|
'rspContentType',
|
|
64
64
|
'timeToFirstByte',
|
|
65
65
|
],
|
|
66
|
+
'Log format': 'JSON',
|
|
66
67
|
'Log file prefix': '{%Y}-{%m}-{%d}T{%H}:{%M}:{%S}.000',
|
|
67
68
|
'Log file suffix': '.log',
|
|
68
69
|
'Log interval': '60 seconds',
|
|
@@ -89,6 +90,7 @@ const CDN_TRANSFORMATIONS = {
|
|
|
89
90
|
'EdgeResponseContentType',
|
|
90
91
|
'EdgeTimeToFirstByteMs',
|
|
91
92
|
],
|
|
93
|
+
'Log format': 'JSON',
|
|
92
94
|
'Ownership token': 'Please reach out to Adobe support for obtaining the token once you completed the configuration.',
|
|
93
95
|
HelpUrl: 'https://developers.cloudflare.com/logs/logpush/logpush-job/enable-destinations/aws-s3/',
|
|
94
96
|
}),
|
|
@@ -112,6 +114,7 @@ const CDN_TRANSFORMATIONS = {
|
|
|
112
114
|
'time-to-first-byte',
|
|
113
115
|
'sc-content-type',
|
|
114
116
|
],
|
|
117
|
+
'Output file format': 'JSON',
|
|
115
118
|
HelpUrl: 'https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/standard-logging.html#enable-standard-logging-cross-accounts',
|
|
116
119
|
}),
|
|
117
120
|
'ams-cloudfront': (payload) => ({
|
|
@@ -121,6 +124,7 @@ const CDN_TRANSFORMATIONS = {
|
|
|
121
124
|
'Delivery Destination Name': payload.deliveryDestinationName,
|
|
122
125
|
'Destination AWS Account ID': '640168421876',
|
|
123
126
|
'Path suffix': '/{yyyy}/{MM}/{dd}/{HH}',
|
|
127
|
+
'Output file format': 'JSON',
|
|
124
128
|
'Logged Properties': [
|
|
125
129
|
'date',
|
|
126
130
|
'time',
|
package/src/constants.js
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
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 OPPORTUNITY_TYPES = {
|
|
14
|
+
// Core Audit Types
|
|
15
|
+
ACCESSIBILITY: 'accessibility',
|
|
16
|
+
ALT_TEXT: 'alt-text',
|
|
17
|
+
BROKEN_BACKLINKS: 'broken-backlinks',
|
|
18
|
+
BROKEN_INTERNAL_LINKS: 'broken-internal-links',
|
|
19
|
+
CANONICAL: 'canonical',
|
|
20
|
+
CWV: 'cwv',
|
|
21
|
+
HEADINGS: 'headings',
|
|
22
|
+
HREFLANG: 'hreflang',
|
|
23
|
+
INVALID_OR_MISSING_METADATA: 'meta-tags',
|
|
24
|
+
NOTFOUND: '404',
|
|
25
|
+
PRERENDER: 'prerender',
|
|
26
|
+
SECURITY_CSP: 'security-csp',
|
|
27
|
+
SECURITY_VULNERABILITIES: 'security-vulnerabilities',
|
|
28
|
+
SITEMAP: 'sitemap',
|
|
29
|
+
STRUCTURED_DATA: 'structured-data',
|
|
30
|
+
|
|
31
|
+
// Custom Audit Types (not in shared AUDIT_TYPES)
|
|
32
|
+
LLM_BLOCKED: 'llm-blocked',
|
|
33
|
+
REDIRECT_CHAINS: 'redirect-chains',
|
|
34
|
+
SECURITY_PERMISSIONS: 'security-permissions',
|
|
35
|
+
SECURITY_PERMISSIONS_REDUNDANT: 'security-permissions-redundant',
|
|
36
|
+
SITEMAP_PRODUCT_COVERAGE: 'sitemap-product-coverage',
|
|
37
|
+
|
|
38
|
+
// Experimentation Opportunities
|
|
39
|
+
HIGH_ORGANIC_LOW_CTR: 'high-organic-low-ctr',
|
|
40
|
+
RAGECLICK: 'rageclick',
|
|
41
|
+
HIGH_INORGANIC_HIGH_BOUNCE_RATE: 'high-inorganic-high-bounce-rate',
|
|
42
|
+
|
|
43
|
+
// Forms Opportunities
|
|
44
|
+
HIGH_FORM_VIEWS_LOW_CONVERSIONS: 'high-form-views-low-conversions',
|
|
45
|
+
HIGH_PAGE_VIEWS_LOW_FORM_NAV: 'high-page-views-low-form-nav',
|
|
46
|
+
HIGH_PAGE_VIEWS_LOW_FORM_VIEWS: 'high-page-views-low-form-views',
|
|
47
|
+
FORM_ACCESSIBILITY: 'form-accessibility',
|
|
48
|
+
|
|
49
|
+
// Geo Brand Presence
|
|
50
|
+
DETECT_GEO_BRAND_PRESENCE: 'detect:geo-brand-presence',
|
|
51
|
+
DETECT_GEO_BRAND_PRESENCE_DAILY: 'detect:geo-brand-presence-daily',
|
|
52
|
+
GEO_BRAND_PRESENCE_TRIGGER_REFRESH: 'geo-brand-presence-trigger-refresh',
|
|
53
|
+
GUIDANCE_GEO_FAQ: 'guidance:geo-faq',
|
|
54
|
+
|
|
55
|
+
// Accessibility Sub-types
|
|
56
|
+
A11Y_ASSISTIVE: 'a11y-assistive',
|
|
57
|
+
COLOR_CONTRAST: 'a11y-color-contrast',
|
|
58
|
+
|
|
59
|
+
// Security
|
|
60
|
+
SECURITY_XSS: 'security-xss',
|
|
61
|
+
|
|
62
|
+
// Generic Opportunity
|
|
63
|
+
GENERIC_OPPORTUNITY: 'generic-opportunity',
|
|
64
|
+
|
|
65
|
+
// Paid Cookie Consent
|
|
66
|
+
PAID_COOKIE_CONSENT: 'paid-cookie-consent',
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
export {
|
|
70
|
+
OPPORTUNITY_TYPES,
|
|
71
|
+
};
|
package/src/index.d.ts
CHANGED
|
@@ -272,6 +272,28 @@ export function isoCalendarWeekSunday(date: Date): Date;
|
|
|
272
272
|
|
|
273
273
|
export function isoCalendarWeekMonday(date: Date): Date;
|
|
274
274
|
|
|
275
|
+
/**
|
|
276
|
+
* Extracts URLs from a suggestion based on the opportunity type.
|
|
277
|
+
* @param opts - Options object
|
|
278
|
+
* @param opts.opportunity - The opportunity object
|
|
279
|
+
* @param opts.suggestion - The suggestion object
|
|
280
|
+
* @returns An array of extracted URLs
|
|
281
|
+
*/
|
|
282
|
+
export function extractUrlsFromSuggestion(opts: {
|
|
283
|
+
opportunity: any;
|
|
284
|
+
suggestion: any;
|
|
285
|
+
}): string[];
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Extracts URLs from an opportunity based on the opportunity type.
|
|
289
|
+
* @param opts - Options object
|
|
290
|
+
* @param opts.opportunity - The opportunity object
|
|
291
|
+
* @returns An array of extracted URLs
|
|
292
|
+
*/
|
|
293
|
+
export function extractUrlsFromOpportunity(opts: {
|
|
294
|
+
opportunity: any;
|
|
295
|
+
}): string[];
|
|
296
|
+
|
|
275
297
|
export * as llmoConfig from './llmo-config.js';
|
|
276
298
|
export * as schemas from './schemas.js';
|
|
277
299
|
|
package/src/index.js
CHANGED
|
@@ -68,6 +68,11 @@ export {
|
|
|
68
68
|
urlMatchesFilter,
|
|
69
69
|
} from './url-helpers.js';
|
|
70
70
|
|
|
71
|
+
export {
|
|
72
|
+
extractUrlsFromOpportunity,
|
|
73
|
+
extractUrlsFromSuggestion,
|
|
74
|
+
} from './url-extractors.js';
|
|
75
|
+
|
|
71
76
|
export { getStoredMetrics, storeMetrics } from './metrics-store.js';
|
|
72
77
|
|
|
73
78
|
export { s3Wrapper } from './s3.js';
|
|
@@ -0,0 +1,183 @@
|
|
|
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 { OPPORTUNITY_TYPES } from './constants.js';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Function to extract the URL from a suggestion based on a particular type
|
|
17
|
+
* @param {*} suggestion
|
|
18
|
+
*/
|
|
19
|
+
function extractUrlsFromSuggestion(opts) {
|
|
20
|
+
const {
|
|
21
|
+
opportunity,
|
|
22
|
+
suggestion,
|
|
23
|
+
} = opts;
|
|
24
|
+
|
|
25
|
+
const urls = [];
|
|
26
|
+
|
|
27
|
+
try {
|
|
28
|
+
const opportunityType = opportunity.getType();
|
|
29
|
+
const data = suggestion.getData ? suggestion.getData() : suggestion.data;
|
|
30
|
+
|
|
31
|
+
switch (opportunityType) {
|
|
32
|
+
case OPPORTUNITY_TYPES.ALT_TEXT:
|
|
33
|
+
{
|
|
34
|
+
const recommendations = data?.recommendations;
|
|
35
|
+
if (Array.isArray(recommendations)) {
|
|
36
|
+
recommendations.forEach((rec) => {
|
|
37
|
+
if (rec.pageUrl && typeof rec.pageUrl === 'string') {
|
|
38
|
+
urls.push(rec.pageUrl);
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
break;
|
|
44
|
+
case OPPORTUNITY_TYPES.ACCESSIBILITY:
|
|
45
|
+
case OPPORTUNITY_TYPES.COLOR_CONTRAST:
|
|
46
|
+
case OPPORTUNITY_TYPES.STRUCTURED_DATA:
|
|
47
|
+
case OPPORTUNITY_TYPES.CANONICAL:
|
|
48
|
+
case OPPORTUNITY_TYPES.HREFLANG:
|
|
49
|
+
case OPPORTUNITY_TYPES.HEADINGS:
|
|
50
|
+
case OPPORTUNITY_TYPES.INVALID_OR_MISSING_METADATA:
|
|
51
|
+
case OPPORTUNITY_TYPES.SITEMAP_PRODUCT_COVERAGE:
|
|
52
|
+
{
|
|
53
|
+
const url = data?.url;
|
|
54
|
+
if (url && typeof url === 'string') {
|
|
55
|
+
urls.push(url);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
break;
|
|
59
|
+
case OPPORTUNITY_TYPES.CWV:
|
|
60
|
+
{
|
|
61
|
+
const { type } = data;
|
|
62
|
+
const url = data?.url;
|
|
63
|
+
if (type === 'url' && url && typeof url === 'string') {
|
|
64
|
+
urls.push(url);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
break;
|
|
68
|
+
case OPPORTUNITY_TYPES.REDIRECT_CHAINS:
|
|
69
|
+
{
|
|
70
|
+
const sourceUrl = data?.sourceUrl;
|
|
71
|
+
if (sourceUrl && typeof sourceUrl === 'string') {
|
|
72
|
+
urls.push(sourceUrl);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
break;
|
|
76
|
+
case OPPORTUNITY_TYPES.SECURITY_XSS:
|
|
77
|
+
{
|
|
78
|
+
const url = data?.link;
|
|
79
|
+
if (url && typeof url === 'string') {
|
|
80
|
+
urls.push(url);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
break;
|
|
84
|
+
case OPPORTUNITY_TYPES.SECURITY_CSP:
|
|
85
|
+
{
|
|
86
|
+
const findings = data?.findings;
|
|
87
|
+
if (Array.isArray(findings)) {
|
|
88
|
+
findings.forEach((finding) => {
|
|
89
|
+
if (finding.url && typeof finding.url === 'string') {
|
|
90
|
+
urls.push(finding.url);
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
break;
|
|
96
|
+
case OPPORTUNITY_TYPES.SECURITY_PERMISSIONS:
|
|
97
|
+
{
|
|
98
|
+
const url = data?.path;
|
|
99
|
+
if (url && typeof url === 'string') {
|
|
100
|
+
urls.push(url);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
break;
|
|
104
|
+
case OPPORTUNITY_TYPES.BROKEN_BACKLINKS:
|
|
105
|
+
case OPPORTUNITY_TYPES.BROKEN_INTERNAL_LINKS:
|
|
106
|
+
{
|
|
107
|
+
const url = data?.url_to;
|
|
108
|
+
if (url && typeof url === 'string') {
|
|
109
|
+
urls.push(url);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
break;
|
|
113
|
+
case OPPORTUNITY_TYPES.SITEMAP:
|
|
114
|
+
{
|
|
115
|
+
const url = data?.pageUrl;
|
|
116
|
+
if (url && typeof url === 'string') {
|
|
117
|
+
urls.push(url);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
break;
|
|
121
|
+
default:
|
|
122
|
+
break;
|
|
123
|
+
}
|
|
124
|
+
} catch (error) {
|
|
125
|
+
// Silently handle errors and return empty array
|
|
126
|
+
}
|
|
127
|
+
return urls;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Function to extract the URL from an opportunity based on a particular type
|
|
132
|
+
* @param {*} opportunity
|
|
133
|
+
*/
|
|
134
|
+
function extractUrlsFromOpportunity(opts) {
|
|
135
|
+
const {
|
|
136
|
+
opportunity,
|
|
137
|
+
} = opts;
|
|
138
|
+
const urls = [];
|
|
139
|
+
|
|
140
|
+
try {
|
|
141
|
+
const opportunityType = opportunity.getType();
|
|
142
|
+
const data = opportunity.getData ? opportunity.getData() : opportunity.data;
|
|
143
|
+
|
|
144
|
+
switch (opportunityType) {
|
|
145
|
+
case OPPORTUNITY_TYPES.HIGH_ORGANIC_LOW_CTR:
|
|
146
|
+
{
|
|
147
|
+
const url = data?.page;
|
|
148
|
+
if (url && typeof url === 'string') {
|
|
149
|
+
urls.push(url);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
break;
|
|
153
|
+
case OPPORTUNITY_TYPES.HIGH_FORM_VIEWS_LOW_CONVERSIONS:
|
|
154
|
+
case OPPORTUNITY_TYPES.HIGH_PAGE_VIEWS_LOW_FORM_NAV:
|
|
155
|
+
case OPPORTUNITY_TYPES.HIGH_PAGE_VIEWS_LOW_FORM_VIEWS:
|
|
156
|
+
{
|
|
157
|
+
const url = data?.form;
|
|
158
|
+
if (url && typeof url === 'string') {
|
|
159
|
+
urls.push(url);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
break;
|
|
163
|
+
case OPPORTUNITY_TYPES.FORM_ACCESSIBILITY:
|
|
164
|
+
{
|
|
165
|
+
const url = data?.url;
|
|
166
|
+
if (url && typeof url === 'string') {
|
|
167
|
+
urls.push(url);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
break;
|
|
171
|
+
default:
|
|
172
|
+
break;
|
|
173
|
+
}
|
|
174
|
+
} catch (error) {
|
|
175
|
+
// Silently handle errors and return empty array
|
|
176
|
+
}
|
|
177
|
+
return urls;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export {
|
|
181
|
+
extractUrlsFromSuggestion,
|
|
182
|
+
extractUrlsFromOpportunity,
|
|
183
|
+
};
|