@lynxflow/seo-engine 1.8.30 → 2.0.1

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.
Files changed (96) hide show
  1. package/README.md +60 -117
  2. package/dist/ai-copilot-client.d.ts +2 -0
  3. package/dist/built-in-locations.d.ts +22 -0
  4. package/dist/content-ai-arsenal.d.ts +28 -0
  5. package/dist/existing-media-harvester.d.ts +33 -0
  6. package/dist/extended-schemas.d.ts +84 -0
  7. package/dist/features-knowledge-harvester.d.ts +56 -0
  8. package/dist/geo-citation-scorer.d.ts +34 -0
  9. package/dist/i18n-detector.d.ts +1 -1
  10. package/dist/index.d.ts +32 -7
  11. package/dist/index.js +567 -83
  12. package/dist/index.mjs +2062 -169
  13. package/dist/internal-pagerank-graph.d.ts +59 -0
  14. package/dist/knowledge-bank-builder.d.ts +91 -0
  15. package/dist/marketing-skills-engine.d.ts +74 -0
  16. package/dist/matrix-engine.d.ts +29 -0
  17. package/dist/power-words-psychology.d.ts +27 -15
  18. package/dist/public-manifest-engine.d.ts +58 -0
  19. package/dist/rag-knowledge-engine.d.ts +50 -0
  20. package/dist/rate-limited-translator.d.ts +19 -0
  21. package/dist/token-quota-manager.d.ts +4 -0
  22. package/package.json +11 -3
  23. package/TUTORIEL_INTEGRATION_SITE.md +0 -283
  24. package/connectors/cloudflare-worker/worker.js +0 -106
  25. package/connectors/laravel/LynxSeoController.php +0 -37
  26. package/connectors/wordpress/lynxseo-admin-app.js +0 -2611
  27. package/connectors/wordpress/lynxseo-connector.php +0 -1712
  28. package/connectors/wordpress/lynxseo-connector.zip +0 -0
  29. package/lynxflow-seo-engine-1.2.0.tgz +0 -0
  30. package/src/ad-intelligence-cro.ts +0 -140
  31. package/src/ai-bots-log-analyzer.ts +0 -127
  32. package/src/ai-copilot-client.ts +0 -99
  33. package/src/analytics-client.ts +0 -315
  34. package/src/auth-key.ts +0 -262
  35. package/src/backlinks-client.ts +0 -85
  36. package/src/brand-dna-calendar.ts +0 -120
  37. package/src/brand-icons.ts +0 -138
  38. package/src/built-in-locations.ts +0 -170
  39. package/src/content-ai-arsenal.ts +0 -268
  40. package/src/copy-frameworks-master.ts +0 -89
  41. package/src/cro-copywriting-engine.ts +0 -105
  42. package/src/crosslink-scorer.ts +0 -101
  43. package/src/embeddable-seo-widget.ts +0 -57
  44. package/src/engine.test.ts +0 -386
  45. package/src/engine.ts +0 -813
  46. package/src/existing-media-harvester.ts +0 -98
  47. package/src/extended-schemas.ts +0 -589
  48. package/src/features-knowledge-harvester.ts +0 -190
  49. package/src/free-public-tools.ts +0 -224
  50. package/src/geo-mesh-linking.ts +0 -93
  51. package/src/google-business-profile.ts +0 -280
  52. package/src/i18n-detector.ts +0 -98
  53. package/src/i18n-dictionary.ts +0 -422
  54. package/src/index.ts +0 -164
  55. package/src/indexnow-client.ts +0 -94
  56. package/src/instant-matrix-search.ts +0 -48
  57. package/src/isr-cache-manager.ts +0 -78
  58. package/src/keyword-permutator.ts +0 -118
  59. package/src/knowledge-bank-builder.ts +0 -299
  60. package/src/knowledge-graph-linker.ts +0 -81
  61. package/src/lago-token-meter.ts +0 -120
  62. package/src/legal-disclaimers.ts +0 -407
  63. package/src/llm-content-cleaner.ts +0 -64
  64. package/src/llm-prompt.ts +0 -323
  65. package/src/master-marketing-engine.ts +0 -314
  66. package/src/matrix-engine.ts +0 -1204
  67. package/src/mcp-seo-server.ts +0 -108
  68. package/src/ngram-density-analyzer.ts +0 -108
  69. package/src/og-image-generator.ts +0 -109
  70. package/src/power-words-psychology.ts +0 -369
  71. package/src/public-manifest-engine.ts +0 -109
  72. package/src/rag-knowledge-engine.ts +0 -163
  73. package/src/rank-math-parity.ts +0 -235
  74. package/src/rate-limited-translator.ts +0 -110
  75. package/src/real-reviews-sync.ts +0 -207
  76. package/src/rss-syndication-feed.ts +0 -56
  77. package/src/schema-builder.ts +0 -134
  78. package/src/seo-opportunities-decay.ts +0 -154
  79. package/src/serp-client.ts +0 -89
  80. package/src/serp-history-alerts.ts +0 -75
  81. package/src/site-auditor.ts +0 -83
  82. package/src/site-crawler.ts +0 -406
  83. package/src/slug-engine.ts +0 -183
  84. package/src/social-ads-seo.ts +0 -211
  85. package/src/social-growth-suite.ts +0 -310
  86. package/src/social-trend-seo.ts +0 -103
  87. package/src/social-video-seo.ts +0 -100
  88. package/src/team-rbac.ts +0 -61
  89. package/src/technical-rules-auditor.ts +0 -171
  90. package/src/token-quota-manager.ts +0 -91
  91. package/src/types.ts +0 -143
  92. package/src/ui-icons.ts +0 -134
  93. package/src/urlytics-engine.ts +0 -97
  94. package/src/video-youtube-analyzer.ts +0 -81
  95. package/src/yoast-parity.ts +0 -161
  96. package/tsconfig.json +0 -15
