@adobe/mysticat-shared-seo-client 1.5.0 → 1.6.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 +13 -0
- package/package.json +3 -3
- package/src/client.js +41 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,16 @@
|
|
|
1
|
+
## [@adobe/mysticat-shared-seo-client-v1.6.0](https://github.com/adobe/spacecat-shared/compare/@adobe/mysticat-shared-seo-client-v1.5.1...@adobe/mysticat-shared-seo-client-v1.6.0) (2026-06-08)
|
|
2
|
+
|
|
3
|
+
### Features
|
|
4
|
+
|
|
5
|
+
* **seo-client:** add quality filters to broken-backlinks Semrush call (404+410, follow, text, no lostlinks) ([#1652](https://github.com/adobe/spacecat-shared/issues/1652)) ([875d92c](https://github.com/adobe/spacecat-shared/commit/875d92c4ceb306b5010118e29287bc81906e980e))
|
|
6
|
+
|
|
7
|
+
## [@adobe/mysticat-shared-seo-client-v1.5.1](https://github.com/adobe/spacecat-shared/compare/@adobe/mysticat-shared-seo-client-v1.5.0...@adobe/mysticat-shared-seo-client-v1.5.1) (2026-05-29)
|
|
8
|
+
|
|
9
|
+
### Bug Fixes
|
|
10
|
+
|
|
11
|
+
* **deps:** update adobe fixes ([#1609](https://github.com/adobe/spacecat-shared/issues/1609)) ([ccdcc2b](https://github.com/adobe/spacecat-shared/commit/ccdcc2b88c1b03e5c58ed682daaaadb382638395))
|
|
12
|
+
* **deps:** update external major (major) ([#1611](https://github.com/adobe/spacecat-shared/issues/1611)) ([5a80797](https://github.com/adobe/spacecat-shared/commit/5a807979187f1ebcc32b088faee9165aa38cc461))
|
|
13
|
+
|
|
1
14
|
## [@adobe/mysticat-shared-seo-client-v1.5.0](https://github.com/adobe/spacecat-shared/compare/@adobe/mysticat-shared-seo-client-v1.4.0...@adobe/mysticat-shared-seo-client-v1.5.0) (2026-05-27)
|
|
2
15
|
|
|
3
16
|
### Features
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adobe/mysticat-shared-seo-client",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.0",
|
|
4
4
|
"description": "Shared modules of the SpaceCat Services - SEO Client",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
37
|
"@adobe/fetch": "4.3.0",
|
|
38
|
-
"@adobe/helix-universal": "5.4.
|
|
38
|
+
"@adobe/helix-universal": "5.4.2",
|
|
39
39
|
"@adobe/spacecat-shared-utils": "1.81.1",
|
|
40
40
|
"urijs": "1.19.11"
|
|
41
41
|
},
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"chai": "6.2.2",
|
|
44
44
|
"chai-as-promised": "8.0.2",
|
|
45
45
|
"nock": "14.0.15",
|
|
46
|
-
"sinon": "
|
|
46
|
+
"sinon": "22.0.0",
|
|
47
47
|
"sinon-chai": "4.0.1",
|
|
48
48
|
"typescript": "6.0.3"
|
|
49
49
|
}
|
package/src/client.js
CHANGED
|
@@ -744,25 +744,49 @@ export default class SeoClient {
|
|
|
744
744
|
const epLinks = ENDPOINTS.brokenBacklinks;
|
|
745
745
|
const effectiveLimit = getLimit(limit, 100);
|
|
746
746
|
|
|
747
|
-
// Step 1: Find broken
|
|
748
|
-
|
|
747
|
+
// Step 1: Find broken target pages — 404 and 410 in parallel (Semrush does not support OR
|
|
748
|
+
// within a single display_filter, so we issue two requests and merge the results).
|
|
749
|
+
const pageParams = (code) => ({
|
|
749
750
|
type: epPages.type,
|
|
750
751
|
target: url,
|
|
751
752
|
target_type: 'domain',
|
|
752
753
|
export_columns: epPages.columns,
|
|
753
754
|
display_limit: effectiveLimit,
|
|
754
755
|
display_filter: buildFilter([{
|
|
755
|
-
sign: '+', field: 'responsecode', op: 'Eq', value:
|
|
756
|
+
sign: '+', field: 'responsecode', op: 'Eq', value: code,
|
|
756
757
|
}]),
|
|
757
758
|
...epPages.defaultParams,
|
|
758
|
-
}
|
|
759
|
-
|
|
759
|
+
});
|
|
760
|
+
|
|
761
|
+
const [{ body: pages404Body, fullAuditRef }, { body: pages410Body }] = await Promise.all([
|
|
762
|
+
this.sendRawRequest(pageParams('404'), epPages.path),
|
|
763
|
+
this.sendRawRequest(pageParams('410'), epPages.path),
|
|
764
|
+
]);
|
|
765
|
+
|
|
766
|
+
// Merge, deduplicate by source_url, re-sort by domains_num descending, cap at limit.
|
|
767
|
+
const seenUrls = new Set();
|
|
768
|
+
const brokenPages = [
|
|
769
|
+
...parseCsvResponse(pages404Body),
|
|
770
|
+
...parseCsvResponse(pages410Body),
|
|
771
|
+
]
|
|
772
|
+
.filter((p) => {
|
|
773
|
+
if (seenUrls.has(p.source_url)) {
|
|
774
|
+
return false;
|
|
775
|
+
}
|
|
776
|
+
seenUrls.add(p.source_url);
|
|
777
|
+
return true;
|
|
778
|
+
})
|
|
779
|
+
.sort((a, b) => Number(b.domains_num) - Number(a.domains_num))
|
|
780
|
+
.slice(0, effectiveLimit);
|
|
760
781
|
|
|
761
782
|
if (brokenPages.length === 0) {
|
|
762
783
|
return { result: { backlinks: [] }, fullAuditRef };
|
|
763
784
|
}
|
|
764
785
|
|
|
765
|
-
// Step 2: For each broken page, fetch the top backlink by authority score
|
|
786
|
+
// Step 2: For each broken page, fetch the top backlink by authority score with quality filters:
|
|
787
|
+
// - follow links only (nofollow links carry no SEO value)
|
|
788
|
+
// - text links only (more actionable than image/frame links)
|
|
789
|
+
// - exclude lostlinks (only active broken backlinks, not already-removed ones)
|
|
766
790
|
const brokenUrls = brokenPages.map((p) => p.source_url);
|
|
767
791
|
const linkResults = await this.fanOut(brokenUrls, (brokenUrl) => this.sendRawRequest({
|
|
768
792
|
type: epLinks.type,
|
|
@@ -770,6 +794,17 @@ export default class SeoClient {
|
|
|
770
794
|
target_type: 'url',
|
|
771
795
|
export_columns: epLinks.columns,
|
|
772
796
|
display_limit: 1,
|
|
797
|
+
display_filter: buildFilter([
|
|
798
|
+
{
|
|
799
|
+
sign: '+', field: 'type', op: '', value: 'follow',
|
|
800
|
+
},
|
|
801
|
+
{
|
|
802
|
+
sign: '+', field: 'type', op: '', value: 'text',
|
|
803
|
+
},
|
|
804
|
+
{
|
|
805
|
+
sign: '-', field: 'type', op: '', value: 'lostlink',
|
|
806
|
+
},
|
|
807
|
+
]),
|
|
773
808
|
...epLinks.defaultParams,
|
|
774
809
|
}, epLinks.path), 'getBrokenBacklinks');
|
|
775
810
|
|