@adobe/spacecat-shared-utils 1.112.3 → 1.112.5
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 +12 -0
- package/package.json +2 -2
- package/src/bot-blocker-detect/bot-blocker-detect.js +7 -2
- package/src/calendar-week-helper.js +6 -2
- package/src/cdn-helpers.js +2 -3
- package/src/formcalc.js +3 -1
- package/src/functions.js +27 -9
- package/src/token-grant-config.js +12 -4
- package/src/url-helpers.js +16 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
## [@adobe/spacecat-shared-utils-v1.112.5](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-utils-v1.112.4...@adobe/spacecat-shared-utils-v1.112.5) (2026-04-10)
|
|
2
|
+
|
|
3
|
+
### Bug Fixes
|
|
4
|
+
|
|
5
|
+
* byocdn-imperva shows correct config ([#1529](https://github.com/adobe/spacecat-shared/issues/1529)) ([8aaaf1d](https://github.com/adobe/spacecat-shared/commit/8aaaf1df7b5081184f9f6c3824b915f260c0d898))
|
|
6
|
+
|
|
7
|
+
## [@adobe/spacecat-shared-utils-v1.112.4](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-utils-v1.112.3...@adobe/spacecat-shared-utils-v1.112.4) (2026-04-09)
|
|
8
|
+
|
|
9
|
+
### Bug Fixes
|
|
10
|
+
|
|
11
|
+
* **utils:** detect Akamai blocking via akamai-cache-status and akamai-grn headers ([#1524](https://github.com/adobe/spacecat-shared/issues/1524)) ([85f93a2](https://github.com/adobe/spacecat-shared/commit/85f93a276be0668a75fa3058102d86e537898a79)), closes [#1523](https://github.com/adobe/spacecat-shared/issues/1523)
|
|
12
|
+
|
|
1
13
|
## [@adobe/spacecat-shared-utils-v1.112.3](https://github.com/adobe/spacecat-shared/compare/@adobe/spacecat-shared-utils-v1.112.2...@adobe/spacecat-shared-utils-v1.112.3) (2026-04-08)
|
|
2
14
|
|
|
3
15
|
### Bug Fixes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adobe/spacecat-shared-utils",
|
|
3
|
-
"version": "1.112.
|
|
3
|
+
"version": "1.112.5",
|
|
4
4
|
"description": "Shared modules of the Spacecat Services - utils",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -83,7 +83,7 @@
|
|
|
83
83
|
"sinon-chai": "4.0.1"
|
|
84
84
|
},
|
|
85
85
|
"dependencies": {
|
|
86
|
-
"@adobe/fetch": "4.
|
|
86
|
+
"@adobe/fetch": "4.3.0",
|
|
87
87
|
"@aws-sdk/client-s3": "3.1024.0",
|
|
88
88
|
"@aws-sdk/client-sqs": "3.1024.0",
|
|
89
89
|
"@json2csv/plainjs": "7.0.6",
|
|
@@ -91,6 +91,7 @@ const CHALLENGE_PATTERNS = {
|
|
|
91
91
|
akamai: [
|
|
92
92
|
/Access Denied.*Akamai/i,
|
|
93
93
|
/Reference.*Akamai/i,
|
|
94
|
+
/errors\.(edgesuite|edgekey)\.net/i,
|
|
94
95
|
],
|
|
95
96
|
general: [
|
|
96
97
|
/captcha/i,
|
|
@@ -124,7 +125,9 @@ function analyzeResponse(response, html = null) {
|
|
|
124
125
|
const hasImperva = () => headers.get('x-iinfo') || headers.get('x-cdn') === 'Incapsula';
|
|
125
126
|
const hasAkamai = () => headers.get('x-akamai-request-id')
|
|
126
127
|
|| headers.get('x-akamai-session-id')
|
|
127
|
-
|| headers.get('server')?.includes('AkamaiGHost')
|
|
128
|
+
|| headers.get('server')?.includes('AkamaiGHost')
|
|
129
|
+
|| headers.get('akamai-cache-status')
|
|
130
|
+
|| headers.get('akamai-grn');
|
|
128
131
|
const hasFastly = () => headers.get('x-served-by')?.startsWith('cache-')
|
|
129
132
|
|| headers.get('fastly-io-info');
|
|
130
133
|
const hasCloudFront = () => headers.get('x-amz-cf-id')
|
|
@@ -133,7 +136,9 @@ function analyzeResponse(response, html = null) {
|
|
|
133
136
|
|
|
134
137
|
// Check HTML content for challenge page patterns (if HTML provided)
|
|
135
138
|
const htmlHasChallenge = (patterns) => {
|
|
136
|
-
if (!html)
|
|
139
|
+
if (!html) {
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
137
142
|
return patterns.some((pattern) => pattern.test(html));
|
|
138
143
|
};
|
|
139
144
|
|
|
@@ -40,8 +40,12 @@ function has53CalendarWeeks(year) {
|
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
function isValidWeek(week, year) {
|
|
43
|
-
if (!Number.isInteger(year) || year < 100 || !Number.isInteger(week) || week < 1)
|
|
44
|
-
|
|
43
|
+
if (!Number.isInteger(year) || year < 100 || !Number.isInteger(week) || week < 1) {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
if (week === 53) {
|
|
47
|
+
return has53CalendarWeeks(year);
|
|
48
|
+
}
|
|
45
49
|
return week <= 52;
|
|
46
50
|
}
|
|
47
51
|
|
package/src/cdn-helpers.js
CHANGED
|
@@ -189,13 +189,12 @@ const CDN_TRANSFORMATIONS = {
|
|
|
189
189
|
'byocdn-imperva': (payload) => ({
|
|
190
190
|
'Log integration mode': 'Push mode',
|
|
191
191
|
'Delivery method': 'Amazon S3 ARN',
|
|
192
|
-
'Bucket Name': payload.bucketName,
|
|
193
192
|
Region: payload.region,
|
|
194
|
-
Path: payload.allowedPaths?.[0] || '',
|
|
193
|
+
Path: `${payload.bucketName}/${payload.allowedPaths?.[0] || ''}`.replace(/\/$/, ''),
|
|
195
194
|
'Log types': 'Cloud WAF',
|
|
196
195
|
'Log level': 'Access logs',
|
|
197
196
|
Format: 'W3C',
|
|
198
|
-
'Compress logs': '
|
|
197
|
+
'Compress logs': 'No',
|
|
199
198
|
HelpUrl: 'https://docs-cybersec.thalesgroup.com/bundle/cloud-application-security/page/siem-log-configuration.htm',
|
|
200
199
|
}),
|
|
201
200
|
'byocdn-other': (payload) => ({
|
package/src/formcalc.js
CHANGED
|
@@ -157,7 +157,9 @@ export function getHighPageViewsLowFormCtrMetrics(formVitalsCollection) {
|
|
|
157
157
|
}
|
|
158
158
|
|
|
159
159
|
// Skip entry if no valid maxPageviewUrl is found
|
|
160
|
-
if (!maxPageviewUrl)
|
|
160
|
+
if (!maxPageviewUrl) {
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
161
163
|
|
|
162
164
|
// Calculate `y`: find the CTA with the highest clicks and include the source
|
|
163
165
|
const y = maxPageviewUrl.CTAs.reduce((maxCta, cta) => {
|
package/src/functions.js
CHANGED
|
@@ -94,30 +94,48 @@ function isNonEmptyObject(value) {
|
|
|
94
94
|
* @return {boolean} True if the objects or arrays are equal, false otherwise.
|
|
95
95
|
*/
|
|
96
96
|
function deepEqual(x, y) {
|
|
97
|
-
if (x === y)
|
|
97
|
+
if (x === y) {
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
98
100
|
|
|
99
101
|
if (isArray(x) && isArray(y)) {
|
|
100
|
-
if (x.length !== y.length)
|
|
102
|
+
if (x.length !== y.length) {
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
101
105
|
for (let i = 0; i < x.length; i += 1) {
|
|
102
|
-
if (!deepEqual(x[i], y[i]))
|
|
106
|
+
if (!deepEqual(x[i], y[i])) {
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
103
109
|
}
|
|
104
110
|
return true;
|
|
105
111
|
}
|
|
106
112
|
|
|
107
|
-
if (!isObject(x) || !isObject(y))
|
|
113
|
+
if (!isObject(x) || !isObject(y)) {
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
108
116
|
|
|
109
|
-
if (x.constructor !== y.constructor)
|
|
117
|
+
if (x.constructor !== y.constructor) {
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
110
120
|
|
|
111
|
-
if (x instanceof Date)
|
|
112
|
-
|
|
121
|
+
if (x instanceof Date) {
|
|
122
|
+
return x.getTime() === y.getTime();
|
|
123
|
+
}
|
|
124
|
+
if (x instanceof RegExp) {
|
|
125
|
+
return x.toString() === y.toString();
|
|
126
|
+
}
|
|
113
127
|
|
|
114
128
|
const xKeys = Object.keys(x).filter((key) => typeof x[key] !== 'function');
|
|
115
129
|
const yKeys = Object.keys(y).filter((key) => typeof y[key] !== 'function');
|
|
116
130
|
|
|
117
|
-
if (xKeys.length !== yKeys.length)
|
|
131
|
+
if (xKeys.length !== yKeys.length) {
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
118
134
|
|
|
119
135
|
for (const key of xKeys) {
|
|
120
|
-
if (!Object.prototype.hasOwnProperty.call(y, key) || !deepEqual(x[key], y[key]))
|
|
136
|
+
if (!Object.prototype.hasOwnProperty.call(y, key) || !deepEqual(x[key], y[key])) {
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
121
139
|
}
|
|
122
140
|
|
|
123
141
|
return true;
|
|
@@ -90,9 +90,13 @@ export function getCurrentCycle(cycleFormat) {
|
|
|
90
90
|
* cycleFormat: string, currentCycle: string }|undefined}
|
|
91
91
|
*/
|
|
92
92
|
export function getTokenGrantConfig(tokenType) {
|
|
93
|
-
if (!hasText(tokenType))
|
|
93
|
+
if (!hasText(tokenType)) {
|
|
94
|
+
return undefined;
|
|
95
|
+
}
|
|
94
96
|
const entry = TOKEN_GRANT_CONFIG[tokenType];
|
|
95
|
-
if (!entry)
|
|
97
|
+
if (!entry) {
|
|
98
|
+
return undefined;
|
|
99
|
+
}
|
|
96
100
|
return { ...entry, currentCycle: getCurrentCycle(entry.cycleFormat) };
|
|
97
101
|
}
|
|
98
102
|
|
|
@@ -105,9 +109,13 @@ export function getTokenGrantConfig(tokenType) {
|
|
|
105
109
|
* tokenType: string }|undefined}
|
|
106
110
|
*/
|
|
107
111
|
export function getTokenGrantConfigByOpportunity(opportunityName) {
|
|
108
|
-
if (!hasText(opportunityName))
|
|
112
|
+
if (!hasText(opportunityName)) {
|
|
113
|
+
return undefined;
|
|
114
|
+
}
|
|
109
115
|
const tokenType = getTokenTypeForOpportunity(opportunityName);
|
|
110
116
|
const config = getTokenGrantConfig(tokenType);
|
|
111
|
-
if (!config)
|
|
117
|
+
if (!config) {
|
|
118
|
+
return undefined;
|
|
119
|
+
}
|
|
112
120
|
return { ...config, tokenType };
|
|
113
121
|
}
|
package/src/url-helpers.js
CHANGED
|
@@ -184,7 +184,9 @@ async function resolveCanonicalUrl(urlString, method = 'HEAD') {
|
|
|
184
184
|
* @returns {string} The normalized URL
|
|
185
185
|
*/
|
|
186
186
|
function normalizeUrl(url) {
|
|
187
|
-
if (!url || typeof url !== 'string')
|
|
187
|
+
if (!url || typeof url !== 'string') {
|
|
188
|
+
return url;
|
|
189
|
+
}
|
|
188
190
|
// Trim whitespace from beginning and end
|
|
189
191
|
let normalized = url.trim();
|
|
190
192
|
// Handle trailing slashes - normalize multiple trailing slashes to single slash
|
|
@@ -207,8 +209,13 @@ function normalizeUrl(url) {
|
|
|
207
209
|
* @returns {string} The normalized pathname
|
|
208
210
|
*/
|
|
209
211
|
function normalizePathname(pathname) {
|
|
210
|
-
|
|
211
|
-
if (pathname
|
|
212
|
+
/* c8 ignore next 3 */
|
|
213
|
+
if (!pathname || typeof pathname !== 'string') {
|
|
214
|
+
return pathname;
|
|
215
|
+
}
|
|
216
|
+
if (pathname === '/') {
|
|
217
|
+
return '/';
|
|
218
|
+
}
|
|
212
219
|
return pathname.replace(/\/+$/, '');
|
|
213
220
|
}
|
|
214
221
|
|
|
@@ -219,7 +226,9 @@ function normalizePathname(pathname) {
|
|
|
219
226
|
* @returns {boolean} True if URL matches any filter URL, false if any URL is invalid
|
|
220
227
|
*/
|
|
221
228
|
function urlMatchesFilter(url, filterUrls) {
|
|
222
|
-
if (!filterUrls || filterUrls.length === 0)
|
|
229
|
+
if (!filterUrls || filterUrls.length === 0) {
|
|
230
|
+
return true;
|
|
231
|
+
}
|
|
223
232
|
try {
|
|
224
233
|
// Normalize the input URL
|
|
225
234
|
const normalizedInputUrl = normalizeUrl(url);
|
|
@@ -268,7 +277,9 @@ function hasNonWWWSubdomain(baseUrl) {
|
|
|
268
277
|
* @returns {string} - The hostname with the www subdomain toggled.
|
|
269
278
|
*/
|
|
270
279
|
function toggleWWWHostname(hostname) {
|
|
271
|
-
if (hasNonWWWSubdomain(`https://${hostname}`))
|
|
280
|
+
if (hasNonWWWSubdomain(`https://${hostname}`)) {
|
|
281
|
+
return hostname;
|
|
282
|
+
}
|
|
272
283
|
return hostname.startsWith('www.') ? hostname.replace('www.', '') : `www.${hostname}`;
|
|
273
284
|
}
|
|
274
285
|
|