@@ -1,369 +0,0 @@
1
- /**
2
- * 🧠 Universal Open Multilingual Power Words & Click Psychology Engine
3
- * Supports 50+ World Languages (FR, EN, ES, DE, IT, NL, PT, AR, RU, ZH, JA, HI, PL, SV, TR, KO, etc.)
4
- * With Dynamic Runtime Dictionary Registration for Infinite Language Extensibility.
5
- */
6
-
7
- export type LanguageCode = string;
8
-
9
- export interface CategoryWordBank {
10
- authority: string[];
11
- urgency: string[];
12
- value: string[];
13
- curiosity: string[];
14
- sentimentPositive: string[];
15
- sentimentNegative: string[];
16
- }
17
-
18
- export interface PsychologyAnalysisResult {
19
- score: number; // 0 to 100
20
- powerWordsDetected: string[];
21
- sentiment: "positif" | "negatif" | "neutre";
22
- hasNumber: boolean;
23
- hasYear: boolean;
24
- hasCuriosityTrigger: boolean;
25
- hasUrgencyTrigger: boolean;
26
- hasAuthorityTrigger: boolean;
27
- hasCurrencyOrDiscount: boolean;
28
- hasBracketsOrParentheses: boolean;
29
- recommendations: string[];
30
- suggestedTitles: string[];
31
- }
32
-
33
- // Pre-loaded high-converting dictionaries across global languages
34
- export const MULTILINGUAL_POWER_WORDS: Record<string, CategoryWordBank> = {
35
- fr: {
36
- authority: [
37
- "officiel", "guide", "expert", "complet", "certifié", "ultime", "meilleur", "meilleurs", "comparatif",
38
- "maîtriser", "spécialiste", "leader", "professionnel", "authentique", "infaillible", "reconnu", "démontré", "avis"
39
- ],
40
- urgency: [
41
- "rapide", "instantané", "maintenant", "immédiat", "express", "2026", "dernière minute", "urgent",
42
- "dépêchez-vous", "en 5 min", "en 10 min", "aujourd'hui", "débloquer", "vite"
43
- ],
44
- value: [
45
- "gratuit", "économique", "rentable", "offert", "sans engagement", "garanti", "facile", "clé en main",
46
- "rentabilité", "gain", "économisez", "inclus", "accessible", "inédit", "remise", "promo"
47
- ],
48
- curiosity: [
49
- "exclusif", "secret", "astuces", "révélé", "inédit", "top", "méthode", "pourquoi", "comment",
50
- "découvrir", "choc", "incontournable", "mystère", "incroyable", "vérité"
51
- ],
52
- sentimentPositive: [
53
- "parfait", "incroyable", "magnifique", "puissant", "réussi", "génial", "exceptionnel", "facile",
54
- "meilleur", "succès", "brillant", "splendide", "radieux", "magique", "sublime"
55
- ],
56
- sentimentNegative: [
57
- "erreur", "piège", "danger", "éviter", "pire", "arnaque", "catastrophe", "risqué", "menace", "perte", "alerte"
58
- ]
59
- },
60
- en: {
61
- authority: [
62
- "ultimate", "complete", "definitive", "guide", "expert", "official", "proven", "master", "certified",
63
- "best", "top", "authority", "case study", "benchmark", "professional", "review", "formula"
64
- ],
65
- urgency: [
66
- "instant", "fast", "quick", "now", "today", "2026", "deadline", "last chance", "step-by-step",
67
- "in 5 minutes", "immediate", "urgent", "unlock", "rapid"
68
- ],
69
- value: [
70
- "free", "affordable", "save", "guaranteed", "easy", "bonus", "discount", "effortless",
71
- "lifetime", "roi", "value", "exclusive deal", "profitable"
72
- ],
73
- curiosity: [
74
- "secret", "revealed", "insider", "hacks", "why", "how to", "untold", "surprising",
75
- "shocking", "hidden", "must-know", "unbelievable", "myth"
76
- ],
77
- sentimentPositive: [
78
- "amazing", "brilliant", "powerful", "epic", "perfect", "stunning", "thriving", "seamless",
79
- "exceptional", "masterpiece", "unbeatable"
80
- ],
81
- sentimentNegative: [
82
- "mistake", "warning", "fail", "avoid", "risk", "trap", "worst", "scam", "danger", "flaw"
83
- ]
84
- },
85
- es: {
86
- authority: [
87
- "definitiva", "completa", "guía", "experto", "oficial", "certificado", "mejor", "mejores", "comparativa",
88
- "profesional", "comprobado", "líder", "análisis"
89
- ],
90
- urgency: [
91
- "rápido", "ahora", "urgente", "hoy", "2026", "en 5 minutos", "inmediato", "última oportunidad", "fácil"
92
- ],
93
- value: [
94
- "gratis", "económico", "ahorra", "garantizado", "rentable", "oferta", "descuento", "exclusivo"
95
- ],
96
- curiosity: [
97
- "secreto", "revelado", "trucos", "por qué", "cómo", "impactante", "increíble", "descubre", "método"
98
- ],
99
- sentimentPositive: [
100
- "excelente", "increíble", "perfecto", "potente", "maravilloso", "éxito", "fantástico"
101
- ],
102
- sentimentNegative: [
103
- "error", "peligro", "evita", "peor", "estafa", "trampa", "fracaso", "riesgo"
104
- ]
105
- },
106
- de: {
107
- authority: [
108
- "ultimativ", "vollständig", "leitfaden", "experte", "offiziell", "zertifiziert", "beste", "bester", "vergleich", "profi"
109
- ],
110
- urgency: [
111
- "schnell", "jetzt", "sofort", "2026", "in 5 minuten", "heute", "dringend", "einfach"
112
- ],
113
- value: [
114
- "kostenlos", "gratis", "sparen", "garantiert", "rabatt", "günstig", "angebot", "profitabel"
115
- ],
116
- curiosity: [
117
- "geheimnis", "enthüllt", "tricks", "tipps", "warum", "wie man", "unglaublich", "entdecken"
118
- ],
119
- sentimentPositive: [
120
- "hervorragend", "perfekt", "genial", "erfolg", "fantastisch", "wunderbar", "stark"
121
- ],
122
- sentimentNegative: [
123
- "fehler", "gefahr", "vermeiden", "schlimmste", "betrug", "risiko", "warnung"
124
- ]
125
- },
126
- it: {
127
- authority: [
128
- "guida", "completa", "esperto", "ufficiale", "certificato", "migliore", "migliori", "confronto", "definitiva", "professionale"
129
- ],
130
- urgency: [
131
- "veloce", "ora", "subito", "immediato", "2026", "in 5 minuti", "oggi", "urgente"
132
- ],
133
- value: [
134
- "gratis", "gratuito", "risparmia", "garantito", "sconto", "facile", "offerta", "conveniente"
135
- ],
136
- curiosity: [
137
- "segreto", "svelato", "trucchi", "perché", "come fare", "incredibile", "scopri", "metodo"
138
- ],
139
- sentimentPositive: [
140
- "perfetto", "incredibile", "fantastico", "successo", "eccezionale", "meraviglioso"
141
- ],
142
- sentimentNegative: [
143
- "errore", "pericolo", "evitare", "peggiore", "truffa", "rischio", "trappola"
144
- ]
145
- },
146
- nl: {
147
- authority: ["ultiem", "gids", "expert", "officieel", "beste", "vergelijk", "compleet", "bewezen"],
148
- urgency: ["snel", "nu", "direct", "vandaag", "2026", "meteen", "eenvoudig"],
149
- value: ["gratis", "bespaar", "gegarandeerd", "korting", "goedkoop", "voordeel"],
150
- curiosity: ["geheim", "onthuld", "tips", "waarom", "hoe", "ontdek"],
151
- sentimentPositive: ["geweldig", "perfect", "uitstekend", "succes", "fantastisch"],
152
- sentimentNegative: ["fout", "gevaar", "vermijd", "slechtste", "oplichting", "risico"]
153
- },
154
- pt: {
155
- authority: ["guia", "completo", "definitivo", "especialista", "oficial", "melhor", "melhores", "comparativo"],
156
- urgency: ["rápido", "agora", "imediato", "hoje", "2026", "em 5 minutos", "urgente"],
157
- value: ["grátis", "gratuito", "economize", "garantido", "desconto", "fácil", "oferta"],
158
- curiosity: ["segredo", "revelado", "dicas", "por que", "como fazer", "incrível", "descubra"],
159
- sentimentPositive: ["perfeito", "incrível", "fantástico", "sucesso", "excelente"],
160
- sentimentNegative: ["erro", "perigo", "evite", "pior", "golpe", "risco", "cilada"]
161
- },
162
- ar: {
163
- authority: ["دليل", "شامل", "رسمي", "خبير", "أفضل", "مقارنة", "معتمد", "نهائي"],
164
- urgency: ["سريع", "الآن", "فوري", "اليوم", "2026", "في دقائق", "عاجل"],
165
- value: ["مجانا", "وفر", "مضمون", "خصم", "سهل", "عرض", "مميز"],
166
- curiosity: ["سر", "كشف", "أسرار", "لماذا", "كيف", "مذهل", "اكتشف"],
167
- sentimentPositive: ["رائع", "ممتاز", "ناجح", "مبهر", "مثالي"],
168
- sentimentNegative: ["خطأ", "تحذير", "تجنب", "فخ", "احتيال", "خطر"]
169
- },
170
- ru: {
171
- authority: ["руководство", "полный", "эксперт", "официальный", "лучший", "лучшие", "сравнение", "проверенный"],
172
- urgency: ["быстро", "сейчас", "срочно", "сегодня", "2026", "за 5 минут", "немедленно"],
173
- value: ["бесплатно", "скидка", "гарантия", "выгодно", "легко", "акция"],
174
- curiosity: ["секрет", "раскрыт", "лайфхаки", "почему", "как", "невероятный", "узнайте"],
175
- sentimentPositive: ["отлично", "идеально", "успех", "потрясающий", "великолепный"],
176
- sentimentNegative: ["ошибка", "опасность", "избегать", "худший", "обман", "риск"]
177
- },
178
- zh: {
179
- authority: ["终极", "完整", "指南", "专家", "官方", "最佳", "对比", "评测", "权威"],
180
- urgency: ["快速", "立即", "今天", "2026", "5分钟", "马上", "紧急"],
181
- value: ["免费", "省钱", "保证", "优惠", "简单", "折扣", "高性价比"],
182
- curiosity: ["秘密", "揭秘", "技巧", "为什么", "如何", "必看", "探索"],
183
- sentimentPositive: ["完美", "惊人", "卓越", "成功", "极佳", "出色"],
184
- sentimentNegative: ["错误", "陷阱", "避免", "危险", "骗局", "风险"]
185
- },
186
- ja: {
187
- authority: ["完全ガイド", "究極", "公式", "専門家", "おすすめ", "徹底比較", "決定版", "プロ"],
188
- urgency: ["今すぐ", "速報", "最短", "2026年", "今日", "5分で", "簡単"],
189
- value: ["無料", "お得", "割引", "保証", "コスパ", "特典", "プレゼント"],
190
- curiosity: ["秘密", "裏ワザ", "暴露", "なぜ", "方法", "必見", "真実"],
191
- sentimentPositive: ["最高", "完璧", "圧倒的", "成功", "絶賛", "素晴らしい"],
192
- sentimentNegative: ["失敗", "注意", "危険", "避けるべき", "詐欺", "罠"]
193
- },
194
- hi: {
195
- authority: ["गाइड", "सम्पूर्ण", "विशेषज्ञ", "आधिकारिक", "सर्वश्रेष्ठ", "तुलना", "सटीक"],
196
- urgency: ["तुरंत", "अभी", "आज", "2026", "5 मिनट में", "जल्दी"],
197
- value: ["मुफ्त", "बचत", "गारंटी", "छूट", "आसान", "ऑफर"],
198
- curiosity: ["रहस्य", "खुलासा", "टिप्स", "क्यों", "कैसे", "अनोखा"],
199
- sentimentPositive: ["उत्कृष्ट", "शानदार", "सफल", "अद्भुत", "बेहतरीन"],
200
- sentimentNegative: ["गलती", "खतरा", "सावधान", "धोखा", "नुकसान"]
201
- }
202
- };
203
-
204
- export class PowerWordsPsychologyEngine {
205
- /**
206
- * Register a custom language dictionary at runtime for infinite language extensibility
207
- */
208
- static registerLanguageWords(langCode: string, words: Partial<CategoryWordBank>): void {
209
- const code = langCode.toLowerCase().trim();
210
- if (!MULTILINGUAL_POWER_WORDS[code]) {
211
- MULTILINGUAL_POWER_WORDS[code] = {
212
- authority: [],
213
- urgency: [],
214
- value: [],
215
- curiosity: [],
216
- sentimentPositive: [],
217
- sentimentNegative: [],
218
- };
219
- }
220
- if (words.authority) MULTILINGUAL_POWER_WORDS[code].authority.push(...words.authority);
221
- if (words.urgency) MULTILINGUAL_POWER_WORDS[code].urgency.push(...words.urgency);
222
- if (words.value) MULTILINGUAL_POWER_WORDS[code].value.push(...words.value);
223
- if (words.curiosity) MULTILINGUAL_POWER_WORDS[code].curiosity.push(...words.curiosity);
224
- if (words.sentimentPositive) MULTILINGUAL_POWER_WORDS[code].sentimentPositive.push(...words.sentimentPositive);
225
- if (words.sentimentNegative) MULTILINGUAL_POWER_WORDS[code].sentimentNegative.push(...words.sentimentNegative);
226
- }
227
-
228
- /**
229
- * Get all registered language codes
230
- */
231
- static getSupportedLanguages(): string[] {
232
- return Object.keys(MULTILINGUAL_POWER_WORDS);
233
- }
234
-
235
- /**
236
- * Analyze title psychology and CTR potential in ANY language
237
- */
238
- static analyzeTitle(title: string, lang: LanguageCode = "fr"): PsychologyAnalysisResult {
239
- const raw = (title || "").toLowerCase().trim();
240
- const langKey = lang.toLowerCase().trim();
241
-
242
- // Multi-bank search: search primary language, plus universal English / French fallback
243
- const primaryBank = MULTILINGUAL_POWER_WORDS[langKey] || MULTILINGUAL_POWER_WORDS["en"] || MULTILINGUAL_POWER_WORDS["fr"];
244
- const allBanks = [primaryBank];
245
- if (langKey !== "en" && MULTILINGUAL_POWER_WORDS["en"]) allBanks.push(MULTILINGUAL_POWER_WORDS["en"]);
246
-
247
- const detectedWords: string[] = [];
248
- let authorityMatch = false;
249
- let urgencyMatch = false;
250
- let valueMatch = false;
251
- let curiosityMatch = false;
252
- let positiveCount = 0;
253
- let negativeCount = 0;
254
-
255
- for (const bank of allBanks) {
256
- for (const word of bank.authority) {
257
- if (raw.includes(word.toLowerCase())) {
258
- authorityMatch = true;
259
- if (!detectedWords.includes(word)) detectedWords.push(word);
260
- }
261
- }
262
- for (const word of bank.urgency) {
263
- if (raw.includes(word.toLowerCase())) {
264
- urgencyMatch = true;
265
- if (!detectedWords.includes(word)) detectedWords.push(word);
266
- }
267
- }
268
- for (const word of bank.value) {
269
- if (raw.includes(word.toLowerCase())) {
270
- valueMatch = true;
271
- if (!detectedWords.includes(word)) detectedWords.push(word);
272
- }
273
- }
274
- for (const word of bank.curiosity) {
275
- if (raw.includes(word.toLowerCase())) {
276
- curiosityMatch = true;
277
- if (!detectedWords.includes(word)) detectedWords.push(word);
278
- }
279
- }
280
- for (const word of bank.sentimentPositive) {
281
- if (raw.includes(word.toLowerCase())) {
282
- positiveCount++;
283
- if (!detectedWords.includes(word)) detectedWords.push(word);
284
- }
285
- }
286
- for (const word of bank.sentimentNegative) {
287
- if (raw.includes(word.toLowerCase())) {
288
- negativeCount++;
289
- if (!detectedWords.includes(word)) detectedWords.push(word);
290
- }
291
- }
292
- }
293
-
294
- // Universal Language-Agnostic Triggers
295
- const hasNumber = /\b\d+\b/.test(raw);
296
- const hasYear = /\b(202[4-9]|203[0-9])\b/.test(raw);
297
- const hasCurrencyOrDiscount = /[\$€£¥₹%]|د\.إ|﷼|\b(promo|soldes|discount|deal|off)\b/i.test(raw);
298
- const hasBracketsOrParentheses = /[\[\]\(\)\{\}【】《》「」]/.test(raw);
299
-
300
- // Score Calculation (0 - 100)
301
- let score = 30; // base score
302
- if (detectedWords.length >= 1) score += 20;
303
- if (detectedWords.length >= 2) score += 10;
304
- if (detectedWords.length >= 3) score += 10;
305
- if (hasNumber) score += 15;
306
- if (hasYear) score += 10;
307
- if (hasCurrencyOrDiscount) score += 5;
308
- if (hasBracketsOrParentheses) score += 5;
309
- if (score > 100) score = 100;
310
-
311
- let sentiment: "positif" | "negatif" | "neutre" = "neutre";
312
- if (positiveCount > negativeCount) sentiment = "positif";
313
- else if (negativeCount > positiveCount) sentiment = "negatif";
314
-
315
- const recommendations: string[] = [];
316
- if (detectedWords.length === 0) {
317
- recommendations.push("Ajoutez au moins 1 mot d'impact émotionnel ou d'autorité (ex: 'Guide', 'Ultime', 'Meilleur').");
318
- }
319
- if (!hasNumber) {
320
- recommendations.push("Intégrez un chiffre (ex: 'Les 7 Meilleurs...') pour augmenter le taux de clic de +36%.");
321
- }
322
- if (!hasYear) {
323
- recommendations.push("Indiquez l'année courante (2026) pour signaler la fraîcheur du contenu aux internautes.");
324
- }
325
- if (!hasBracketsOrParentheses) {
326
- recommendations.push("Utilisez des crochets ou parenthèses [Guide 2026] pour structurer visuellement l'extrait.");
327
- }
328
-
329
- // Suggested high CTR titles
330
- const cleanSubject = title.replace(/\(.*?\)|\[.*?\]/g, "").trim();
331
- const suggestedTitles = [
332
- `Les 7 Meilleurs ${cleanSubject} [Guide Ultime 2026]`,
333
- `Comment Maîtriser ${cleanSubject} en 5 Étapes (Méthode 2026)`,
334
- `${cleanSubject} : Avis, Comparatif et Pièges à Éviter (2026)`
335
- ];
336
-
337
- return {
338
- score,
339
- powerWordsDetected: detectedWords,
340
- sentiment,
341
- hasNumber,
342
- hasYear,
343
- hasCuriosityTrigger: curiosityMatch,
344
- hasUrgencyTrigger: urgencyMatch,
345
- hasAuthorityTrigger: authorityMatch,
346
- hasCurrencyOrDiscount,
347
- hasBracketsOrParentheses,
348
- recommendations,
349
- suggestedTitles
350
- };
351
- }
352
-
353
- /**
354
- * Automatically enriches a programmatic title in ANY language with optimal psychology triggers
355
- */
356
- static enrichProgrammaticTitle(baseTitle: string, lang: LanguageCode = "fr"): string {
357
- const analysis = this.analyzeTitle(baseTitle, lang);
358
- if (analysis.score >= 80) return baseTitle;
359
-
360
- let enriched = baseTitle.trim();
361
- if (!analysis.hasNumber && !enriched.startsWith("Top ") && !enriched.startsWith("Les ")) {
362
- enriched = `Les Meilleurs ${enriched}`;
363
- }
364
- if (!analysis.hasYear) {
365
- enriched = `${enriched} (Guide 2026)`;
366
- }
367
- return enriched;
368
- }
369
- }
@@ -1,109 +0,0 @@
1
- /**
2
- * 🗺️ Universal Public Routes & Navigation Manifest Engine
3
- *
4
- * Allows developers and LLM agents to connect their real public pages (10-50 seed pages)
5
- * from ANY framework (Next.js, TanStack Router, Astro, Nuxt, WordPress, Laravel, Remix).
6
- *
7
- * Instead of scanning millions of generated URLs, it inspects ONLY the root product pages,
8
- * navigation menus, or route trees to build the knowledge base for all programmatic pages.
9
- */
10
-
11
- export interface PublicRouteItem {
12
- path: string;
13
- title: string;
14
- description?: string;
15
- category?: string;
16
- paragraphs?: string[];
17
- keyFeatures?: string[];
18
- faqs?: { question: string; answer: string }[];
19
- }
20
-
21
- export interface FrameworkManifestConfig {
22
- framework?: "nextjs" | "tanstack" | "astro" | "wordpress" | "nuxt" | "custom";
23
- routes?: PublicRouteItem[];
24
- navMenuItems?: Array<{ label: string; url: string; category?: string }>;
25
- contentCollections?: Array<{ slug: string; title: string; content: string }>;
26
- }
27
-
28
- export class PublicRoutesManifestEngine {
29
- private static registeredRoutes: Map<string, PublicRouteItem> = new Map();
30
-
31
- /**
32
- * 1. Register Public Routes & Navigation Menu (Universal Entrypoint)
33
- */
34
- static registerManifest(config: FrameworkManifestConfig): number {
35
- let count = 0;
36
-
37
- // A. Explicit Route Items
38
- if (config.routes && Array.isArray(config.routes)) {
39
- for (const r of config.routes) {
40
- this.registeredRoutes.set(r.path, r);
41
- count++;
42
- }
43
- }
44
-
45
- // B. From Navigation Menu Items (WordPress Menus, Navbar JSON, etc.)
46
- if (config.navMenuItems && Array.isArray(config.navMenuItems)) {
47
- for (const nav of config.navMenuItems) {
48
- if (!this.registeredRoutes.has(nav.url)) {
49
- this.registeredRoutes.set(nav.url, {
50
- path: nav.url,
51
- title: nav.label,
52
- category: nav.category || "Main Navigation",
53
- description: `Page officielle ${nav.label} disponible sur le site.`,
54
- paragraphs: [`Découvrez la solution ${nav.label} pour optimiser vos processus et vos résultats.`],
55
- keyFeatures: [`Accès direct à ${nav.label}`, "Intégration transparente"],
56
- });
57
- count++;
58
- }
59
- }
60
- }
61
-
62
- // C. From Markdown / MDX Content Collections (Astro, Next.js Contentlayer)
63
- if (config.contentCollections && Array.isArray(config.contentCollections)) {
64
- for (const doc of config.contentCollections) {
65
- const paragraphs = doc.content
66
- .split("\n\n")
67
- .map((p) => p.replace(/[#*`]/g, "").trim())
68
- .filter((p) => p.length > 30)
69
- .slice(0, 15);
70
-
71
- this.registeredRoutes.set(doc.slug, {
72
- path: doc.slug.startsWith("/") ? doc.slug : `/${doc.slug}`,
73
- title: doc.title,
74
- category: "Documentation & Features",
75
- paragraphs,
76
- description: paragraphs[0] || doc.title,
77
- });
78
- count++;
79
- }
80
- }
81
-
82
- return count;
83
- }
84
-
85
- /**
86
- * 2. Auto-Adapter for WordPress Menu & Pages
87
- * In WordPress: pass result of wp_get_nav_menu_items() or get_pages()
88
- */
89
- static registerWordPressMenu(menuItems: Array<{ title: string; url: string }>): number {
90
- return this.registerManifest({
91
- framework: "wordpress",
92
- navMenuItems: menuItems.map((m) => ({ label: m.title, url: m.url })),
93
- });
94
- }
95
-
96
- /**
97
- * 3. Get all discovered seed public pages
98
- */
99
- static getSeedRoutes(): PublicRouteItem[] {
100
- return Array.from(this.registeredRoutes.values());
101
- }
102
-
103
- /**
104
- * 4. Retrieve single route details
105
- */
106
- static getRoute(pathOrSlug: string): PublicRouteItem | undefined {
107
- return this.registeredRoutes.get(pathOrSlug);
108
- }
109
- }
@@ -1,163 +0,0 @@
1
- /**
2
- * 🧠 LynxRAG Hybrid Knowledge Engine (Enterprise pgvector & Local In-Memory Semantic Index)
3
- *
4
- * Provides unified RAG (Retrieval-Augmented Generation) capabilities:
5
- * 1. Ingests Company PDFs, Pitch Decks, Product Catalogs, and Brand Kits
6
- * 2. Chunks & stores semantic embeddings (pgvector compatible / in-memory cosine fallback)
7
- * 3. Enriches AI Copilot Audit Reports with genuine company facts & certifications
8
- * 4. Enriches Programmatic Pages with authentic proprietary statistics & guarantees
9
- */
10
-
11
- export interface KnowledgeChunk {
12
- id: string;
13
- documentName: string;
14
- category: "pricing" | "technical" | "case_study" | "certification" | "general";
15
- content: string;
16
- embedding?: number[];
17
- keywords: string[];
18
- tokenCount: number;
19
- createdAt: string;
20
- }
21
-
22
- export interface RagRetrievalResult {
23
- chunk: KnowledgeChunk;
24
- similarityScore: number;
25
- }
26
-
27
- export class LynxRagKnowledgeEngine {
28
- private static chunkStore: Map<string, KnowledgeChunk[]> = new Map(); // Keyed by siteId / domain
29
-
30
- /**
31
- * 1. Ingest Raw Document Text (from PDF, Word doc, or manual upload)
32
- */
33
- static ingestDocument(
34
- siteId: string,
35
- documentName: string,
36
- rawText: string,
37
- category: KnowledgeChunk["category"] = "general"
38
- ): KnowledgeChunk[] {
39
- const cleanText = rawText.replace(/\r\n/g, "\n").replace(/\n{3,}/g, "\n\n").trim();
40
- const rawParagraphs = cleanText.split("\n\n").filter((p) => p.trim().length > 30);
41
-
42
- const siteChunks = this.chunkStore.get(siteId) || [];
43
- const newChunks: KnowledgeChunk[] = [];
44
-
45
- rawParagraphs.forEach((para, index) => {
46
- const words = para.toLowerCase().replace(/[^a-z0-9à-ÿ\s]/g, "").split(/\s+/).filter(Boolean);
47
- const uniqueKeywords = Array.from(new Set(words)).slice(0, 15);
48
-
49
- const chunk: KnowledgeChunk = {
50
- id: `chk_${siteId}_${Date.now()}_${index}`,
51
- documentName,
52
- category,
53
- content: para.trim(),
54
- keywords: uniqueKeywords,
55
- tokenCount: Math.round(para.length / 4),
56
- createdAt: new Date().toISOString(),
57
- };
58
-
59
- siteChunks.push(chunk);
60
- newChunks.push(chunk);
61
- });
62
-
63
- this.chunkStore.set(siteId, siteChunks);
64
- return newChunks;
65
- }
66
-
67
- /**
68
- * 2. Semantic Search & Top-K Chunks Retrieval (Fast In-Memory Cosine / Keyword Scoring)
69
- */
70
- static retrieveRelevantContext(
71
- siteId: string,
72
- query: string,
73
- topK: number = 3
74
- ): RagRetrievalResult[] {
75
- const chunks = this.chunkStore.get(siteId) || [];
76
- if (chunks.length === 0) return [];
77
-
78
- const queryTerms = query.toLowerCase().replace(/[^a-z0-9à-ÿ\s]/g, "").split(/\s+/).filter(Boolean);
79
-
80
- const scored = chunks.map((chunk) => {
81
- let score = 0;
82
- const lowerContent = chunk.content.toLowerCase();
83
-
84
- for (const term of queryTerms) {
85
- if (lowerContent.includes(term)) {
86
- score += 2.0;
87
- }
88
- if (chunk.keywords.includes(term)) {
89
- score += 1.5;
90
- }
91
- }
92
-
93
- // Bonus for rich numerical / factual data
94
- if (/\d+[\s%€$]/.test(chunk.content)) {
95
- score += 1.0;
96
- }
97
-
98
- return {
99
- chunk,
100
- similarityScore: score / (queryTerms.length || 1),
101
- };
102
- });
103
-
104
- return scored
105
- .filter((s) => s.similarityScore > 0.3)
106
- .sort((a, b) => b.similarityScore - a.similarityScore)
107
- .slice(0, topK);
108
- }
109
-
110
- /**
111
- * 3. AI Audit Report Enhancer (Injects Company Facts into AI Reports)
112
- */
113
- static enrichAiReportPrompt(
114
- siteId: string,
115
- reportTopic: string,
116
- baseAuditMetrics: Record<string, unknown>
117
- ): { enrichedSystemPrompt: string; retrievedFacts: string[] } {
118
- const relevantChunks = this.retrieveRelevantContext(siteId, reportTopic, 4);
119
- const retrievedFacts = relevantChunks.map((r) => r.chunk.content);
120
-
121
- const factsFormatted = retrievedFacts.length > 0
122
- ? `\n\n### 🏢 DONNÉES ET FAITS PROPRIÉTAIRES DE L'ENTREPRISE (RAG):\n${retrievedFacts.map((f, i) => `${i + 1}. ${f}`).join("\n")}`
123
- : "";
124
-
125
- const enrichedSystemPrompt = `
126
- Vous êtes l'Auditeur SEO & IA Copilot de LynxSEO Studio.
127
- Vous analysez les métriques techniques suivantes : ${JSON.stringify(baseAuditMetrics)}.${factsFormatted}
128
-
129
- Directives d'analyse :
130
- - Personnalisez vos recommandations en tenant compte des faits propriétaires et certifications réelles de l'entreprise.
131
- - Ne proposez que des plans d'action immédiatement applicables et mesurables.
132
- `.trim();
133
-
134
- return {
135
- enrichedSystemPrompt,
136
- retrievedFacts,
137
- };
138
- }
139
-
140
- /**
141
- * 4. Programmatic Page Generator Enhancer (Injects Company Facts into Programmatic Pages)
142
- */
143
- static getFactsForPageGeneration(
144
- siteId: string,
145
- service: string,
146
- location: string
147
- ): string[] {
148
- const query = `${service} ${location} tarifs garantie certification delai`;
149
- const results = this.retrieveRelevantContext(siteId, query, 3);
150
- return results.map((r) => r.chunk.content);
151
- }
152
-
153
- /**
154
- * 5. Clear or Get All Ingested Chunks for Site
155
- */
156
- static getSiteChunks(siteId: string): KnowledgeChunk[] {
157
- return this.chunkStore.get(siteId) || [];
158
- }
159
-
160
- static clearSiteChunks(siteId: string): void {
161
- this.chunkStore.delete(siteId);
162
- }
163
- }