@adobe/mysticat-shared-seo-client 1.1.1 → 1.1.3
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 +3 -3
- package/src/client.js +5 -0
- package/src/utils.js +15 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
## [@adobe/mysticat-shared-seo-client-v1.1.3](https://github.com/adobe/spacecat-shared/compare/@adobe/mysticat-shared-seo-client-v1.1.2...@adobe/mysticat-shared-seo-client-v1.1.3) (2026-04-09)
|
|
2
|
+
|
|
3
|
+
### Bug Fixes
|
|
4
|
+
|
|
5
|
+
* **deps:** update adobe fixes ([#1503](https://github.com/adobe/spacecat-shared/issues/1503)) ([21d4d4b](https://github.com/adobe/spacecat-shared/commit/21d4d4b3cfa95ca7748ef4b65b04cace24768dfc))
|
|
6
|
+
|
|
7
|
+
## [@adobe/mysticat-shared-seo-client-v1.1.2](https://github.com/adobe/spacecat-shared/compare/@adobe/mysticat-shared-seo-client-v1.1.1...@adobe/mysticat-shared-seo-client-v1.1.2) (2026-04-06)
|
|
8
|
+
|
|
9
|
+
### Bug Fixes
|
|
10
|
+
|
|
11
|
+
* **launchdarkly-client:** route SDK logs through Lambda logger at debug level ([#1511](https://github.com/adobe/spacecat-shared/issues/1511)) ([9efd5a9](https://github.com/adobe/spacecat-shared/commit/9efd5a95db96ac9f28db723e070be7ffa8d09546))
|
|
12
|
+
|
|
1
13
|
## [@adobe/mysticat-shared-seo-client-v1.1.1](https://github.com/adobe/spacecat-shared/compare/@adobe/mysticat-shared-seo-client-v1.1.0...@adobe/mysticat-shared-seo-client-v1.1.1) (2026-04-06)
|
|
2
14
|
|
|
3
15
|
### Bug Fixes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adobe/mysticat-shared-seo-client",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.3",
|
|
4
4
|
"description": "Shared modules of the SpaceCat Services - SEO Client",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -34,8 +34,8 @@
|
|
|
34
34
|
"access": "public"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@adobe/fetch": "4.
|
|
38
|
-
"@adobe/helix-universal": "5.4.
|
|
37
|
+
"@adobe/fetch": "4.3.0",
|
|
38
|
+
"@adobe/helix-universal": "5.4.1",
|
|
39
39
|
"@adobe/spacecat-shared-utils": "1.81.1",
|
|
40
40
|
"urijs": "1.19.11"
|
|
41
41
|
},
|
package/src/client.js
CHANGED
|
@@ -99,6 +99,7 @@ export default class SeoClient {
|
|
|
99
99
|
// SEO API returns HTTP 200 with "ERROR XX :: message" body on errors
|
|
100
100
|
if (body.startsWith('ERROR')) {
|
|
101
101
|
const isRateLimit = body.includes('LIMIT EXCEEDED');
|
|
102
|
+
const isNoData = body.includes('NOTHING FOUND');
|
|
102
103
|
if (isRateLimit && attempt < MAX_RETRIES) {
|
|
103
104
|
// Exponential backoff: 1s, 2s, 4s, 8s + random jitter 0-500ms
|
|
104
105
|
const retryDelay = (RATE_LIMIT_BASE_DELAY_MS * (2 ** attempt))
|
|
@@ -109,6 +110,10 @@ export default class SeoClient {
|
|
|
109
110
|
// eslint-disable-next-line no-continue
|
|
110
111
|
continue;
|
|
111
112
|
}
|
|
113
|
+
if (isNoData) {
|
|
114
|
+
this.log.info(`SEO API returned no data for domain=${queryParams.domain || queryParams.target || '-'} type=${queryParams.type || 'unknown'}`);
|
|
115
|
+
return { body: '', fullAuditRef };
|
|
116
|
+
}
|
|
112
117
|
const prefix = isRateLimit && attempt >= MAX_RETRIES
|
|
113
118
|
? `SEO API request failed after ${MAX_RETRIES} retries: `
|
|
114
119
|
: 'SEO API request failed: ';
|
package/src/utils.js
CHANGED
|
@@ -101,9 +101,13 @@ function splitCsvLine(line) {
|
|
|
101
101
|
* @returns {object[]} Array of row objects keyed by column codes
|
|
102
102
|
*/
|
|
103
103
|
export function parseCsvResponse(text) {
|
|
104
|
-
if (!text || typeof text !== 'string')
|
|
104
|
+
if (!text || typeof text !== 'string') {
|
|
105
|
+
return [];
|
|
106
|
+
}
|
|
105
107
|
const lines = text.trim().split('\n');
|
|
106
|
-
if (lines.length < 2)
|
|
108
|
+
if (lines.length < 2) {
|
|
109
|
+
return [];
|
|
110
|
+
}
|
|
107
111
|
const headers = splitCsvLine(lines[0]).map((h) => normalizeHeader(h.trim()));
|
|
108
112
|
return lines.slice(1).map((line) => {
|
|
109
113
|
const values = splitCsvLine(line);
|
|
@@ -120,7 +124,9 @@ export function parseCsvResponse(text) {
|
|
|
120
124
|
* @returns {*} Coerced value
|
|
121
125
|
*/
|
|
122
126
|
export function coerceValue(value, type) {
|
|
123
|
-
if (value === '' || value === undefined || value === null)
|
|
127
|
+
if (value === '' || value === undefined || value === null) {
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
124
130
|
switch (type) {
|
|
125
131
|
case 'int': {
|
|
126
132
|
const parsed = parseInt(value, 10);
|
|
@@ -169,7 +175,9 @@ export function todayISO() {
|
|
|
169
175
|
* @returns {string|null} Date in YYYY-MM-DD format, or null if invalid
|
|
170
176
|
*/
|
|
171
177
|
export function fromApiDate(apiDate) {
|
|
172
|
-
if (!apiDate || apiDate.length < 8 || !/^\d{8}$/.test(apiDate))
|
|
178
|
+
if (!apiDate || apiDate.length < 8 || !/^\d{8}$/.test(apiDate)) {
|
|
179
|
+
return null;
|
|
180
|
+
}
|
|
173
181
|
return `${apiDate.slice(0, 4)}-${apiDate.slice(4, 6)}-${apiDate.slice(6, 8)}`;
|
|
174
182
|
}
|
|
175
183
|
|
|
@@ -205,7 +213,9 @@ export function buildFilter(filters) {
|
|
|
205
213
|
* @returns {string} Lowercase brand name
|
|
206
214
|
*/
|
|
207
215
|
export function extractBrand(domain) {
|
|
208
|
-
if (!domain)
|
|
216
|
+
if (!domain) {
|
|
217
|
+
return '';
|
|
218
|
+
}
|
|
209
219
|
const normalized = domain.includes('://') ? domain : `https://${domain}`;
|
|
210
220
|
const registrable = new URI(normalized).domain();
|
|
211
221
|
return registrable.split('.')[0].toLowerCase();
|