@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 CHANGED
@@ -4,7 +4,7 @@ High-Performance Universal Programmatic SEO & Structured Data Engine for Modern
4
4
 
5
5
  [![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue.svg)](https://www.typescriptlang.org/)
6
6
  [![License](https://img.shields.io/badge/License-Proprietary-green.svg)](LICENSE)
7
- [![Version](https://img.shields.io/badge/Version-2.6.0-orange.svg)](https://www.npmjs.com/package/@lynxflow/seo-engine)
7
+ [![Version](https://img.shields.io/badge/Version-2.7.0-orange.svg)](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
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lynxflow/seo-engine",
3
- "version": "2.6.0",
3
+ "version": "2.7.0",
4
4
  "description": "High-Performance Universal Programmatic SEO & AI Search Engine SDK",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",