@lynxflow/seo-engine 2.5.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/dist/index.js CHANGED
@@ -11049,6 +11049,543 @@ class KeywordDensityGuard {
11049
11049
  }
11050
11050
  }
11051
11051
 
11052
+ // src/content-quality-scorer.ts
11053
+ class ContentQualityScorer {
11054
+ static VAGUE_WORDS = [
11055
+ /\b(?:many|some|various|numerous|several|often|sometimes|usually|generally|typically|significantly|substantial|greatly|very|really|quite|relatively)\b/gi
11056
+ ];
11057
+ static SPECIFICITY_DATA_PATTERNS = [
11058
+ /\b\d{1,3}%\b/g,
11059
+ /(?:\$|€|£)\s*[\d,]+(?:\.\d{2})?\b/g,
11060
+ /\b\d{4}\b/g,
11061
+ /\b\d+(?:,\d{3})*\s*(?:users?|customers?|businesses?|downloads?|hours?|minutes?)\b/gi,
11062
+ /\"[^\"]{10,120}\"/g
11063
+ ];
11064
+ static CONVERSATIONAL_DEVICES = [
11065
+ /\([^)]{5,40}\)/g,
11066
+ /\?(?:\s|$)/g,
11067
+ /\b(?:don't|can't|won't|you're|you've|it's|that's|here's|let's|we've|we're|I've)\b/gi,
11068
+ /(?:^|\.\s+)(?:Look|Here['’]s the thing|The truth is|Sound familiar|Trust me|Regardez|En clair)/gi
11069
+ ];
11070
+ static scoreContent(content, metadata) {
11071
+ if (!content || content.trim().length === 0) {
11072
+ return this.emptyReport();
11073
+ }
11074
+ const words = content.split(/\s+/).filter(Boolean);
11075
+ const totalWords = words.length;
11076
+ let conversationalHits = 0;
11077
+ for (const pat of this.CONVERSATIONAL_DEVICES) {
11078
+ conversationalHits += (content.match(pat) || []).length;
11079
+ }
11080
+ const humanityRaw = Math.min(100, Math.round(conversationalHits / Math.max(1, totalWords / 80) * 60) + 30);
11081
+ const humanityScore = Math.max(20, Math.min(100, humanityRaw));
11082
+ let dataPointsCount = 0;
11083
+ for (const pat of this.SPECIFICITY_DATA_PATTERNS) {
11084
+ dataPointsCount += (content.match(pat) || []).length;
11085
+ }
11086
+ let vagueWordsCount = 0;
11087
+ for (const pat of this.VAGUE_WORDS) {
11088
+ vagueWordsCount += (content.match(pat) || []).length;
11089
+ }
11090
+ const specificityRatio = dataPointsCount / Math.max(1, vagueWordsCount * 0.5 + 2);
11091
+ const specificityScore = Math.max(25, Math.min(100, Math.round(specificityRatio * 35 + 40)));
11092
+ const lines = content.split(`
11093
+ `).map((l) => l.trim()).filter(Boolean);
11094
+ const listLines = lines.filter((l) => l.startsWith("-") || l.startsWith("*") || l.startsWith("1.") || l.startsWith("2.")).length;
11095
+ const proseLines = lines.filter((l) => !l.startsWith("#") && !l.startsWith("-") && !l.startsWith("*") && !l.startsWith("1.")).length;
11096
+ const totalContentLines = Math.max(1, listLines + proseLines);
11097
+ const proseRatio = Math.round(proseLines / totalContentLines * 100) / 100;
11098
+ let structureScore = 85;
11099
+ const structureIssues = [];
11100
+ if (proseRatio < 0.4) {
11101
+ structureScore = 50;
11102
+ structureIssues.push("Too list-heavy (> 60% bullet points). Add explanatory narrative prose paragraphs between lists.");
11103
+ } else if (proseRatio > 0.85) {
11104
+ structureScore = 65;
11105
+ structureIssues.push("Too prose-heavy (> 85% text walls). Break up long sections with bullet lists, comparison tables, or callouts.");
11106
+ }
11107
+ let seoScore = 80;
11108
+ const seoIssues = [];
11109
+ const hasH1 = lines.some((l) => l.startsWith("# "));
11110
+ const h2Count = lines.filter((l) => l.startsWith("## ")).length;
11111
+ if (!hasH1) {
11112
+ seoScore -= 25;
11113
+ seoIssues.push("Missing H1 title heading.");
11114
+ }
11115
+ if (h2Count < 2) {
11116
+ seoScore -= 20;
11117
+ seoIssues.push("Fewer than 2 H2 subheadings found. Structure content with clear sub-topics.");
11118
+ }
11119
+ if (metadata?.primaryKeyword && !content.toLowerCase().includes(metadata.primaryKeyword.toLowerCase())) {
11120
+ seoScore -= 20;
11121
+ seoIssues.push(`Target keyword "${metadata.primaryKeyword}" is missing from body text.`);
11122
+ }
11123
+ const avgSentenceLength = Math.round(totalWords / Math.max(1, content.split(/[.?!]/).length) * 10) / 10;
11124
+ let readabilityScore = 85;
11125
+ const readIssues = [];
11126
+ if (avgSentenceLength > 20) {
11127
+ readabilityScore = 55;
11128
+ readIssues.push(`Average sentence length is ${avgSentenceLength} words (Target: 13-17 words).`);
11129
+ }
11130
+ const composite = Math.round(humanityScore * 0.3 + specificityScore * 0.25 + structureScore * 0.2 + seoScore * 0.15 + readabilityScore * 0.1);
11131
+ const passes = composite >= 70;
11132
+ let grade = "F";
11133
+ if (composite >= 90)
11134
+ grade = "A+";
11135
+ else if (composite >= 80)
11136
+ grade = "A";
11137
+ else if (composite >= 70)
11138
+ grade = "B";
11139
+ else if (composite >= 55)
11140
+ grade = "C";
11141
+ const topFixes = [
11142
+ ...structureIssues,
11143
+ ...seoIssues,
11144
+ ...readIssues
11145
+ ];
11146
+ if (specificityScore < 70) {
11147
+ topFixes.push("Inject verified data points (specific dollar amounts, percentage improvements, dates) instead of vague generalities.");
11148
+ }
11149
+ if (humanityScore < 70) {
11150
+ topFixes.push("Adopt a conversational tone: add contractions (you're, it's), rhetorical questions, and casual transition words.");
11151
+ }
11152
+ return {
11153
+ compositeScore: composite,
11154
+ passesQualityThreshold: passes,
11155
+ grade,
11156
+ dimensions: {
11157
+ humanity: {
11158
+ score: humanityScore,
11159
+ weight: 0.3,
11160
+ weightedScore: Math.round(humanityScore * 0.3),
11161
+ strengths: humanityScore >= 75 ? ["Conversational tone", "Natural pacing"] : [],
11162
+ issues: humanityScore < 75 ? ["Sounds slightly robotic or formal"] : []
11163
+ },
11164
+ specificity: {
11165
+ score: specificityScore,
11166
+ weight: 0.25,
11167
+ weightedScore: Math.round(specificityScore * 0.25),
11168
+ strengths: specificityScore >= 75 ? ["High hard-data density", "Concrete benchmarks"] : [],
11169
+ issues: specificityScore < 75 ? ["Excessive vague words (many, several, often)"] : []
11170
+ },
11171
+ structureBalance: {
11172
+ score: structureScore,
11173
+ weight: 0.2,
11174
+ weightedScore: Math.round(structureScore * 0.2),
11175
+ strengths: structureScore >= 75 ? ["Balanced mix of prose and scannable lists"] : [],
11176
+ issues: structureIssues
11177
+ },
11178
+ seoCompliance: {
11179
+ score: seoScore,
11180
+ weight: 0.15,
11181
+ weightedScore: Math.round(seoScore * 0.15),
11182
+ strengths: seoScore >= 75 ? ["Proper H1/H2 hierarchy"] : [],
11183
+ issues: seoIssues
11184
+ },
11185
+ readability: {
11186
+ score: readabilityScore,
11187
+ weight: 0.1,
11188
+ weightedScore: Math.round(readabilityScore * 0.1),
11189
+ strengths: readabilityScore >= 75 ? ["Punchy sentence lengths"] : [],
11190
+ issues: readIssues
11191
+ }
11192
+ },
11193
+ proseToListRatio: proseRatio,
11194
+ topPriorityFixes: topFixes
11195
+ };
11196
+ }
11197
+ static emptyReport() {
11198
+ return {
11199
+ compositeScore: 0,
11200
+ passesQualityThreshold: false,
11201
+ grade: "F",
11202
+ dimensions: {
11203
+ humanity: { score: 0, weight: 0.3, weightedScore: 0, strengths: [], issues: [] },
11204
+ specificity: { score: 0, weight: 0.25, weightedScore: 0, strengths: [], issues: [] },
11205
+ structureBalance: { score: 0, weight: 0.2, weightedScore: 0, strengths: [], issues: [] },
11206
+ seoCompliance: { score: 0, weight: 0.15, weightedScore: 0, strengths: [], issues: [] },
11207
+ readability: { score: 0, weight: 0.1, weightedScore: 0, strengths: [], issues: [] }
11208
+ },
11209
+ proseToListRatio: 0,
11210
+ topPriorityFixes: ["Provide text content to score."]
11211
+ };
11212
+ }
11213
+ }
11214
+
11215
+ // src/landing-cro-checker.ts
11216
+ class LandingCroChecker {
11217
+ static auditLandingPage(content, config = {}) {
11218
+ const checks = [];
11219
+ const lines = content.split(`
11220
+ `).map((l) => l.trim()).filter(Boolean);
11221
+ const h1 = lines.find((l) => l.startsWith("# "))?.replace("# ", "") || "";
11222
+ const h1Words = h1.split(/\s+/).filter(Boolean).length;
11223
+ const isH1Clear = h1Words >= 4 && h1Words <= 14;
11224
+ checks.push({
11225
+ id: "h1_length",
11226
+ name: "Headline Length & Outcome Focus",
11227
+ category: "headline",
11228
+ passed: isH1Clear,
11229
+ importance: "critical",
11230
+ feedback: isH1Clear ? "H1 is clear, outcome-focused, and scannable in < 3s." : "H1 is missing or too long/vague."
11231
+ });
11232
+ const hasValueProp = /save|boost|cut|grow|automate|increase|réduire|augmenter|gagner/i.test(content);
11233
+ checks.push({
11234
+ id: "value_prop_benefit",
11235
+ name: "Concrete Benefit & Outcome",
11236
+ category: "value_prop",
11237
+ passed: hasValueProp,
11238
+ importance: "critical",
11239
+ feedback: hasValueProp ? "Explicit outcome/benefit articulated in content." : "Lacks explicit outcome/benefit statements."
11240
+ });
11241
+ const hasReviews = /rated|reviews?|avis|clients?|customers?|businesses?|\d{1,3}(?:,\d{3})*\+/i.test(content);
11242
+ checks.push({
11243
+ id: "social_proof_ratings",
11244
+ name: "Customer Reviews & Numbers",
11245
+ category: "social_proof",
11246
+ passed: hasReviews,
11247
+ importance: "important",
11248
+ feedback: hasReviews ? "Social proof count or star rating present." : "No social proof numbers found."
11249
+ });
11250
+ const hasActionCta = /(?:start|try|get|claim|démarrer|essayer|profiter|obtenir)\s+(?:free|trial|now|today|gratuit)/i.test(content);
11251
+ checks.push({
11252
+ id: "cta_action_verb",
11253
+ name: "High-Contrast Outcome CTA",
11254
+ category: "ctas",
11255
+ passed: hasActionCta,
11256
+ importance: "critical",
11257
+ feedback: hasActionCta ? "High-converting action verb present in CTA." : "CTA is generic or missing action verb."
11258
+ });
11259
+ const hasFaq = /faq|frequently asked|questions|foire aux questions/i.test(content);
11260
+ checks.push({
11261
+ id: "faq_objection_handling",
11262
+ name: "Objection Handling & FAQ Section",
11263
+ category: "objection_handling",
11264
+ passed: hasFaq,
11265
+ importance: "important",
11266
+ feedback: hasFaq ? "FAQ section handles user hesitations." : "Missing FAQ / objection handling section."
11267
+ });
11268
+ const hasRiskReversal = /no credit card|cancel anytime|money-back|sans carte|sans engagement|garantie/i.test(content);
11269
+ checks.push({
11270
+ id: "risk_reversal_guarantee",
11271
+ name: "Zero-Risk Micro-Copy",
11272
+ category: "risk_reversal",
11273
+ passed: hasRiskReversal,
11274
+ importance: "important",
11275
+ feedback: hasRiskReversal ? "Risk reversal micro-copy found near CTA." : "No risk reversal guarantee found."
11276
+ });
11277
+ const hasUrgency = /today|now|instant|limited|aujourd'hui|maintenant|immédiat/i.test(content);
11278
+ checks.push({
11279
+ id: "urgency_trigger",
11280
+ name: "Immediate Access & Momentum",
11281
+ category: "urgency",
11282
+ passed: hasUrgency,
11283
+ importance: "enhancement",
11284
+ feedback: hasUrgency ? "Presents immediate access momentum." : "Lacks urgency or immediacy trigger."
11285
+ });
11286
+ const hasSubheadings = lines.filter((l) => l.startsWith("## ")).length >= 3;
11287
+ checks.push({
11288
+ id: "structure_scannability",
11289
+ name: "Visual Hierarchy & H2 Subheadings",
11290
+ category: "structure",
11291
+ passed: hasSubheadings,
11292
+ importance: "important",
11293
+ feedback: hasSubheadings ? "Clean visual hierarchy with 3+ H2 sections." : "Fewer than 3 H2 subheadings found."
11294
+ });
11295
+ const totalChecks = checks.length;
11296
+ const passedChecks = checks.filter((c) => c.passed).length;
11297
+ const criticalFailures = checks.filter((c) => !c.passed && c.importance === "critical");
11298
+ const warnings = checks.filter((c) => !c.passed && c.importance === "important");
11299
+ const overallCroScore = Math.round(passedChecks / totalChecks * 100);
11300
+ const passes = overallCroScore >= 70 && criticalFailures.length === 0;
11301
+ let grade = "F";
11302
+ if (overallCroScore >= 90)
11303
+ grade = "A+";
11304
+ else if (overallCroScore >= 80)
11305
+ grade = "A";
11306
+ else if (overallCroScore >= 70)
11307
+ grade = "B";
11308
+ else if (overallCroScore >= 55)
11309
+ grade = "C";
11310
+ const categoryScores = {};
11311
+ for (const check of checks) {
11312
+ if (!categoryScores[check.category]) {
11313
+ categoryScores[check.category] = { score: 0, passedCount: 0, totalCount: 0 };
11314
+ }
11315
+ categoryScores[check.category].totalCount++;
11316
+ if (check.passed)
11317
+ categoryScores[check.category].passedCount++;
11318
+ }
11319
+ for (const cat in categoryScores) {
11320
+ categoryScores[cat].score = Math.round(categoryScores[cat].passedCount / categoryScores[cat].totalCount * 100);
11321
+ }
11322
+ const roadmap = checks.filter((c) => !c.passed).map((c) => `[${c.importance.toUpperCase()}] ${c.category}: ${c.feedback}`);
11323
+ return {
11324
+ overallCroScore,
11325
+ passesCroAudit: passes,
11326
+ grade,
11327
+ criticalFailuresCount: criticalFailures.length,
11328
+ warningsCount: warnings.length,
11329
+ checks,
11330
+ categoryScores,
11331
+ conversionRoadmap: roadmap
11332
+ };
11333
+ }
11334
+ }
11335
+
11336
+ // src/social-community-insights.ts
11337
+ class SocialCommunityInsightsAggregator {
11338
+ static aggregate(topic, rawInsights) {
11339
+ const painPoints = [];
11340
+ const realLanguage = [];
11341
+ const quotables = [];
11342
+ for (const item of rawInsights) {
11343
+ if (item.type === "pain_point" || item.type === "complaint") {
11344
+ if (!painPoints.includes(item.quotableText)) {
11345
+ painPoints.push(item.quotableText);
11346
+ }
11347
+ }
11348
+ if (item.quotableText) {
11349
+ quotables.push(`"${item.quotableText}" (${item.source})`);
11350
+ }
11351
+ for (const phrase of item.userVocabulary) {
11352
+ if (!realLanguage.includes(phrase)) {
11353
+ realLanguage.push(phrase);
11354
+ }
11355
+ }
11356
+ }
11357
+ const recommendedAngle = painPoints.length > 0 ? `Address the #1 community frustration: "${painPoints[0]}" by demonstrating immediate resolution in the hero hook.` : `Highlight community-tested best practices and back every recommendation with verified user metrics.`;
11358
+ return {
11359
+ topic,
11360
+ totalInsights: rawInsights.length,
11361
+ painPoints,
11362
+ realLanguagePhrases: realLanguage,
11363
+ quotableExcerpts: quotables,
11364
+ recommendedAngleForCopy: recommendedAngle
11365
+ };
11366
+ }
11367
+ }
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
+
11052
11589
  // src/index.ts
11053
11590
  function createLynxSeoEngine(config) {
11054
11591
  return new LynxSeoEngine(config);
@@ -11074,6 +11611,12 @@ var LynxSeo = {
11074
11611
  competitorGap: CompetitorGapBlueprintEngine,
11075
11612
  engagementHook: EngagementHookAnalyzer,
11076
11613
  keywordDensity: KeywordDensityGuard,
11614
+ contentQuality: ContentQualityScorer,
11615
+ landingCroChecker: LandingCroChecker,
11616
+ socialInsights: SocialCommunityInsightsAggregator,
11617
+ channelScorer: LandingChannelScorer,
11618
+ articlePlanner: ArticleStrategicPlanner,
11619
+ topicClusters: TopicClusterArchitect,
11077
11620
  inspectMeta: SiteAuditor.inspectMeta,
11078
11621
  crawlDomain: DeepCrawlerAuditor.crawlAndAuditDomain,
11079
11622
  inspectHtmlSnapshot: DeepCrawlerAuditor.inspectHtmlSnapshot,
@@ -11134,12 +11677,14 @@ export {
11134
11677
  UrlyticsEngine,
11135
11678
  UI_ICONS,
11136
11679
  TrustSignalsExtractor,
11680
+ TopicClusterArchitect,
11137
11681
  TokenQuotaManager,
11138
11682
  TechnicalRulesAuditor,
11139
11683
  TeamRbacEngine,
11140
11684
  SocialVideoSeoAnalyticsEngine,
11141
11685
  SocialTrendSeoGenerator,
11142
11686
  SocialGrowthSuite,
11687
+ SocialCommunityInsightsAggregator,
11143
11688
  SocialAdsSocialSeoEngine,
11144
11689
  SiteAuditor,
11145
11690
  SerpRankHistoryEngine,
@@ -11177,6 +11722,8 @@ export {
11177
11722
  LynxAnalyticsClient,
11178
11723
  LlmContentCleaner,
11179
11724
  LegalDisclaimerEngine,
11725
+ LandingCroChecker,
11726
+ LandingChannelScorer,
11180
11727
  LagoTokenMeter,
11181
11728
  KnowledgeGraphLinker,
11182
11729
  KnowledgeBankBuilder,
@@ -11204,12 +11751,14 @@ export {
11204
11751
  CroCopywritingEngine,
11205
11752
  CopywritingFrameworksMaster,
11206
11753
  ContextTemplatesGenerator,
11754
+ ContentQualityScorer,
11207
11755
  CompetitorGapBlueprintEngine,
11208
11756
  CONTENT_AI_40_TOOLS,
11209
11757
  BrandDnaCalendarEngine,
11210
11758
  BacklinksClient,
11211
11759
  BUILT_IN_LOCATIONS_DATABASE,
11212
11760
  BRAND_ICONS,
11761
+ ArticleStrategicPlanner,
11213
11762
  ApiKeyGuardian,
11214
11763
  AiCopilotClient,
11215
11764
  AiBotsLogAnalyzer,