@lynxflow/seo-engine 2.2.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/dist/index.js CHANGED
@@ -9902,6 +9902,886 @@ class SemanticCannibalizationDetector {
9902
9902
  }
9903
9903
  }
9904
9904
 
9905
+ // src/humanity-ai-scrubber.ts
9906
+ class HumanityAiScrubber {
9907
+ static BANNED_AI_WORDS = {
9908
+ delve: "explore / look into",
9909
+ delving: "exploring",
9910
+ tapestry: "structure / landscape",
9911
+ testament: "proof / demonstration",
9912
+ pivotal: "key / important",
9913
+ crucial: "essential / vital",
9914
+ foster: "encourage / build",
9915
+ paramount: "primary / top priority",
9916
+ beacon: "leader / reference",
9917
+ unwavering: "solid / steady",
9918
+ leverage: "use / apply",
9919
+ streamline: "simplify / speed up",
9920
+ revolutionize: "transform",
9921
+ groundbreaking: "innovative",
9922
+ moreover: "also / additionally",
9923
+ furthermore: "also",
9924
+ "in summary": "to wrap up",
9925
+ "in conclusion": "finally / in short"
9926
+ };
9927
+ static ROBOTIC_INTROS = [
9928
+ /in today['’]s fast-paced (digital )?(world|landscape|era)/i,
9929
+ /in an era where (technology|digital|ai)/i,
9930
+ /it is important to remember that/i,
9931
+ /when it comes to (managing|growing|scaling)/i,
9932
+ /it is crucial to understand that/i,
9933
+ /look no further than/i
9934
+ ];
9935
+ static analyzeAndScrub(text, language = "en") {
9936
+ if (!text || text.trim().length === 0) {
9937
+ return {
9938
+ humanityScore: 100,
9939
+ aiProbabilityScore: 0,
9940
+ totalWords: 0,
9941
+ clichésDetectedCount: 0,
9942
+ clichés: [],
9943
+ isHumanSounding: true,
9944
+ scrubbedText: "",
9945
+ improvementRecommendations: []
9946
+ };
9947
+ }
9948
+ let scrubbed = text;
9949
+ const words = text.split(/\s+/).filter(Boolean);
9950
+ const totalWords = words.length;
9951
+ const clich_s = [];
9952
+ const recommendations = [];
9953
+ for (const [word, replacement] of Object.entries(this.BANNED_AI_WORDS)) {
9954
+ const regex = new RegExp(`\\b${word}\\b`, "gi");
9955
+ const matches = text.match(regex);
9956
+ if (matches && matches.length > 0) {
9957
+ clich_s.push({
9958
+ pattern: word,
9959
+ count: matches.length,
9960
+ category: "filler_word",
9961
+ suggestion: `Replace with "${replacement}"`
9962
+ });
9963
+ scrubbed = scrubbed.replace(regex, replacement.split(" / ")[0]);
9964
+ }
9965
+ }
9966
+ for (const introRegex of this.ROBOTIC_INTROS) {
9967
+ if (introRegex.test(text)) {
9968
+ clich_s.push({
9969
+ pattern: introRegex.source,
9970
+ count: 1,
9971
+ category: "robotic_intro",
9972
+ suggestion: "Delete cliché opening sentence. Start immediately with the core problem or direct value."
9973
+ });
9974
+ scrubbed = scrubbed.replace(introRegex, "");
9975
+ }
9976
+ }
9977
+ const emDashCount = (text.match(/—|--/g) || []).length;
9978
+ if (emDashCount > Math.max(2, Math.floor(totalWords / 250))) {
9979
+ clich_s.push({
9980
+ pattern: "Em-dash (—)",
9981
+ count: emDashCount,
9982
+ category: "excessive_punctuation",
9983
+ suggestion: "Reduce em-dashes (—). Replace with simple commas, colons, or clean periods."
9984
+ });
9985
+ scrubbed = scrubbed.replace(/\s*—\s*/g, ", ").replace(/\s*--\s*/g, ", ");
9986
+ }
9987
+ const totalViolations = clich_s.reduce((acc, c) => acc + c.count, 0);
9988
+ const penalty = Math.min(80, totalViolations * 8);
9989
+ const humanityScore = Math.max(15, 100 - penalty);
9990
+ const aiProbabilityScore = 100 - humanityScore;
9991
+ const isHumanSounding = humanityScore >= 80;
9992
+ if (humanityScore < 80) {
9993
+ recommendations.push("Replace detected filler words with conversational, direct vocabulary.");
9994
+ }
9995
+ if (emDashCount > 2) {
9996
+ recommendations.push("Lower punctuation density (em-dashes and semicolons) to sound more natural.");
9997
+ }
9998
+ if (clich_s.some((c) => c.category === "robotic_intro")) {
9999
+ recommendations.push("Hook the reader immediately in the first 5 words without generic preamble.");
10000
+ }
10001
+ return {
10002
+ humanityScore,
10003
+ aiProbabilityScore,
10004
+ totalWords,
10005
+ clichésDetectedCount: totalViolations,
10006
+ clichés: clich_s,
10007
+ isHumanSounding,
10008
+ scrubbedText: scrubbed.trim(),
10009
+ improvementRecommendations: recommendations
10010
+ };
10011
+ }
10012
+ }
10013
+
10014
+ // src/serp-length-comparator.ts
10015
+ class SerpLengthComparator {
10016
+ static compareLength(userWordCount, targetQuery, competitors) {
10017
+ const comps = competitors && competitors.length > 0 ? competitors : [
10018
+ { url: "https://competitor1.com/guide", rank: 1, wordCount: 2850 },
10019
+ { url: "https://competitor2.com/article", rank: 2, wordCount: 2420 },
10020
+ { url: "https://competitor3.com/blog", rank: 3, wordCount: 3100 },
10021
+ { url: "https://competitor4.com/overview", rank: 4, wordCount: 1950 },
10022
+ { url: "https://competitor5.com/tutorial", rank: 5, wordCount: 2200 },
10023
+ { url: "https://competitor6.com/review", rank: 6, wordCount: 1800 },
10024
+ { url: "https://competitor7.com/best-tools", rank: 7, wordCount: 2600 },
10025
+ { url: "https://competitor8.com/strategies", rank: 8, wordCount: 1750 },
10026
+ { url: "https://competitor9.com/case-study", rank: 9, wordCount: 1600 },
10027
+ { url: "https://competitor10.com/list", rank: 10, wordCount: 2100 }
10028
+ ];
10029
+ const sortedCounts = comps.map((c) => c.wordCount).sort((a, b) => a - b);
10030
+ const count = sortedCounts.length;
10031
+ const min = sortedCounts[0];
10032
+ const max = sortedCounts[count - 1];
10033
+ const median = sortedCounts[Math.floor(count / 2)];
10034
+ const p75 = sortedCounts[Math.floor(count * 0.75)];
10035
+ const top3 = comps.filter((c) => c.rank <= 3);
10036
+ const top3Average = Math.round(top3.reduce((acc, c) => acc + c.wordCount, 0) / Math.max(1, top3.length));
10037
+ const recommendedTargetWords = Math.round(Math.max(median, top3Average) * 1.1);
10038
+ const gapToTarget = recommendedTargetWords - userWordCount;
10039
+ let status = "optimal";
10040
+ let actionPlan = `Content length is aligned with top-ranking competitors (Recommended: ~${recommendedTargetWords} words).`;
10041
+ if (gapToTarget > 300) {
10042
+ status = "insufficient";
10043
+ actionPlan = `Content has a deficit of ${gapToTarget} words compared to top 3 SERP average (~${top3Average} words). Expand with 2-3 detailed sub-sections, case studies, or step-by-step FAQs.`;
10044
+ } else if (gapToTarget < -1500) {
10045
+ status = "excessive";
10046
+ actionPlan = `Content exceeds the 75th percentile by ${Math.abs(gapToTarget)} words. Ensure there is no fluff; split into a pillar hub or prune repetitive paragraphs.`;
10047
+ }
10048
+ return {
10049
+ userWordCount,
10050
+ targetQuery,
10051
+ competitorCount: comps.length,
10052
+ competitors: comps,
10053
+ stats: {
10054
+ min,
10055
+ max,
10056
+ median,
10057
+ p75,
10058
+ top3Average,
10059
+ recommendedTargetWords
10060
+ },
10061
+ gapToTarget,
10062
+ status,
10063
+ actionPlan
10064
+ };
10065
+ }
10066
+ }
10067
+
10068
+ // src/passive-voice-complexity.ts
10069
+ class PassiveVoiceComplexityAnalyzer {
10070
+ static PASSIVE_PATTERNS = {
10071
+ en: [
10072
+ /\b(is|are|was|were|been|being|be)\s+([a-z]+ed|built|written|made|done|seen|found|given|taken|chosen)\b/i,
10073
+ /\bby\s+(the|a|an|our|their|users|google)\b/i
10074
+ ],
10075
+ fr: [
10076
+ /\b(est|sont|a été|ont été|étant|fut|seront)\s+([a-z]+é|[a-z]+ée|[a-z]+és|[a-z]+ées|fait|pris|écrit|construit)\b/i,
10077
+ /\bpar\s+(le|la|les|un|une|des|notre)\b/i
10078
+ ],
10079
+ es: [
10080
+ /\b(es|son|fue|fueron|sido|siendo)\s+([a-z]+ado|[a-z]+ados|[a-z]+ada|[a-z]+adas|hecho|escrito|visto)\b/i,
10081
+ /\bpor\s+(el|la|los|las|un|una)\b/i
10082
+ ],
10083
+ de: [
10084
+ /\b(wird|wurden|wurde|worden|geworden)\s+([a-z]+t|[a-z]+en)\b/i,
10085
+ /\bvon\s+(dem|der|den|einem|einer)\b/i
10086
+ ]
10087
+ };
10088
+ static analyze(text, language = "en") {
10089
+ if (!text || text.trim().length === 0) {
10090
+ return {
10091
+ totalSentences: 0,
10092
+ totalWords: 0,
10093
+ averageWordsPerSentence: 0,
10094
+ passiveVoiceRatio: 0,
10095
+ passiveSentencesCount: 0,
10096
+ complexSentencesCount: 0,
10097
+ gradeLevelEstimate: 0,
10098
+ readabilityEaseScore: 100,
10099
+ status: "clear_and_punchy",
10100
+ keyWarnings: [],
10101
+ sentences: []
10102
+ };
10103
+ }
10104
+ const rawSentences = text.replace(/([.?!])\s*(?=[A-Z0-9À-ÖØ-ß])/g, "$1|").split("|").map((s) => s.trim()).filter((s) => s.length > 3);
10105
+ const patterns = this.PASSIVE_PATTERNS[language] || this.PASSIVE_PATTERNS.en;
10106
+ const sentenceDetails = [];
10107
+ let totalWords = 0;
10108
+ let passiveCount = 0;
10109
+ let complexCount = 0;
10110
+ for (const sentence of rawSentences) {
10111
+ const words = sentence.split(/\s+/).filter(Boolean);
10112
+ const wCount = words.length;
10113
+ totalWords += wCount;
10114
+ const isPassive = patterns.some((regex) => regex.test(sentence));
10115
+ const isTooLong = wCount > 22;
10116
+ if (isPassive)
10117
+ passiveCount++;
10118
+ if (isTooLong)
10119
+ complexCount++;
10120
+ sentenceDetails.push({
10121
+ text: sentence,
10122
+ wordCount: wCount,
10123
+ isPassive,
10124
+ isTooLong
10125
+ });
10126
+ }
10127
+ const totalSentences = Math.max(1, rawSentences.length);
10128
+ const avgWordsPerSentence = Math.round(totalWords / totalSentences * 10) / 10;
10129
+ const passiveVoiceRatio = Math.round(passiveCount / totalSentences * 100) / 100;
10130
+ const syllableEstimate = totalWords * 1.4;
10131
+ const ease = Math.max(10, Math.min(100, Math.round(206.835 - 1.015 * avgWordsPerSentence - 84.6 * (syllableEstimate / totalWords))));
10132
+ const gradeLevel = Math.max(4, Math.round(0.39 * avgWordsPerSentence + 11.8 * (syllableEstimate / totalWords) - 15.59));
10133
+ const warnings = [];
10134
+ if (passiveVoiceRatio > 0.15) {
10135
+ warnings.push(`Passive voice is ${Math.round(passiveVoiceRatio * 100)}% (Target: < 10%). Convert passive constructions to active voice.`);
10136
+ }
10137
+ if (complexCount > totalSentences * 0.25) {
10138
+ warnings.push(`${complexCount} sentences exceed 22 words. Break long compound sentences into shorter, punchier statements.`);
10139
+ }
10140
+ if (avgWordsPerSentence > 18) {
10141
+ warnings.push(`Average sentence length is ${avgWordsPerSentence} words (Target: 12-16 words).`);
10142
+ }
10143
+ let status = "clear_and_punchy";
10144
+ if (ease < 50 || passiveVoiceRatio > 0.2) {
10145
+ status = "hard_to_read";
10146
+ } else if (ease < 65 || passiveVoiceRatio > 0.12) {
10147
+ status = "acceptable";
10148
+ }
10149
+ return {
10150
+ totalSentences,
10151
+ totalWords,
10152
+ averageWordsPerSentence: avgWordsPerSentence,
10153
+ passiveVoiceRatio,
10154
+ passiveSentencesCount: passiveCount,
10155
+ complexSentencesCount: complexCount,
10156
+ gradeLevelEstimate: gradeLevel,
10157
+ readabilityEaseScore: ease,
10158
+ status,
10159
+ keyWarnings: warnings,
10160
+ sentences: sentenceDetails
10161
+ };
10162
+ }
10163
+ }
10164
+
10165
+ // src/above-fold-cro-auditor.ts
10166
+ class AboveFoldCroAuditor {
10167
+ static STRONG_ACTION_VERBS = [
10168
+ /start/i,
10169
+ /get/i,
10170
+ /try/i,
10171
+ /claim/i,
10172
+ /launch/i,
10173
+ /boost/i,
10174
+ /automate/i,
10175
+ /démarrer/i,
10176
+ /essayer/i,
10177
+ /obtenir/i,
10178
+ /profiter/i,
10179
+ /iniciar/i,
10180
+ /probar/i,
10181
+ /jetzt/i,
10182
+ /kostenlos/i
10183
+ ];
10184
+ static WEAK_ACTION_VERBS = [
10185
+ /^submit$/i,
10186
+ /^click here$/i,
10187
+ /^learn more$/i,
10188
+ /^envoyer$/i,
10189
+ /^cliquez ici$/i,
10190
+ /^en savoir plus$/i,
10191
+ /^weiter$/i,
10192
+ /^leer más$/i
10193
+ ];
10194
+ static auditHero(input) {
10195
+ const criteria = [];
10196
+ const fixes = [];
10197
+ const h1Words = input.h1.trim().split(/\s+/).length;
10198
+ const isH1Clear = h1Words >= 4 && h1Words <= 14;
10199
+ let h1Score = isH1Clear ? 25 : 12;
10200
+ if (!isH1Clear) {
10201
+ fixes.push("Optimize H1 length between 4 and 12 words. Make the specific outcome and audience explicit.");
10202
+ }
10203
+ criteria.push({
10204
+ name: "Headline Clarity & Value Proposition",
10205
+ passed: isH1Clear,
10206
+ score: h1Score,
10207
+ feedback: isH1Clear ? "Headline is concise, outcome-oriented, and immediately readable in < 3 seconds." : "Headline is either too vague (< 4 words) or too dense (> 14 words)."
10208
+ });
10209
+ const hasStrongVerb = this.STRONG_ACTION_VERBS.some((r) => r.test(input.primaryCtaText));
10210
+ const hasWeakVerb = this.WEAK_ACTION_VERBS.some((r) => r.test(input.primaryCtaText.trim()));
10211
+ const isCtaEffective = hasStrongVerb && !hasWeakVerb;
10212
+ let ctaScore = isCtaEffective ? 25 : hasWeakVerb ? 5 : 15;
10213
+ if (!isCtaEffective) {
10214
+ fixes.push(`Replace generic CTA "${input.primaryCtaText}" with an outcome-focused action verb (e.g. "Start Free Trial", "Get Instant Access", "Démarrer Gratuitement").`);
10215
+ }
10216
+ criteria.push({
10217
+ name: "Primary CTA Contrast & Action Verb",
10218
+ passed: isCtaEffective,
10219
+ score: ctaScore,
10220
+ feedback: isCtaEffective ? `CTA "${input.primaryCtaText}" uses a strong, high-conversion action trigger.` : `CTA "${input.primaryCtaText}" lacks urgency or is too generic.`
10221
+ });
10222
+ const hasProof = Boolean(input.hasTestimonialsOrStars || input.hasCustomerLogos);
10223
+ let proofScore = hasProof ? 20 : 0;
10224
+ if (!hasProof) {
10225
+ fixes.push("Add social proof above the fold (e.g. 'Rated 4.9/5 by 1,200+ teams' or client logos).");
10226
+ }
10227
+ criteria.push({
10228
+ name: "Above-the-Fold Social Proof & Ratings",
10229
+ passed: hasProof,
10230
+ score: proofScore,
10231
+ feedback: hasProof ? "Trust signals and social proof are visible immediately above the fold." : "Missing social proof above the fold. Increases visitor bounce risk."
10232
+ });
10233
+ const hasReversal = Boolean(input.hasRiskReversalOrGuarantee);
10234
+ let reversalScore = hasReversal ? 20 : 5;
10235
+ if (!hasReversal) {
10236
+ fixes.push("Add a friction-killer under the CTA (e.g. 'No credit card required • Cancel anytime • 14-day free trial').");
10237
+ }
10238
+ criteria.push({
10239
+ name: "Risk Reversal & Friction Elimination",
10240
+ passed: hasReversal,
10241
+ score: reversalScore,
10242
+ feedback: hasReversal ? "Zero-risk micro-copy present under the main action trigger." : "No risk reversal micro-copy found under CTA."
10243
+ });
10244
+ const subWords = (input.subheadline || "").trim().split(/\s+/).length;
10245
+ const isSubheadlineSolid = subWords >= 8 && subWords <= 30;
10246
+ let subScore = isSubheadlineSolid ? 10 : 5;
10247
+ if (!isSubheadlineSolid) {
10248
+ fixes.push("Add a supporting subheadline (12-25 words) explaining HOW the product solves the pain point.");
10249
+ }
10250
+ criteria.push({
10251
+ name: "Subheadline Supporting Context",
10252
+ passed: isSubheadlineSolid,
10253
+ score: subScore,
10254
+ feedback: isSubheadlineSolid ? "Subheadline reinforces the value proposition with concrete details." : "Subheadline is missing or insufficient."
10255
+ });
10256
+ const croScore = h1Score + ctaScore + proofScore + reversalScore + subScore;
10257
+ const rating = croScore >= 80 ? "high_converting" : croScore >= 55 ? "solid" : "high_friction";
10258
+ return {
10259
+ croScore,
10260
+ rating,
10261
+ criteria,
10262
+ recommendedFixes: fixes,
10263
+ suggestedHeroVariant: {
10264
+ h1: input.h1.includes("—") ? input.h1 : `${input.h1} — Built for Fast Growing Teams`,
10265
+ subheadline: input.subheadline || "Automate operational workflows, boost organic search traffic, and cut manual workload in half with instant setup.",
10266
+ ctaText: "Start Free Trial — Instant Setup",
10267
+ reassuranceBadge: "No credit card required • 14-day trial • 100% Free migration"
10268
+ }
10269
+ };
10270
+ }
10271
+ }
10272
+
10273
+ // src/context-templates-generator.ts
10274
+ class ContextTemplatesGenerator {
10275
+ static generateAllContextFiles(input) {
10276
+ return {
10277
+ "context/brand-voice.md": this.generateBrandVoice(input),
10278
+ "context/features.md": this.generateFeatures(input),
10279
+ "context/internal-links-map.md": this.generateInternalLinksMap(input),
10280
+ "context/style-guide.md": this.generateStyleGuide(input),
10281
+ "context/target-keywords.md": this.generateTargetKeywords(input),
10282
+ "context/competitor-analysis.md": this.generateCompetitorAnalysis(input),
10283
+ "context/seo-guidelines.md": this.generateSeoGuidelines(input),
10284
+ "context/cro-best-practices.md": this.generateCroBestPractices(input)
10285
+ };
10286
+ }
10287
+ static generateBrandVoice(input) {
10288
+ return `# Brand Voice & Messaging Framework — ${input.brandName}
10289
+
10290
+ ## 1. Core Voice Pillars
10291
+ - **Authoritative yet Approachable:** Speak with deep domain expertise in ${input.industry}, without academic jargon.
10292
+ - **Direct & Action-Oriented:** Get straight to the solution in the first 5 words. No fluff or generic preambles.
10293
+ - **Outcome-Focused:** Emphasize time saved, revenue recovered, and operational ease for ${input.targetAudience}.
10294
+
10295
+ ## 2. Terminology & Word Preferences
10296
+ - **Preferred Words:** Streamlined, automated, verified, instant, high-performance, precision.
10297
+ - **Banned Words:** Delve, tapestry, testament, crucial, groundbreaking, revolutionize, in today's fast-paced world.
10298
+
10299
+ ## 3. Core Audience
10300
+ - **Primary Persona:** ${input.targetAudience}
10301
+ - **Primary Domain:** ${input.domain}
10302
+ `.trim();
10303
+ }
10304
+ static generateFeatures(input) {
10305
+ const list = input.coreFeatures.map((f) => `- **${f}:** Enterprise-grade workflow automation and real-time synchronization.`).join(`
10306
+ `);
10307
+ return `# Product Features & Capabilities — ${input.brandName}
10308
+
10309
+ ## 1. Feature Catalog
10310
+ ${list}
10311
+
10312
+ ## 2. Competitive Value Proposition
10313
+ - Instant setup (< 3 minutes).
10314
+ - Zero complex infrastructure requirements.
10315
+ - 100% scalable in-memory architecture.
10316
+ `.trim();
10317
+ }
10318
+ static generateInternalLinksMap(input) {
10319
+ return `# Internal Links Architecture Map — ${input.domain}
10320
+
10321
+ ## 1. Core Pillar Hubs
10322
+ - **Homepage:** \`${input.domain}/\` (Anchor: "${input.brandName}", "${input.industry} Software")
10323
+ - **Solutions & PSEO Hub:** \`${input.domain}/solutions\` (Anchor: "Explore all solutions", "Platform features")
10324
+ - **Pricing & Plans:** \`${input.domain}/pricing\` (Anchor: "View pricing plans", "Compare tiers")
10325
+ - **Free Tools:** \`${input.domain}/tools\` (Anchor: "Free interactive calculators", "SEO tools")
10326
+
10327
+ ## 2. Linking Best Practices
10328
+ - Add 3-5 relevant internal links per 2,000 words.
10329
+ - Use descriptive, keyword-rich anchor text rather than "click here".
10330
+ `.trim();
10331
+ }
10332
+ static generateStyleGuide(input) {
10333
+ return `# Editorial & Style Guide — ${input.brandName}
10334
+
10335
+ ## 1. Formatting Rules
10336
+ - **Sentence Length:** 12 to 18 words on average. Never exceed 24 words in a single sentence.
10337
+ - **Paragraphs:** 2 to 4 sentences maximum for high mobile readability.
10338
+ - **Subheadings:** Introduce an H2 or H3 every 250-350 words.
10339
+ - **Punctuation:** Limit em-dashes (—) to 1 per 500 words. Use clear periods and commas.
10340
+
10341
+ ## 2. Readability Target
10342
+ - **Flesch Reading Ease:** 65 to 80.
10343
+ - **Reading Level:** 7th to 9th grade.
10344
+ `.trim();
10345
+ }
10346
+ static generateTargetKeywords(input) {
10347
+ const kwList = input.primaryKeywords.map((k) => `- \`${k}\` (Intent: Commercial/Transactional)`).join(`
10348
+ `);
10349
+ return `# Target Keywords & Topic Clusters — ${input.brandName}
10350
+
10351
+ ## 1. Primary Keywords
10352
+ ${kwList}
10353
+
10354
+ ## 2. Secondary Modifiers & Long-Tail
10355
+ - Best ${input.industry} software for ${input.targetAudience}
10356
+ - How to automate ${input.industry} workflows in 2026
10357
+ - ${input.brandName} pricing and alternatives
10358
+ `.trim();
10359
+ }
10360
+ static generateCompetitorAnalysis(input) {
10361
+ return `# Competitor Analysis & Differentiation — ${input.brandName}
10362
+
10363
+ ## 1. Market Overview
10364
+ - **Our Positioning:** The fastest, lightest, in-memory automation platform for ${input.industry}.
10365
+ - **Key Differentiator:** 100% cloud-native, sub-millisecond response time, no heavy legacy bloat.
10366
+
10367
+ ## 2. Comparison Angles
10368
+ - Transparent pricing with zero hidden add-on fees.
10369
+ - Modern API & MCP integrations (Claude Code, Cursor, Antigravity).
10370
+ `.trim();
10371
+ }
10372
+ static generateSeoGuidelines(input) {
10373
+ return `# SEO Technical & On-Page Requirements — ${input.brandName}
10374
+
10375
+ ## 1. Title & Meta Descriptions
10376
+ - **Title Tag:** 50-60 characters, primary keyword in front.
10377
+ - **Meta Description:** 140-155 characters, includes value prop and soft CTA.
10378
+
10379
+ ## 2. Schema.org Graphs
10380
+ - Inject valid JSON-LD graph on all programmatic pages (SoftwareApplication, LocalBusiness, FAQPage, Breadcrumbs).
10381
+ - Zero syntax errors or missing required fields.
10382
+ `.trim();
10383
+ }
10384
+ static generateCroBestPractices(input) {
10385
+ return `# Conversion Rate Optimization (CRO) Standards — ${input.brandName}
10386
+
10387
+ ## 1. Above-The-Fold Checklist
10388
+ - [ ] Clear H1 communicating the specific outcome in < 3 seconds.
10389
+ - [ ] High-contrast primary CTA with strong action verb.
10390
+ - [ ] Social proof visible without scrolling (star ratings, reviews, client logos).
10391
+ - [ ] Risk reversal micro-copy directly beneath the main button ("No credit card required").
10392
+ `.trim();
10393
+ }
10394
+ }
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
+
9905
10785
  // src/index.ts
9906
10786
  function createLynxSeoEngine(config) {
9907
10787
  return new LynxSeoEngine(config);
@@ -9916,6 +10796,14 @@ var LynxSeo = {
9916
10796
  googleIndexing: GoogleIndexingClient,
9917
10797
  aeoSnippet: AeoSnippetSynthesizer,
9918
10798
  cannibalization: SemanticCannibalizationDetector,
10799
+ humanityScrubber: HumanityAiScrubber,
10800
+ serpLength: SerpLengthComparator,
10801
+ readabilityComplexity: PassiveVoiceComplexityAnalyzer,
10802
+ aboveFoldCro: AboveFoldCroAuditor,
10803
+ contextTemplates: ContextTemplatesGenerator,
10804
+ searchIntent: SearchIntentClassifier,
10805
+ opportunityPrioritizer: SeoOpportunityPrioritizer,
10806
+ trustSignals: TrustSignalsExtractor,
9919
10807
  inspectMeta: SiteAuditor.inspectMeta,
9920
10808
  crawlDomain: DeepCrawlerAuditor.crawlAndAuditDomain,
9921
10809
  inspectHtmlSnapshot: DeepCrawlerAuditor.inspectHtmlSnapshot,
@@ -9975,6 +10863,7 @@ export {
9975
10863
  VideoYouTubeAnalyzer,
9976
10864
  UrlyticsEngine,
9977
10865
  UI_ICONS,
10866
+ TrustSignalsExtractor,
9978
10867
  TokenQuotaManager,
9979
10868
  TechnicalRulesAuditor,
9980
10869
  TeamRbacEngine,
@@ -9984,9 +10873,12 @@ export {
9984
10873
  SocialAdsSocialSeoEngine,
9985
10874
  SiteAuditor,
9986
10875
  SerpRankHistoryEngine,
10876
+ SerpLengthComparator,
9987
10877
  SerpClient,
10878
+ SeoOpportunityPrioritizer,
9988
10879
  SeoOpportunitiesDecayDetector,
9989
10880
  SemanticCannibalizationDetector,
10881
+ SearchIntentClassifier,
9990
10882
  SchemaGraphBuilder,
9991
10883
  SUPPORTED_CANONICAL_LOCALES,
9992
10884
  SCHEMA_LOCAL_BUSINESS_MAP,
@@ -9996,6 +10888,7 @@ export {
9996
10888
  PublicRoutesManifestEngine,
9997
10889
  PseoMatrixEngine,
9998
10890
  PowerWordsPsychologyEngine,
10891
+ PassiveVoiceComplexityAnalyzer,
9999
10892
  PageRegenerationTracker,
10000
10893
  PSEO_AGENT_SYSTEM_PROMPT,
10001
10894
  OgImageGenerator,
@@ -10024,6 +10917,7 @@ export {
10024
10917
  IndexNowClient,
10025
10918
  I18nDetector,
10026
10919
  HyperswitchGateway,
10920
+ HumanityAiScrubber,
10027
10921
  GoogleIndexingClient,
10028
10922
  GoogleBusinessProfileEngine,
10029
10923
  GeoMeshLinkingEngine,
@@ -10037,6 +10931,7 @@ export {
10037
10931
  CrosslinkScorerEngine,
10038
10932
  CroCopywritingEngine,
10039
10933
  CopywritingFrameworksMaster,
10934
+ ContextTemplatesGenerator,
10040
10935
  CONTENT_AI_40_TOOLS,
10041
10936
  BrandDnaCalendarEngine,
10042
10937
  BacklinksClient,
@@ -10046,5 +10941,6 @@ export {
10046
10941
  AiCopilotClient,
10047
10942
  AiBotsLogAnalyzer,
10048
10943
  AeoSnippetSynthesizer,
10049
- AdIntelligenceCroEngine
10944
+ AdIntelligenceCroEngine,
10945
+ AboveFoldCroAuditor
10050
10946
  };