@lynxflow/seo-engine 2.6.0 → 2.7.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/article-strategic-planner.d.ts +35 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +226 -0
- package/dist/index.mjs +226 -0
- package/dist/landing-channel-scorer.d.ts +41 -0
- package/dist/topic-cluster-architect.d.ts +39 -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
|
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 📝 Strategic Section-by-Section Article Planner
|
|
3
|
+
* Ported & optimized from Python article_planner.py (15.5 KB).
|
|
4
|
+
* Builds a blueprint with word count targets per section, direct answer snippet cards, CTA intensities, and internal linking map.
|
|
5
|
+
*/
|
|
6
|
+
export type SectionType = "intro" | "how_to" | "comparison" | "explanation" | "faq" | "conclusion";
|
|
7
|
+
export type CtaIntensity = "soft" | "medium" | "strong";
|
|
8
|
+
export interface ArticleSectionPlan {
|
|
9
|
+
sectionNumber: number;
|
|
10
|
+
type: SectionType;
|
|
11
|
+
heading: string;
|
|
12
|
+
targetWordCount: number;
|
|
13
|
+
strategicObjective: string;
|
|
14
|
+
hasFeaturedSnippetTarget: boolean;
|
|
15
|
+
ctaIntensity?: CtaIntensity;
|
|
16
|
+
internalLinkAnchors: string[];
|
|
17
|
+
}
|
|
18
|
+
export interface ComprehensiveArticlePlan {
|
|
19
|
+
topic: string;
|
|
20
|
+
targetTotalWords: number;
|
|
21
|
+
estimatedReadingTimeMinutes: number;
|
|
22
|
+
meta: {
|
|
23
|
+
titleOptions: string[];
|
|
24
|
+
metaDescription: string;
|
|
25
|
+
targetKeywords: string[];
|
|
26
|
+
};
|
|
27
|
+
sections: ArticleSectionPlan[];
|
|
28
|
+
ctaMap: Record<string, number>;
|
|
29
|
+
}
|
|
30
|
+
export declare class ArticleStrategicPlanner {
|
|
31
|
+
/**
|
|
32
|
+
* Generates a complete strategic article plan tailored to a target keyword and desired depth.
|
|
33
|
+
*/
|
|
34
|
+
static planArticle(topic: string, primaryKeyword: string, depth?: "standard" | "exhaustive_pillar"): ComprehensiveArticlePlan;
|
|
35
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -128,6 +128,9 @@ import { KeywordDensityGuard } from "./keyword-density-guard";
|
|
|
128
128
|
import { ContentQualityScorer } from "./content-quality-scorer";
|
|
129
129
|
import { LandingCroChecker } from "./landing-cro-checker";
|
|
130
130
|
import { SocialCommunityInsightsAggregator } from "./social-community-insights";
|
|
131
|
+
import { LandingChannelScorer } from "./landing-channel-scorer";
|
|
132
|
+
import { ArticleStrategicPlanner } from "./article-strategic-planner";
|
|
133
|
+
import { TopicClusterArchitect } from "./topic-cluster-architect";
|
|
131
134
|
export declare function createLynxSeoEngine(config: EngineConfig): LynxSeoEngine;
|
|
132
135
|
export declare const LynxSeo: {
|
|
133
136
|
createEngine: typeof createLynxSeoEngine;
|
|
@@ -153,6 +156,9 @@ export declare const LynxSeo: {
|
|
|
153
156
|
contentQuality: typeof ContentQualityScorer;
|
|
154
157
|
landingCroChecker: typeof LandingCroChecker;
|
|
155
158
|
socialInsights: typeof SocialCommunityInsightsAggregator;
|
|
159
|
+
channelScorer: typeof LandingChannelScorer;
|
|
160
|
+
articlePlanner: typeof ArticleStrategicPlanner;
|
|
161
|
+
topicClusters: typeof TopicClusterArchitect;
|
|
156
162
|
inspectMeta: typeof SiteAuditor.inspectMeta;
|
|
157
163
|
crawlDomain: typeof DeepCrawlerAuditor.crawlAndAuditDomain;
|
|
158
164
|
inspectHtmlSnapshot: typeof DeepCrawlerAuditor.inspectHtmlSnapshot;
|
|
@@ -194,6 +200,9 @@ export declare const LynxSeo: {
|
|
|
194
200
|
marketingSkills: typeof MarketingSkillsEngine;
|
|
195
201
|
regenerationTracker: typeof PageRegenerationTracker;
|
|
196
202
|
};
|
|
203
|
+
export * from "./landing-channel-scorer";
|
|
204
|
+
export * from "./article-strategic-planner";
|
|
205
|
+
export * from "./topic-cluster-architect";
|
|
197
206
|
export * from "./content-quality-scorer";
|
|
198
207
|
export * from "./landing-cro-checker";
|
|
199
208
|
export * from "./social-community-insights";
|
package/dist/index.js
CHANGED
|
@@ -11366,6 +11366,226 @@ class SocialCommunityInsightsAggregator {
|
|
|
11366
11366
|
}
|
|
11367
11367
|
}
|
|
11368
11368
|
|
|
11369
|
+
// src/landing-channel-scorer.ts
|
|
11370
|
+
class LandingChannelScorer {
|
|
11371
|
+
static CONFIGS = {
|
|
11372
|
+
seo: { minWords: 1200, optimalWords: 2000, maxWords: 3000, minCtas: 3, optimalCtas: 5, maxDistractions: 6 },
|
|
11373
|
+
ppc: { minWords: 350, optimalWords: 600, maxWords: 900, minCtas: 2, optimalCtas: 3, maxDistractions: 1 }
|
|
11374
|
+
};
|
|
11375
|
+
static scorePage(content, options) {
|
|
11376
|
+
const config = this.CONFIGS[options.channel] || this.CONFIGS.seo;
|
|
11377
|
+
const words = content.split(/\s+/).filter(Boolean);
|
|
11378
|
+
const totalWords = words.length;
|
|
11379
|
+
const ctaMatches = content.match(/(?:start|try|get|claim|demo|free trial|sign up|démarrer|essayer|profiter|obtenir)\s+(?:free|trial|now|today|gratuit|demo)?/gi) || [];
|
|
11380
|
+
const ctaCount = ctaMatches.length;
|
|
11381
|
+
const linkMatches = content.match(/\[.*?\]\(.*?\)/g) || [];
|
|
11382
|
+
const distractionLinksCount = linkMatches.filter((l) => !l.toLowerCase().includes("pricing") && !l.toLowerCase().includes("signup")).length;
|
|
11383
|
+
const hasH1 = /^#\s+.+/m.test(content);
|
|
11384
|
+
const aboveFoldScore = hasH1 && ctaCount >= 1 ? 25 : 12;
|
|
11385
|
+
let ctaScore = 15;
|
|
11386
|
+
if (ctaCount >= config.minCtas && ctaCount <= config.optimalCtas + 2) {
|
|
11387
|
+
ctaScore = 25;
|
|
11388
|
+
} else if (ctaCount === 0) {
|
|
11389
|
+
ctaScore = 0;
|
|
11390
|
+
}
|
|
11391
|
+
const hasProof = /rated|reviews?|avis|clients?|customers?|\d{1,3}(?:,\d{3})*\+/i.test(content);
|
|
11392
|
+
const trustScore = hasProof ? 20 : 5;
|
|
11393
|
+
const hasH2s = (content.match(/^##\s+.+/gm) || []).length >= 2;
|
|
11394
|
+
const structureScore = hasH2s ? 15 : 8;
|
|
11395
|
+
let channelFitScore = 15;
|
|
11396
|
+
const recommendations = [];
|
|
11397
|
+
if (options.channel === "seo") {
|
|
11398
|
+
if (totalWords < config.minWords) {
|
|
11399
|
+
channelFitScore -= 8;
|
|
11400
|
+
recommendations.push(`SEO landing pages need depth (${totalWords}/${config.minWords} words). Expand with FAQ, feature breakdown, and case studies.`);
|
|
11401
|
+
}
|
|
11402
|
+
} else {
|
|
11403
|
+
if (totalWords > config.maxWords) {
|
|
11404
|
+
channelFitScore -= 8;
|
|
11405
|
+
recommendations.push(`PPC landing page is too long (${totalWords} words). Cut text walls down to ~${config.optimalWords} words to maximize instant conversions.`);
|
|
11406
|
+
}
|
|
11407
|
+
if (distractionLinksCount > config.maxDistractions) {
|
|
11408
|
+
channelFitScore -= 7;
|
|
11409
|
+
recommendations.push(`Remove ${distractionLinksCount} navigation/leak links. PPC pages should only offer ONE path to conversion.`);
|
|
11410
|
+
}
|
|
11411
|
+
}
|
|
11412
|
+
if (ctaCount < config.minCtas) {
|
|
11413
|
+
recommendations.push(`Add ${config.minCtas - ctaCount} additional CTA button(s) distributed across the page.`);
|
|
11414
|
+
}
|
|
11415
|
+
if (!hasProof) {
|
|
11416
|
+
recommendations.push("Add social proof indicators (reviews, user counts, client logos).");
|
|
11417
|
+
}
|
|
11418
|
+
const totalScore = Math.max(10, Math.min(100, aboveFoldScore + ctaScore + trustScore + structureScore + channelFitScore));
|
|
11419
|
+
const passed = totalScore >= 70;
|
|
11420
|
+
return {
|
|
11421
|
+
channel: options.channel,
|
|
11422
|
+
score: totalScore,
|
|
11423
|
+
passed,
|
|
11424
|
+
wordCount: totalWords,
|
|
11425
|
+
wordCountTarget: { min: config.minWords, optimal: config.optimalWords, max: config.maxWords },
|
|
11426
|
+
ctaCount,
|
|
11427
|
+
ctaTarget: { min: config.minCtas, optimal: config.optimalWords ? config.optimalCtas : 3 },
|
|
11428
|
+
distractionLinksCount,
|
|
11429
|
+
categoryBreakdown: {
|
|
11430
|
+
aboveTheFold: aboveFoldScore,
|
|
11431
|
+
ctaAlignment: ctaScore,
|
|
11432
|
+
trustSignals: trustScore,
|
|
11433
|
+
structure: structureScore,
|
|
11434
|
+
channelFit: channelFitScore
|
|
11435
|
+
},
|
|
11436
|
+
recommendations
|
|
11437
|
+
};
|
|
11438
|
+
}
|
|
11439
|
+
}
|
|
11440
|
+
|
|
11441
|
+
// src/article-strategic-planner.ts
|
|
11442
|
+
class ArticleStrategicPlanner {
|
|
11443
|
+
static planArticle(topic, primaryKeyword, depth = "standard") {
|
|
11444
|
+
const totalWords = depth === "exhaustive_pillar" ? 3200 : 2200;
|
|
11445
|
+
const readingTime = Math.round(totalWords / 200);
|
|
11446
|
+
const sections = [
|
|
11447
|
+
{
|
|
11448
|
+
sectionNumber: 1,
|
|
11449
|
+
type: "intro",
|
|
11450
|
+
heading: `Introduction & Executive Direct Answer`,
|
|
11451
|
+
targetWordCount: 250,
|
|
11452
|
+
strategicObjective: "Hook the reader with a thought-provoking metric and provide a 40-word Direct Answer for SearchGPT & Gemini AI Overviews.",
|
|
11453
|
+
hasFeaturedSnippetTarget: true,
|
|
11454
|
+
internalLinkAnchors: ["Product Overview", "Platform Features"]
|
|
11455
|
+
},
|
|
11456
|
+
{
|
|
11457
|
+
sectionNumber: 2,
|
|
11458
|
+
type: "explanation",
|
|
11459
|
+
heading: `Why ${primaryKeyword} Matters in 2026`,
|
|
11460
|
+
targetWordCount: depth === "exhaustive_pillar" ? 600 : 450,
|
|
11461
|
+
strategicObjective: "Establish market context, operational bottlenecks of manual workflows, and revenue impact.",
|
|
11462
|
+
hasFeaturedSnippetTarget: false,
|
|
11463
|
+
ctaIntensity: "soft",
|
|
11464
|
+
internalLinkAnchors: ["Pricing & Plans"]
|
|
11465
|
+
},
|
|
11466
|
+
{
|
|
11467
|
+
sectionNumber: 3,
|
|
11468
|
+
type: "comparison",
|
|
11469
|
+
heading: `Top Approaches & Benchmarks Matrix`,
|
|
11470
|
+
targetWordCount: depth === "exhaustive_pillar" ? 750 : 500,
|
|
11471
|
+
strategicObjective: "Present an objective comparison table with speed, latency, feature availability, and pricing.",
|
|
11472
|
+
hasFeaturedSnippetTarget: true,
|
|
11473
|
+
ctaIntensity: "medium",
|
|
11474
|
+
internalLinkAnchors: ["Interactive Calculator", "Case Studies"]
|
|
11475
|
+
},
|
|
11476
|
+
{
|
|
11477
|
+
sectionNumber: 4,
|
|
11478
|
+
type: "how_to",
|
|
11479
|
+
heading: `Step-by-Step Implementation Blueprint`,
|
|
11480
|
+
targetWordCount: depth === "exhaustive_pillar" ? 800 : 550,
|
|
11481
|
+
strategicObjective: "Provide actionable step-by-step tutorial with code snippets, configuration files, and verification criteria.",
|
|
11482
|
+
hasFeaturedSnippetTarget: false,
|
|
11483
|
+
ctaIntensity: "strong",
|
|
11484
|
+
internalLinkAnchors: ["Documentation Hub", "API Reference"]
|
|
11485
|
+
},
|
|
11486
|
+
{
|
|
11487
|
+
sectionNumber: 5,
|
|
11488
|
+
type: "faq",
|
|
11489
|
+
heading: `Frequently Asked Questions about ${primaryKeyword}`,
|
|
11490
|
+
targetWordCount: 350,
|
|
11491
|
+
strategicObjective: "Address top 4 'People Also Ask' search queries to qualify for Google FAQ rich snippet.",
|
|
11492
|
+
hasFeaturedSnippetTarget: true,
|
|
11493
|
+
internalLinkAnchors: []
|
|
11494
|
+
},
|
|
11495
|
+
{
|
|
11496
|
+
sectionNumber: 6,
|
|
11497
|
+
type: "conclusion",
|
|
11498
|
+
heading: `Final Takeaways & Next Steps`,
|
|
11499
|
+
targetWordCount: 150,
|
|
11500
|
+
strategicObjective: "Summarize key findings and present final primary action button.",
|
|
11501
|
+
hasFeaturedSnippetTarget: false,
|
|
11502
|
+
ctaIntensity: "strong",
|
|
11503
|
+
internalLinkAnchors: ["Start Free Trial"]
|
|
11504
|
+
}
|
|
11505
|
+
];
|
|
11506
|
+
return {
|
|
11507
|
+
topic,
|
|
11508
|
+
targetTotalWords: totalWords,
|
|
11509
|
+
estimatedReadingTimeMinutes: readingTime,
|
|
11510
|
+
meta: {
|
|
11511
|
+
titleOptions: [
|
|
11512
|
+
`${topic}: Complete Guide for 2026`,
|
|
11513
|
+
`How to Master ${topic} (Step-by-Step)`,
|
|
11514
|
+
`The Ultimate ${topic} Blueprint: Speed & Accuracy`
|
|
11515
|
+
],
|
|
11516
|
+
metaDescription: `Discover how to master ${topic} in 2026. Explore step-by-step implementation, benchmarks, comparison matrices, and FAQ.`,
|
|
11517
|
+
targetKeywords: [primaryKeyword, `${primaryKeyword} guide`, `best ${primaryKeyword} tools`]
|
|
11518
|
+
},
|
|
11519
|
+
sections,
|
|
11520
|
+
ctaMap: {
|
|
11521
|
+
soft: 2,
|
|
11522
|
+
medium: 3,
|
|
11523
|
+
strong: 4
|
|
11524
|
+
}
|
|
11525
|
+
};
|
|
11526
|
+
}
|
|
11527
|
+
}
|
|
11528
|
+
|
|
11529
|
+
// src/topic-cluster-architect.ts
|
|
11530
|
+
class TopicClusterArchitect {
|
|
11531
|
+
static buildCluster(pillarTopic, subTopics) {
|
|
11532
|
+
const pillarSlug = `/${pillarTopic.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "")}`;
|
|
11533
|
+
let totalVolume = 0;
|
|
11534
|
+
const spokes = subTopics.map((st) => {
|
|
11535
|
+
const slug = `${pillarSlug}/${st.name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "")}`;
|
|
11536
|
+
const vol = st.volume || 1200;
|
|
11537
|
+
totalVolume += vol;
|
|
11538
|
+
return {
|
|
11539
|
+
subTopic: st.name,
|
|
11540
|
+
slug,
|
|
11541
|
+
searchIntent: st.intent || "informational",
|
|
11542
|
+
monthlySearchVolume: vol,
|
|
11543
|
+
keywordDifficulty: st.difficulty || 35,
|
|
11544
|
+
targetWordCount: 1800,
|
|
11545
|
+
inboundLinks: [pillarSlug],
|
|
11546
|
+
outboundLinks: [pillarSlug]
|
|
11547
|
+
};
|
|
11548
|
+
});
|
|
11549
|
+
const crossLinks = [];
|
|
11550
|
+
for (const spoke of spokes) {
|
|
11551
|
+
crossLinks.push({
|
|
11552
|
+
fromSlug: spoke.slug,
|
|
11553
|
+
toSlug: pillarSlug,
|
|
11554
|
+
anchorText: `${pillarTopic} Complete Overview`
|
|
11555
|
+
});
|
|
11556
|
+
crossLinks.push({
|
|
11557
|
+
fromSlug: pillarSlug,
|
|
11558
|
+
toSlug: spoke.slug,
|
|
11559
|
+
anchorText: `Deep dive on ${spoke.subTopic}`
|
|
11560
|
+
});
|
|
11561
|
+
}
|
|
11562
|
+
for (let i = 0;i < spokes.length; i++) {
|
|
11563
|
+
const nextSpoke = spokes[(i + 1) % spokes.length];
|
|
11564
|
+
crossLinks.push({
|
|
11565
|
+
fromSlug: spokes[i].slug,
|
|
11566
|
+
toSlug: nextSpoke.slug,
|
|
11567
|
+
anchorText: `Related: ${nextSpoke.subTopic}`
|
|
11568
|
+
});
|
|
11569
|
+
spokes[i].outboundLinks.push(nextSpoke.slug);
|
|
11570
|
+
nextSpoke.inboundLinks.push(spokes[i].slug);
|
|
11571
|
+
}
|
|
11572
|
+
let authority = "emerging";
|
|
11573
|
+
if (spokes.length >= 6 && totalVolume > 5000)
|
|
11574
|
+
authority = "dominant";
|
|
11575
|
+
else if (spokes.length >= 3)
|
|
11576
|
+
authority = "strong";
|
|
11577
|
+
return {
|
|
11578
|
+
pillarTopic,
|
|
11579
|
+
pillarSlug,
|
|
11580
|
+
totalSubSpokes: spokes.length,
|
|
11581
|
+
totalEstimatedSearchVolume: totalVolume,
|
|
11582
|
+
spokes,
|
|
11583
|
+
crossLinkMatrix: crossLinks,
|
|
11584
|
+
clusterAuthorityPotential: authority
|
|
11585
|
+
};
|
|
11586
|
+
}
|
|
11587
|
+
}
|
|
11588
|
+
|
|
11369
11589
|
// src/index.ts
|
|
11370
11590
|
function createLynxSeoEngine(config) {
|
|
11371
11591
|
return new LynxSeoEngine(config);
|
|
@@ -11394,6 +11614,9 @@ var LynxSeo = {
|
|
|
11394
11614
|
contentQuality: ContentQualityScorer,
|
|
11395
11615
|
landingCroChecker: LandingCroChecker,
|
|
11396
11616
|
socialInsights: SocialCommunityInsightsAggregator,
|
|
11617
|
+
channelScorer: LandingChannelScorer,
|
|
11618
|
+
articlePlanner: ArticleStrategicPlanner,
|
|
11619
|
+
topicClusters: TopicClusterArchitect,
|
|
11397
11620
|
inspectMeta: SiteAuditor.inspectMeta,
|
|
11398
11621
|
crawlDomain: DeepCrawlerAuditor.crawlAndAuditDomain,
|
|
11399
11622
|
inspectHtmlSnapshot: DeepCrawlerAuditor.inspectHtmlSnapshot,
|
|
@@ -11454,6 +11677,7 @@ export {
|
|
|
11454
11677
|
UrlyticsEngine,
|
|
11455
11678
|
UI_ICONS,
|
|
11456
11679
|
TrustSignalsExtractor,
|
|
11680
|
+
TopicClusterArchitect,
|
|
11457
11681
|
TokenQuotaManager,
|
|
11458
11682
|
TechnicalRulesAuditor,
|
|
11459
11683
|
TeamRbacEngine,
|
|
@@ -11499,6 +11723,7 @@ export {
|
|
|
11499
11723
|
LlmContentCleaner,
|
|
11500
11724
|
LegalDisclaimerEngine,
|
|
11501
11725
|
LandingCroChecker,
|
|
11726
|
+
LandingChannelScorer,
|
|
11502
11727
|
LagoTokenMeter,
|
|
11503
11728
|
KnowledgeGraphLinker,
|
|
11504
11729
|
KnowledgeBankBuilder,
|
|
@@ -11533,6 +11758,7 @@ export {
|
|
|
11533
11758
|
BacklinksClient,
|
|
11534
11759
|
BUILT_IN_LOCATIONS_DATABASE,
|
|
11535
11760
|
BRAND_ICONS,
|
|
11761
|
+
ArticleStrategicPlanner,
|
|
11536
11762
|
ApiKeyGuardian,
|
|
11537
11763
|
AiCopilotClient,
|
|
11538
11764
|
AiBotsLogAnalyzer,
|
package/dist/index.mjs
CHANGED
|
@@ -11366,6 +11366,226 @@ class SocialCommunityInsightsAggregator {
|
|
|
11366
11366
|
}
|
|
11367
11367
|
}
|
|
11368
11368
|
|
|
11369
|
+
// src/landing-channel-scorer.ts
|
|
11370
|
+
class LandingChannelScorer {
|
|
11371
|
+
static CONFIGS = {
|
|
11372
|
+
seo: { minWords: 1200, optimalWords: 2000, maxWords: 3000, minCtas: 3, optimalCtas: 5, maxDistractions: 6 },
|
|
11373
|
+
ppc: { minWords: 350, optimalWords: 600, maxWords: 900, minCtas: 2, optimalCtas: 3, maxDistractions: 1 }
|
|
11374
|
+
};
|
|
11375
|
+
static scorePage(content, options) {
|
|
11376
|
+
const config = this.CONFIGS[options.channel] || this.CONFIGS.seo;
|
|
11377
|
+
const words = content.split(/\s+/).filter(Boolean);
|
|
11378
|
+
const totalWords = words.length;
|
|
11379
|
+
const ctaMatches = content.match(/(?:start|try|get|claim|demo|free trial|sign up|démarrer|essayer|profiter|obtenir)\s+(?:free|trial|now|today|gratuit|demo)?/gi) || [];
|
|
11380
|
+
const ctaCount = ctaMatches.length;
|
|
11381
|
+
const linkMatches = content.match(/\[.*?\]\(.*?\)/g) || [];
|
|
11382
|
+
const distractionLinksCount = linkMatches.filter((l) => !l.toLowerCase().includes("pricing") && !l.toLowerCase().includes("signup")).length;
|
|
11383
|
+
const hasH1 = /^#\s+.+/m.test(content);
|
|
11384
|
+
const aboveFoldScore = hasH1 && ctaCount >= 1 ? 25 : 12;
|
|
11385
|
+
let ctaScore = 15;
|
|
11386
|
+
if (ctaCount >= config.minCtas && ctaCount <= config.optimalCtas + 2) {
|
|
11387
|
+
ctaScore = 25;
|
|
11388
|
+
} else if (ctaCount === 0) {
|
|
11389
|
+
ctaScore = 0;
|
|
11390
|
+
}
|
|
11391
|
+
const hasProof = /rated|reviews?|avis|clients?|customers?|\d{1,3}(?:,\d{3})*\+/i.test(content);
|
|
11392
|
+
const trustScore = hasProof ? 20 : 5;
|
|
11393
|
+
const hasH2s = (content.match(/^##\s+.+/gm) || []).length >= 2;
|
|
11394
|
+
const structureScore = hasH2s ? 15 : 8;
|
|
11395
|
+
let channelFitScore = 15;
|
|
11396
|
+
const recommendations = [];
|
|
11397
|
+
if (options.channel === "seo") {
|
|
11398
|
+
if (totalWords < config.minWords) {
|
|
11399
|
+
channelFitScore -= 8;
|
|
11400
|
+
recommendations.push(`SEO landing pages need depth (${totalWords}/${config.minWords} words). Expand with FAQ, feature breakdown, and case studies.`);
|
|
11401
|
+
}
|
|
11402
|
+
} else {
|
|
11403
|
+
if (totalWords > config.maxWords) {
|
|
11404
|
+
channelFitScore -= 8;
|
|
11405
|
+
recommendations.push(`PPC landing page is too long (${totalWords} words). Cut text walls down to ~${config.optimalWords} words to maximize instant conversions.`);
|
|
11406
|
+
}
|
|
11407
|
+
if (distractionLinksCount > config.maxDistractions) {
|
|
11408
|
+
channelFitScore -= 7;
|
|
11409
|
+
recommendations.push(`Remove ${distractionLinksCount} navigation/leak links. PPC pages should only offer ONE path to conversion.`);
|
|
11410
|
+
}
|
|
11411
|
+
}
|
|
11412
|
+
if (ctaCount < config.minCtas) {
|
|
11413
|
+
recommendations.push(`Add ${config.minCtas - ctaCount} additional CTA button(s) distributed across the page.`);
|
|
11414
|
+
}
|
|
11415
|
+
if (!hasProof) {
|
|
11416
|
+
recommendations.push("Add social proof indicators (reviews, user counts, client logos).");
|
|
11417
|
+
}
|
|
11418
|
+
const totalScore = Math.max(10, Math.min(100, aboveFoldScore + ctaScore + trustScore + structureScore + channelFitScore));
|
|
11419
|
+
const passed = totalScore >= 70;
|
|
11420
|
+
return {
|
|
11421
|
+
channel: options.channel,
|
|
11422
|
+
score: totalScore,
|
|
11423
|
+
passed,
|
|
11424
|
+
wordCount: totalWords,
|
|
11425
|
+
wordCountTarget: { min: config.minWords, optimal: config.optimalWords, max: config.maxWords },
|
|
11426
|
+
ctaCount,
|
|
11427
|
+
ctaTarget: { min: config.minCtas, optimal: config.optimalWords ? config.optimalCtas : 3 },
|
|
11428
|
+
distractionLinksCount,
|
|
11429
|
+
categoryBreakdown: {
|
|
11430
|
+
aboveTheFold: aboveFoldScore,
|
|
11431
|
+
ctaAlignment: ctaScore,
|
|
11432
|
+
trustSignals: trustScore,
|
|
11433
|
+
structure: structureScore,
|
|
11434
|
+
channelFit: channelFitScore
|
|
11435
|
+
},
|
|
11436
|
+
recommendations
|
|
11437
|
+
};
|
|
11438
|
+
}
|
|
11439
|
+
}
|
|
11440
|
+
|
|
11441
|
+
// src/article-strategic-planner.ts
|
|
11442
|
+
class ArticleStrategicPlanner {
|
|
11443
|
+
static planArticle(topic, primaryKeyword, depth = "standard") {
|
|
11444
|
+
const totalWords = depth === "exhaustive_pillar" ? 3200 : 2200;
|
|
11445
|
+
const readingTime = Math.round(totalWords / 200);
|
|
11446
|
+
const sections = [
|
|
11447
|
+
{
|
|
11448
|
+
sectionNumber: 1,
|
|
11449
|
+
type: "intro",
|
|
11450
|
+
heading: `Introduction & Executive Direct Answer`,
|
|
11451
|
+
targetWordCount: 250,
|
|
11452
|
+
strategicObjective: "Hook the reader with a thought-provoking metric and provide a 40-word Direct Answer for SearchGPT & Gemini AI Overviews.",
|
|
11453
|
+
hasFeaturedSnippetTarget: true,
|
|
11454
|
+
internalLinkAnchors: ["Product Overview", "Platform Features"]
|
|
11455
|
+
},
|
|
11456
|
+
{
|
|
11457
|
+
sectionNumber: 2,
|
|
11458
|
+
type: "explanation",
|
|
11459
|
+
heading: `Why ${primaryKeyword} Matters in 2026`,
|
|
11460
|
+
targetWordCount: depth === "exhaustive_pillar" ? 600 : 450,
|
|
11461
|
+
strategicObjective: "Establish market context, operational bottlenecks of manual workflows, and revenue impact.",
|
|
11462
|
+
hasFeaturedSnippetTarget: false,
|
|
11463
|
+
ctaIntensity: "soft",
|
|
11464
|
+
internalLinkAnchors: ["Pricing & Plans"]
|
|
11465
|
+
},
|
|
11466
|
+
{
|
|
11467
|
+
sectionNumber: 3,
|
|
11468
|
+
type: "comparison",
|
|
11469
|
+
heading: `Top Approaches & Benchmarks Matrix`,
|
|
11470
|
+
targetWordCount: depth === "exhaustive_pillar" ? 750 : 500,
|
|
11471
|
+
strategicObjective: "Present an objective comparison table with speed, latency, feature availability, and pricing.",
|
|
11472
|
+
hasFeaturedSnippetTarget: true,
|
|
11473
|
+
ctaIntensity: "medium",
|
|
11474
|
+
internalLinkAnchors: ["Interactive Calculator", "Case Studies"]
|
|
11475
|
+
},
|
|
11476
|
+
{
|
|
11477
|
+
sectionNumber: 4,
|
|
11478
|
+
type: "how_to",
|
|
11479
|
+
heading: `Step-by-Step Implementation Blueprint`,
|
|
11480
|
+
targetWordCount: depth === "exhaustive_pillar" ? 800 : 550,
|
|
11481
|
+
strategicObjective: "Provide actionable step-by-step tutorial with code snippets, configuration files, and verification criteria.",
|
|
11482
|
+
hasFeaturedSnippetTarget: false,
|
|
11483
|
+
ctaIntensity: "strong",
|
|
11484
|
+
internalLinkAnchors: ["Documentation Hub", "API Reference"]
|
|
11485
|
+
},
|
|
11486
|
+
{
|
|
11487
|
+
sectionNumber: 5,
|
|
11488
|
+
type: "faq",
|
|
11489
|
+
heading: `Frequently Asked Questions about ${primaryKeyword}`,
|
|
11490
|
+
targetWordCount: 350,
|
|
11491
|
+
strategicObjective: "Address top 4 'People Also Ask' search queries to qualify for Google FAQ rich snippet.",
|
|
11492
|
+
hasFeaturedSnippetTarget: true,
|
|
11493
|
+
internalLinkAnchors: []
|
|
11494
|
+
},
|
|
11495
|
+
{
|
|
11496
|
+
sectionNumber: 6,
|
|
11497
|
+
type: "conclusion",
|
|
11498
|
+
heading: `Final Takeaways & Next Steps`,
|
|
11499
|
+
targetWordCount: 150,
|
|
11500
|
+
strategicObjective: "Summarize key findings and present final primary action button.",
|
|
11501
|
+
hasFeaturedSnippetTarget: false,
|
|
11502
|
+
ctaIntensity: "strong",
|
|
11503
|
+
internalLinkAnchors: ["Start Free Trial"]
|
|
11504
|
+
}
|
|
11505
|
+
];
|
|
11506
|
+
return {
|
|
11507
|
+
topic,
|
|
11508
|
+
targetTotalWords: totalWords,
|
|
11509
|
+
estimatedReadingTimeMinutes: readingTime,
|
|
11510
|
+
meta: {
|
|
11511
|
+
titleOptions: [
|
|
11512
|
+
`${topic}: Complete Guide for 2026`,
|
|
11513
|
+
`How to Master ${topic} (Step-by-Step)`,
|
|
11514
|
+
`The Ultimate ${topic} Blueprint: Speed & Accuracy`
|
|
11515
|
+
],
|
|
11516
|
+
metaDescription: `Discover how to master ${topic} in 2026. Explore step-by-step implementation, benchmarks, comparison matrices, and FAQ.`,
|
|
11517
|
+
targetKeywords: [primaryKeyword, `${primaryKeyword} guide`, `best ${primaryKeyword} tools`]
|
|
11518
|
+
},
|
|
11519
|
+
sections,
|
|
11520
|
+
ctaMap: {
|
|
11521
|
+
soft: 2,
|
|
11522
|
+
medium: 3,
|
|
11523
|
+
strong: 4
|
|
11524
|
+
}
|
|
11525
|
+
};
|
|
11526
|
+
}
|
|
11527
|
+
}
|
|
11528
|
+
|
|
11529
|
+
// src/topic-cluster-architect.ts
|
|
11530
|
+
class TopicClusterArchitect {
|
|
11531
|
+
static buildCluster(pillarTopic, subTopics) {
|
|
11532
|
+
const pillarSlug = `/${pillarTopic.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "")}`;
|
|
11533
|
+
let totalVolume = 0;
|
|
11534
|
+
const spokes = subTopics.map((st) => {
|
|
11535
|
+
const slug = `${pillarSlug}/${st.name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "")}`;
|
|
11536
|
+
const vol = st.volume || 1200;
|
|
11537
|
+
totalVolume += vol;
|
|
11538
|
+
return {
|
|
11539
|
+
subTopic: st.name,
|
|
11540
|
+
slug,
|
|
11541
|
+
searchIntent: st.intent || "informational",
|
|
11542
|
+
monthlySearchVolume: vol,
|
|
11543
|
+
keywordDifficulty: st.difficulty || 35,
|
|
11544
|
+
targetWordCount: 1800,
|
|
11545
|
+
inboundLinks: [pillarSlug],
|
|
11546
|
+
outboundLinks: [pillarSlug]
|
|
11547
|
+
};
|
|
11548
|
+
});
|
|
11549
|
+
const crossLinks = [];
|
|
11550
|
+
for (const spoke of spokes) {
|
|
11551
|
+
crossLinks.push({
|
|
11552
|
+
fromSlug: spoke.slug,
|
|
11553
|
+
toSlug: pillarSlug,
|
|
11554
|
+
anchorText: `${pillarTopic} Complete Overview`
|
|
11555
|
+
});
|
|
11556
|
+
crossLinks.push({
|
|
11557
|
+
fromSlug: pillarSlug,
|
|
11558
|
+
toSlug: spoke.slug,
|
|
11559
|
+
anchorText: `Deep dive on ${spoke.subTopic}`
|
|
11560
|
+
});
|
|
11561
|
+
}
|
|
11562
|
+
for (let i = 0;i < spokes.length; i++) {
|
|
11563
|
+
const nextSpoke = spokes[(i + 1) % spokes.length];
|
|
11564
|
+
crossLinks.push({
|
|
11565
|
+
fromSlug: spokes[i].slug,
|
|
11566
|
+
toSlug: nextSpoke.slug,
|
|
11567
|
+
anchorText: `Related: ${nextSpoke.subTopic}`
|
|
11568
|
+
});
|
|
11569
|
+
spokes[i].outboundLinks.push(nextSpoke.slug);
|
|
11570
|
+
nextSpoke.inboundLinks.push(spokes[i].slug);
|
|
11571
|
+
}
|
|
11572
|
+
let authority = "emerging";
|
|
11573
|
+
if (spokes.length >= 6 && totalVolume > 5000)
|
|
11574
|
+
authority = "dominant";
|
|
11575
|
+
else if (spokes.length >= 3)
|
|
11576
|
+
authority = "strong";
|
|
11577
|
+
return {
|
|
11578
|
+
pillarTopic,
|
|
11579
|
+
pillarSlug,
|
|
11580
|
+
totalSubSpokes: spokes.length,
|
|
11581
|
+
totalEstimatedSearchVolume: totalVolume,
|
|
11582
|
+
spokes,
|
|
11583
|
+
crossLinkMatrix: crossLinks,
|
|
11584
|
+
clusterAuthorityPotential: authority
|
|
11585
|
+
};
|
|
11586
|
+
}
|
|
11587
|
+
}
|
|
11588
|
+
|
|
11369
11589
|
// src/index.ts
|
|
11370
11590
|
function createLynxSeoEngine(config) {
|
|
11371
11591
|
return new LynxSeoEngine(config);
|
|
@@ -11394,6 +11614,9 @@ var LynxSeo = {
|
|
|
11394
11614
|
contentQuality: ContentQualityScorer,
|
|
11395
11615
|
landingCroChecker: LandingCroChecker,
|
|
11396
11616
|
socialInsights: SocialCommunityInsightsAggregator,
|
|
11617
|
+
channelScorer: LandingChannelScorer,
|
|
11618
|
+
articlePlanner: ArticleStrategicPlanner,
|
|
11619
|
+
topicClusters: TopicClusterArchitect,
|
|
11397
11620
|
inspectMeta: SiteAuditor.inspectMeta,
|
|
11398
11621
|
crawlDomain: DeepCrawlerAuditor.crawlAndAuditDomain,
|
|
11399
11622
|
inspectHtmlSnapshot: DeepCrawlerAuditor.inspectHtmlSnapshot,
|
|
@@ -11454,6 +11677,7 @@ export {
|
|
|
11454
11677
|
UrlyticsEngine,
|
|
11455
11678
|
UI_ICONS,
|
|
11456
11679
|
TrustSignalsExtractor,
|
|
11680
|
+
TopicClusterArchitect,
|
|
11457
11681
|
TokenQuotaManager,
|
|
11458
11682
|
TechnicalRulesAuditor,
|
|
11459
11683
|
TeamRbacEngine,
|
|
@@ -11499,6 +11723,7 @@ export {
|
|
|
11499
11723
|
LlmContentCleaner,
|
|
11500
11724
|
LegalDisclaimerEngine,
|
|
11501
11725
|
LandingCroChecker,
|
|
11726
|
+
LandingChannelScorer,
|
|
11502
11727
|
LagoTokenMeter,
|
|
11503
11728
|
KnowledgeGraphLinker,
|
|
11504
11729
|
KnowledgeBankBuilder,
|
|
@@ -11533,6 +11758,7 @@ export {
|
|
|
11533
11758
|
BacklinksClient,
|
|
11534
11759
|
BUILT_IN_LOCATIONS_DATABASE,
|
|
11535
11760
|
BRAND_ICONS,
|
|
11761
|
+
ArticleStrategicPlanner,
|
|
11536
11762
|
ApiKeyGuardian,
|
|
11537
11763
|
AiCopilotClient,
|
|
11538
11764
|
AiBotsLogAnalyzer,
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 🎯 Dual-Channel Landing Page Scorer (SEO vs. PPC)
|
|
3
|
+
* Ported & optimized from Python landing_page_scorer.py (28 KB).
|
|
4
|
+
* Evaluates landing pages with tailored benchmarks for SEO (1,500-2,500 words) vs PPC (400-800 words, zero leak links).
|
|
5
|
+
*/
|
|
6
|
+
export interface ChannelScoringConfig {
|
|
7
|
+
channel: "seo" | "ppc";
|
|
8
|
+
conversionGoal?: "trial" | "demo" | "lead";
|
|
9
|
+
}
|
|
10
|
+
export interface ChannelScoringReport {
|
|
11
|
+
channel: "seo" | "ppc";
|
|
12
|
+
score: number;
|
|
13
|
+
passed: boolean;
|
|
14
|
+
wordCount: number;
|
|
15
|
+
wordCountTarget: {
|
|
16
|
+
min: number;
|
|
17
|
+
optimal: number;
|
|
18
|
+
max: number;
|
|
19
|
+
};
|
|
20
|
+
ctaCount: number;
|
|
21
|
+
ctaTarget: {
|
|
22
|
+
min: number;
|
|
23
|
+
optimal: number;
|
|
24
|
+
};
|
|
25
|
+
distractionLinksCount: number;
|
|
26
|
+
categoryBreakdown: {
|
|
27
|
+
aboveTheFold: number;
|
|
28
|
+
ctaAlignment: number;
|
|
29
|
+
trustSignals: number;
|
|
30
|
+
structure: number;
|
|
31
|
+
channelFit: number;
|
|
32
|
+
};
|
|
33
|
+
recommendations: string[];
|
|
34
|
+
}
|
|
35
|
+
export declare class LandingChannelScorer {
|
|
36
|
+
private static readonly CONFIGS;
|
|
37
|
+
/**
|
|
38
|
+
* Scores landing page according to the specific acquisition channel.
|
|
39
|
+
*/
|
|
40
|
+
static scorePage(content: string, options: ChannelScoringConfig): ChannelScoringReport;
|
|
41
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 🕸️ Topic Cluster & Pillar-Spoke Cross-Linking Architect
|
|
3
|
+
* Ported & optimized from Python research_topic_clusters.py (22 KB).
|
|
4
|
+
* Builds a complete 1-to-N topic cluster map with pillar hub, long-tail sub-spokes, and bidirectional internal links.
|
|
5
|
+
*/
|
|
6
|
+
export interface TopicSpokeNode {
|
|
7
|
+
subTopic: string;
|
|
8
|
+
slug: string;
|
|
9
|
+
searchIntent: "informational" | "commercial_investigation" | "transactional";
|
|
10
|
+
monthlySearchVolume: number;
|
|
11
|
+
keywordDifficulty: number;
|
|
12
|
+
targetWordCount: number;
|
|
13
|
+
inboundLinks: string[];
|
|
14
|
+
outboundLinks: string[];
|
|
15
|
+
}
|
|
16
|
+
export interface TopicClusterTopology {
|
|
17
|
+
pillarTopic: string;
|
|
18
|
+
pillarSlug: string;
|
|
19
|
+
totalSubSpokes: number;
|
|
20
|
+
totalEstimatedSearchVolume: number;
|
|
21
|
+
spokes: TopicSpokeNode[];
|
|
22
|
+
crossLinkMatrix: Array<{
|
|
23
|
+
fromSlug: string;
|
|
24
|
+
toSlug: string;
|
|
25
|
+
anchorText: string;
|
|
26
|
+
}>;
|
|
27
|
+
clusterAuthorityPotential: "dominant" | "strong" | "emerging";
|
|
28
|
+
}
|
|
29
|
+
export declare class TopicClusterArchitect {
|
|
30
|
+
/**
|
|
31
|
+
* Generates a fully mapped topic cluster with cross-links.
|
|
32
|
+
*/
|
|
33
|
+
static buildCluster(pillarTopic: string, subTopics: Array<{
|
|
34
|
+
name: string;
|
|
35
|
+
volume?: number;
|
|
36
|
+
difficulty?: number;
|
|
37
|
+
intent?: "informational" | "commercial_investigation" | "transactional";
|
|
38
|
+
}>): TopicClusterTopology;
|
|
39
|
+
}
|