@lynxflow/seo-engine 2.3.0 → 2.4.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/README.md +1 -1
- package/dist/index.d.ts +9 -0
- package/dist/index.js +395 -0
- package/dist/index.mjs +395 -0
- package/dist/search-intent-classifier.d.ts +28 -0
- package/dist/seo-opportunity-prioritizer.d.ts +37 -0
- package/dist/trust-signals-extractor.d.ts +28 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@ High-Performance Universal Programmatic SEO & Structured Data Engine for Modern
|
|
|
4
4
|
|
|
5
5
|
[](https://www.typescriptlang.org/)
|
|
6
6
|
[](LICENSE)
|
|
7
|
-
[](https://www.npmjs.com/package/@lynxflow/seo-engine)
|
|
8
8
|
|
|
9
9
|
---
|
|
10
10
|
|
package/dist/index.d.ts
CHANGED
|
@@ -119,6 +119,9 @@ import { SerpLengthComparator } from "./serp-length-comparator";
|
|
|
119
119
|
import { PassiveVoiceComplexityAnalyzer } from "./passive-voice-complexity";
|
|
120
120
|
import { AboveFoldCroAuditor } from "./above-fold-cro-auditor";
|
|
121
121
|
import { ContextTemplatesGenerator } from "./context-templates-generator";
|
|
122
|
+
import { SearchIntentClassifier } from "./search-intent-classifier";
|
|
123
|
+
import { SeoOpportunityPrioritizer } from "./seo-opportunity-prioritizer";
|
|
124
|
+
import { TrustSignalsExtractor } from "./trust-signals-extractor";
|
|
122
125
|
export declare function createLynxSeoEngine(config: EngineConfig): LynxSeoEngine;
|
|
123
126
|
export declare const LynxSeo: {
|
|
124
127
|
createEngine: typeof createLynxSeoEngine;
|
|
@@ -135,6 +138,9 @@ export declare const LynxSeo: {
|
|
|
135
138
|
readabilityComplexity: typeof PassiveVoiceComplexityAnalyzer;
|
|
136
139
|
aboveFoldCro: typeof AboveFoldCroAuditor;
|
|
137
140
|
contextTemplates: typeof ContextTemplatesGenerator;
|
|
141
|
+
searchIntent: typeof SearchIntentClassifier;
|
|
142
|
+
opportunityPrioritizer: typeof SeoOpportunityPrioritizer;
|
|
143
|
+
trustSignals: typeof TrustSignalsExtractor;
|
|
138
144
|
inspectMeta: typeof SiteAuditor.inspectMeta;
|
|
139
145
|
crawlDomain: typeof DeepCrawlerAuditor.crawlAndAuditDomain;
|
|
140
146
|
inspectHtmlSnapshot: typeof DeepCrawlerAuditor.inspectHtmlSnapshot;
|
|
@@ -176,6 +182,9 @@ export declare const LynxSeo: {
|
|
|
176
182
|
marketingSkills: typeof MarketingSkillsEngine;
|
|
177
183
|
regenerationTracker: typeof PageRegenerationTracker;
|
|
178
184
|
};
|
|
185
|
+
export * from "./search-intent-classifier";
|
|
186
|
+
export * from "./seo-opportunity-prioritizer";
|
|
187
|
+
export * from "./trust-signals-extractor";
|
|
179
188
|
export * from "./humanity-ai-scrubber";
|
|
180
189
|
export * from "./serp-length-comparator";
|
|
181
190
|
export * from "./passive-voice-complexity";
|
package/dist/index.js
CHANGED
|
@@ -10393,6 +10393,395 @@ ${kwList}
|
|
|
10393
10393
|
}
|
|
10394
10394
|
}
|
|
10395
10395
|
|
|
10396
|
+
// src/search-intent-classifier.ts
|
|
10397
|
+
class SearchIntentClassifier {
|
|
10398
|
+
static INFORMATIONAL_SIGNALS = [
|
|
10399
|
+
"what",
|
|
10400
|
+
"why",
|
|
10401
|
+
"how",
|
|
10402
|
+
"when",
|
|
10403
|
+
"where",
|
|
10404
|
+
"who",
|
|
10405
|
+
"guide",
|
|
10406
|
+
"tutorial",
|
|
10407
|
+
"learn",
|
|
10408
|
+
"tips",
|
|
10409
|
+
"best practices",
|
|
10410
|
+
"explained",
|
|
10411
|
+
"definition",
|
|
10412
|
+
"meaning",
|
|
10413
|
+
"comment",
|
|
10414
|
+
"pourquoi",
|
|
10415
|
+
"qu'est ce que",
|
|
10416
|
+
"tutoriel",
|
|
10417
|
+
"guide",
|
|
10418
|
+
"astuces",
|
|
10419
|
+
"que es",
|
|
10420
|
+
"como",
|
|
10421
|
+
"guia",
|
|
10422
|
+
"was ist",
|
|
10423
|
+
"wie",
|
|
10424
|
+
"anleitung"
|
|
10425
|
+
];
|
|
10426
|
+
static NAVIGATIONAL_SIGNALS = [
|
|
10427
|
+
"login",
|
|
10428
|
+
"sign in",
|
|
10429
|
+
"website",
|
|
10430
|
+
"official",
|
|
10431
|
+
"home page",
|
|
10432
|
+
"account",
|
|
10433
|
+
"dashboard",
|
|
10434
|
+
"portal",
|
|
10435
|
+
"app",
|
|
10436
|
+
"connexion",
|
|
10437
|
+
"se connecter",
|
|
10438
|
+
"portail",
|
|
10439
|
+
"iniciar sesion",
|
|
10440
|
+
"anmelden",
|
|
10441
|
+
"konto"
|
|
10442
|
+
];
|
|
10443
|
+
static TRANSACTIONAL_SIGNALS = [
|
|
10444
|
+
"buy",
|
|
10445
|
+
"purchase",
|
|
10446
|
+
"order",
|
|
10447
|
+
"download",
|
|
10448
|
+
"get",
|
|
10449
|
+
"pricing",
|
|
10450
|
+
"cost",
|
|
10451
|
+
"free trial",
|
|
10452
|
+
"sign up",
|
|
10453
|
+
"subscribe",
|
|
10454
|
+
"install",
|
|
10455
|
+
"coupon",
|
|
10456
|
+
"deal",
|
|
10457
|
+
"discount",
|
|
10458
|
+
"cheap",
|
|
10459
|
+
"affordable",
|
|
10460
|
+
"acheter",
|
|
10461
|
+
"commander",
|
|
10462
|
+
"telecharger",
|
|
10463
|
+
"tarif",
|
|
10464
|
+
"prix",
|
|
10465
|
+
"essai gratuit",
|
|
10466
|
+
"inscription",
|
|
10467
|
+
"comprar",
|
|
10468
|
+
"precio",
|
|
10469
|
+
"kaufen",
|
|
10470
|
+
"preise"
|
|
10471
|
+
];
|
|
10472
|
+
static COMMERCIAL_SIGNALS = [
|
|
10473
|
+
"best",
|
|
10474
|
+
"top",
|
|
10475
|
+
"review",
|
|
10476
|
+
"vs",
|
|
10477
|
+
"versus",
|
|
10478
|
+
"compare",
|
|
10479
|
+
"comparison",
|
|
10480
|
+
"alternative",
|
|
10481
|
+
"alternatives",
|
|
10482
|
+
"like",
|
|
10483
|
+
"similar",
|
|
10484
|
+
"better than",
|
|
10485
|
+
"instead of",
|
|
10486
|
+
"or",
|
|
10487
|
+
"option",
|
|
10488
|
+
"choice",
|
|
10489
|
+
"meilleur",
|
|
10490
|
+
"avis",
|
|
10491
|
+
"comparatif",
|
|
10492
|
+
"alternatives a",
|
|
10493
|
+
"mejor",
|
|
10494
|
+
"opiniones",
|
|
10495
|
+
"beste",
|
|
10496
|
+
"test",
|
|
10497
|
+
"vergleich"
|
|
10498
|
+
];
|
|
10499
|
+
static classify(keyword, serpFeatures) {
|
|
10500
|
+
const clean = keyword.toLowerCase().trim();
|
|
10501
|
+
const scores = {
|
|
10502
|
+
informational: 0,
|
|
10503
|
+
navigational: 0,
|
|
10504
|
+
transactional: 0,
|
|
10505
|
+
commercial_investigation: 0
|
|
10506
|
+
};
|
|
10507
|
+
const detectedSignals = [];
|
|
10508
|
+
for (const signal of this.INFORMATIONAL_SIGNALS) {
|
|
10509
|
+
if (new RegExp(`\\b${signal}\\b`, "i").test(clean)) {
|
|
10510
|
+
scores.informational += 35;
|
|
10511
|
+
detectedSignals.push(`[info] ${signal}`);
|
|
10512
|
+
}
|
|
10513
|
+
}
|
|
10514
|
+
for (const signal of this.NAVIGATIONAL_SIGNALS) {
|
|
10515
|
+
if (new RegExp(`\\b${signal}\\b`, "i").test(clean)) {
|
|
10516
|
+
scores.navigational += 45;
|
|
10517
|
+
detectedSignals.push(`[nav] ${signal}`);
|
|
10518
|
+
}
|
|
10519
|
+
}
|
|
10520
|
+
for (const signal of this.TRANSACTIONAL_SIGNALS) {
|
|
10521
|
+
if (new RegExp(`\\b${signal}\\b`, "i").test(clean)) {
|
|
10522
|
+
scores.transactional += 40;
|
|
10523
|
+
detectedSignals.push(`[trans] ${signal}`);
|
|
10524
|
+
}
|
|
10525
|
+
}
|
|
10526
|
+
for (const signal of this.COMMERCIAL_SIGNALS) {
|
|
10527
|
+
if (new RegExp(`\\b${signal}\\b`, "i").test(clean)) {
|
|
10528
|
+
scores.commercial_investigation += 40;
|
|
10529
|
+
detectedSignals.push(`[comm] ${signal}`);
|
|
10530
|
+
}
|
|
10531
|
+
}
|
|
10532
|
+
if (serpFeatures && serpFeatures.length > 0) {
|
|
10533
|
+
for (const feature of serpFeatures) {
|
|
10534
|
+
const f = feature.toLowerCase();
|
|
10535
|
+
if (f.includes("shopping") || f.includes("local_pack") || f.includes("ads")) {
|
|
10536
|
+
scores.transactional += 25;
|
|
10537
|
+
} else if (f.includes("snippet") || f.includes("people_also_ask") || f.includes("knowledge")) {
|
|
10538
|
+
scores.informational += 25;
|
|
10539
|
+
} else if (f.includes("carousel") || f.includes("reviews")) {
|
|
10540
|
+
scores.commercial_investigation += 20;
|
|
10541
|
+
}
|
|
10542
|
+
}
|
|
10543
|
+
}
|
|
10544
|
+
let primaryIntent = "informational";
|
|
10545
|
+
let maxScore = scores.informational;
|
|
10546
|
+
if (scores.commercial_investigation > maxScore) {
|
|
10547
|
+
primaryIntent = "commercial_investigation";
|
|
10548
|
+
maxScore = scores.commercial_investigation;
|
|
10549
|
+
}
|
|
10550
|
+
if (scores.transactional > maxScore) {
|
|
10551
|
+
primaryIntent = "transactional";
|
|
10552
|
+
maxScore = scores.transactional;
|
|
10553
|
+
}
|
|
10554
|
+
if (scores.navigational > maxScore) {
|
|
10555
|
+
primaryIntent = "navigational";
|
|
10556
|
+
maxScore = scores.navigational;
|
|
10557
|
+
}
|
|
10558
|
+
if (maxScore === 0) {
|
|
10559
|
+
if (clean.split(" ").length <= 2) {
|
|
10560
|
+
primaryIntent = "navigational";
|
|
10561
|
+
} else {
|
|
10562
|
+
primaryIntent = "informational";
|
|
10563
|
+
}
|
|
10564
|
+
maxScore = 40;
|
|
10565
|
+
}
|
|
10566
|
+
const confidenceScore = Math.min(98, Math.max(45, maxScore));
|
|
10567
|
+
const formatMap = {
|
|
10568
|
+
informational: "Long-form Step-by-Step Guide, FAQ Accordion, Definition Card, or Tutorial Video.",
|
|
10569
|
+
commercial_investigation: "Comparison Matrix Table (VS), Tiered Review Breakdown, Pros/Cons List.",
|
|
10570
|
+
transactional: "High-Converting Landing Page, Pricing Calculator, Frictionless Signup Flow.",
|
|
10571
|
+
navigational: "Direct Brand Portal, Login Page, Dashboard Directory."
|
|
10572
|
+
};
|
|
10573
|
+
return {
|
|
10574
|
+
primaryIntent,
|
|
10575
|
+
confidenceScore,
|
|
10576
|
+
scores,
|
|
10577
|
+
detectedSignals,
|
|
10578
|
+
recommendedContentFormat: formatMap[primaryIntent]
|
|
10579
|
+
};
|
|
10580
|
+
}
|
|
10581
|
+
}
|
|
10582
|
+
|
|
10583
|
+
// src/seo-opportunity-prioritizer.ts
|
|
10584
|
+
class SeoOpportunityPrioritizer {
|
|
10585
|
+
static BENCHMARK_CTR = {
|
|
10586
|
+
1: 0.316,
|
|
10587
|
+
2: 0.157,
|
|
10588
|
+
3: 0.105,
|
|
10589
|
+
4: 0.075,
|
|
10590
|
+
5: 0.059,
|
|
10591
|
+
6: 0.048,
|
|
10592
|
+
7: 0.041,
|
|
10593
|
+
8: 0.035,
|
|
10594
|
+
9: 0.031,
|
|
10595
|
+
10: 0.027,
|
|
10596
|
+
11: 0.018,
|
|
10597
|
+
12: 0.015,
|
|
10598
|
+
13: 0.013,
|
|
10599
|
+
14: 0.012,
|
|
10600
|
+
15: 0.011,
|
|
10601
|
+
16: 0.01,
|
|
10602
|
+
17: 0.009,
|
|
10603
|
+
18: 0.008,
|
|
10604
|
+
19: 0.008,
|
|
10605
|
+
20: 0.007
|
|
10606
|
+
};
|
|
10607
|
+
static evaluate(input) {
|
|
10608
|
+
const pos = input.currentPosition ?? 25;
|
|
10609
|
+
const vol = input.searchVolume || input.monthlyImpressions || 500;
|
|
10610
|
+
const diff = input.difficulty ?? 40;
|
|
10611
|
+
let volumeScore = Math.min(100, Math.round(Math.log10(Math.max(10, vol)) / 4.5 * 100));
|
|
10612
|
+
let positionScore = 0;
|
|
10613
|
+
let archetype = "high_volume_gap";
|
|
10614
|
+
if (pos >= 11 && pos <= 20) {
|
|
10615
|
+
positionScore = 95;
|
|
10616
|
+
archetype = "quick_win";
|
|
10617
|
+
} else if (pos >= 4 && pos <= 10) {
|
|
10618
|
+
positionScore = 85;
|
|
10619
|
+
archetype = "top_3_push";
|
|
10620
|
+
} else if (pos >= 1 && pos <= 3) {
|
|
10621
|
+
positionScore = 40;
|
|
10622
|
+
} else {
|
|
10623
|
+
positionScore = Math.max(10, 70 - (pos - 20) * 1.5);
|
|
10624
|
+
}
|
|
10625
|
+
let intentScore = 60;
|
|
10626
|
+
if (input.intent === "transactional")
|
|
10627
|
+
intentScore = 100;
|
|
10628
|
+
else if (input.intent === "commercial_investigation")
|
|
10629
|
+
intentScore = 85;
|
|
10630
|
+
else if (input.intent === "informational")
|
|
10631
|
+
intentScore = 65;
|
|
10632
|
+
else if (input.intent === "navigational")
|
|
10633
|
+
intentScore = 40;
|
|
10634
|
+
const competitionScore = Math.max(10, 100 - diff);
|
|
10635
|
+
const expectedCtr = this.BENCHMARK_CTR[Math.min(20, Math.max(1, Math.round(pos)))] || 0.005;
|
|
10636
|
+
const actualCtr = input.actualCtr ?? (input.monthlyClicks || 0) / Math.max(1, input.monthlyImpressions || 1);
|
|
10637
|
+
const isUnderperforming = actualCtr < expectedCtr * 0.7;
|
|
10638
|
+
if (isUnderperforming && pos <= 10) {
|
|
10639
|
+
archetype = "underperformer";
|
|
10640
|
+
}
|
|
10641
|
+
const ctrImprovementScore = isUnderperforming ? 90 : 50;
|
|
10642
|
+
const opportunityScore = Math.round(volumeScore * 0.25 + positionScore * 0.25 + intentScore * 0.2 + competitionScore * 0.15 + ctrImprovementScore * 0.15);
|
|
10643
|
+
const targetClicks = Math.round(vol * 0.15);
|
|
10644
|
+
const currentClicks = input.monthlyClicks || Math.round(vol * (input.actualCtr || 0.01));
|
|
10645
|
+
const estimatedClickGain = Math.max(50, targetClicks - currentClicks);
|
|
10646
|
+
let priorityLevel = "low";
|
|
10647
|
+
if (opportunityScore >= 80)
|
|
10648
|
+
priorityLevel = "critical";
|
|
10649
|
+
else if (opportunityScore >= 65)
|
|
10650
|
+
priorityLevel = "high";
|
|
10651
|
+
else if (opportunityScore >= 50)
|
|
10652
|
+
priorityLevel = "medium";
|
|
10653
|
+
let actionPlan = "";
|
|
10654
|
+
if (archetype === "quick_win") {
|
|
10655
|
+
actionPlan = `Quick Win on Position #${pos} (~${estimatedClickGain} clicks/mo upside). Add 2 internal links with exact anchor text and update H2 subheadings to jump to Page 1.`;
|
|
10656
|
+
} else if (archetype === "underperformer") {
|
|
10657
|
+
actionPlan = `Underperforming CTR (${(actualCtr * 100).toFixed(1)}% vs expected ${(expectedCtr * 100).toFixed(1)}%). Rewrite Meta Title and Description with numbers and power words to boost clicks immediately.`;
|
|
10658
|
+
} else if (archetype === "top_3_push") {
|
|
10659
|
+
actionPlan = `Push from Position #${pos} to Top 3. Add rich Schema.org FAQPage and inject 1 AEO direct answer block.`;
|
|
10660
|
+
} else {
|
|
10661
|
+
actionPlan = `Expand topic cluster depth and build 3-5 high-authority backlinks to improve domain topical equity.`;
|
|
10662
|
+
}
|
|
10663
|
+
return {
|
|
10664
|
+
opportunityScore,
|
|
10665
|
+
archetype,
|
|
10666
|
+
estimatedClickGain,
|
|
10667
|
+
factors: {
|
|
10668
|
+
volumeScore,
|
|
10669
|
+
positionScore,
|
|
10670
|
+
intentScore,
|
|
10671
|
+
competitionScore,
|
|
10672
|
+
ctrImprovementScore
|
|
10673
|
+
},
|
|
10674
|
+
priorityLevel,
|
|
10675
|
+
actionPlan
|
|
10676
|
+
};
|
|
10677
|
+
}
|
|
10678
|
+
}
|
|
10679
|
+
|
|
10680
|
+
// src/trust-signals-extractor.ts
|
|
10681
|
+
class TrustSignalsExtractor {
|
|
10682
|
+
static SOCIAL_PROOF_PATTERNS = [
|
|
10683
|
+
/\b(\d{1,3}(?:[,\s]\d{3})*\+?)\s*(?:customers?|users?|businesses?|clients?|teams?|creators?|entreprises?|utilisateurs?)\b/gi,
|
|
10684
|
+
/(?:trusted|used|loved)\s+by\s+(\d{1,3}(?:[,\s]\d{3})*\+?)/gi,
|
|
10685
|
+
/\b(\d+(?:\.\d+)?[kmb])\+?\s*(?:users?|businesses?|downloads?)/gi
|
|
10686
|
+
];
|
|
10687
|
+
static RESULT_PATTERNS = [
|
|
10688
|
+
/\b(\d+%\s*(?:increase|decrease|growth|improvement|reduction|croissance|gain|economie|économies))\b/gi,
|
|
10689
|
+
/\b(\d+x\s*(?:faster|more|growth|plus vite|plus rapide))\b/gi,
|
|
10690
|
+
/(?:\$|€|£)\s*(\d{1,3}(?:[,\s]\d{3})*(?:\.\d{2})?)\s*(?:saved|économisé|gagné)?/gi
|
|
10691
|
+
];
|
|
10692
|
+
static RISK_REVERSAL_PATTERNS = [
|
|
10693
|
+
/\b(\d+[- ]days?\s+free\s+trial|free\s+trial|essai\s+gratuit(?:\s+de\s+\d+\s+jours)?)\b/gi,
|
|
10694
|
+
/\b(no\s+credit\s+card\s+required|sans\s+carte\s+bancaire|sans\s+engagement)\b/gi,
|
|
10695
|
+
/\b(cancel\s+anytime|résiliation\s+à\s+tout\s+moment|satisfait\s+ou\s+remboursé|money[- ]back\s+guarantee)\b/gi,
|
|
10696
|
+
/\b(100%\s+free\s+migration|migration\s+gratuite)\b/gi
|
|
10697
|
+
];
|
|
10698
|
+
static AUTHORITY_PATTERNS = [
|
|
10699
|
+
/(?:featured|seen|mentioned)\s+(?:in|on)\s+([A-Za-z0-9\s,]+)/gi,
|
|
10700
|
+
/(?:vu\s+dans|mentionné\s+par)\s+([A-Za-z0-9\s,]+)/gi,
|
|
10701
|
+
/\b(award[- ]winning|best[- ]rated|élu\s+meilleur|noté\s+4\.\d\/5|rated\s+4\.\d\/5)\b/gi
|
|
10702
|
+
];
|
|
10703
|
+
static extract(content) {
|
|
10704
|
+
if (!content || content.trim().length === 0) {
|
|
10705
|
+
return {
|
|
10706
|
+
totalSignalsCount: 0,
|
|
10707
|
+
testimonials: [],
|
|
10708
|
+
socialProofCounts: [],
|
|
10709
|
+
specificResults: [],
|
|
10710
|
+
riskReversals: [],
|
|
10711
|
+
authorityMentions: [],
|
|
10712
|
+
trustScore: 0,
|
|
10713
|
+
isSufficientForConversion: false
|
|
10714
|
+
};
|
|
10715
|
+
}
|
|
10716
|
+
const socialProofCounts = [];
|
|
10717
|
+
const specificResults = [];
|
|
10718
|
+
const riskReversals = [];
|
|
10719
|
+
const authorityMentions = [];
|
|
10720
|
+
const testimonials = [];
|
|
10721
|
+
for (const pattern of this.SOCIAL_PROOF_PATTERNS) {
|
|
10722
|
+
const matches = content.match(pattern);
|
|
10723
|
+
if (matches) {
|
|
10724
|
+
matches.forEach((m) => {
|
|
10725
|
+
if (!socialProofCounts.includes(m.trim()))
|
|
10726
|
+
socialProofCounts.push(m.trim());
|
|
10727
|
+
});
|
|
10728
|
+
}
|
|
10729
|
+
}
|
|
10730
|
+
for (const pattern of this.RESULT_PATTERNS) {
|
|
10731
|
+
const matches = content.match(pattern);
|
|
10732
|
+
if (matches) {
|
|
10733
|
+
matches.forEach((m) => {
|
|
10734
|
+
if (!specificResults.includes(m.trim()))
|
|
10735
|
+
specificResults.push(m.trim());
|
|
10736
|
+
});
|
|
10737
|
+
}
|
|
10738
|
+
}
|
|
10739
|
+
for (const pattern of this.RISK_REVERSAL_PATTERNS) {
|
|
10740
|
+
const matches = content.match(pattern);
|
|
10741
|
+
if (matches) {
|
|
10742
|
+
matches.forEach((m) => {
|
|
10743
|
+
if (!riskReversals.includes(m.trim()))
|
|
10744
|
+
riskReversals.push(m.trim());
|
|
10745
|
+
});
|
|
10746
|
+
}
|
|
10747
|
+
}
|
|
10748
|
+
for (const pattern of this.AUTHORITY_PATTERNS) {
|
|
10749
|
+
const matches = content.match(pattern);
|
|
10750
|
+
if (matches) {
|
|
10751
|
+
matches.forEach((m) => {
|
|
10752
|
+
if (!authorityMentions.includes(m.trim()))
|
|
10753
|
+
authorityMentions.push(m.trim());
|
|
10754
|
+
});
|
|
10755
|
+
}
|
|
10756
|
+
}
|
|
10757
|
+
const quoteRegex = /"([^"]{25,250})"\s*(?:—|-|by)\s*\*?([A-Z][a-z]+(?:\s+[A-Z]\.?)?)\*?/g;
|
|
10758
|
+
let quoteMatch;
|
|
10759
|
+
while ((quoteMatch = quoteRegex.exec(content)) !== null) {
|
|
10760
|
+
testimonials.push({
|
|
10761
|
+
quote: quoteMatch[1].trim(),
|
|
10762
|
+
author: quoteMatch[2]?.trim()
|
|
10763
|
+
});
|
|
10764
|
+
}
|
|
10765
|
+
const totalSignalsCount = socialProofCounts.length + specificResults.length + riskReversals.length + authorityMentions.length + testimonials.length;
|
|
10766
|
+
let trustScore = Math.min(100, totalSignalsCount * 18);
|
|
10767
|
+
if (testimonials.length > 0)
|
|
10768
|
+
trustScore = Math.min(100, trustScore + 15);
|
|
10769
|
+
if (riskReversals.length > 0)
|
|
10770
|
+
trustScore = Math.min(100, trustScore + 15);
|
|
10771
|
+
const isSufficientForConversion = trustScore >= 50;
|
|
10772
|
+
return {
|
|
10773
|
+
totalSignalsCount,
|
|
10774
|
+
testimonials,
|
|
10775
|
+
socialProofCounts,
|
|
10776
|
+
specificResults,
|
|
10777
|
+
riskReversals,
|
|
10778
|
+
authorityMentions,
|
|
10779
|
+
trustScore,
|
|
10780
|
+
isSufficientForConversion
|
|
10781
|
+
};
|
|
10782
|
+
}
|
|
10783
|
+
}
|
|
10784
|
+
|
|
10396
10785
|
// src/index.ts
|
|
10397
10786
|
function createLynxSeoEngine(config) {
|
|
10398
10787
|
return new LynxSeoEngine(config);
|
|
@@ -10412,6 +10801,9 @@ var LynxSeo = {
|
|
|
10412
10801
|
readabilityComplexity: PassiveVoiceComplexityAnalyzer,
|
|
10413
10802
|
aboveFoldCro: AboveFoldCroAuditor,
|
|
10414
10803
|
contextTemplates: ContextTemplatesGenerator,
|
|
10804
|
+
searchIntent: SearchIntentClassifier,
|
|
10805
|
+
opportunityPrioritizer: SeoOpportunityPrioritizer,
|
|
10806
|
+
trustSignals: TrustSignalsExtractor,
|
|
10415
10807
|
inspectMeta: SiteAuditor.inspectMeta,
|
|
10416
10808
|
crawlDomain: DeepCrawlerAuditor.crawlAndAuditDomain,
|
|
10417
10809
|
inspectHtmlSnapshot: DeepCrawlerAuditor.inspectHtmlSnapshot,
|
|
@@ -10471,6 +10863,7 @@ export {
|
|
|
10471
10863
|
VideoYouTubeAnalyzer,
|
|
10472
10864
|
UrlyticsEngine,
|
|
10473
10865
|
UI_ICONS,
|
|
10866
|
+
TrustSignalsExtractor,
|
|
10474
10867
|
TokenQuotaManager,
|
|
10475
10868
|
TechnicalRulesAuditor,
|
|
10476
10869
|
TeamRbacEngine,
|
|
@@ -10482,8 +10875,10 @@ export {
|
|
|
10482
10875
|
SerpRankHistoryEngine,
|
|
10483
10876
|
SerpLengthComparator,
|
|
10484
10877
|
SerpClient,
|
|
10878
|
+
SeoOpportunityPrioritizer,
|
|
10485
10879
|
SeoOpportunitiesDecayDetector,
|
|
10486
10880
|
SemanticCannibalizationDetector,
|
|
10881
|
+
SearchIntentClassifier,
|
|
10487
10882
|
SchemaGraphBuilder,
|
|
10488
10883
|
SUPPORTED_CANONICAL_LOCALES,
|
|
10489
10884
|
SCHEMA_LOCAL_BUSINESS_MAP,
|
package/dist/index.mjs
CHANGED
|
@@ -10393,6 +10393,395 @@ ${kwList}
|
|
|
10393
10393
|
}
|
|
10394
10394
|
}
|
|
10395
10395
|
|
|
10396
|
+
// src/search-intent-classifier.ts
|
|
10397
|
+
class SearchIntentClassifier {
|
|
10398
|
+
static INFORMATIONAL_SIGNALS = [
|
|
10399
|
+
"what",
|
|
10400
|
+
"why",
|
|
10401
|
+
"how",
|
|
10402
|
+
"when",
|
|
10403
|
+
"where",
|
|
10404
|
+
"who",
|
|
10405
|
+
"guide",
|
|
10406
|
+
"tutorial",
|
|
10407
|
+
"learn",
|
|
10408
|
+
"tips",
|
|
10409
|
+
"best practices",
|
|
10410
|
+
"explained",
|
|
10411
|
+
"definition",
|
|
10412
|
+
"meaning",
|
|
10413
|
+
"comment",
|
|
10414
|
+
"pourquoi",
|
|
10415
|
+
"qu'est ce que",
|
|
10416
|
+
"tutoriel",
|
|
10417
|
+
"guide",
|
|
10418
|
+
"astuces",
|
|
10419
|
+
"que es",
|
|
10420
|
+
"como",
|
|
10421
|
+
"guia",
|
|
10422
|
+
"was ist",
|
|
10423
|
+
"wie",
|
|
10424
|
+
"anleitung"
|
|
10425
|
+
];
|
|
10426
|
+
static NAVIGATIONAL_SIGNALS = [
|
|
10427
|
+
"login",
|
|
10428
|
+
"sign in",
|
|
10429
|
+
"website",
|
|
10430
|
+
"official",
|
|
10431
|
+
"home page",
|
|
10432
|
+
"account",
|
|
10433
|
+
"dashboard",
|
|
10434
|
+
"portal",
|
|
10435
|
+
"app",
|
|
10436
|
+
"connexion",
|
|
10437
|
+
"se connecter",
|
|
10438
|
+
"portail",
|
|
10439
|
+
"iniciar sesion",
|
|
10440
|
+
"anmelden",
|
|
10441
|
+
"konto"
|
|
10442
|
+
];
|
|
10443
|
+
static TRANSACTIONAL_SIGNALS = [
|
|
10444
|
+
"buy",
|
|
10445
|
+
"purchase",
|
|
10446
|
+
"order",
|
|
10447
|
+
"download",
|
|
10448
|
+
"get",
|
|
10449
|
+
"pricing",
|
|
10450
|
+
"cost",
|
|
10451
|
+
"free trial",
|
|
10452
|
+
"sign up",
|
|
10453
|
+
"subscribe",
|
|
10454
|
+
"install",
|
|
10455
|
+
"coupon",
|
|
10456
|
+
"deal",
|
|
10457
|
+
"discount",
|
|
10458
|
+
"cheap",
|
|
10459
|
+
"affordable",
|
|
10460
|
+
"acheter",
|
|
10461
|
+
"commander",
|
|
10462
|
+
"telecharger",
|
|
10463
|
+
"tarif",
|
|
10464
|
+
"prix",
|
|
10465
|
+
"essai gratuit",
|
|
10466
|
+
"inscription",
|
|
10467
|
+
"comprar",
|
|
10468
|
+
"precio",
|
|
10469
|
+
"kaufen",
|
|
10470
|
+
"preise"
|
|
10471
|
+
];
|
|
10472
|
+
static COMMERCIAL_SIGNALS = [
|
|
10473
|
+
"best",
|
|
10474
|
+
"top",
|
|
10475
|
+
"review",
|
|
10476
|
+
"vs",
|
|
10477
|
+
"versus",
|
|
10478
|
+
"compare",
|
|
10479
|
+
"comparison",
|
|
10480
|
+
"alternative",
|
|
10481
|
+
"alternatives",
|
|
10482
|
+
"like",
|
|
10483
|
+
"similar",
|
|
10484
|
+
"better than",
|
|
10485
|
+
"instead of",
|
|
10486
|
+
"or",
|
|
10487
|
+
"option",
|
|
10488
|
+
"choice",
|
|
10489
|
+
"meilleur",
|
|
10490
|
+
"avis",
|
|
10491
|
+
"comparatif",
|
|
10492
|
+
"alternatives a",
|
|
10493
|
+
"mejor",
|
|
10494
|
+
"opiniones",
|
|
10495
|
+
"beste",
|
|
10496
|
+
"test",
|
|
10497
|
+
"vergleich"
|
|
10498
|
+
];
|
|
10499
|
+
static classify(keyword, serpFeatures) {
|
|
10500
|
+
const clean = keyword.toLowerCase().trim();
|
|
10501
|
+
const scores = {
|
|
10502
|
+
informational: 0,
|
|
10503
|
+
navigational: 0,
|
|
10504
|
+
transactional: 0,
|
|
10505
|
+
commercial_investigation: 0
|
|
10506
|
+
};
|
|
10507
|
+
const detectedSignals = [];
|
|
10508
|
+
for (const signal of this.INFORMATIONAL_SIGNALS) {
|
|
10509
|
+
if (new RegExp(`\\b${signal}\\b`, "i").test(clean)) {
|
|
10510
|
+
scores.informational += 35;
|
|
10511
|
+
detectedSignals.push(`[info] ${signal}`);
|
|
10512
|
+
}
|
|
10513
|
+
}
|
|
10514
|
+
for (const signal of this.NAVIGATIONAL_SIGNALS) {
|
|
10515
|
+
if (new RegExp(`\\b${signal}\\b`, "i").test(clean)) {
|
|
10516
|
+
scores.navigational += 45;
|
|
10517
|
+
detectedSignals.push(`[nav] ${signal}`);
|
|
10518
|
+
}
|
|
10519
|
+
}
|
|
10520
|
+
for (const signal of this.TRANSACTIONAL_SIGNALS) {
|
|
10521
|
+
if (new RegExp(`\\b${signal}\\b`, "i").test(clean)) {
|
|
10522
|
+
scores.transactional += 40;
|
|
10523
|
+
detectedSignals.push(`[trans] ${signal}`);
|
|
10524
|
+
}
|
|
10525
|
+
}
|
|
10526
|
+
for (const signal of this.COMMERCIAL_SIGNALS) {
|
|
10527
|
+
if (new RegExp(`\\b${signal}\\b`, "i").test(clean)) {
|
|
10528
|
+
scores.commercial_investigation += 40;
|
|
10529
|
+
detectedSignals.push(`[comm] ${signal}`);
|
|
10530
|
+
}
|
|
10531
|
+
}
|
|
10532
|
+
if (serpFeatures && serpFeatures.length > 0) {
|
|
10533
|
+
for (const feature of serpFeatures) {
|
|
10534
|
+
const f = feature.toLowerCase();
|
|
10535
|
+
if (f.includes("shopping") || f.includes("local_pack") || f.includes("ads")) {
|
|
10536
|
+
scores.transactional += 25;
|
|
10537
|
+
} else if (f.includes("snippet") || f.includes("people_also_ask") || f.includes("knowledge")) {
|
|
10538
|
+
scores.informational += 25;
|
|
10539
|
+
} else if (f.includes("carousel") || f.includes("reviews")) {
|
|
10540
|
+
scores.commercial_investigation += 20;
|
|
10541
|
+
}
|
|
10542
|
+
}
|
|
10543
|
+
}
|
|
10544
|
+
let primaryIntent = "informational";
|
|
10545
|
+
let maxScore = scores.informational;
|
|
10546
|
+
if (scores.commercial_investigation > maxScore) {
|
|
10547
|
+
primaryIntent = "commercial_investigation";
|
|
10548
|
+
maxScore = scores.commercial_investigation;
|
|
10549
|
+
}
|
|
10550
|
+
if (scores.transactional > maxScore) {
|
|
10551
|
+
primaryIntent = "transactional";
|
|
10552
|
+
maxScore = scores.transactional;
|
|
10553
|
+
}
|
|
10554
|
+
if (scores.navigational > maxScore) {
|
|
10555
|
+
primaryIntent = "navigational";
|
|
10556
|
+
maxScore = scores.navigational;
|
|
10557
|
+
}
|
|
10558
|
+
if (maxScore === 0) {
|
|
10559
|
+
if (clean.split(" ").length <= 2) {
|
|
10560
|
+
primaryIntent = "navigational";
|
|
10561
|
+
} else {
|
|
10562
|
+
primaryIntent = "informational";
|
|
10563
|
+
}
|
|
10564
|
+
maxScore = 40;
|
|
10565
|
+
}
|
|
10566
|
+
const confidenceScore = Math.min(98, Math.max(45, maxScore));
|
|
10567
|
+
const formatMap = {
|
|
10568
|
+
informational: "Long-form Step-by-Step Guide, FAQ Accordion, Definition Card, or Tutorial Video.",
|
|
10569
|
+
commercial_investigation: "Comparison Matrix Table (VS), Tiered Review Breakdown, Pros/Cons List.",
|
|
10570
|
+
transactional: "High-Converting Landing Page, Pricing Calculator, Frictionless Signup Flow.",
|
|
10571
|
+
navigational: "Direct Brand Portal, Login Page, Dashboard Directory."
|
|
10572
|
+
};
|
|
10573
|
+
return {
|
|
10574
|
+
primaryIntent,
|
|
10575
|
+
confidenceScore,
|
|
10576
|
+
scores,
|
|
10577
|
+
detectedSignals,
|
|
10578
|
+
recommendedContentFormat: formatMap[primaryIntent]
|
|
10579
|
+
};
|
|
10580
|
+
}
|
|
10581
|
+
}
|
|
10582
|
+
|
|
10583
|
+
// src/seo-opportunity-prioritizer.ts
|
|
10584
|
+
class SeoOpportunityPrioritizer {
|
|
10585
|
+
static BENCHMARK_CTR = {
|
|
10586
|
+
1: 0.316,
|
|
10587
|
+
2: 0.157,
|
|
10588
|
+
3: 0.105,
|
|
10589
|
+
4: 0.075,
|
|
10590
|
+
5: 0.059,
|
|
10591
|
+
6: 0.048,
|
|
10592
|
+
7: 0.041,
|
|
10593
|
+
8: 0.035,
|
|
10594
|
+
9: 0.031,
|
|
10595
|
+
10: 0.027,
|
|
10596
|
+
11: 0.018,
|
|
10597
|
+
12: 0.015,
|
|
10598
|
+
13: 0.013,
|
|
10599
|
+
14: 0.012,
|
|
10600
|
+
15: 0.011,
|
|
10601
|
+
16: 0.01,
|
|
10602
|
+
17: 0.009,
|
|
10603
|
+
18: 0.008,
|
|
10604
|
+
19: 0.008,
|
|
10605
|
+
20: 0.007
|
|
10606
|
+
};
|
|
10607
|
+
static evaluate(input) {
|
|
10608
|
+
const pos = input.currentPosition ?? 25;
|
|
10609
|
+
const vol = input.searchVolume || input.monthlyImpressions || 500;
|
|
10610
|
+
const diff = input.difficulty ?? 40;
|
|
10611
|
+
let volumeScore = Math.min(100, Math.round(Math.log10(Math.max(10, vol)) / 4.5 * 100));
|
|
10612
|
+
let positionScore = 0;
|
|
10613
|
+
let archetype = "high_volume_gap";
|
|
10614
|
+
if (pos >= 11 && pos <= 20) {
|
|
10615
|
+
positionScore = 95;
|
|
10616
|
+
archetype = "quick_win";
|
|
10617
|
+
} else if (pos >= 4 && pos <= 10) {
|
|
10618
|
+
positionScore = 85;
|
|
10619
|
+
archetype = "top_3_push";
|
|
10620
|
+
} else if (pos >= 1 && pos <= 3) {
|
|
10621
|
+
positionScore = 40;
|
|
10622
|
+
} else {
|
|
10623
|
+
positionScore = Math.max(10, 70 - (pos - 20) * 1.5);
|
|
10624
|
+
}
|
|
10625
|
+
let intentScore = 60;
|
|
10626
|
+
if (input.intent === "transactional")
|
|
10627
|
+
intentScore = 100;
|
|
10628
|
+
else if (input.intent === "commercial_investigation")
|
|
10629
|
+
intentScore = 85;
|
|
10630
|
+
else if (input.intent === "informational")
|
|
10631
|
+
intentScore = 65;
|
|
10632
|
+
else if (input.intent === "navigational")
|
|
10633
|
+
intentScore = 40;
|
|
10634
|
+
const competitionScore = Math.max(10, 100 - diff);
|
|
10635
|
+
const expectedCtr = this.BENCHMARK_CTR[Math.min(20, Math.max(1, Math.round(pos)))] || 0.005;
|
|
10636
|
+
const actualCtr = input.actualCtr ?? (input.monthlyClicks || 0) / Math.max(1, input.monthlyImpressions || 1);
|
|
10637
|
+
const isUnderperforming = actualCtr < expectedCtr * 0.7;
|
|
10638
|
+
if (isUnderperforming && pos <= 10) {
|
|
10639
|
+
archetype = "underperformer";
|
|
10640
|
+
}
|
|
10641
|
+
const ctrImprovementScore = isUnderperforming ? 90 : 50;
|
|
10642
|
+
const opportunityScore = Math.round(volumeScore * 0.25 + positionScore * 0.25 + intentScore * 0.2 + competitionScore * 0.15 + ctrImprovementScore * 0.15);
|
|
10643
|
+
const targetClicks = Math.round(vol * 0.15);
|
|
10644
|
+
const currentClicks = input.monthlyClicks || Math.round(vol * (input.actualCtr || 0.01));
|
|
10645
|
+
const estimatedClickGain = Math.max(50, targetClicks - currentClicks);
|
|
10646
|
+
let priorityLevel = "low";
|
|
10647
|
+
if (opportunityScore >= 80)
|
|
10648
|
+
priorityLevel = "critical";
|
|
10649
|
+
else if (opportunityScore >= 65)
|
|
10650
|
+
priorityLevel = "high";
|
|
10651
|
+
else if (opportunityScore >= 50)
|
|
10652
|
+
priorityLevel = "medium";
|
|
10653
|
+
let actionPlan = "";
|
|
10654
|
+
if (archetype === "quick_win") {
|
|
10655
|
+
actionPlan = `Quick Win on Position #${pos} (~${estimatedClickGain} clicks/mo upside). Add 2 internal links with exact anchor text and update H2 subheadings to jump to Page 1.`;
|
|
10656
|
+
} else if (archetype === "underperformer") {
|
|
10657
|
+
actionPlan = `Underperforming CTR (${(actualCtr * 100).toFixed(1)}% vs expected ${(expectedCtr * 100).toFixed(1)}%). Rewrite Meta Title and Description with numbers and power words to boost clicks immediately.`;
|
|
10658
|
+
} else if (archetype === "top_3_push") {
|
|
10659
|
+
actionPlan = `Push from Position #${pos} to Top 3. Add rich Schema.org FAQPage and inject 1 AEO direct answer block.`;
|
|
10660
|
+
} else {
|
|
10661
|
+
actionPlan = `Expand topic cluster depth and build 3-5 high-authority backlinks to improve domain topical equity.`;
|
|
10662
|
+
}
|
|
10663
|
+
return {
|
|
10664
|
+
opportunityScore,
|
|
10665
|
+
archetype,
|
|
10666
|
+
estimatedClickGain,
|
|
10667
|
+
factors: {
|
|
10668
|
+
volumeScore,
|
|
10669
|
+
positionScore,
|
|
10670
|
+
intentScore,
|
|
10671
|
+
competitionScore,
|
|
10672
|
+
ctrImprovementScore
|
|
10673
|
+
},
|
|
10674
|
+
priorityLevel,
|
|
10675
|
+
actionPlan
|
|
10676
|
+
};
|
|
10677
|
+
}
|
|
10678
|
+
}
|
|
10679
|
+
|
|
10680
|
+
// src/trust-signals-extractor.ts
|
|
10681
|
+
class TrustSignalsExtractor {
|
|
10682
|
+
static SOCIAL_PROOF_PATTERNS = [
|
|
10683
|
+
/\b(\d{1,3}(?:[,\s]\d{3})*\+?)\s*(?:customers?|users?|businesses?|clients?|teams?|creators?|entreprises?|utilisateurs?)\b/gi,
|
|
10684
|
+
/(?:trusted|used|loved)\s+by\s+(\d{1,3}(?:[,\s]\d{3})*\+?)/gi,
|
|
10685
|
+
/\b(\d+(?:\.\d+)?[kmb])\+?\s*(?:users?|businesses?|downloads?)/gi
|
|
10686
|
+
];
|
|
10687
|
+
static RESULT_PATTERNS = [
|
|
10688
|
+
/\b(\d+%\s*(?:increase|decrease|growth|improvement|reduction|croissance|gain|economie|économies))\b/gi,
|
|
10689
|
+
/\b(\d+x\s*(?:faster|more|growth|plus vite|plus rapide))\b/gi,
|
|
10690
|
+
/(?:\$|€|£)\s*(\d{1,3}(?:[,\s]\d{3})*(?:\.\d{2})?)\s*(?:saved|économisé|gagné)?/gi
|
|
10691
|
+
];
|
|
10692
|
+
static RISK_REVERSAL_PATTERNS = [
|
|
10693
|
+
/\b(\d+[- ]days?\s+free\s+trial|free\s+trial|essai\s+gratuit(?:\s+de\s+\d+\s+jours)?)\b/gi,
|
|
10694
|
+
/\b(no\s+credit\s+card\s+required|sans\s+carte\s+bancaire|sans\s+engagement)\b/gi,
|
|
10695
|
+
/\b(cancel\s+anytime|résiliation\s+à\s+tout\s+moment|satisfait\s+ou\s+remboursé|money[- ]back\s+guarantee)\b/gi,
|
|
10696
|
+
/\b(100%\s+free\s+migration|migration\s+gratuite)\b/gi
|
|
10697
|
+
];
|
|
10698
|
+
static AUTHORITY_PATTERNS = [
|
|
10699
|
+
/(?:featured|seen|mentioned)\s+(?:in|on)\s+([A-Za-z0-9\s,]+)/gi,
|
|
10700
|
+
/(?:vu\s+dans|mentionné\s+par)\s+([A-Za-z0-9\s,]+)/gi,
|
|
10701
|
+
/\b(award[- ]winning|best[- ]rated|élu\s+meilleur|noté\s+4\.\d\/5|rated\s+4\.\d\/5)\b/gi
|
|
10702
|
+
];
|
|
10703
|
+
static extract(content) {
|
|
10704
|
+
if (!content || content.trim().length === 0) {
|
|
10705
|
+
return {
|
|
10706
|
+
totalSignalsCount: 0,
|
|
10707
|
+
testimonials: [],
|
|
10708
|
+
socialProofCounts: [],
|
|
10709
|
+
specificResults: [],
|
|
10710
|
+
riskReversals: [],
|
|
10711
|
+
authorityMentions: [],
|
|
10712
|
+
trustScore: 0,
|
|
10713
|
+
isSufficientForConversion: false
|
|
10714
|
+
};
|
|
10715
|
+
}
|
|
10716
|
+
const socialProofCounts = [];
|
|
10717
|
+
const specificResults = [];
|
|
10718
|
+
const riskReversals = [];
|
|
10719
|
+
const authorityMentions = [];
|
|
10720
|
+
const testimonials = [];
|
|
10721
|
+
for (const pattern of this.SOCIAL_PROOF_PATTERNS) {
|
|
10722
|
+
const matches = content.match(pattern);
|
|
10723
|
+
if (matches) {
|
|
10724
|
+
matches.forEach((m) => {
|
|
10725
|
+
if (!socialProofCounts.includes(m.trim()))
|
|
10726
|
+
socialProofCounts.push(m.trim());
|
|
10727
|
+
});
|
|
10728
|
+
}
|
|
10729
|
+
}
|
|
10730
|
+
for (const pattern of this.RESULT_PATTERNS) {
|
|
10731
|
+
const matches = content.match(pattern);
|
|
10732
|
+
if (matches) {
|
|
10733
|
+
matches.forEach((m) => {
|
|
10734
|
+
if (!specificResults.includes(m.trim()))
|
|
10735
|
+
specificResults.push(m.trim());
|
|
10736
|
+
});
|
|
10737
|
+
}
|
|
10738
|
+
}
|
|
10739
|
+
for (const pattern of this.RISK_REVERSAL_PATTERNS) {
|
|
10740
|
+
const matches = content.match(pattern);
|
|
10741
|
+
if (matches) {
|
|
10742
|
+
matches.forEach((m) => {
|
|
10743
|
+
if (!riskReversals.includes(m.trim()))
|
|
10744
|
+
riskReversals.push(m.trim());
|
|
10745
|
+
});
|
|
10746
|
+
}
|
|
10747
|
+
}
|
|
10748
|
+
for (const pattern of this.AUTHORITY_PATTERNS) {
|
|
10749
|
+
const matches = content.match(pattern);
|
|
10750
|
+
if (matches) {
|
|
10751
|
+
matches.forEach((m) => {
|
|
10752
|
+
if (!authorityMentions.includes(m.trim()))
|
|
10753
|
+
authorityMentions.push(m.trim());
|
|
10754
|
+
});
|
|
10755
|
+
}
|
|
10756
|
+
}
|
|
10757
|
+
const quoteRegex = /"([^"]{25,250})"\s*(?:—|-|by)\s*\*?([A-Z][a-z]+(?:\s+[A-Z]\.?)?)\*?/g;
|
|
10758
|
+
let quoteMatch;
|
|
10759
|
+
while ((quoteMatch = quoteRegex.exec(content)) !== null) {
|
|
10760
|
+
testimonials.push({
|
|
10761
|
+
quote: quoteMatch[1].trim(),
|
|
10762
|
+
author: quoteMatch[2]?.trim()
|
|
10763
|
+
});
|
|
10764
|
+
}
|
|
10765
|
+
const totalSignalsCount = socialProofCounts.length + specificResults.length + riskReversals.length + authorityMentions.length + testimonials.length;
|
|
10766
|
+
let trustScore = Math.min(100, totalSignalsCount * 18);
|
|
10767
|
+
if (testimonials.length > 0)
|
|
10768
|
+
trustScore = Math.min(100, trustScore + 15);
|
|
10769
|
+
if (riskReversals.length > 0)
|
|
10770
|
+
trustScore = Math.min(100, trustScore + 15);
|
|
10771
|
+
const isSufficientForConversion = trustScore >= 50;
|
|
10772
|
+
return {
|
|
10773
|
+
totalSignalsCount,
|
|
10774
|
+
testimonials,
|
|
10775
|
+
socialProofCounts,
|
|
10776
|
+
specificResults,
|
|
10777
|
+
riskReversals,
|
|
10778
|
+
authorityMentions,
|
|
10779
|
+
trustScore,
|
|
10780
|
+
isSufficientForConversion
|
|
10781
|
+
};
|
|
10782
|
+
}
|
|
10783
|
+
}
|
|
10784
|
+
|
|
10396
10785
|
// src/index.ts
|
|
10397
10786
|
function createLynxSeoEngine(config) {
|
|
10398
10787
|
return new LynxSeoEngine(config);
|
|
@@ -10412,6 +10801,9 @@ var LynxSeo = {
|
|
|
10412
10801
|
readabilityComplexity: PassiveVoiceComplexityAnalyzer,
|
|
10413
10802
|
aboveFoldCro: AboveFoldCroAuditor,
|
|
10414
10803
|
contextTemplates: ContextTemplatesGenerator,
|
|
10804
|
+
searchIntent: SearchIntentClassifier,
|
|
10805
|
+
opportunityPrioritizer: SeoOpportunityPrioritizer,
|
|
10806
|
+
trustSignals: TrustSignalsExtractor,
|
|
10415
10807
|
inspectMeta: SiteAuditor.inspectMeta,
|
|
10416
10808
|
crawlDomain: DeepCrawlerAuditor.crawlAndAuditDomain,
|
|
10417
10809
|
inspectHtmlSnapshot: DeepCrawlerAuditor.inspectHtmlSnapshot,
|
|
@@ -10471,6 +10863,7 @@ export {
|
|
|
10471
10863
|
VideoYouTubeAnalyzer,
|
|
10472
10864
|
UrlyticsEngine,
|
|
10473
10865
|
UI_ICONS,
|
|
10866
|
+
TrustSignalsExtractor,
|
|
10474
10867
|
TokenQuotaManager,
|
|
10475
10868
|
TechnicalRulesAuditor,
|
|
10476
10869
|
TeamRbacEngine,
|
|
@@ -10482,8 +10875,10 @@ export {
|
|
|
10482
10875
|
SerpRankHistoryEngine,
|
|
10483
10876
|
SerpLengthComparator,
|
|
10484
10877
|
SerpClient,
|
|
10878
|
+
SeoOpportunityPrioritizer,
|
|
10485
10879
|
SeoOpportunitiesDecayDetector,
|
|
10486
10880
|
SemanticCannibalizationDetector,
|
|
10881
|
+
SearchIntentClassifier,
|
|
10487
10882
|
SchemaGraphBuilder,
|
|
10488
10883
|
SUPPORTED_CANONICAL_LOCALES,
|
|
10489
10884
|
SCHEMA_LOCAL_BUSINESS_MAP,
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 🎯 Search Intent Classifier & SERP Feature Intent Mapper
|
|
3
|
+
* Ported & optimized from Python search_intent_analyzer.py.
|
|
4
|
+
* Classifies queries as: Informational, Navigational, Transactional, or Commercial Investigation.
|
|
5
|
+
*/
|
|
6
|
+
export type QueryIntentClassification = "informational" | "navigational" | "transactional" | "commercial_investigation";
|
|
7
|
+
export interface SearchIntentResult {
|
|
8
|
+
primaryIntent: QueryIntentClassification;
|
|
9
|
+
confidenceScore: number;
|
|
10
|
+
scores: {
|
|
11
|
+
informational: number;
|
|
12
|
+
navigational: number;
|
|
13
|
+
transactional: number;
|
|
14
|
+
commercial_investigation: number;
|
|
15
|
+
};
|
|
16
|
+
detectedSignals: string[];
|
|
17
|
+
recommendedContentFormat: string;
|
|
18
|
+
}
|
|
19
|
+
export declare class SearchIntentClassifier {
|
|
20
|
+
private static readonly INFORMATIONAL_SIGNALS;
|
|
21
|
+
private static readonly NAVIGATIONAL_SIGNALS;
|
|
22
|
+
private static readonly TRANSACTIONAL_SIGNALS;
|
|
23
|
+
private static readonly COMMERCIAL_SIGNALS;
|
|
24
|
+
/**
|
|
25
|
+
* Classifies query intent in < 0.01ms.
|
|
26
|
+
*/
|
|
27
|
+
static classify(keyword: string, serpFeatures?: string[]): SearchIntentResult;
|
|
28
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 📈 Multi-Factor SEO Opportunity Prioritizer
|
|
3
|
+
* Ported & optimized from Python opportunity_scorer.py.
|
|
4
|
+
* Computes an 8-factor Opportunity Score (0-100) using real CTR curves & GSC ranking proximity.
|
|
5
|
+
*/
|
|
6
|
+
export type OpportunityArchetype = "quick_win" | "top_3_push" | "high_volume_gap" | "underperformer" | "declining";
|
|
7
|
+
export interface OpportunityInput {
|
|
8
|
+
keyword: string;
|
|
9
|
+
currentPosition?: number;
|
|
10
|
+
monthlyImpressions?: number;
|
|
11
|
+
monthlyClicks?: number;
|
|
12
|
+
actualCtr?: number;
|
|
13
|
+
searchVolume?: number;
|
|
14
|
+
difficulty?: number;
|
|
15
|
+
intent?: "informational" | "commercial_investigation" | "transactional" | "navigational";
|
|
16
|
+
}
|
|
17
|
+
export interface OpportunityScoreResult {
|
|
18
|
+
opportunityScore: number;
|
|
19
|
+
archetype: OpportunityArchetype;
|
|
20
|
+
estimatedClickGain: number;
|
|
21
|
+
factors: {
|
|
22
|
+
volumeScore: number;
|
|
23
|
+
positionScore: number;
|
|
24
|
+
intentScore: number;
|
|
25
|
+
competitionScore: number;
|
|
26
|
+
ctrImprovementScore: number;
|
|
27
|
+
};
|
|
28
|
+
priorityLevel: "critical" | "high" | "medium" | "low";
|
|
29
|
+
actionPlan: string;
|
|
30
|
+
}
|
|
31
|
+
export declare class SeoOpportunityPrioritizer {
|
|
32
|
+
private static readonly BENCHMARK_CTR;
|
|
33
|
+
/**
|
|
34
|
+
* Calculates comprehensive opportunity score for content prioritization.
|
|
35
|
+
*/
|
|
36
|
+
static evaluate(input: OpportunityInput): OpportunityScoreResult;
|
|
37
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 🛡️ Trust Signals & Social Proof Extractor
|
|
3
|
+
* Ported & optimized from Python trust_signal_analyzer.py.
|
|
4
|
+
* Extracts testimonials, customer numbers, percentage growth stats, risk reversals, and media mentions.
|
|
5
|
+
*/
|
|
6
|
+
export interface ExtractedTrustSignals {
|
|
7
|
+
totalSignalsCount: number;
|
|
8
|
+
testimonials: Array<{
|
|
9
|
+
quote: string;
|
|
10
|
+
author?: string;
|
|
11
|
+
}>;
|
|
12
|
+
socialProofCounts: string[];
|
|
13
|
+
specificResults: string[];
|
|
14
|
+
riskReversals: string[];
|
|
15
|
+
authorityMentions: string[];
|
|
16
|
+
trustScore: number;
|
|
17
|
+
isSufficientForConversion: boolean;
|
|
18
|
+
}
|
|
19
|
+
export declare class TrustSignalsExtractor {
|
|
20
|
+
private static readonly SOCIAL_PROOF_PATTERNS;
|
|
21
|
+
private static readonly RESULT_PATTERNS;
|
|
22
|
+
private static readonly RISK_REVERSAL_PATTERNS;
|
|
23
|
+
private static readonly AUTHORITY_PATTERNS;
|
|
24
|
+
/**
|
|
25
|
+
* Scans text or HTML and extracts all verifiable trust signals.
|
|
26
|
+
*/
|
|
27
|
+
static extract(content: string): ExtractedTrustSignals;
|
|
28
|
+
}
|