seo-intel 1.4.4 → 1.4.6
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/server.js +81 -19
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.4.6 (2026-04-09)
|
|
4
|
+
|
|
5
|
+
### Export: rich actionable content
|
|
6
|
+
- Insights export now renders type-specific tables (quick wins show issue + fix + impact, keyword gaps show coverage, etc.)
|
|
7
|
+
- Schema markup export scoped to own site only — no competitor schema dumps
|
|
8
|
+
- SKILL.md updated with export profiles documentation
|
|
9
|
+
|
|
10
|
+
## 1.4.5 (2026-04-09)
|
|
11
|
+
|
|
12
|
+
### Export: actionable summaries only
|
|
13
|
+
- Technical export: per-page issue summary (own site only) — lists specific problems per URL
|
|
14
|
+
- Links export: per-page link issue summary (own site only) — orphan pages, missing anchors, excessive external links
|
|
15
|
+
- No more raw data dumps in profile exports — every row is an action item
|
|
16
|
+
|
|
3
17
|
## 1.4.4 (2026-04-08)
|
|
4
18
|
|
|
5
19
|
### Export Profiles
|
package/package.json
CHANGED
package/server.js
CHANGED
|
@@ -759,12 +759,24 @@ async function handleRequest(req, res) {
|
|
|
759
759
|
}
|
|
760
760
|
case 'technical': {
|
|
761
761
|
if (!Array.isArray(data) || prof === 'ai-pipeline') return data;
|
|
762
|
-
//
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
762
|
+
// Own site only, per-page issue summary
|
|
763
|
+
const own = data.filter(r => r.role === 'target' || r.role === 'owned');
|
|
764
|
+
const issues = [];
|
|
765
|
+
for (const r of own) {
|
|
766
|
+
const problems = [];
|
|
767
|
+
if (r.status_code >= 400) problems.push(`HTTP ${r.status_code}`);
|
|
768
|
+
if (!r.has_canonical) problems.push('no canonical');
|
|
769
|
+
if (!r.has_og_tags) problems.push('no OG tags');
|
|
770
|
+
if (!r.has_schema) problems.push('no schema');
|
|
771
|
+
if (!r.has_robots) problems.push('no robots meta');
|
|
772
|
+
if (!r.is_mobile_ok) problems.push('not mobile-friendly');
|
|
773
|
+
if (r.load_ms && r.load_ms > 3000) problems.push(`slow (${r.load_ms}ms)`);
|
|
774
|
+
if (r.word_count != null && r.word_count < 100) problems.push(`thin content (${r.word_count} words)`);
|
|
775
|
+
if (problems.length) {
|
|
776
|
+
issues.push({ url: r.url, domain: r.domain, issues: problems.join(', '), status: r.status_code, load_ms: r.load_ms, word_count: r.word_count });
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
return issues;
|
|
768
780
|
}
|
|
769
781
|
case 'headings': {
|
|
770
782
|
if (!Array.isArray(data)) return data;
|
|
@@ -795,18 +807,30 @@ async function handleRequest(req, res) {
|
|
|
795
807
|
}
|
|
796
808
|
case 'links': {
|
|
797
809
|
if (!Array.isArray(data)) return data;
|
|
798
|
-
//
|
|
799
|
-
const
|
|
800
|
-
const
|
|
801
|
-
|
|
802
|
-
const
|
|
803
|
-
|
|
810
|
+
// Own site only, summarize to per-page link issues
|
|
811
|
+
const ownLinks = data.filter(r => r.role === 'target' || r.role === 'owned');
|
|
812
|
+
const internalTargets = new Set(ownLinks.filter(l => l.is_internal).map(l => l.target_url));
|
|
813
|
+
const byPage = {};
|
|
814
|
+
for (const r of ownLinks) (byPage[r.source_url] ||= []).push(r);
|
|
815
|
+
const issues = [];
|
|
816
|
+
for (const [url, links] of Object.entries(byPage)) {
|
|
817
|
+
const problems = [];
|
|
818
|
+
const noAnchor = links.filter(l => !l.anchor_text);
|
|
819
|
+
if (noAnchor.length) problems.push(`${noAnchor.length} links missing anchor text`);
|
|
820
|
+
if (!internalTargets.has(url)) problems.push('orphan page (no internal links point here)');
|
|
821
|
+
const extLinks = links.filter(l => !l.is_internal);
|
|
822
|
+
// flag if page has excessive external links
|
|
823
|
+
if (extLinks.length > 20) problems.push(`${extLinks.length} external links`);
|
|
824
|
+
if (problems.length) {
|
|
825
|
+
issues.push({ url, domain: links[0].domain, issues: problems.join(', '), total_links: links.length, internal: links.filter(l => l.is_internal).length, external: extLinks.length });
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
return issues;
|
|
804
829
|
}
|
|
805
830
|
case 'schemas': {
|
|
806
|
-
if (!Array.isArray(data)
|
|
807
|
-
//
|
|
808
|
-
|
|
809
|
-
return data;
|
|
831
|
+
if (!Array.isArray(data)) return data;
|
|
832
|
+
// Own site only for all profiles
|
|
833
|
+
return data.filter(r => r.role === 'target' || r.role === 'owned');
|
|
810
834
|
}
|
|
811
835
|
case 'aeo': {
|
|
812
836
|
if (!Array.isArray(data)) return data;
|
|
@@ -880,9 +904,47 @@ async function handleRequest(req, res) {
|
|
|
880
904
|
for (const r of data) { (grouped[r._type] ||= []).push(r); }
|
|
881
905
|
for (const [type, items] of Object.entries(grouped)) {
|
|
882
906
|
md += `### ${type.replace(/_/g, ' ')} (${items.length})\n\n`;
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
907
|
+
switch (type) {
|
|
908
|
+
case 'quick_win':
|
|
909
|
+
md += '| Page | Issue | Fix | Impact |\n|------|-------|-----|--------|\n';
|
|
910
|
+
for (const i of items) md += `| ${i.page || ''} | ${i.issue || ''} | ${i.fix || ''} | ${i.impact || ''} |\n`;
|
|
911
|
+
break;
|
|
912
|
+
case 'keyword_gap':
|
|
913
|
+
md += '| Keyword | Your Coverage | Competitor Coverage |\n|---------|--------------|--------------------|\n';
|
|
914
|
+
for (const i of items) md += `| ${i.keyword || ''} | ${i.your_coverage || i.target_count || 'none'} | ${i.competitor_coverage || i.competitor_count || ''} |\n`;
|
|
915
|
+
break;
|
|
916
|
+
case 'long_tail':
|
|
917
|
+
md += '| Phrase | Parent Keyword | Opportunity |\n|-------|----------------|-------------|\n';
|
|
918
|
+
for (const i of items) md += `| ${i.phrase || ''} | ${i.parent || i.keyword || ''} | ${i.opportunity || i.rationale || ''} |\n`;
|
|
919
|
+
break;
|
|
920
|
+
case 'new_page':
|
|
921
|
+
md += '| Title | Target Keyword | Rationale |\n|-------|----------------|----------|\n';
|
|
922
|
+
for (const i of items) md += `| ${i.title || ''} | ${i.target_keyword || ''} | ${i.rationale || ''} |\n`;
|
|
923
|
+
break;
|
|
924
|
+
case 'content_gap':
|
|
925
|
+
md += '| Topic | Gap | Suggestion |\n|-------|-----|------------|\n';
|
|
926
|
+
for (const i of items) md += `| ${i.topic || ''} | ${i.gap || ''} | ${i.suggestion || ''} |\n`;
|
|
927
|
+
break;
|
|
928
|
+
case 'technical_gap':
|
|
929
|
+
md += '| Issue | Affected | Recommendation |\n|-------|----------|----------------|\n';
|
|
930
|
+
for (const i of items) md += `| ${i.gap || i.issue || ''} | ${i.affected || i.pages || ''} | ${i.recommendation || i.fix || ''} |\n`;
|
|
931
|
+
break;
|
|
932
|
+
case 'citability_gap':
|
|
933
|
+
md += '| URL | Score | Weakest Signals |\n|-----|-------|----------------|\n';
|
|
934
|
+
for (const i of items) md += `| ${i.url || ''} | ${i.score ?? ''} | ${i.weak_signals || ''} |\n`;
|
|
935
|
+
break;
|
|
936
|
+
case 'keyword_inventor':
|
|
937
|
+
md += '| Phrase | Cluster | Search Potential |\n|-------|---------|------------------|\n';
|
|
938
|
+
for (const i of items) md += `| ${i.phrase || ''} | ${i.cluster || ''} | ${i.potential || i.volume || ''} |\n`;
|
|
939
|
+
break;
|
|
940
|
+
case 'site_watch':
|
|
941
|
+
md += '| URL | Event | Details |\n|-----|-------|--------|\n';
|
|
942
|
+
for (const i of items) md += `| ${i.url || ''} | ${i.event_type || ''} | ${i.details || ''} |\n`;
|
|
943
|
+
break;
|
|
944
|
+
default:
|
|
945
|
+
for (const i of items) {
|
|
946
|
+
md += `- ${i.phrase || i.keyword || i.title || i.page || i.message || JSON.stringify(i).slice(0, 120)}\n`;
|
|
947
|
+
}
|
|
886
948
|
}
|
|
887
949
|
md += '\n';
|
|
888
950
|
}
|