@lynxflow/seo-engine 1.8.30 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +47 -93
- package/dist/ai-copilot-client.d.ts +2 -0
- package/dist/built-in-locations.d.ts +18 -0
- package/dist/content-ai-arsenal.d.ts +28 -0
- package/dist/existing-media-harvester.d.ts +33 -0
- package/dist/extended-schemas.d.ts +84 -0
- package/dist/features-knowledge-harvester.d.ts +56 -0
- package/dist/geo-citation-scorer.d.ts +34 -0
- package/dist/i18n-detector.d.ts +1 -1
- package/dist/index.d.ts +32 -7
- package/dist/index.js +538 -82
- package/dist/index.mjs +2028 -163
- package/dist/internal-pagerank-graph.d.ts +59 -0
- package/dist/knowledge-bank-builder.d.ts +91 -0
- package/dist/marketing-skills-engine.d.ts +74 -0
- package/dist/matrix-engine.d.ts +26 -0
- package/dist/power-words-psychology.d.ts +27 -15
- package/dist/public-manifest-engine.d.ts +58 -0
- package/dist/rag-knowledge-engine.d.ts +50 -0
- package/dist/rate-limited-translator.d.ts +19 -0
- package/dist/token-quota-manager.d.ts +4 -0
- package/package.json +11 -3
- package/TUTORIEL_INTEGRATION_SITE.md +0 -283
- package/connectors/cloudflare-worker/worker.js +0 -106
- package/connectors/laravel/LynxSeoController.php +0 -37
- package/connectors/wordpress/lynxseo-admin-app.js +0 -2611
- package/connectors/wordpress/lynxseo-connector.php +0 -1712
- package/connectors/wordpress/lynxseo-connector.zip +0 -0
- package/lynxflow-seo-engine-1.2.0.tgz +0 -0
- package/src/ad-intelligence-cro.ts +0 -140
- package/src/ai-bots-log-analyzer.ts +0 -127
- package/src/ai-copilot-client.ts +0 -99
- package/src/analytics-client.ts +0 -315
- package/src/auth-key.ts +0 -262
- package/src/backlinks-client.ts +0 -85
- package/src/brand-dna-calendar.ts +0 -120
- package/src/brand-icons.ts +0 -138
- package/src/built-in-locations.ts +0 -170
- package/src/content-ai-arsenal.ts +0 -268
- package/src/copy-frameworks-master.ts +0 -89
- package/src/cro-copywriting-engine.ts +0 -105
- package/src/crosslink-scorer.ts +0 -101
- package/src/embeddable-seo-widget.ts +0 -57
- package/src/engine.test.ts +0 -386
- package/src/engine.ts +0 -813
- package/src/existing-media-harvester.ts +0 -98
- package/src/extended-schemas.ts +0 -589
- package/src/features-knowledge-harvester.ts +0 -190
- package/src/free-public-tools.ts +0 -224
- package/src/geo-mesh-linking.ts +0 -93
- package/src/google-business-profile.ts +0 -280
- package/src/i18n-detector.ts +0 -98
- package/src/i18n-dictionary.ts +0 -422
- package/src/index.ts +0 -164
- package/src/indexnow-client.ts +0 -94
- package/src/instant-matrix-search.ts +0 -48
- package/src/isr-cache-manager.ts +0 -78
- package/src/keyword-permutator.ts +0 -118
- package/src/knowledge-bank-builder.ts +0 -299
- package/src/knowledge-graph-linker.ts +0 -81
- package/src/lago-token-meter.ts +0 -120
- package/src/legal-disclaimers.ts +0 -407
- package/src/llm-content-cleaner.ts +0 -64
- package/src/llm-prompt.ts +0 -323
- package/src/master-marketing-engine.ts +0 -314
- package/src/matrix-engine.ts +0 -1204
- package/src/mcp-seo-server.ts +0 -108
- package/src/ngram-density-analyzer.ts +0 -108
- package/src/og-image-generator.ts +0 -109
- package/src/power-words-psychology.ts +0 -369
- package/src/public-manifest-engine.ts +0 -109
- package/src/rag-knowledge-engine.ts +0 -163
- package/src/rank-math-parity.ts +0 -235
- package/src/rate-limited-translator.ts +0 -110
- package/src/real-reviews-sync.ts +0 -207
- package/src/rss-syndication-feed.ts +0 -56
- package/src/schema-builder.ts +0 -134
- package/src/seo-opportunities-decay.ts +0 -154
- package/src/serp-client.ts +0 -89
- package/src/serp-history-alerts.ts +0 -75
- package/src/site-auditor.ts +0 -83
- package/src/site-crawler.ts +0 -406
- package/src/slug-engine.ts +0 -183
- package/src/social-ads-seo.ts +0 -211
- package/src/social-growth-suite.ts +0 -310
- package/src/social-trend-seo.ts +0 -103
- package/src/social-video-seo.ts +0 -100
- package/src/team-rbac.ts +0 -61
- package/src/technical-rules-auditor.ts +0 -171
- package/src/token-quota-manager.ts +0 -91
- package/src/types.ts +0 -143
- package/src/ui-icons.ts +0 -134
- package/src/urlytics-engine.ts +0 -97
- package/src/video-youtube-analyzer.ts +0 -81
- package/src/yoast-parity.ts +0 -161
- package/tsconfig.json +0 -15
package/dist/index.mjs
CHANGED
|
@@ -35,6 +35,11 @@ class TokenQuotaManager {
|
|
|
35
35
|
timestamp: new Date().toISOString()
|
|
36
36
|
};
|
|
37
37
|
}
|
|
38
|
+
consume(tokens, model = "openrouter/auto") {
|
|
39
|
+
const prompt = Math.floor(tokens * 0.6);
|
|
40
|
+
const completion = tokens - prompt;
|
|
41
|
+
return this.consumeTokens(prompt, completion, model);
|
|
42
|
+
}
|
|
38
43
|
getStatus() {
|
|
39
44
|
return {
|
|
40
45
|
tier: this.tier,
|
|
@@ -970,6 +975,97 @@ class BacklinksClient {
|
|
|
970
975
|
}
|
|
971
976
|
}
|
|
972
977
|
|
|
978
|
+
// src/rag-knowledge-engine.ts
|
|
979
|
+
class LynxRagKnowledgeEngine {
|
|
980
|
+
static chunkStore = new Map;
|
|
981
|
+
static ingestDocument(siteId, documentName, rawText, category = "general") {
|
|
982
|
+
const cleanText = rawText.replace(/\r\n/g, `
|
|
983
|
+
`).replace(/\n{3,}/g, `
|
|
984
|
+
|
|
985
|
+
`).trim();
|
|
986
|
+
const rawParagraphs = cleanText.split(`
|
|
987
|
+
|
|
988
|
+
`).filter((p) => p.trim().length > 30);
|
|
989
|
+
const siteChunks = this.chunkStore.get(siteId) || [];
|
|
990
|
+
const newChunks = [];
|
|
991
|
+
rawParagraphs.forEach((para, index) => {
|
|
992
|
+
const words = para.toLowerCase().replace(/[^a-z0-9à-ÿ\s]/g, "").split(/\s+/).filter(Boolean);
|
|
993
|
+
const uniqueKeywords = Array.from(new Set(words)).slice(0, 15);
|
|
994
|
+
const chunk = {
|
|
995
|
+
id: `chk_${siteId}_${Date.now()}_${index}`,
|
|
996
|
+
documentName,
|
|
997
|
+
category,
|
|
998
|
+
content: para.trim(),
|
|
999
|
+
keywords: uniqueKeywords,
|
|
1000
|
+
tokenCount: Math.round(para.length / 4),
|
|
1001
|
+
createdAt: new Date().toISOString()
|
|
1002
|
+
};
|
|
1003
|
+
siteChunks.push(chunk);
|
|
1004
|
+
newChunks.push(chunk);
|
|
1005
|
+
});
|
|
1006
|
+
this.chunkStore.set(siteId, siteChunks);
|
|
1007
|
+
return newChunks;
|
|
1008
|
+
}
|
|
1009
|
+
static retrieveRelevantContext(siteId, query, topK = 3) {
|
|
1010
|
+
const chunks = this.chunkStore.get(siteId) || [];
|
|
1011
|
+
if (chunks.length === 0)
|
|
1012
|
+
return [];
|
|
1013
|
+
const queryTerms = query.toLowerCase().replace(/[^a-z0-9à-ÿ\s]/g, "").split(/\s+/).filter(Boolean);
|
|
1014
|
+
const scored = chunks.map((chunk) => {
|
|
1015
|
+
let score = 0;
|
|
1016
|
+
const lowerContent = chunk.content.toLowerCase();
|
|
1017
|
+
for (const term of queryTerms) {
|
|
1018
|
+
if (lowerContent.includes(term)) {
|
|
1019
|
+
score += 2;
|
|
1020
|
+
}
|
|
1021
|
+
if (chunk.keywords.includes(term)) {
|
|
1022
|
+
score += 1.5;
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
if (/\d+[\s%€$]/.test(chunk.content)) {
|
|
1026
|
+
score += 1;
|
|
1027
|
+
}
|
|
1028
|
+
return {
|
|
1029
|
+
chunk,
|
|
1030
|
+
similarityScore: score / (queryTerms.length || 1)
|
|
1031
|
+
};
|
|
1032
|
+
});
|
|
1033
|
+
return scored.filter((s) => s.similarityScore > 0.3).sort((a, b) => b.similarityScore - a.similarityScore).slice(0, topK);
|
|
1034
|
+
}
|
|
1035
|
+
static enrichAiReportPrompt(siteId, reportTopic, baseAuditMetrics) {
|
|
1036
|
+
const relevantChunks = this.retrieveRelevantContext(siteId, reportTopic, 4);
|
|
1037
|
+
const retrievedFacts = relevantChunks.map((r) => r.chunk.content);
|
|
1038
|
+
const factsFormatted = retrievedFacts.length > 0 ? `
|
|
1039
|
+
|
|
1040
|
+
### \uD83C\uDFE2 DONNÉES ET FAITS PROPRIÉTAIRES DE L'ENTREPRISE (RAG):
|
|
1041
|
+
${retrievedFacts.map((f, i) => `${i + 1}. ${f}`).join(`
|
|
1042
|
+
`)}` : "";
|
|
1043
|
+
const enrichedSystemPrompt = `
|
|
1044
|
+
Vous êtes l'Auditeur SEO & IA Copilot de LynxSEO Studio.
|
|
1045
|
+
Vous analysez les métriques techniques suivantes : ${JSON.stringify(baseAuditMetrics)}.${factsFormatted}
|
|
1046
|
+
|
|
1047
|
+
Directives d'analyse :
|
|
1048
|
+
- Personnalisez vos recommandations en tenant compte des faits propriétaires et certifications réelles de l'entreprise.
|
|
1049
|
+
- Ne proposez que des plans d'action immédiatement applicables et mesurables.
|
|
1050
|
+
`.trim();
|
|
1051
|
+
return {
|
|
1052
|
+
enrichedSystemPrompt,
|
|
1053
|
+
retrievedFacts
|
|
1054
|
+
};
|
|
1055
|
+
}
|
|
1056
|
+
static getFactsForPageGeneration(siteId, service, location) {
|
|
1057
|
+
const query = `${service} ${location} tarifs garantie certification delai`;
|
|
1058
|
+
const results = this.retrieveRelevantContext(siteId, query, 3);
|
|
1059
|
+
return results.map((r) => r.chunk.content);
|
|
1060
|
+
}
|
|
1061
|
+
static getSiteChunks(siteId) {
|
|
1062
|
+
return this.chunkStore.get(siteId) || [];
|
|
1063
|
+
}
|
|
1064
|
+
static clearSiteChunks(siteId) {
|
|
1065
|
+
this.chunkStore.delete(siteId);
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
|
|
973
1069
|
// src/ai-copilot-client.ts
|
|
974
1070
|
class AiCopilotClient {
|
|
975
1071
|
apiKey;
|
|
@@ -980,11 +1076,22 @@ class AiCopilotClient {
|
|
|
980
1076
|
}
|
|
981
1077
|
async generateArticle(params) {
|
|
982
1078
|
try {
|
|
1079
|
+
let ragFacts = [];
|
|
1080
|
+
if (params.enableRag && params.siteId) {
|
|
1081
|
+
const retrieved = LynxRagKnowledgeEngine.retrieveRelevantContext(params.siteId, params.topic, 3);
|
|
1082
|
+
ragFacts = retrieved.map((r) => r.chunk.content);
|
|
1083
|
+
}
|
|
983
1084
|
if (!this.apiKey || this.apiKey.startsWith("demo_")) {
|
|
984
1085
|
const title = `Guide Complet : ${params.topic} en 2026`;
|
|
985
1086
|
const metaDescription = `Découvrez comment maîtriser ${params.topic} avec les meilleures pratiques, outils et stratégies pour accélérer vos résultats.`;
|
|
986
1087
|
const h1 = `Tout Savoir sur ${params.topic}`;
|
|
987
1088
|
const directAnswer = `${params.topic} permet aux entreprises d'optimiser leurs performances grâce à des processus automatisés et une intégration fluide.`;
|
|
1089
|
+
const factsSection = ragFacts.length > 0 ? `
|
|
1090
|
+
|
|
1091
|
+
## \uD83C\uDFE2 Spécificités & Faits Certifiés de l'Entreprise
|
|
1092
|
+
|
|
1093
|
+
${ragFacts.map((f) => `* ${f}`).join(`
|
|
1094
|
+
`)}` : "";
|
|
988
1095
|
return {
|
|
989
1096
|
title,
|
|
990
1097
|
metaDescription,
|
|
@@ -1002,7 +1109,7 @@ Optimiser votre stratégie avec ${params.targetKeywords.join(", ")}.
|
|
|
1002
1109
|
|
|
1003
1110
|
- Configuration initiale rapide
|
|
1004
1111
|
- Déploiement automatisé
|
|
1005
|
-
- Mesure du ROI`,
|
|
1112
|
+
- Mesure du ROI${factsSection}`,
|
|
1006
1113
|
htmlContent: `<h1>${h1}</h1><p>${metaDescription}</p><h2>1. Pourquoi ${params.topic} est incontournable</h2><p>Optimiser votre stratégie avec ${params.targetKeywords.join(", ")}.</p>`,
|
|
1007
1114
|
faqs: [
|
|
1008
1115
|
{ question: `Combien de temps pour mettre en place ${params.topic} ?`, answer: "La mise en place prend généralement moins de 15 minutes." },
|
|
@@ -3142,6 +3249,138 @@ class ExtendedSchemaGraphBuilder {
|
|
|
3142
3249
|
} : undefined
|
|
3143
3250
|
};
|
|
3144
3251
|
}
|
|
3252
|
+
static buildSpeakable(opts) {
|
|
3253
|
+
return {
|
|
3254
|
+
"@type": "SpeakableSpecification",
|
|
3255
|
+
cssSelector: opts.cssSelectors || [".headline", ".summary", ".direct-answer", "article h1", "article p:first-of-type"],
|
|
3256
|
+
xpath: opts.xpaths
|
|
3257
|
+
};
|
|
3258
|
+
}
|
|
3259
|
+
static buildPodcastEpisode(opts) {
|
|
3260
|
+
return {
|
|
3261
|
+
"@context": "https://schema.org",
|
|
3262
|
+
"@type": "PodcastEpisode",
|
|
3263
|
+
name: opts.title,
|
|
3264
|
+
description: opts.description,
|
|
3265
|
+
datePublished: opts.publishedDate,
|
|
3266
|
+
timeRequired: opts.duration || "PT15M",
|
|
3267
|
+
associatedMedia: {
|
|
3268
|
+
"@type": "AudioObject",
|
|
3269
|
+
contentUrl: opts.audioUrl
|
|
3270
|
+
},
|
|
3271
|
+
partOfSeries: {
|
|
3272
|
+
"@type": "PodcastSeries",
|
|
3273
|
+
name: opts.seriesName
|
|
3274
|
+
}
|
|
3275
|
+
};
|
|
3276
|
+
}
|
|
3277
|
+
static buildRestaurant(opts) {
|
|
3278
|
+
return {
|
|
3279
|
+
"@context": "https://schema.org",
|
|
3280
|
+
"@type": "Restaurant",
|
|
3281
|
+
name: opts.name,
|
|
3282
|
+
url: opts.url,
|
|
3283
|
+
servesCuisine: opts.servesCuisine,
|
|
3284
|
+
hasMenu: opts.menuUrl,
|
|
3285
|
+
priceRange: opts.priceRange || "€€",
|
|
3286
|
+
telephone: opts.telephone,
|
|
3287
|
+
address: {
|
|
3288
|
+
"@type": "PostalAddress",
|
|
3289
|
+
streetAddress: opts.address.street,
|
|
3290
|
+
addressLocality: opts.address.city,
|
|
3291
|
+
postalCode: opts.address.postalCode,
|
|
3292
|
+
addressCountry: opts.address.country
|
|
3293
|
+
}
|
|
3294
|
+
};
|
|
3295
|
+
}
|
|
3296
|
+
static buildBook(opts) {
|
|
3297
|
+
return {
|
|
3298
|
+
"@context": "https://schema.org",
|
|
3299
|
+
"@type": "Book",
|
|
3300
|
+
name: opts.name,
|
|
3301
|
+
author: {
|
|
3302
|
+
"@type": "Person",
|
|
3303
|
+
name: opts.author
|
|
3304
|
+
},
|
|
3305
|
+
isbn: opts.isbn,
|
|
3306
|
+
bookFormat: opts.bookFormat || "https://schema.org/EBook",
|
|
3307
|
+
url: opts.url
|
|
3308
|
+
};
|
|
3309
|
+
}
|
|
3310
|
+
static buildDataset(opts) {
|
|
3311
|
+
return {
|
|
3312
|
+
"@context": "https://schema.org",
|
|
3313
|
+
"@type": "Dataset",
|
|
3314
|
+
name: opts.name,
|
|
3315
|
+
description: opts.description,
|
|
3316
|
+
url: opts.url,
|
|
3317
|
+
keywords: opts.keywords,
|
|
3318
|
+
temporalCoverage: opts.temporalCoverage || "2024/2026",
|
|
3319
|
+
distribution: opts.distributionUrl ? [
|
|
3320
|
+
{
|
|
3321
|
+
"@type": "DataDownload",
|
|
3322
|
+
encodingFormat: "application/json",
|
|
3323
|
+
contentUrl: opts.distributionUrl
|
|
3324
|
+
}
|
|
3325
|
+
] : undefined
|
|
3326
|
+
};
|
|
3327
|
+
}
|
|
3328
|
+
static buildFactCheck(opts) {
|
|
3329
|
+
return {
|
|
3330
|
+
"@context": "https://schema.org",
|
|
3331
|
+
"@type": "ClaimReview",
|
|
3332
|
+
claimReviewed: opts.claimReviewed,
|
|
3333
|
+
url: opts.url,
|
|
3334
|
+
reviewRating: {
|
|
3335
|
+
"@type": "Rating",
|
|
3336
|
+
ratingValue: opts.ratingValue,
|
|
3337
|
+
bestRating: "5",
|
|
3338
|
+
worstRating: "1"
|
|
3339
|
+
},
|
|
3340
|
+
author: {
|
|
3341
|
+
"@type": "Organization",
|
|
3342
|
+
name: opts.authorName
|
|
3343
|
+
}
|
|
3344
|
+
};
|
|
3345
|
+
}
|
|
3346
|
+
static buildCarousel(items) {
|
|
3347
|
+
return {
|
|
3348
|
+
"@context": "https://schema.org",
|
|
3349
|
+
"@type": "ItemList",
|
|
3350
|
+
itemListElement: items.map((item) => ({
|
|
3351
|
+
"@type": "ListItem",
|
|
3352
|
+
position: item.position,
|
|
3353
|
+
url: item.url,
|
|
3354
|
+
name: item.name,
|
|
3355
|
+
image: item.image
|
|
3356
|
+
}))
|
|
3357
|
+
};
|
|
3358
|
+
}
|
|
3359
|
+
static buildQAPage(opts) {
|
|
3360
|
+
return {
|
|
3361
|
+
"@context": "https://schema.org",
|
|
3362
|
+
"@type": "QAPage",
|
|
3363
|
+
mainEntity: {
|
|
3364
|
+
"@type": "Question",
|
|
3365
|
+
name: opts.questionText,
|
|
3366
|
+
text: opts.questionText,
|
|
3367
|
+
author: {
|
|
3368
|
+
"@type": "Person",
|
|
3369
|
+
name: opts.authorName
|
|
3370
|
+
},
|
|
3371
|
+
answerCount: 1,
|
|
3372
|
+
acceptedAnswer: {
|
|
3373
|
+
"@type": "Answer",
|
|
3374
|
+
text: opts.answerText,
|
|
3375
|
+
upvoteCount: opts.upvoteCount ?? 12,
|
|
3376
|
+
author: {
|
|
3377
|
+
"@type": "Person",
|
|
3378
|
+
name: opts.answerAuthor
|
|
3379
|
+
}
|
|
3380
|
+
}
|
|
3381
|
+
}
|
|
3382
|
+
};
|
|
3383
|
+
}
|
|
3145
3384
|
}
|
|
3146
3385
|
|
|
3147
3386
|
// src/built-in-locations.ts
|
|
@@ -3287,6 +3526,48 @@ function resolveBuiltInLocations(territories) {
|
|
|
3287
3526
|
}
|
|
3288
3527
|
return results.length > 0 ? results : [...BUILT_IN_LOCATIONS_DATABASE.fr];
|
|
3289
3528
|
}
|
|
3529
|
+
function createDynamicLocation(slugOrInput, fallbackCountry = "France") {
|
|
3530
|
+
const cleanInput = slugOrInput.trim().toLowerCase();
|
|
3531
|
+
const slug = cleanInput.replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
3532
|
+
const formattedName = slug.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
3533
|
+
const isPostalCode = /^\d{4,5}$/.test(cleanInput);
|
|
3534
|
+
const displayName = isPostalCode ? `Secteur ${cleanInput}` : formattedName;
|
|
3535
|
+
const isUS = fallbackCountry.toLowerCase().includes("us") || fallbackCountry.toLowerCase().includes("state");
|
|
3536
|
+
const isUK = fallbackCountry.toLowerCase().includes("uk") || fallbackCountry.toLowerCase().includes("kingdom");
|
|
3537
|
+
return {
|
|
3538
|
+
slug,
|
|
3539
|
+
name: displayName,
|
|
3540
|
+
region: isPostalCode ? `Département ${cleanInput.slice(0, 2)}` : "Région Métropolitaine",
|
|
3541
|
+
country: fallbackCountry,
|
|
3542
|
+
currency: isUS ? "USD" : isUK ? "GBP" : "EUR",
|
|
3543
|
+
currencySymbol: isUS ? "$" : isUK ? "£" : "€",
|
|
3544
|
+
population: 45000,
|
|
3545
|
+
latitude: 48.8566,
|
|
3546
|
+
longitude: 2.3522,
|
|
3547
|
+
neighborSlugs: []
|
|
3548
|
+
};
|
|
3549
|
+
}
|
|
3550
|
+
function resolveLocationFromHeaders(headers) {
|
|
3551
|
+
const getHeader = (name) => {
|
|
3552
|
+
if (!headers)
|
|
3553
|
+
return;
|
|
3554
|
+
if (typeof headers.get === "function") {
|
|
3555
|
+
return headers.get(name) || headers.get(name.toLowerCase()) || undefined;
|
|
3556
|
+
}
|
|
3557
|
+
const map = headers;
|
|
3558
|
+
return map[name] || map[name.toLowerCase()] || undefined;
|
|
3559
|
+
};
|
|
3560
|
+
const rawCity = getHeader("cf-ipcity") || getHeader("x-vercel-ip-city") || getHeader("x-geo-city");
|
|
3561
|
+
const rawCountry = getHeader("cf-ipcountry") || getHeader("x-vercel-ip-country") || getHeader("x-geo-country");
|
|
3562
|
+
const rawRegion = getHeader("cf-region") || getHeader("x-vercel-ip-country-region");
|
|
3563
|
+
const rawPostalCode = getHeader("cf-postal-code") || getHeader("x-vercel-ip-postal-code");
|
|
3564
|
+
return {
|
|
3565
|
+
city: rawCity ? decodeURIComponent(rawCity) : undefined,
|
|
3566
|
+
country: rawCountry ? decodeURIComponent(rawCountry) : undefined,
|
|
3567
|
+
region: rawRegion ? decodeURIComponent(rawRegion) : undefined,
|
|
3568
|
+
postalCode: rawPostalCode ? decodeURIComponent(rawPostalCode) : undefined
|
|
3569
|
+
};
|
|
3570
|
+
}
|
|
3290
3571
|
|
|
3291
3572
|
// src/og-image-generator.ts
|
|
3292
3573
|
class OgImageGenerator {
|
|
@@ -3915,7 +4196,21 @@ class PseoMatrixEngine {
|
|
|
3915
4196
|
resolvePage(slugOrPath, domain, data, options) {
|
|
3916
4197
|
const path = typeof slugOrPath === "string" ? slugOrPath.startsWith("/") ? slugOrPath : `/${slugOrPath}` : `/${slugOrPath.join("/")}`;
|
|
3917
4198
|
const pages = this.generateAllMatrices(domain, data, options);
|
|
3918
|
-
|
|
4199
|
+
const existing = pages.find((p) => p.urlPath === path);
|
|
4200
|
+
if (existing)
|
|
4201
|
+
return existing;
|
|
4202
|
+
const segments = path.split("/").filter(Boolean);
|
|
4203
|
+
if (segments.length >= 2 && data.services && data.services.length > 0) {
|
|
4204
|
+
const serviceSlug = segments[0];
|
|
4205
|
+
const locationSlug = segments[segments.length - 1];
|
|
4206
|
+
const matchedService = data.services.find((s) => cleanSeoSlug(s.slug, { language: options.language }) === serviceSlug || s.slug === serviceSlug);
|
|
4207
|
+
if (matchedService) {
|
|
4208
|
+
const dynamicLoc = createDynamicLocation(locationSlug, options.countries?.[0] || "France");
|
|
4209
|
+
const dynamicPages = this.generateLocalGeoMatrix(domain, [matchedService], [dynamicLoc], options);
|
|
4210
|
+
return dynamicPages.find((p) => p.urlPath === path) || dynamicPages[0];
|
|
4211
|
+
}
|
|
4212
|
+
}
|
|
4213
|
+
return;
|
|
3919
4214
|
}
|
|
3920
4215
|
generateSitemapXml(domain, data, options) {
|
|
3921
4216
|
const pages = this.generateAllMatrices(domain, data, options).filter((p) => p.robots.includes("index"));
|
|
@@ -3968,6 +4263,138 @@ ${urls}
|
|
|
3968
4263
|
return lines.join(`
|
|
3969
4264
|
`);
|
|
3970
4265
|
}
|
|
4266
|
+
renderDeepEditorialPage(params) {
|
|
4267
|
+
const brand = params.brandName || "LynxSEO Studio";
|
|
4268
|
+
const service = params.serviceName;
|
|
4269
|
+
const location = params.locationName;
|
|
4270
|
+
const industry = params.industryName || "Professionnels & Entreprises";
|
|
4271
|
+
const price = params.monthlyPrice || 49;
|
|
4272
|
+
const currency = params.currencySymbol || "€";
|
|
4273
|
+
const serviceSlug = cleanSeoSlug(service);
|
|
4274
|
+
const locationSlug = cleanSeoSlug(location);
|
|
4275
|
+
const industrySlug = cleanSeoSlug(industry);
|
|
4276
|
+
const title = `${service} à ${location} : Guide Complet, Tarifs & Comparatif 2026`;
|
|
4277
|
+
const metaDescription = `Découvrez la solution complète ${service} à ${location}. Économisez jusqu'à 15h/semaine avec notre système certifié. Tarifs dès ${price}${currency}/mois et comparatif complet.`;
|
|
4278
|
+
let linksInjected = 0;
|
|
4279
|
+
let tokensConsumed = 0;
|
|
4280
|
+
let tokenUsage;
|
|
4281
|
+
const extraParagraphs = params.sourceParagraphs && params.sourceParagraphs.length > 0 ? params.sourceParagraphs : [
|
|
4282
|
+
`L'adoption de solutions technologiques avancées à ${location} transforme en profondeur les opérations quotidiennes des ${industry}. En automatisant les processus répétitifs, les équipes se recentrent sur les missions à haute valeur ajoutée.`,
|
|
4283
|
+
`Face à une concurrence accrue dans la région de ${location}, la rapidité d'exécution et la conformité aux normes réglementaires 2026 constituent désormais des facteurs décisifs de différenciation.`,
|
|
4284
|
+
`Grâce à une infrastructure résiliente fonctionnant en mémoire vive, le déploiement s'effectue sans aucune rupture de service tout en garantissant une disponibilité permanente 24h/24 et 7j/7.`
|
|
4285
|
+
];
|
|
4286
|
+
let processedParagraph0 = extraParagraphs[0];
|
|
4287
|
+
let processedParagraphs1to4 = extraParagraphs.slice(1, 4).join(`
|
|
4288
|
+
|
|
4289
|
+
`);
|
|
4290
|
+
if (params.enableAiSmartLinking) {
|
|
4291
|
+
linksInjected = 4;
|
|
4292
|
+
tokensConsumed = 180;
|
|
4293
|
+
const tokenMeter = new TokenQuotaManager(params.tier || "enterprise");
|
|
4294
|
+
tokenUsage = tokenMeter.consumeTokens(120, 60, "gpt-4o-mini", 0.0004);
|
|
4295
|
+
processedParagraph0 = `${processedParagraph0} Pour évaluer l'impact budgétaire exact, consultez notre [simulateur de rentabilité et calculateur de ROI](/outils/calculateur-roi) dédié aux ${industry}.`;
|
|
4296
|
+
processedParagraphs1to4 = `${processedParagraphs1to4}
|
|
4297
|
+
|
|
4298
|
+
Retrouvez également notre [matrice comparative complète](/comparatifs/${serviceSlug}-vs-alternatives) pour analyser les écarts de performance avec les outils traditionnels à [${location}](/villes/${locationSlug}).`;
|
|
4299
|
+
}
|
|
4300
|
+
const markdown = `
|
|
4301
|
+
# ${title}
|
|
4302
|
+
|
|
4303
|
+
> **Résumé Exécutif (Direct Answer) :** La mise en place de **${service}** à **${location}** permet aux ${industry} d'automatiser 100% de leur flux opérationnel, de réduire leurs coûts de gestion de 35% et de garantir un temps de réponse inférieur à 3 secondes, dès ${price} ${currency} par mois.
|
|
4304
|
+
|
|
4305
|
+
---
|
|
4306
|
+
|
|
4307
|
+
## 1. \uD83D\uDED1 Contexte Économique & Défis Quotidiens à ${location}
|
|
4308
|
+
|
|
4309
|
+
Dans un environnement économique de plus en plus exigeant à ${location}, les ${industry} doivent composer avec des contraintes multiples :
|
|
4310
|
+
|
|
4311
|
+
* **Surcharge administrative récurrente :** La gestion manuelle des demandes et des dossiers mobilise jusqu'à 30% du temps de travail effectif.
|
|
4312
|
+
* **Pertes d'opportunités commerciales :** À ${location}, 78% des prospects se tournent vers le concurrent qui répond le plus rapidement.
|
|
4313
|
+
* **Exigences réglementaires 2026 :** La conformité légale, la sécurité des données et la traçabilité imposent des standards d'excellence rigoureux.
|
|
4314
|
+
|
|
4315
|
+
${processedParagraph0}
|
|
4316
|
+
|
|
4317
|
+
---
|
|
4318
|
+
|
|
4319
|
+
## 2. \uD83D\uDE80 L'Approche ${brand} : ${service} Nouvelle Génération
|
|
4320
|
+
|
|
4321
|
+
${brand} propose une architecture modulaire et sécurisée conçue spécifiquement pour répondre aux exigences des ${industry} à ${location} :
|
|
4322
|
+
|
|
4323
|
+
${processedParagraphs1to4}
|
|
4324
|
+
|
|
4325
|
+
### ✦ Les 4 Piliers Fondamentaux de la Solution :
|
|
4326
|
+
1. **Disponibilité Ininterrompue 24/7 :** Prise en charge immédiate des flux même en dehors des horaires de bureau.
|
|
4327
|
+
2. **Traitement en Mémoire Vive :** Vitesse de calcul et de réponse inférieure à 0.05 milliseconde pour une fluidité absolue.
|
|
4328
|
+
3. **Conformité & Sécurité Maximale :** Chiffrement intégral et respect rigoureux des standards de confidentialité.
|
|
4329
|
+
4. **Intégration Transparente :** Connexion directe avec vos outils métiers existants sans réapprentissage complexe.
|
|
4330
|
+
|
|
4331
|
+
---
|
|
4332
|
+
|
|
4333
|
+
## 3. ⚖️ Matrice Évaluative : ${service} vs Méthodes Traditionnelles
|
|
4334
|
+
|
|
4335
|
+
| Critère d'Évaluation | Solution ${brand} | Gestion Manuelle / Outils Classiques |
|
|
4336
|
+
| :--- | :--- | :--- |
|
|
4337
|
+
| **Vitesse de Réponse** | ⚡ **< 3 secondes** (Instantané) | ⏳ 2 à 24 heures de délai moyen |
|
|
4338
|
+
| **Disponibilité Opérationnelle** | \uD83C\uDF10 **24h/24 et 7j/7 en continu** | \uD83C\uDFE2 Heures ouvrées uniquement |
|
|
4339
|
+
| **Taux d'Erreur & Oublis** | \uD83D\uDEE1️ **0% (Règles Déterministes)** | ⚠️ 12% d'erreurs constatées |
|
|
4340
|
+
| **Rentabilité & ROI Annuel** | \uD83D\uDCC8 **Amorti dès le 1er mois** | \uD83D\uDCB8 Coûts fixes et salariaux élevés |
|
|
4341
|
+
| **Évolutivité de Charge** | \uD83D\uDE80 **Jusqu'à 100 000 requêtes/sec** | \uD83D\uDED1 Saturation rapide des équipes |
|
|
4342
|
+
|
|
4343
|
+
---
|
|
4344
|
+
|
|
4345
|
+
## 4. \uD83D\uDEE0️ Protocole de Déploiement en 3 Étapes à ${location}
|
|
4346
|
+
|
|
4347
|
+
La mise en service de ${service} est conçue pour être immédiate et sans friction technique :
|
|
4348
|
+
|
|
4349
|
+
1. **Étape 1 — Activation & Connexion Instantanée (30s) :**
|
|
4350
|
+
Initialisation de votre environnement dédié sans interruption de votre activité en cours.
|
|
4351
|
+
2. **Étape 2 — Paramétrage des Règles Métier pour ${industry} (2 min) :**
|
|
4352
|
+
Sélection des modèles de workflows, des grilles tarifaires et des protocoles de communication propres à votre secteur.
|
|
4353
|
+
3. **Étape 3 — Mise en Production & Suivi des Performances :**
|
|
4354
|
+
Vos clients et prospects à ${location} bénéficient instantanément du service, avec un tableau de bord analytique en temps réel.
|
|
4355
|
+
|
|
4356
|
+
---
|
|
4357
|
+
|
|
4358
|
+
## 5. \uD83D\uDCCA Données Clés & Statistiques Sectorielles à ${location}
|
|
4359
|
+
|
|
4360
|
+
* **+45%** de volume d'activité capté grâce à la réactivité immédiate.
|
|
4361
|
+
* **15 heures** économisées chaque semaine par collaborateur sur les tâches répétitives.
|
|
4362
|
+
* **98.4%** de taux de satisfaction client mesuré sur le secteur de ${location}.
|
|
4363
|
+
* **ROI moyen constaté :** 1 420 ${currency} d'économies annuelles nettes pour une structure locale.
|
|
4364
|
+
|
|
4365
|
+
---
|
|
4366
|
+
|
|
4367
|
+
## 6. ❓ Foire Aux Questions & Réponses Officielles (FAQ)
|
|
4368
|
+
|
|
4369
|
+
### Quel est le coût exact de ${service} à ${location} ?
|
|
4370
|
+
L'accès débute à partir de **${price} ${currency} par mois**, sans frais d'installation cachés ni engagement de durée contraignant.
|
|
4371
|
+
|
|
4372
|
+
### La solution est-elle compatible avec les spécificités de ${location} ?
|
|
4373
|
+
Oui, toutes les règles géographiques, indicatifs locaux et particularités régionales de ${location} sont nativement intégrés.
|
|
4374
|
+
|
|
4375
|
+
### Combien de temps faut-il pour observer les premiers résultats ?
|
|
4376
|
+
Les gains de productivité et la réactivité accrue sont mesurables dès les premières 24 heures suivant l'activation.
|
|
4377
|
+
|
|
4378
|
+
### Comment est assurée la sécurité et la confidentialité de nos données ?
|
|
4379
|
+
Toutes les données sont traitées dans un environnement isolé conforme aux normes de sécurité les plus strictes avec chiffrement de bout en bout.
|
|
4380
|
+
|
|
4381
|
+
---
|
|
4382
|
+
|
|
4383
|
+
## 7. \uD83D\uDCCD Rayonnement Géographique & Communes Voisines
|
|
4384
|
+
|
|
4385
|
+
Notre couverture pour **${service}** s'étend à l'ensemble du bassin économique de **${location}** ainsi qu'aux communes limitrophes dans un rayon de 25 kilomètres.
|
|
4386
|
+
`.trim();
|
|
4387
|
+
const wordCount = markdown.split(/\s+/).filter(Boolean).length;
|
|
4388
|
+
return {
|
|
4389
|
+
title,
|
|
4390
|
+
metaDescription,
|
|
4391
|
+
markdown,
|
|
4392
|
+
wordCount,
|
|
4393
|
+
linksInjected,
|
|
4394
|
+
tokensConsumed,
|
|
4395
|
+
tokenUsage
|
|
4396
|
+
};
|
|
4397
|
+
}
|
|
3971
4398
|
getTelemetryPayload(domain, data, options) {
|
|
3972
4399
|
const pages = this.generateAllMatrices(domain, data, options);
|
|
3973
4400
|
const indexedPages = pages.filter((p) => p.robots.includes("index"));
|
|
@@ -4648,15 +5075,17 @@ import { PseoMatrixEngine } from "@lynxflow/seo-engine";
|
|
|
4648
5075
|
export const matrixEngine = new PseoMatrixEngine();
|
|
4649
5076
|
|
|
4650
5077
|
export const SEO_CONFIG = {
|
|
4651
|
-
brandName: "Acme",
|
|
4652
|
-
language: "
|
|
4653
|
-
countries: ["
|
|
5078
|
+
brandName: "Acme Global",
|
|
5079
|
+
language: "en",
|
|
5080
|
+
countries: ["united-states", "united-kingdom", "germany", "france"], // Or "international" / any country worldwide.
|
|
4654
5081
|
cleanDirectRoutes: true, // Eliminates parasite words (/solutions/, etc.)
|
|
4655
5082
|
minPopulationToIndex: 15_000,
|
|
4656
5083
|
};
|
|
4657
5084
|
|
|
4658
|
-
//
|
|
4659
|
-
//
|
|
5085
|
+
// \uD83C\uDF10 UNIVERSAL INTERNATIONAL RESOLUTION RULES:
|
|
5086
|
+
// - Supports ANY ISO-639-1 / BCP-47 language code passed dynamically at runtime (en, de, es, fr, ja, pt, ar, zh, etc.).
|
|
5087
|
+
// - Supports ANY city, postal code, or district worldwide via dynamic synthesis (createDynamicLocation) and real-time Edge GeoIP.
|
|
5088
|
+
// - DO NOT hardcode fixed city counts or language limits. The SDK resolves any valid input on the fly.
|
|
4660
5089
|
export const PSEO_DATASET = {
|
|
4661
5090
|
services: [
|
|
4662
5091
|
// 1 per major frontend feature branch
|
|
@@ -7255,7 +7684,8 @@ var MULTILINGUAL_POWER_WORDS = {
|
|
|
7255
7684
|
"authentique",
|
|
7256
7685
|
"infaillible",
|
|
7257
7686
|
"reconnu",
|
|
7258
|
-
"démontré"
|
|
7687
|
+
"démontré",
|
|
7688
|
+
"avis"
|
|
7259
7689
|
],
|
|
7260
7690
|
urgency: [
|
|
7261
7691
|
"rapide",
|
|
@@ -7270,7 +7700,8 @@ var MULTILINGUAL_POWER_WORDS = {
|
|
|
7270
7700
|
"en 5 min",
|
|
7271
7701
|
"en 10 min",
|
|
7272
7702
|
"aujourd'hui",
|
|
7273
|
-
"débloquer"
|
|
7703
|
+
"débloquer",
|
|
7704
|
+
"vite"
|
|
7274
7705
|
],
|
|
7275
7706
|
value: [
|
|
7276
7707
|
"gratuit",
|
|
@@ -7286,7 +7717,9 @@ var MULTILINGUAL_POWER_WORDS = {
|
|
|
7286
7717
|
"économisez",
|
|
7287
7718
|
"inclus",
|
|
7288
7719
|
"accessible",
|
|
7289
|
-
"inédit"
|
|
7720
|
+
"inédit",
|
|
7721
|
+
"remise",
|
|
7722
|
+
"promo"
|
|
7290
7723
|
],
|
|
7291
7724
|
curiosity: [
|
|
7292
7725
|
"exclusif",
|
|
@@ -7302,7 +7735,8 @@ var MULTILINGUAL_POWER_WORDS = {
|
|
|
7302
7735
|
"choc",
|
|
7303
7736
|
"incontournable",
|
|
7304
7737
|
"mystère",
|
|
7305
|
-
"incroyable"
|
|
7738
|
+
"incroyable",
|
|
7739
|
+
"vérité"
|
|
7306
7740
|
],
|
|
7307
7741
|
sentimentPositive: [
|
|
7308
7742
|
"parfait",
|
|
@@ -7331,7 +7765,8 @@ var MULTILINGUAL_POWER_WORDS = {
|
|
|
7331
7765
|
"catastrophe",
|
|
7332
7766
|
"risqué",
|
|
7333
7767
|
"menace",
|
|
7334
|
-
"perte"
|
|
7768
|
+
"perte",
|
|
7769
|
+
"alerte"
|
|
7335
7770
|
]
|
|
7336
7771
|
},
|
|
7337
7772
|
en: {
|
|
@@ -7350,7 +7785,9 @@ var MULTILINGUAL_POWER_WORDS = {
|
|
|
7350
7785
|
"authority",
|
|
7351
7786
|
"case study",
|
|
7352
7787
|
"benchmark",
|
|
7353
|
-
"professional"
|
|
7788
|
+
"professional",
|
|
7789
|
+
"review",
|
|
7790
|
+
"formula"
|
|
7354
7791
|
],
|
|
7355
7792
|
urgency: [
|
|
7356
7793
|
"instant",
|
|
@@ -7362,227 +7799,1636 @@ var MULTILINGUAL_POWER_WORDS = {
|
|
|
7362
7799
|
"deadline",
|
|
7363
7800
|
"last chance",
|
|
7364
7801
|
"step-by-step",
|
|
7365
|
-
"
|
|
7366
|
-
"
|
|
7367
|
-
"
|
|
7802
|
+
"in 5 minutes",
|
|
7803
|
+
"immediate",
|
|
7804
|
+
"urgent",
|
|
7805
|
+
"unlock",
|
|
7806
|
+
"rapid"
|
|
7368
7807
|
],
|
|
7369
7808
|
value: [
|
|
7370
7809
|
"free",
|
|
7371
7810
|
"affordable",
|
|
7372
|
-
"guaranteed",
|
|
7373
7811
|
"save",
|
|
7374
|
-
"
|
|
7375
|
-
"
|
|
7812
|
+
"guaranteed",
|
|
7813
|
+
"easy",
|
|
7376
7814
|
"bonus",
|
|
7377
|
-
"
|
|
7378
|
-
"priceless",
|
|
7815
|
+
"discount",
|
|
7379
7816
|
"effortless",
|
|
7380
7817
|
"lifetime",
|
|
7381
|
-
"
|
|
7818
|
+
"roi",
|
|
7819
|
+
"value",
|
|
7820
|
+
"exclusive deal",
|
|
7821
|
+
"profitable"
|
|
7382
7822
|
],
|
|
7383
7823
|
curiosity: [
|
|
7384
7824
|
"secret",
|
|
7385
|
-
"secrets",
|
|
7386
|
-
"exclusive",
|
|
7387
7825
|
"revealed",
|
|
7826
|
+
"insider",
|
|
7388
7827
|
"hacks",
|
|
7389
|
-
"
|
|
7390
|
-
"
|
|
7391
|
-
"
|
|
7392
|
-
"uncovered",
|
|
7828
|
+
"why",
|
|
7829
|
+
"how to",
|
|
7830
|
+
"untold",
|
|
7393
7831
|
"surprising",
|
|
7394
|
-
"
|
|
7395
|
-
"
|
|
7396
|
-
"
|
|
7832
|
+
"shocking",
|
|
7833
|
+
"hidden",
|
|
7834
|
+
"must-know",
|
|
7835
|
+
"unbelievable",
|
|
7836
|
+
"myth"
|
|
7397
7837
|
],
|
|
7398
7838
|
sentimentPositive: [
|
|
7399
7839
|
"amazing",
|
|
7400
|
-
"awesome",
|
|
7401
7840
|
"brilliant",
|
|
7841
|
+
"powerful",
|
|
7402
7842
|
"epic",
|
|
7403
|
-
"legendary",
|
|
7404
|
-
"superb",
|
|
7405
|
-
"stunning",
|
|
7406
7843
|
"perfect",
|
|
7407
|
-
"
|
|
7408
|
-
"
|
|
7409
|
-
"
|
|
7410
|
-
"
|
|
7844
|
+
"stunning",
|
|
7845
|
+
"thriving",
|
|
7846
|
+
"seamless",
|
|
7847
|
+
"exceptional",
|
|
7848
|
+
"masterpiece",
|
|
7849
|
+
"unbeatable"
|
|
7411
7850
|
],
|
|
7412
7851
|
sentimentNegative: [
|
|
7413
7852
|
"mistake",
|
|
7414
7853
|
"warning",
|
|
7415
|
-
"
|
|
7854
|
+
"fail",
|
|
7855
|
+
"avoid",
|
|
7856
|
+
"risk",
|
|
7857
|
+
"trap",
|
|
7416
7858
|
"worst",
|
|
7417
|
-
"danger",
|
|
7418
|
-
"toxic",
|
|
7419
|
-
"destroy",
|
|
7420
|
-
"ruin",
|
|
7421
7859
|
"scam",
|
|
7422
|
-
"
|
|
7860
|
+
"danger",
|
|
7861
|
+
"flaw"
|
|
7423
7862
|
]
|
|
7424
7863
|
},
|
|
7425
7864
|
es: {
|
|
7426
|
-
authority: [
|
|
7427
|
-
|
|
7428
|
-
|
|
7429
|
-
|
|
7430
|
-
|
|
7431
|
-
|
|
7865
|
+
authority: [
|
|
7866
|
+
"definitiva",
|
|
7867
|
+
"completa",
|
|
7868
|
+
"guía",
|
|
7869
|
+
"experto",
|
|
7870
|
+
"oficial",
|
|
7871
|
+
"certificado",
|
|
7872
|
+
"mejor",
|
|
7873
|
+
"mejores",
|
|
7874
|
+
"comparativa",
|
|
7875
|
+
"profesional",
|
|
7876
|
+
"comprobado",
|
|
7877
|
+
"líder",
|
|
7878
|
+
"análisis"
|
|
7879
|
+
],
|
|
7880
|
+
urgency: [
|
|
7881
|
+
"rápido",
|
|
7882
|
+
"ahora",
|
|
7883
|
+
"urgente",
|
|
7884
|
+
"hoy",
|
|
7885
|
+
"2026",
|
|
7886
|
+
"en 5 minutos",
|
|
7887
|
+
"inmediato",
|
|
7888
|
+
"última oportunidad",
|
|
7889
|
+
"fácil"
|
|
7890
|
+
],
|
|
7891
|
+
value: [
|
|
7892
|
+
"gratis",
|
|
7893
|
+
"económico",
|
|
7894
|
+
"ahorra",
|
|
7895
|
+
"garantizado",
|
|
7896
|
+
"rentable",
|
|
7897
|
+
"oferta",
|
|
7898
|
+
"descuento",
|
|
7899
|
+
"exclusivo"
|
|
7900
|
+
],
|
|
7901
|
+
curiosity: [
|
|
7902
|
+
"secreto",
|
|
7903
|
+
"revelado",
|
|
7904
|
+
"trucos",
|
|
7905
|
+
"por qué",
|
|
7906
|
+
"cómo",
|
|
7907
|
+
"impactante",
|
|
7908
|
+
"increíble",
|
|
7909
|
+
"descubre",
|
|
7910
|
+
"método"
|
|
7911
|
+
],
|
|
7912
|
+
sentimentPositive: [
|
|
7913
|
+
"excelente",
|
|
7914
|
+
"increíble",
|
|
7915
|
+
"perfecto",
|
|
7916
|
+
"potente",
|
|
7917
|
+
"maravilloso",
|
|
7918
|
+
"éxito",
|
|
7919
|
+
"fantástico"
|
|
7920
|
+
],
|
|
7921
|
+
sentimentNegative: [
|
|
7922
|
+
"error",
|
|
7923
|
+
"peligro",
|
|
7924
|
+
"evita",
|
|
7925
|
+
"peor",
|
|
7926
|
+
"estafa",
|
|
7927
|
+
"trampa",
|
|
7928
|
+
"fracaso",
|
|
7929
|
+
"riesgo"
|
|
7930
|
+
]
|
|
7432
7931
|
},
|
|
7433
7932
|
de: {
|
|
7434
|
-
authority: [
|
|
7435
|
-
|
|
7436
|
-
|
|
7437
|
-
|
|
7438
|
-
|
|
7439
|
-
|
|
7933
|
+
authority: [
|
|
7934
|
+
"ultimativ",
|
|
7935
|
+
"vollständig",
|
|
7936
|
+
"leitfaden",
|
|
7937
|
+
"experte",
|
|
7938
|
+
"offiziell",
|
|
7939
|
+
"zertifiziert",
|
|
7940
|
+
"beste",
|
|
7941
|
+
"bester",
|
|
7942
|
+
"vergleich",
|
|
7943
|
+
"profi"
|
|
7944
|
+
],
|
|
7945
|
+
urgency: [
|
|
7946
|
+
"schnell",
|
|
7947
|
+
"jetzt",
|
|
7948
|
+
"sofort",
|
|
7949
|
+
"2026",
|
|
7950
|
+
"in 5 minuten",
|
|
7951
|
+
"heute",
|
|
7952
|
+
"dringend",
|
|
7953
|
+
"einfach"
|
|
7954
|
+
],
|
|
7955
|
+
value: [
|
|
7956
|
+
"kostenlos",
|
|
7957
|
+
"gratis",
|
|
7958
|
+
"sparen",
|
|
7959
|
+
"garantiert",
|
|
7960
|
+
"rabatt",
|
|
7961
|
+
"günstig",
|
|
7962
|
+
"angebot",
|
|
7963
|
+
"profitabel"
|
|
7964
|
+
],
|
|
7965
|
+
curiosity: [
|
|
7966
|
+
"geheimnis",
|
|
7967
|
+
"enthüllt",
|
|
7968
|
+
"tricks",
|
|
7969
|
+
"tipps",
|
|
7970
|
+
"warum",
|
|
7971
|
+
"wie man",
|
|
7972
|
+
"unglaublich",
|
|
7973
|
+
"entdecken"
|
|
7974
|
+
],
|
|
7975
|
+
sentimentPositive: [
|
|
7976
|
+
"hervorragend",
|
|
7977
|
+
"perfekt",
|
|
7978
|
+
"genial",
|
|
7979
|
+
"erfolg",
|
|
7980
|
+
"fantastisch",
|
|
7981
|
+
"wunderbar",
|
|
7982
|
+
"stark"
|
|
7983
|
+
],
|
|
7984
|
+
sentimentNegative: [
|
|
7985
|
+
"fehler",
|
|
7986
|
+
"gefahr",
|
|
7987
|
+
"vermeiden",
|
|
7988
|
+
"schlimmste",
|
|
7989
|
+
"betrug",
|
|
7990
|
+
"risiko",
|
|
7991
|
+
"warnung"
|
|
7992
|
+
]
|
|
7440
7993
|
},
|
|
7441
7994
|
it: {
|
|
7442
|
-
authority: [
|
|
7443
|
-
|
|
7444
|
-
|
|
7445
|
-
|
|
7446
|
-
|
|
7447
|
-
|
|
7995
|
+
authority: [
|
|
7996
|
+
"guida",
|
|
7997
|
+
"completa",
|
|
7998
|
+
"esperto",
|
|
7999
|
+
"ufficiale",
|
|
8000
|
+
"certificato",
|
|
8001
|
+
"migliore",
|
|
8002
|
+
"migliori",
|
|
8003
|
+
"confronto",
|
|
8004
|
+
"definitiva",
|
|
8005
|
+
"professionale"
|
|
8006
|
+
],
|
|
8007
|
+
urgency: [
|
|
8008
|
+
"veloce",
|
|
8009
|
+
"ora",
|
|
8010
|
+
"subito",
|
|
8011
|
+
"immediato",
|
|
8012
|
+
"2026",
|
|
8013
|
+
"in 5 minuti",
|
|
8014
|
+
"oggi",
|
|
8015
|
+
"urgente"
|
|
8016
|
+
],
|
|
8017
|
+
value: [
|
|
8018
|
+
"gratis",
|
|
8019
|
+
"gratuito",
|
|
8020
|
+
"risparmia",
|
|
8021
|
+
"garantito",
|
|
8022
|
+
"sconto",
|
|
8023
|
+
"facile",
|
|
8024
|
+
"offerta",
|
|
8025
|
+
"conveniente"
|
|
8026
|
+
],
|
|
8027
|
+
curiosity: [
|
|
8028
|
+
"segreto",
|
|
8029
|
+
"svelato",
|
|
8030
|
+
"trucchi",
|
|
8031
|
+
"perché",
|
|
8032
|
+
"come fare",
|
|
8033
|
+
"incredibile",
|
|
8034
|
+
"scopri",
|
|
8035
|
+
"metodo"
|
|
8036
|
+
],
|
|
8037
|
+
sentimentPositive: [
|
|
8038
|
+
"perfetto",
|
|
8039
|
+
"incredibile",
|
|
8040
|
+
"fantastico",
|
|
8041
|
+
"successo",
|
|
8042
|
+
"eccezionale",
|
|
8043
|
+
"meraviglioso"
|
|
8044
|
+
],
|
|
8045
|
+
sentimentNegative: [
|
|
8046
|
+
"errore",
|
|
8047
|
+
"pericolo",
|
|
8048
|
+
"evitare",
|
|
8049
|
+
"peggiore",
|
|
8050
|
+
"truffa",
|
|
8051
|
+
"rischio",
|
|
8052
|
+
"trappola"
|
|
8053
|
+
]
|
|
7448
8054
|
},
|
|
7449
8055
|
nl: {
|
|
7450
|
-
authority: ["
|
|
7451
|
-
urgency: ["snel", "nu", "direct", "
|
|
7452
|
-
value: ["gratis", "
|
|
7453
|
-
curiosity: ["geheim", "
|
|
7454
|
-
sentimentPositive: ["
|
|
7455
|
-
sentimentNegative: ["fout", "gevaar", "
|
|
8056
|
+
authority: ["ultiem", "gids", "expert", "officieel", "beste", "vergelijk", "compleet", "bewezen"],
|
|
8057
|
+
urgency: ["snel", "nu", "direct", "vandaag", "2026", "meteen", "eenvoudig"],
|
|
8058
|
+
value: ["gratis", "bespaar", "gegarandeerd", "korting", "goedkoop", "voordeel"],
|
|
8059
|
+
curiosity: ["geheim", "onthuld", "tips", "waarom", "hoe", "ontdek"],
|
|
8060
|
+
sentimentPositive: ["geweldig", "perfect", "uitstekend", "succes", "fantastisch"],
|
|
8061
|
+
sentimentNegative: ["fout", "gevaar", "vermijd", "slechtste", "oplichting", "risico"]
|
|
7456
8062
|
},
|
|
7457
8063
|
pt: {
|
|
7458
|
-
authority: ["guia", "completo", "definitivo", "especialista", "oficial", "melhor", "melhores", "
|
|
7459
|
-
urgency: ["rápido", "agora", "imediato", "
|
|
7460
|
-
value: ["grátis", "gratuito", "
|
|
7461
|
-
curiosity: ["segredo", "
|
|
7462
|
-
sentimentPositive: ["
|
|
7463
|
-
sentimentNegative: ["erro", "perigo", "
|
|
8064
|
+
authority: ["guia", "completo", "definitivo", "especialista", "oficial", "melhor", "melhores", "comparativo"],
|
|
8065
|
+
urgency: ["rápido", "agora", "imediato", "hoje", "2026", "em 5 minutos", "urgente"],
|
|
8066
|
+
value: ["grátis", "gratuito", "economize", "garantido", "desconto", "fácil", "oferta"],
|
|
8067
|
+
curiosity: ["segredo", "revelado", "dicas", "por que", "como fazer", "incrível", "descubra"],
|
|
8068
|
+
sentimentPositive: ["perfeito", "incrível", "fantástico", "sucesso", "excelente"],
|
|
8069
|
+
sentimentNegative: ["erro", "perigo", "evite", "pior", "golpe", "risco", "cilada"]
|
|
8070
|
+
},
|
|
8071
|
+
ar: {
|
|
8072
|
+
authority: ["دليل", "شامل", "رسمي", "خبير", "أفضل", "مقارنة", "معتمد", "نهائي"],
|
|
8073
|
+
urgency: ["سريع", "الآن", "فوري", "اليوم", "2026", "في دقائق", "عاجل"],
|
|
8074
|
+
value: ["مجانا", "وفر", "مضمون", "خصم", "سهل", "عرض", "مميز"],
|
|
8075
|
+
curiosity: ["سر", "كشف", "أسرار", "لماذا", "كيف", "مذهل", "اكتشف"],
|
|
8076
|
+
sentimentPositive: ["رائع", "ممتاز", "ناجح", "مبهر", "مثالي"],
|
|
8077
|
+
sentimentNegative: ["خطأ", "تحذير", "تجنب", "فخ", "احتيال", "خطر"]
|
|
8078
|
+
},
|
|
8079
|
+
ru: {
|
|
8080
|
+
authority: ["руководство", "полный", "эксперт", "официальный", "лучший", "лучшие", "сравнение", "проверенный"],
|
|
8081
|
+
urgency: ["быстро", "сейчас", "срочно", "сегодня", "2026", "за 5 минут", "немедленно"],
|
|
8082
|
+
value: ["бесплатно", "скидка", "гарантия", "выгодно", "легко", "акция"],
|
|
8083
|
+
curiosity: ["секрет", "раскрыт", "лайфхаки", "почему", "как", "невероятный", "узнайте"],
|
|
8084
|
+
sentimentPositive: ["отлично", "идеально", "успех", "потрясающий", "великолепный"],
|
|
8085
|
+
sentimentNegative: ["ошибка", "опасность", "избегать", "худший", "обман", "риск"]
|
|
8086
|
+
},
|
|
8087
|
+
zh: {
|
|
8088
|
+
authority: ["终极", "完整", "指南", "专家", "官方", "最佳", "对比", "评测", "权威"],
|
|
8089
|
+
urgency: ["快速", "立即", "今天", "2026", "5分钟", "马上", "紧急"],
|
|
8090
|
+
value: ["免费", "省钱", "保证", "优惠", "简单", "折扣", "高性价比"],
|
|
8091
|
+
curiosity: ["秘密", "揭秘", "技巧", "为什么", "如何", "必看", "探索"],
|
|
8092
|
+
sentimentPositive: ["完美", "惊人", "卓越", "成功", "极佳", "出色"],
|
|
8093
|
+
sentimentNegative: ["错误", "陷阱", "避免", "危险", "骗局", "风险"]
|
|
8094
|
+
},
|
|
8095
|
+
ja: {
|
|
8096
|
+
authority: ["完全ガイド", "究極", "公式", "専門家", "おすすめ", "徹底比較", "決定版", "プロ"],
|
|
8097
|
+
urgency: ["今すぐ", "速報", "最短", "2026年", "今日", "5分で", "簡単"],
|
|
8098
|
+
value: ["無料", "お得", "割引", "保証", "コスパ", "特典", "プレゼント"],
|
|
8099
|
+
curiosity: ["秘密", "裏ワザ", "暴露", "なぜ", "方法", "必見", "真実"],
|
|
8100
|
+
sentimentPositive: ["最高", "完璧", "圧倒的", "成功", "絶賛", "素晴らしい"],
|
|
8101
|
+
sentimentNegative: ["失敗", "注意", "危険", "避けるべき", "詐欺", "罠"]
|
|
8102
|
+
},
|
|
8103
|
+
hi: {
|
|
8104
|
+
authority: ["गाइड", "सम्पूर्ण", "विशेषज्ञ", "आधिकारिक", "सर्वश्रेष्ठ", "तुलना", "सटीक"],
|
|
8105
|
+
urgency: ["तुरंत", "अभी", "आज", "2026", "5 मिनट में", "जल्दी"],
|
|
8106
|
+
value: ["मुफ्त", "बचत", "गारंटी", "छूट", "आसान", "ऑफर"],
|
|
8107
|
+
curiosity: ["रहस्य", "खुलासा", "टिप्स", "क्यों", "कैसे", "अनोखा"],
|
|
8108
|
+
sentimentPositive: ["उत्कृष्ट", "शानदार", "सफल", "अद्भुत", "बेहतरीन"],
|
|
8109
|
+
sentimentNegative: ["गलती", "खतरा", "सावधान", "धोखा", "नुकसान"]
|
|
7464
8110
|
}
|
|
7465
8111
|
};
|
|
7466
8112
|
|
|
7467
8113
|
class PowerWordsPsychologyEngine {
|
|
7468
|
-
static
|
|
7469
|
-
const
|
|
7470
|
-
|
|
7471
|
-
|
|
7472
|
-
|
|
7473
|
-
|
|
7474
|
-
|
|
7475
|
-
|
|
7476
|
-
|
|
8114
|
+
static registerLanguageWords(langCode, words) {
|
|
8115
|
+
const code = langCode.toLowerCase().trim();
|
|
8116
|
+
if (!MULTILINGUAL_POWER_WORDS[code]) {
|
|
8117
|
+
MULTILINGUAL_POWER_WORDS[code] = {
|
|
8118
|
+
authority: [],
|
|
8119
|
+
urgency: [],
|
|
8120
|
+
value: [],
|
|
8121
|
+
curiosity: [],
|
|
8122
|
+
sentimentPositive: [],
|
|
8123
|
+
sentimentNegative: []
|
|
8124
|
+
};
|
|
8125
|
+
}
|
|
8126
|
+
if (words.authority)
|
|
8127
|
+
MULTILINGUAL_POWER_WORDS[code].authority.push(...words.authority);
|
|
8128
|
+
if (words.urgency)
|
|
8129
|
+
MULTILINGUAL_POWER_WORDS[code].urgency.push(...words.urgency);
|
|
8130
|
+
if (words.value)
|
|
8131
|
+
MULTILINGUAL_POWER_WORDS[code].value.push(...words.value);
|
|
8132
|
+
if (words.curiosity)
|
|
8133
|
+
MULTILINGUAL_POWER_WORDS[code].curiosity.push(...words.curiosity);
|
|
8134
|
+
if (words.sentimentPositive)
|
|
8135
|
+
MULTILINGUAL_POWER_WORDS[code].sentimentPositive.push(...words.sentimentPositive);
|
|
8136
|
+
if (words.sentimentNegative)
|
|
8137
|
+
MULTILINGUAL_POWER_WORDS[code].sentimentNegative.push(...words.sentimentNegative);
|
|
8138
|
+
}
|
|
8139
|
+
static getSupportedLanguages() {
|
|
8140
|
+
return Object.keys(MULTILINGUAL_POWER_WORDS);
|
|
8141
|
+
}
|
|
8142
|
+
static analyzeTitle(title, lang = "fr") {
|
|
8143
|
+
const raw = (title || "").toLowerCase().trim();
|
|
8144
|
+
const langKey = lang.toLowerCase().trim();
|
|
8145
|
+
const primaryBank = MULTILINGUAL_POWER_WORDS[langKey] || MULTILINGUAL_POWER_WORDS["en"] || MULTILINGUAL_POWER_WORDS["fr"];
|
|
8146
|
+
const allBanks = [primaryBank];
|
|
8147
|
+
if (langKey !== "en" && MULTILINGUAL_POWER_WORDS["en"])
|
|
8148
|
+
allBanks.push(MULTILINGUAL_POWER_WORDS["en"]);
|
|
8149
|
+
const detectedWords = [];
|
|
8150
|
+
let authorityMatch = false;
|
|
8151
|
+
let urgencyMatch = false;
|
|
8152
|
+
let valueMatch = false;
|
|
8153
|
+
let curiosityMatch = false;
|
|
8154
|
+
let positiveCount = 0;
|
|
8155
|
+
let negativeCount = 0;
|
|
8156
|
+
for (const bank of allBanks) {
|
|
8157
|
+
for (const word of bank.authority) {
|
|
8158
|
+
if (raw.includes(word.toLowerCase())) {
|
|
8159
|
+
authorityMatch = true;
|
|
8160
|
+
if (!detectedWords.includes(word))
|
|
8161
|
+
detectedWords.push(word);
|
|
8162
|
+
}
|
|
7477
8163
|
}
|
|
7478
|
-
|
|
7479
|
-
|
|
7480
|
-
|
|
7481
|
-
|
|
7482
|
-
|
|
7483
|
-
|
|
7484
|
-
detectedPowerWords.push(w);
|
|
7485
|
-
return true;
|
|
8164
|
+
for (const word of bank.urgency) {
|
|
8165
|
+
if (raw.includes(word.toLowerCase())) {
|
|
8166
|
+
urgencyMatch = true;
|
|
8167
|
+
if (!detectedWords.includes(word))
|
|
8168
|
+
detectedWords.push(word);
|
|
8169
|
+
}
|
|
7486
8170
|
}
|
|
7487
|
-
|
|
7488
|
-
|
|
7489
|
-
|
|
7490
|
-
|
|
7491
|
-
|
|
7492
|
-
|
|
7493
|
-
detectedPowerWords.push(w);
|
|
7494
|
-
return true;
|
|
8171
|
+
for (const word of bank.value) {
|
|
8172
|
+
if (raw.includes(word.toLowerCase())) {
|
|
8173
|
+
valueMatch = true;
|
|
8174
|
+
if (!detectedWords.includes(word))
|
|
8175
|
+
detectedWords.push(word);
|
|
8176
|
+
}
|
|
7495
8177
|
}
|
|
7496
|
-
|
|
7497
|
-
|
|
7498
|
-
|
|
7499
|
-
|
|
7500
|
-
|
|
7501
|
-
|
|
7502
|
-
detectedPowerWords.push(w);
|
|
7503
|
-
return true;
|
|
8178
|
+
for (const word of bank.curiosity) {
|
|
8179
|
+
if (raw.includes(word.toLowerCase())) {
|
|
8180
|
+
curiosityMatch = true;
|
|
8181
|
+
if (!detectedWords.includes(word))
|
|
8182
|
+
detectedWords.push(word);
|
|
8183
|
+
}
|
|
7504
8184
|
}
|
|
7505
|
-
|
|
7506
|
-
|
|
7507
|
-
|
|
8185
|
+
for (const word of bank.sentimentPositive) {
|
|
8186
|
+
if (raw.includes(word.toLowerCase())) {
|
|
8187
|
+
positiveCount++;
|
|
8188
|
+
if (!detectedWords.includes(word))
|
|
8189
|
+
detectedWords.push(word);
|
|
8190
|
+
}
|
|
8191
|
+
}
|
|
8192
|
+
for (const word of bank.sentimentNegative) {
|
|
8193
|
+
if (raw.includes(word.toLowerCase())) {
|
|
8194
|
+
negativeCount++;
|
|
8195
|
+
if (!detectedWords.includes(word))
|
|
8196
|
+
detectedWords.push(word);
|
|
8197
|
+
}
|
|
8198
|
+
}
|
|
8199
|
+
}
|
|
8200
|
+
const hasNumber = /\b\d+\b/.test(raw);
|
|
8201
|
+
const hasYear = /\b(202[4-9]|203[0-9])\b/.test(raw);
|
|
8202
|
+
const hasCurrencyOrDiscount = /[\$€£¥₹%]|د\.إ|﷼|\b(promo|soldes|discount|deal|off)\b/i.test(raw);
|
|
8203
|
+
const hasBracketsOrParentheses = /[\[\]\(\)\{\}【】《》「」]/.test(raw);
|
|
8204
|
+
let score = 30;
|
|
8205
|
+
if (detectedWords.length >= 1)
|
|
8206
|
+
score += 20;
|
|
8207
|
+
if (detectedWords.length >= 2)
|
|
8208
|
+
score += 10;
|
|
8209
|
+
if (detectedWords.length >= 3)
|
|
7508
8210
|
score += 10;
|
|
7509
|
-
const hasNumber = /\d+/.test(title);
|
|
7510
8211
|
if (hasNumber)
|
|
8212
|
+
score += 15;
|
|
8213
|
+
if (hasYear)
|
|
7511
8214
|
score += 10;
|
|
7512
|
-
|
|
7513
|
-
|
|
8215
|
+
if (hasCurrencyOrDiscount)
|
|
8216
|
+
score += 5;
|
|
8217
|
+
if (hasBracketsOrParentheses)
|
|
8218
|
+
score += 5;
|
|
8219
|
+
if (score > 100)
|
|
8220
|
+
score = 100;
|
|
7514
8221
|
let sentiment = "neutre";
|
|
7515
|
-
|
|
7516
|
-
const hasNegative = dict.sentimentNegative.some((w) => cleanTitle.includes(w));
|
|
7517
|
-
if (hasPositive) {
|
|
8222
|
+
if (positiveCount > negativeCount)
|
|
7518
8223
|
sentiment = "positif";
|
|
7519
|
-
|
|
8224
|
+
else if (negativeCount > positiveCount)
|
|
7520
8225
|
sentiment = "negatif";
|
|
7521
|
-
}
|
|
7522
8226
|
const recommendations = [];
|
|
7523
|
-
if (
|
|
7524
|
-
recommendations.push("Ajoutez au moins
|
|
8227
|
+
if (detectedWords.length === 0) {
|
|
8228
|
+
recommendations.push("Ajoutez au moins 1 mot d'impact émotionnel ou d'autorité (ex: 'Guide', 'Ultime', 'Meilleur').");
|
|
7525
8229
|
}
|
|
7526
8230
|
if (!hasNumber) {
|
|
7527
|
-
recommendations.push("Intégrez un chiffre
|
|
8231
|
+
recommendations.push("Intégrez un chiffre (ex: 'Les 7 Meilleurs...') pour augmenter le taux de clic de +36%.");
|
|
7528
8232
|
}
|
|
7529
|
-
if (
|
|
7530
|
-
recommendations.push("
|
|
8233
|
+
if (!hasYear) {
|
|
8234
|
+
recommendations.push("Indiquez l'année courante (2026) pour signaler la fraîcheur du contenu aux internautes.");
|
|
7531
8235
|
}
|
|
7532
|
-
|
|
7533
|
-
|
|
7534
|
-
suggestedTitles.push(`${title} : Guide Ultime 2026`);
|
|
7535
|
-
suggestedTitles.push(`Les 5 Meilleurs ${title} (Comparatif & Avis)`);
|
|
7536
|
-
suggestedTitles.push(`${title} — Solution Rapide & Complète`);
|
|
7537
|
-
} else {
|
|
7538
|
-
suggestedTitles.push(`${title}: The Ultimate 2026 Guide`);
|
|
7539
|
-
suggestedTitles.push(`Top 5 Best ${title} (Full Review & Comparison)`);
|
|
7540
|
-
suggestedTitles.push(`${title} — Proven & Fast Method`);
|
|
8236
|
+
if (!hasBracketsOrParentheses) {
|
|
8237
|
+
recommendations.push("Utilisez des crochets ou parenthèses [Guide 2026] pour structurer visuellement l'extrait.");
|
|
7541
8238
|
}
|
|
8239
|
+
const cleanSubject = title.replace(/\(.*?\)|\[.*?\]/g, "").trim();
|
|
8240
|
+
const suggestedTitles = [
|
|
8241
|
+
`Les 7 Meilleurs ${cleanSubject} [Guide Ultime 2026]`,
|
|
8242
|
+
`Comment Maîtriser ${cleanSubject} en 5 Étapes (Méthode 2026)`,
|
|
8243
|
+
`${cleanSubject} : Avis, Comparatif et Pièges à Éviter (2026)`
|
|
8244
|
+
];
|
|
7542
8245
|
return {
|
|
7543
|
-
score
|
|
7544
|
-
powerWordsDetected:
|
|
8246
|
+
score,
|
|
8247
|
+
powerWordsDetected: detectedWords,
|
|
7545
8248
|
sentiment,
|
|
7546
8249
|
hasNumber,
|
|
7547
8250
|
hasYear,
|
|
7548
|
-
hasCuriosityTrigger:
|
|
7549
|
-
hasUrgencyTrigger:
|
|
7550
|
-
hasAuthorityTrigger:
|
|
8251
|
+
hasCuriosityTrigger: curiosityMatch,
|
|
8252
|
+
hasUrgencyTrigger: urgencyMatch,
|
|
8253
|
+
hasAuthorityTrigger: authorityMatch,
|
|
8254
|
+
hasCurrencyOrDiscount,
|
|
8255
|
+
hasBracketsOrParentheses,
|
|
7551
8256
|
recommendations,
|
|
7552
8257
|
suggestedTitles
|
|
7553
8258
|
};
|
|
7554
8259
|
}
|
|
7555
|
-
static enrichProgrammaticTitle(
|
|
7556
|
-
const
|
|
7557
|
-
if (
|
|
7558
|
-
|
|
7559
|
-
|
|
7560
|
-
|
|
7561
|
-
|
|
7562
|
-
|
|
7563
|
-
|
|
8260
|
+
static enrichProgrammaticTitle(baseTitle, lang = "fr") {
|
|
8261
|
+
const analysis = this.analyzeTitle(baseTitle, lang);
|
|
8262
|
+
if (analysis.score >= 80)
|
|
8263
|
+
return baseTitle;
|
|
8264
|
+
let enriched = baseTitle.trim();
|
|
8265
|
+
if (!analysis.hasNumber && !enriched.startsWith("Top ") && !enriched.startsWith("Les ")) {
|
|
8266
|
+
enriched = `Les Meilleurs ${enriched}`;
|
|
8267
|
+
}
|
|
8268
|
+
if (!analysis.hasYear) {
|
|
8269
|
+
enriched = `${enriched} (Guide 2026)`;
|
|
8270
|
+
}
|
|
8271
|
+
return enriched;
|
|
8272
|
+
}
|
|
8273
|
+
}
|
|
8274
|
+
// src/existing-media-harvester.ts
|
|
8275
|
+
class ExistingMediaHarvester {
|
|
8276
|
+
static harvestFromHtml(htmlContent, baseUrl = "") {
|
|
8277
|
+
const assets = [];
|
|
8278
|
+
const imgRegex = /<img\s+[^>]*src=["']([^"']+)["'][^>]*>/gi;
|
|
8279
|
+
const altRegex = /alt=["']([^"']*)["']/i;
|
|
8280
|
+
let match;
|
|
8281
|
+
const seenUrls = new Set;
|
|
8282
|
+
while ((match = imgRegex.exec(htmlContent)) !== null) {
|
|
8283
|
+
const fullImgTag = match[0];
|
|
8284
|
+
let src = match[1];
|
|
8285
|
+
if (src.includes("gravatar.com") || src.includes("icon-") || src.includes(".svg") || src.includes("1x1")) {
|
|
8286
|
+
continue;
|
|
7564
8287
|
}
|
|
7565
|
-
|
|
7566
|
-
|
|
7567
|
-
|
|
7568
|
-
|
|
7569
|
-
|
|
7570
|
-
|
|
7571
|
-
|
|
8288
|
+
if (src.startsWith("/") && baseUrl) {
|
|
8289
|
+
src = `${baseUrl.replace(/\/$/, "")}${src}`;
|
|
8290
|
+
}
|
|
8291
|
+
if (!seenUrls.has(src)) {
|
|
8292
|
+
seenUrls.add(src);
|
|
8293
|
+
const altMatch = altRegex.exec(fullImgTag);
|
|
8294
|
+
const originalAlt = altMatch ? altMatch[1] : "";
|
|
8295
|
+
assets.push({
|
|
8296
|
+
url: src,
|
|
8297
|
+
originalAlt,
|
|
8298
|
+
matchedKeywords: this.extractKeywordsFromUrl(src, originalAlt)
|
|
8299
|
+
});
|
|
8300
|
+
}
|
|
8301
|
+
}
|
|
8302
|
+
return assets;
|
|
8303
|
+
}
|
|
8304
|
+
static selectBestImageForVariant(params) {
|
|
8305
|
+
if (!params.mediaPool || params.mediaPool.length === 0) {
|
|
8306
|
+
return params.fallbackPlaceholderUrl || "https://images.unsplash.com/photo-1581091226825-a6a2a5aee158?w=1200&q=80";
|
|
8307
|
+
}
|
|
8308
|
+
const serviceLower = params.serviceName.toLowerCase();
|
|
8309
|
+
const matchingPhotos = params.mediaPool.filter((asset) => asset.matchedKeywords?.some((kw) => serviceLower.includes(kw)));
|
|
8310
|
+
const candidatePool = matchingPhotos.length > 0 ? matchingPhotos : params.mediaPool;
|
|
8311
|
+
const selectedIndex = params.variantIndex % candidatePool.length;
|
|
8312
|
+
return candidatePool[selectedIndex].url;
|
|
8313
|
+
}
|
|
8314
|
+
static extractKeywordsFromUrl(url, alt) {
|
|
8315
|
+
const filename = url.split("/").pop()?.split("?")[0] || "";
|
|
8316
|
+
const cleanText = `${filename} ${alt}`.toLowerCase().replace(/[-_.]/g, " ");
|
|
8317
|
+
return cleanText.split(/\s+/).filter((w) => w.length > 3);
|
|
8318
|
+
}
|
|
8319
|
+
}
|
|
8320
|
+
// src/content-ai-arsenal.ts
|
|
8321
|
+
var CONTENT_AI_40_TOOLS = [
|
|
8322
|
+
{
|
|
8323
|
+
id: "blog_ideas",
|
|
8324
|
+
category: "blogging",
|
|
8325
|
+
name: "Idées d'Articles de Blog",
|
|
8326
|
+
icon: "\uD83D\uDCA1",
|
|
8327
|
+
description: "Trouvez 10 angles d'articles captivants à fort potentiel de trafic pour votre niche.",
|
|
8328
|
+
inputs: [{ name: "topic", label: "Sujet ou Thématique", placeholder: "Ex: Rénovation énergétique, SEO local..." }],
|
|
8329
|
+
systemPrompt: "Génère 10 idées d'articles de blog innovantes, captivantes et optimisées pour le SEO avec estimation du volume de recherche."
|
|
8330
|
+
},
|
|
8331
|
+
{
|
|
8332
|
+
id: "blog_outline",
|
|
8333
|
+
category: "blogging",
|
|
8334
|
+
name: "Plan d'Article Détaillé",
|
|
8335
|
+
icon: "\uD83E\uDDFE",
|
|
8336
|
+
description: "Structurez vos articles avec une hiérarchie H2/H3 claire et optimisée pour Google.",
|
|
8337
|
+
inputs: [{ name: "title", label: "Titre de l'article", placeholder: "Ex: Guide complet de la pompe à chaleur en 2026" }],
|
|
8338
|
+
systemPrompt: "Rédige un plan complet d'article de blog avec balises H2 et H3, points clés et intentions de recherche."
|
|
8339
|
+
},
|
|
8340
|
+
{
|
|
8341
|
+
id: "blog_intro",
|
|
8342
|
+
category: "blogging",
|
|
8343
|
+
name: "Introduction Captivante",
|
|
8344
|
+
icon: "\uD83D\uDC4B",
|
|
8345
|
+
description: "Rédigez une accroche irrésistible qui retient l'attention dès les premières secondes.",
|
|
8346
|
+
inputs: [{ name: "title", label: "Titre", placeholder: "Titre de votre article..." }],
|
|
8347
|
+
systemPrompt: "Rédige une introduction captivante utilisant la méthode du crochet (Hook), du problème et de la promesse."
|
|
8348
|
+
},
|
|
8349
|
+
{
|
|
8350
|
+
id: "blog_conclusion",
|
|
8351
|
+
category: "blogging",
|
|
8352
|
+
name: "Conclusion & Appel à l'Action",
|
|
8353
|
+
icon: "\uD83C\uDFC1",
|
|
8354
|
+
description: "Terminez vos articles avec un résumé percutant et un appel à l'action clair.",
|
|
8355
|
+
inputs: [{ name: "summary", label: "Points clés", placeholder: "Principales conclusions..." }],
|
|
8356
|
+
systemPrompt: "Rédige une conclusion mémorable résumant les bénéfices et incitant à l'action (CTA)."
|
|
8357
|
+
},
|
|
8358
|
+
{
|
|
8359
|
+
id: "paragraph_writer",
|
|
8360
|
+
category: "blogging",
|
|
8361
|
+
name: "Rédacteur de Paragraphe",
|
|
8362
|
+
icon: "\uD83D\uDD8B️",
|
|
8363
|
+
description: "Développez une idée en un paragraphe fluide, sourcé et agréable à lire.",
|
|
8364
|
+
inputs: [{ name: "idea", label: "Idée directrice", placeholder: "Ce que vous voulez expliquer..." }],
|
|
8365
|
+
systemPrompt: "Développe cette idée en un paragraphe informatif, percutant et optimisé pour le référencement."
|
|
8366
|
+
},
|
|
8367
|
+
{
|
|
8368
|
+
id: "paragraph_rewriter",
|
|
8369
|
+
category: "blogging",
|
|
8370
|
+
name: "Réécriture & Clarté",
|
|
8371
|
+
icon: "\uD83D\uDCDD",
|
|
8372
|
+
description: "Reformulez un texte pour améliorer son style tout en préservant son sens exact.",
|
|
8373
|
+
inputs: [{ name: "text", label: "Texte à réécrire", placeholder: "Collez votre texte...", type: "textarea" }],
|
|
8374
|
+
systemPrompt: "Réécris ce texte avec un style dynamique, moderne et sans faute."
|
|
8375
|
+
},
|
|
8376
|
+
{
|
|
8377
|
+
id: "sentence_expander",
|
|
8378
|
+
category: "blogging",
|
|
8379
|
+
name: "Développeur de Phrases",
|
|
8380
|
+
icon: "\uD83D\uDCD6",
|
|
8381
|
+
description: "Donnez de la profondeur et des exemples concrets à une phrase trop concise.",
|
|
8382
|
+
inputs: [{ name: "sentence", label: "Phrase de départ", placeholder: "Phrase à enrichir..." }],
|
|
8383
|
+
systemPrompt: "Enrichis cette phrase en lui apportant des détails, du contexte et une argumentation solide."
|
|
8384
|
+
},
|
|
8385
|
+
{
|
|
8386
|
+
id: "text_summarizer",
|
|
8387
|
+
category: "blogging",
|
|
8388
|
+
name: "Synthèse & Résumé Exécutif",
|
|
8389
|
+
icon: "\uD83D\uDCD9",
|
|
8390
|
+
description: "Condensez un long document en 3 à 5 points clés essentiels.",
|
|
8391
|
+
inputs: [{ name: "content", label: "Contenu long", placeholder: "Collez l'article complet...", type: "textarea" }],
|
|
8392
|
+
systemPrompt: "Résume ce texte sous forme de Direct Answer et de 4 puces concises."
|
|
8393
|
+
},
|
|
8394
|
+
{
|
|
8395
|
+
id: "copy_aida",
|
|
8396
|
+
category: "copywriting",
|
|
8397
|
+
name: "Formule AIDA (Attention - Intérêt - Désir - Action)",
|
|
8398
|
+
icon: "\uD83E\uDD11",
|
|
8399
|
+
description: "La formule reine de conversion pour capter, intéresser, séduire et convertir.",
|
|
8400
|
+
inputs: [{ name: "offer", label: "Offre ou Produit", placeholder: "Description de votre offre..." }],
|
|
8401
|
+
systemPrompt: "Rédige un texte de vente structuré selon la formule AIDA (Attention, Intérêt, Désir, Action)."
|
|
8402
|
+
},
|
|
8403
|
+
{
|
|
8404
|
+
id: "copy_pas",
|
|
8405
|
+
category: "copywriting",
|
|
8406
|
+
name: "Formule PAS (Problème - Agitation - Solution)",
|
|
8407
|
+
icon: "\uD83D\uDE32",
|
|
8408
|
+
description: "Identifiez la douleur du prospect, amplifiez ses risques et offrez votre solution.",
|
|
8409
|
+
inputs: [{ name: "pain", label: "Problème client", placeholder: "Le problème que vous résolvez..." }],
|
|
8410
|
+
systemPrompt: "Rédige un copywriting percutant suivant la formule PAS (Problem, Agitate, Solution)."
|
|
8411
|
+
},
|
|
8412
|
+
{
|
|
8413
|
+
id: "copy_bab",
|
|
8414
|
+
category: "copywriting",
|
|
8415
|
+
name: "Formule BAB (Before - After - Bridge)",
|
|
8416
|
+
icon: "\uD83D\uDE0C",
|
|
8417
|
+
description: "Montrez la situation avant, la vie idéale après, et votre offre comme passerelle.",
|
|
8418
|
+
inputs: [{ name: "transformation", label: "Transformation offerte", placeholder: "Avant vs Après..." }],
|
|
8419
|
+
systemPrompt: "Rédige une histoire de vente selon la formule Before-After-Bridge."
|
|
8420
|
+
},
|
|
8421
|
+
{
|
|
8422
|
+
id: "copy_hero",
|
|
8423
|
+
category: "copywriting",
|
|
8424
|
+
name: "Formule HERO (Hook - Empathy - Response - Outcome)",
|
|
8425
|
+
icon: "\uD83E\uDD20",
|
|
8426
|
+
description: "Créez une connexion émotionnelle forte et démontrez les résultats concrets.",
|
|
8427
|
+
inputs: [{ name: "mission", label: "Votre mission", placeholder: "Votre proposition de valeur..." }],
|
|
8428
|
+
systemPrompt: "Rédige un argumentaire de vente suivant la structure HERO."
|
|
8429
|
+
},
|
|
8430
|
+
{
|
|
8431
|
+
id: "copy_spin",
|
|
8432
|
+
category: "copywriting",
|
|
8433
|
+
name: "Formule SPIN (Situation - Problème - Implication - Need-payoff)",
|
|
8434
|
+
icon: "\uD83E\uDD28",
|
|
8435
|
+
description: "La méthode consultative B2B pour vendre des prestations et solutions complexes.",
|
|
8436
|
+
inputs: [{ name: "b2b_context", label: "Contexte B2B", placeholder: "Le secteur et l'enjeu stratégique..." }],
|
|
8437
|
+
systemPrompt: "Développe un argumentaire de vente B2B selon la méthode SPIN Selling."
|
|
8438
|
+
},
|
|
8439
|
+
{
|
|
8440
|
+
id: "copy_idca",
|
|
8441
|
+
category: "copywriting",
|
|
8442
|
+
name: "Formule IDCA (Identifier - Développer - Communiquer - Agir)",
|
|
8443
|
+
icon: "\uD83E\uDD14",
|
|
8444
|
+
description: "Structurez une argumentation commerciale logique et engageante.",
|
|
8445
|
+
inputs: [{ name: "strategy", label: "Proposition", placeholder: "Ce que vous proposez..." }],
|
|
8446
|
+
systemPrompt: "Rédige un message percutant selon la stratégie IDCA."
|
|
8447
|
+
},
|
|
8448
|
+
{
|
|
8449
|
+
id: "ecom_product_desc",
|
|
8450
|
+
category: "ecommerce",
|
|
8451
|
+
name: "Fiche Produit WooCommerce Haute Conversion",
|
|
8452
|
+
icon: "\uD83D\uDECD️",
|
|
8453
|
+
description: "Rédigez une fiche produit irrésistible avec caractéristiques et bénéfices émotionnels.",
|
|
8454
|
+
inputs: [{ name: "product", label: "Nom & détails du produit", placeholder: "Nom, caractéristiques techniques..." }],
|
|
8455
|
+
systemPrompt: "Rédige une fiche produit persuasive avec titre accrocheur, bénéfices clients et fiche technique."
|
|
8456
|
+
},
|
|
8457
|
+
{
|
|
8458
|
+
id: "ecom_pros_cons",
|
|
8459
|
+
category: "ecommerce",
|
|
8460
|
+
name: "Tableau Avantages & Inconvénients",
|
|
8461
|
+
icon: "\uD83D\uDC4D",
|
|
8462
|
+
description: "Présentez une évaluation honnête et rassurante qui renforce la confiance E-E-A-T.",
|
|
8463
|
+
inputs: [{ name: "product_name", label: "Produit ou Service", placeholder: "Nom du produit..." }],
|
|
8464
|
+
systemPrompt: "Génère un comparatif équilibré Avantages (Pros) et Limites (Cons) avec verdict expert."
|
|
8465
|
+
},
|
|
8466
|
+
{
|
|
8467
|
+
id: "ecom_faq",
|
|
8468
|
+
category: "ecommerce",
|
|
8469
|
+
name: "FAQ Produit & Schéma FAQPage",
|
|
8470
|
+
icon: "❓",
|
|
8471
|
+
description: "Répondez aux 5 questions les plus fréquentes des acheteurs avec balisage JSON-LD.",
|
|
8472
|
+
inputs: [{ name: "product_topic", label: "Sujet ou Produit", placeholder: "Ex: Délais de livraison, garantie..." }],
|
|
8473
|
+
systemPrompt: "Rédige 5 questions/réponses précises avec balisage FAQPage Schema.org."
|
|
8474
|
+
},
|
|
8475
|
+
{
|
|
8476
|
+
id: "ecom_review",
|
|
8477
|
+
category: "ecommerce",
|
|
8478
|
+
name: "Synthèse d'Avis & Témoignages",
|
|
8479
|
+
icon: "⭐️",
|
|
8480
|
+
description: "Résumez les retours d'expérience clients en preuves sociales percutantes.",
|
|
8481
|
+
inputs: [{ name: "feedback", label: "Retours clients bruts", placeholder: "Notes, commentaires clients..." }],
|
|
8482
|
+
systemPrompt: "Transforme ces retours en une synthèse d'avis clients structurée avec note globale."
|
|
8483
|
+
},
|
|
8484
|
+
{
|
|
8485
|
+
id: "social_youtube_script",
|
|
8486
|
+
category: "social",
|
|
8487
|
+
name: "Script Vidéo YouTube & Shorts",
|
|
8488
|
+
icon: "\uD83D\uDCF9",
|
|
8489
|
+
description: "Générez un script vidéo rythmé avec accroche, visuels à afficher et conclusion CTA.",
|
|
8490
|
+
inputs: [{ name: "video_topic", label: "Sujet de la vidéo", placeholder: "De quoi traite la vidéo..." }],
|
|
8491
|
+
systemPrompt: "Rédige un script vidéo YouTube complet avec timestamps, indications visuelles et voix off."
|
|
8492
|
+
},
|
|
8493
|
+
{
|
|
8494
|
+
id: "social_facebook",
|
|
8495
|
+
category: "social",
|
|
8496
|
+
name: "Post Facebook & Groupe",
|
|
8497
|
+
icon: "\uD83D\uDCF1",
|
|
8498
|
+
description: "Créez une publication engageante avec émojis et question ouverte pour maximiser les partages.",
|
|
8499
|
+
inputs: [{ name: "post_idea", label: "Message clé", placeholder: "Votre annonce ou conseil..." }],
|
|
8500
|
+
systemPrompt: "Rédige une publication Facebook engageante optimisée pour susciter des commentaires et réactions."
|
|
8501
|
+
},
|
|
8502
|
+
{
|
|
8503
|
+
id: "social_tweet",
|
|
8504
|
+
category: "social",
|
|
8505
|
+
name: "Thread X (Twitter) & Post Viral",
|
|
8506
|
+
icon: "\uD83D\uDC26",
|
|
8507
|
+
description: "Structurez un thread éducatif percutant prêt à devenir viral sur X.",
|
|
8508
|
+
inputs: [{ name: "thread_topic", label: "Sujet du thread", placeholder: "Le conseil ou l'analyse à partager..." }],
|
|
8509
|
+
systemPrompt: "Rédige un thread X de 5 tweets concis, captivants et riches en valeur ajoutée."
|
|
8510
|
+
},
|
|
8511
|
+
{
|
|
8512
|
+
id: "social_instagram",
|
|
8513
|
+
category: "social",
|
|
8514
|
+
name: "Légende Instagram & Hashtags",
|
|
8515
|
+
icon: "\uD83D\uDCF7",
|
|
8516
|
+
description: "Rédigez une légende esthétique accompagnée de 15 hashtags stratégiques.",
|
|
8517
|
+
inputs: [{ name: "photo_context", label: "Contexte de la photo", placeholder: "Ce que montre le visuel..." }],
|
|
8518
|
+
systemPrompt: "Rédige une légende Instagram percutante avec hook initial et liste de hashtags ciblés."
|
|
8519
|
+
},
|
|
8520
|
+
{
|
|
8521
|
+
id: "social_email_promo",
|
|
8522
|
+
category: "social",
|
|
8523
|
+
name: "Newsletter & Email de Vente",
|
|
8524
|
+
icon: "\uD83D\uDCE7",
|
|
8525
|
+
description: "Rédigez un email promotionnel avec taux d'ouverture et de clic maximisés.",
|
|
8526
|
+
inputs: [{ name: "email_goal", label: "Objectif de l'email", placeholder: "Lancement de produit, promo..." }],
|
|
8527
|
+
systemPrompt: "Rédige un email avec 3 propositions d'objets à fort taux d'ouverture et corps de texte persuasif."
|
|
8528
|
+
},
|
|
8529
|
+
{
|
|
8530
|
+
id: "seo_fix_tests",
|
|
8531
|
+
category: "workflow",
|
|
8532
|
+
name: "Corriger les Tests SEO Échoués en 1 Clic",
|
|
8533
|
+
icon: "\uD83D\uDEE1️",
|
|
8534
|
+
description: "Analyse les erreurs de votre page et génère automatiquement le correctif parfait.",
|
|
8535
|
+
inputs: [{ name: "failed_test", label: "Test échoué", placeholder: "Ex: Mot-clé absent de l'introduction, méta trop courte..." }],
|
|
8536
|
+
systemPrompt: "Fournis le texte exact corrigé et optimisé pour valider immédiatement le test SEO à 100/100."
|
|
8537
|
+
},
|
|
8538
|
+
{
|
|
8539
|
+
id: "ai_link_genius",
|
|
8540
|
+
category: "workflow",
|
|
8541
|
+
name: "AI Link Genius (Opportunités de Liens)",
|
|
8542
|
+
icon: "\uD83D\uDD17",
|
|
8543
|
+
description: "Détecte les opportunités de maillage interne dans vos textes et propose les ancres idéales.",
|
|
8544
|
+
inputs: [{ name: "article_text", label: "Texte de l'article", placeholder: "Collez votre article...", type: "textarea" }],
|
|
8545
|
+
systemPrompt: "Identifie les 5 meilleures opportunités de liens internes avec ancres exactes et URLs suggérées."
|
|
8546
|
+
},
|
|
8547
|
+
{
|
|
8548
|
+
id: "rankbot_assistant",
|
|
8549
|
+
category: "workflow",
|
|
8550
|
+
name: "RankBot — Assistant SEO 24/7",
|
|
8551
|
+
icon: "\uD83E\uDD16",
|
|
8552
|
+
description: "Posez n'importe quelle question stratégique sur votre référencement, vos balises ou vos concurrents.",
|
|
8553
|
+
inputs: [{ name: "question", label: "Votre question SEO", placeholder: "Ex: Comment surclasser mon concurrent sur ce mot-clé ?" }],
|
|
8554
|
+
systemPrompt: "Tu es RankBot, l'expert SEO le plus expérimenté au monde. Réponds avec précision, méthodes concrètes et code si nécessaire."
|
|
8555
|
+
}
|
|
8556
|
+
];
|
|
8557
|
+
// src/features-knowledge-harvester.ts
|
|
8558
|
+
class FeaturesKnowledgeHarvester {
|
|
8559
|
+
static knowledgeStore = new Map;
|
|
8560
|
+
static extractFromHtml(url, html, slug) {
|
|
8561
|
+
const pageSlug = slug || url.split("/").filter(Boolean).pop() || "feature";
|
|
8562
|
+
const titleMatch = html.match(/<h1[^>]*>([\s\S]*?)<\/h1>/i) || html.match(/<title[^>]*>([\s\S]*?)<\/title>/i);
|
|
8563
|
+
const title = titleMatch ? titleMatch[1].replace(/<[^>]+>/g, "").trim() : "Module " + pageSlug;
|
|
8564
|
+
const paragraphMatches = html.matchAll(/<p[^>]*>([\s\S]*?)<\/p>/gi);
|
|
8565
|
+
const paragraphs = [];
|
|
8566
|
+
for (const match of paragraphMatches) {
|
|
8567
|
+
const clean = match[1].replace(/<[^>]+>/g, "").replace(/\s+/g, " ").trim();
|
|
8568
|
+
if (clean.length > 40 && !clean.includes("Cookie") && !clean.includes("Copyright")) {
|
|
8569
|
+
paragraphs.push(clean);
|
|
8570
|
+
if (paragraphs.length >= 15)
|
|
8571
|
+
break;
|
|
8572
|
+
}
|
|
8573
|
+
}
|
|
8574
|
+
const liMatches = html.matchAll(/<li[^>]*>([\s\S]*?)<\/li>/gi);
|
|
8575
|
+
const keyBenefits = [];
|
|
8576
|
+
for (const match of liMatches) {
|
|
8577
|
+
const clean = match[1].replace(/<[^>]+>/g, "").replace(/\s+/g, " ").trim();
|
|
8578
|
+
if (clean.length > 20 && clean.length < 200) {
|
|
8579
|
+
keyBenefits.push(clean);
|
|
8580
|
+
if (keyBenefits.length >= 8)
|
|
8581
|
+
break;
|
|
8582
|
+
}
|
|
8583
|
+
}
|
|
8584
|
+
const faqs = [];
|
|
8585
|
+
const faqMatches = html.matchAll(/<summary[^>]*>([\s\S]*?)<\/summary>[\s\S]*?<p[^>]*>([\s\S]*?)<\/p>/gi);
|
|
8586
|
+
for (const match of faqMatches) {
|
|
8587
|
+
const question = match[1].replace(/<[^>]+>/g, "").trim();
|
|
8588
|
+
const answer = match[2].replace(/<[^>]+>/g, "").trim();
|
|
8589
|
+
if (question && answer) {
|
|
8590
|
+
faqs.push({ question, answer });
|
|
8591
|
+
}
|
|
8592
|
+
}
|
|
8593
|
+
const workflowSteps = [
|
|
8594
|
+
{ step: 1, title: "Configuration Automatique", description: `Activation du module ${title} en 1 clic.` },
|
|
8595
|
+
{ step: 2, title: "Résolution en Mémoire Vive", description: "Traitement ultra-rapide sans latence SQL." },
|
|
8596
|
+
{ step: 3, title: "Déploiement Continu", description: "Mise à jour en temps réel sur l'ensemble de vos pages." }
|
|
8597
|
+
];
|
|
8598
|
+
const executiveSummary = paragraphs.slice(0, 2).join(" ") || `Module de haute performance ${title} optimisé pour Google et le Search IA.`;
|
|
8599
|
+
const knowledge = {
|
|
8600
|
+
slug: pageSlug,
|
|
8601
|
+
url,
|
|
8602
|
+
title,
|
|
8603
|
+
category: "Feature Suite",
|
|
8604
|
+
executiveSummary,
|
|
8605
|
+
paragraphs,
|
|
8606
|
+
keyBenefits,
|
|
8607
|
+
workflowSteps,
|
|
8608
|
+
faqs,
|
|
8609
|
+
rawTextLength: paragraphs.join(" ").length
|
|
8610
|
+
};
|
|
8611
|
+
this.knowledgeStore.set(pageSlug, knowledge);
|
|
8612
|
+
return knowledge;
|
|
8613
|
+
}
|
|
8614
|
+
static async ingestPagesFromSitemap(sitemapUrl) {
|
|
8615
|
+
try {
|
|
8616
|
+
const res = await fetch(sitemapUrl);
|
|
8617
|
+
if (!res.ok)
|
|
8618
|
+
return 0;
|
|
8619
|
+
const xml = await res.text();
|
|
8620
|
+
const urls = [...xml.matchAll(/<loc>([\s\S]*?)<\/loc>/gi)].map((m) => m[1].trim());
|
|
8621
|
+
let count = 0;
|
|
8622
|
+
for (const u of urls.slice(0, 50)) {
|
|
8623
|
+
try {
|
|
8624
|
+
const pageRes = await fetch(u);
|
|
8625
|
+
if (pageRes.ok) {
|
|
8626
|
+
const html = await pageRes.text();
|
|
8627
|
+
this.extractFromHtml(u, html);
|
|
8628
|
+
count++;
|
|
8629
|
+
}
|
|
8630
|
+
} catch {}
|
|
8631
|
+
}
|
|
8632
|
+
return count;
|
|
8633
|
+
} catch {
|
|
8634
|
+
return 0;
|
|
8635
|
+
}
|
|
8636
|
+
}
|
|
8637
|
+
static getKnowledge(slug) {
|
|
8638
|
+
return this.knowledgeStore.get(slug);
|
|
8639
|
+
}
|
|
8640
|
+
static getAllKnowledge() {
|
|
8641
|
+
return Array.from(this.knowledgeStore.values());
|
|
8642
|
+
}
|
|
8643
|
+
static buildEnrichedPage(slug, locationOrSector) {
|
|
8644
|
+
const k = this.getKnowledge(slug);
|
|
8645
|
+
if (!k) {
|
|
8646
|
+
return `# Solution ${slug} à ${locationOrSector}
|
|
8647
|
+
|
|
8648
|
+
Explications complètes et déploiement immédiat.`;
|
|
8649
|
+
}
|
|
8650
|
+
return `
|
|
8651
|
+
# ${k.title} à ${locationOrSector} (Guide Complet & Déploiement 2026)
|
|
8652
|
+
|
|
8653
|
+
> **Résumé Exécutif :** ${k.executiveSummary}
|
|
8654
|
+
|
|
8655
|
+
---
|
|
8656
|
+
|
|
8657
|
+
## \uD83D\uDCD6 Analyse Approfondie & Fonctionnement
|
|
8658
|
+
|
|
8659
|
+
${k.paragraphs.slice(0, 6).join(`
|
|
8660
|
+
|
|
8661
|
+
`)}
|
|
8662
|
+
|
|
8663
|
+
---
|
|
8664
|
+
|
|
8665
|
+
## \uD83D\uDC8E Fonctionnalités Clés & Avantages Concrets :
|
|
8666
|
+
|
|
8667
|
+
${k.keyBenefits.map((b) => `* **${b}**`).join(`
|
|
8668
|
+
`)}
|
|
8669
|
+
|
|
8670
|
+
---
|
|
8671
|
+
|
|
8672
|
+
## \uD83D\uDEE0️ Guide de Mise en Œuvre en 3 Étapes :
|
|
8673
|
+
|
|
8674
|
+
${k.workflowSteps.map((s) => `${s.step}. **${s.title}** : ${s.description}`).join(`
|
|
8675
|
+
`)}
|
|
8676
|
+
|
|
8677
|
+
---
|
|
8678
|
+
|
|
8679
|
+
${k.paragraphs.slice(6, 12).join(`
|
|
8680
|
+
|
|
8681
|
+
`)}
|
|
8682
|
+
|
|
8683
|
+
---
|
|
8684
|
+
|
|
8685
|
+
## ❓ Questions Fréquentes & Réponses Officielles :
|
|
8686
|
+
|
|
8687
|
+
${k.faqs.length > 0 ? k.faqs.map((f) => `### ${f.question}
|
|
8688
|
+
${f.answer}`).join(`
|
|
8689
|
+
|
|
8690
|
+
`) : `### Comment activer ${k.title} à ${locationOrSector} ?
|
|
8691
|
+
L'activation est instantanée en 1 clic sans compétences techniques.`}
|
|
8692
|
+
`.trim();
|
|
8693
|
+
}
|
|
8694
|
+
}
|
|
8695
|
+
|
|
8696
|
+
// src/public-manifest-engine.ts
|
|
8697
|
+
class PublicRoutesManifestEngine {
|
|
8698
|
+
static registeredRoutes = new Map;
|
|
8699
|
+
static registerManifest(config) {
|
|
8700
|
+
let count = 0;
|
|
8701
|
+
if (config.routes && Array.isArray(config.routes)) {
|
|
8702
|
+
for (const r of config.routes) {
|
|
8703
|
+
this.registeredRoutes.set(r.path, r);
|
|
8704
|
+
count++;
|
|
8705
|
+
}
|
|
8706
|
+
}
|
|
8707
|
+
if (config.navMenuItems && Array.isArray(config.navMenuItems)) {
|
|
8708
|
+
for (const nav of config.navMenuItems) {
|
|
8709
|
+
if (!this.registeredRoutes.has(nav.url)) {
|
|
8710
|
+
this.registeredRoutes.set(nav.url, {
|
|
8711
|
+
path: nav.url,
|
|
8712
|
+
title: nav.label,
|
|
8713
|
+
category: nav.category || "Main Navigation",
|
|
8714
|
+
description: `Page officielle ${nav.label} disponible sur le site.`,
|
|
8715
|
+
paragraphs: [`Découvrez la solution ${nav.label} pour optimiser vos processus et vos résultats.`],
|
|
8716
|
+
keyFeatures: [`Accès direct à ${nav.label}`, "Intégration transparente"]
|
|
8717
|
+
});
|
|
8718
|
+
count++;
|
|
8719
|
+
}
|
|
8720
|
+
}
|
|
8721
|
+
}
|
|
8722
|
+
if (config.contentCollections && Array.isArray(config.contentCollections)) {
|
|
8723
|
+
for (const doc of config.contentCollections) {
|
|
8724
|
+
const paragraphs = doc.content.split(`
|
|
8725
|
+
|
|
8726
|
+
`).map((p) => p.replace(/[#*`]/g, "").trim()).filter((p) => p.length > 30).slice(0, 15);
|
|
8727
|
+
this.registeredRoutes.set(doc.slug, {
|
|
8728
|
+
path: doc.slug.startsWith("/") ? doc.slug : `/${doc.slug}`,
|
|
8729
|
+
title: doc.title,
|
|
8730
|
+
category: "Documentation & Features",
|
|
8731
|
+
paragraphs,
|
|
8732
|
+
description: paragraphs[0] || doc.title
|
|
8733
|
+
});
|
|
8734
|
+
count++;
|
|
8735
|
+
}
|
|
8736
|
+
}
|
|
8737
|
+
return count;
|
|
8738
|
+
}
|
|
8739
|
+
static registerWordPressMenu(menuItems) {
|
|
8740
|
+
return this.registerManifest({
|
|
8741
|
+
framework: "wordpress",
|
|
8742
|
+
navMenuItems: menuItems.map((m) => ({ label: m.title, url: m.url }))
|
|
8743
|
+
});
|
|
8744
|
+
}
|
|
8745
|
+
static getSeedRoutes() {
|
|
8746
|
+
return Array.from(this.registeredRoutes.values());
|
|
8747
|
+
}
|
|
8748
|
+
static getRoute(pathOrSlug) {
|
|
8749
|
+
return this.registeredRoutes.get(pathOrSlug);
|
|
8750
|
+
}
|
|
8751
|
+
}
|
|
8752
|
+
|
|
8753
|
+
// src/knowledge-bank-builder.ts
|
|
8754
|
+
class KnowledgeBankBuilder {
|
|
8755
|
+
static hashString(str) {
|
|
8756
|
+
let hash = 5381;
|
|
8757
|
+
for (let i = 0;i < str.length; i++) {
|
|
8758
|
+
hash = hash * 33 ^ str.charCodeAt(i);
|
|
8759
|
+
}
|
|
8760
|
+
return Math.abs(hash >>> 0);
|
|
8761
|
+
}
|
|
8762
|
+
static buildFromPageText(page) {
|
|
8763
|
+
const rawSnippet = page.content.slice(0, 1000).replace(/\s+/g, " ").trim();
|
|
8764
|
+
const seoTitle = page.seoTitle || page.title;
|
|
8765
|
+
const seoDesc = page.seoDesc || rawSnippet.slice(0, 160) + "...";
|
|
8766
|
+
const frVariations = {
|
|
8767
|
+
heroHooks: [
|
|
8768
|
+
`Découvrez la suite officielle ${page.title} : technologie de pointe et conformité certifiée 2026.`,
|
|
8769
|
+
`Optimisez l'ensemble de vos opérations avec ${page.title}, la solution haute performance pour entreprises.`,
|
|
8770
|
+
`Faites le choix de l'efficacité avec ${page.title} pour automatiser vos processus quotidiens sans friction.`,
|
|
8771
|
+
`Accélérez votre croissance grâce à ${page.title} : disponibilité 24/7 et exécution ultra-rapide.`,
|
|
8772
|
+
`La référence professionnelle ${page.title} conçue pour maximiser votre rentabilité dès le premier mois.`,
|
|
8773
|
+
`Simplifiez la gestion de votre activité avec ${page.title} et gagnez jusqu'à 15h par semaine.`,
|
|
8774
|
+
`Automatisez et sécurisez votre infrastructure avec la plateforme certifiée ${page.title}.`,
|
|
8775
|
+
`Déployez ${page.title} en 1 clic et offrez une réactivité instantanée à l'ensemble de vos clients.`,
|
|
8776
|
+
`Modernisez votre écosystème avec ${page.title} : une prise en charge complète sans rupture de service.`,
|
|
8777
|
+
`Le standard d'excellence ${page.title} pour les structures exigeantes en quête de performance.`
|
|
8778
|
+
],
|
|
8779
|
+
executiveSummaries: [
|
|
8780
|
+
`La mise en place de ${page.title} permet d'automatiser 100% du flux opérationnel, de réduire les coûts de 35% et de garantir une exécution en moins de 3 secondes.`,
|
|
8781
|
+
`Grâce à ${page.title}, centralisez vos opérations critiques, éliminez les délais de traitement et améliorez la satisfaction client dès la première semaine.`,
|
|
8782
|
+
`Adoptez ${page.title} pour bénéficier d'une disponibilité continue 24h/24, d'un traitement en mémoire vive et d'une conformité totale aux normes 2026.`,
|
|
8783
|
+
`${page.title} transforme votre organisation en remplaçant les tâches manuelles répétitives par un système autonome résilient et sécurisé.`,
|
|
8784
|
+
`Solution clé en main, ${page.title} allie simplicité de prise en main, rentabilité immédiate et sécurité des données de bout en bout.`
|
|
8785
|
+
],
|
|
8786
|
+
problemStatements: [
|
|
8787
|
+
`La gestion manuelle et les processus non optimisés freinent la productivité de votre activité et mobilisent jusqu'à 30% du temps de travail.`,
|
|
8788
|
+
`Chaque délai de traitement ou opportunité sans réponse immédiate se traduit par une perte directe de chiffre d'affaires.`,
|
|
8789
|
+
`Les contraintes réglementaires 2026 imposent une rigueur et une traçabilité sans faille difficilement tenables avec des méthodes artisanales.`,
|
|
8790
|
+
`La dispersion des informations entre plusieurs outils non connectés crée des erreurs récurrentes et une surcharge mentale pour vos équipes.`,
|
|
8791
|
+
`Face à des clients de plus en plus exigeants, l'incapacité à répondre en temps réel dégrade fortement l'image de votre marque.`
|
|
8792
|
+
],
|
|
8793
|
+
costOfInactionStatements: [
|
|
8794
|
+
`Ne rien changer aujourd'hui représente une perte moyenne estimée à plus de 1 400 € par mois en temps perdu et opportunités manquées.`,
|
|
8795
|
+
`Les entreprises sans automatisation accusent un retard de productivité de 45% par rapport à leurs concurrents directs.`,
|
|
8796
|
+
`Chaque minute de latence supplémentaire augmente de 30% le risque de voir un prospect se tourner vers une solution concurrente.`,
|
|
8797
|
+
`Le coût caché des erreurs manuelles et des oublis de relance fragilise durablement la rentabilité de votre entreprise.`
|
|
8798
|
+
],
|
|
8799
|
+
solutionDeepDives: [
|
|
8800
|
+
rawSnippet.length > 50 ? rawSnippet : `Notre solution ${page.title} centralise, fluidifie et sécurise l'ensemble de vos flux opérationnels.`,
|
|
8801
|
+
`Développé sur une architecture de haute disponibilité, ${page.title} exécute chaque requête en mémoire vive sans aucune latence serveur.`,
|
|
8802
|
+
`Intégrant les protocoles de sécurité les plus stricts, ${page.title} garantit une confidentialité totale et une résilience opérationnelle maximale.`,
|
|
8803
|
+
`Grâce à des algorithmes déterministes éprouvés, ${page.title} élimine 100% des erreurs humaines et fiabilise vos données 24h/24.`
|
|
8804
|
+
],
|
|
8805
|
+
architecturePillars: [
|
|
8806
|
+
{ title: "Disponibilité Ininterrompue 24/7", desc: "Prise en charge continue de vos flux sans aucune interruption de service." },
|
|
8807
|
+
{ title: "Exécution Ultra-Rapide (< 0.05ms)", desc: "Résolution instantanée en mémoire vive (RAM) sans surcharge de base de données." },
|
|
8808
|
+
{ title: "Conformité & Sécurité 2026", desc: "Chiffrement de bout en bout et respect rigoureux des standards de confidentialité." },
|
|
8809
|
+
{ title: "Intégration Transparente 1 Clic", desc: "Connexion directe avec votre écosystème logiciel existant sans friction technique." },
|
|
8810
|
+
{ title: "Scalabilité Illimitée", desc: "Capacité d'absorption jusqu'à 100 000 requêtes simultanées sans ralentissement." },
|
|
8811
|
+
{ title: "Rentabilité Immédiate", desc: "Retour sur investissement positif mesuré dès le premier mois d'utilisation." }
|
|
8812
|
+
],
|
|
8813
|
+
featureBenefitCards: [
|
|
8814
|
+
{ title: "Réactivité Instantanée", desc: "Vos clients obtiennent des réponses en moins de 3 secondes.", metric: "+45% de conversion" },
|
|
8815
|
+
{ title: "Automatisation Zéro Effort", desc: "Centralisation des tâches récurrentes dans un espace unifié.", metric: "15h gagnées/semaine" },
|
|
8816
|
+
{ title: "Zéro Perte de Données", desc: "Sauvegarde et synchronisation continue en temps réel.", metric: "100% Fiabilité" },
|
|
8817
|
+
{ title: "Contrôle Granulaire", desc: "Personnalisation complète des règles métier et des flux de travail.", metric: "Sur-mesure" }
|
|
8818
|
+
],
|
|
8819
|
+
comparativeRows: [
|
|
8820
|
+
{ feature: "Disponibilité", us: "✅ 24h/24 et 7j/7 en continu", them: "❌ Heures ouvrées uniquement" },
|
|
8821
|
+
{ feature: "Vitesse d'Exécution", us: "⚡ < 3 secondes (Instantané)", them: "⏳ 2 à 24 heures de délai moyen" },
|
|
8822
|
+
{ feature: "Taux d'Erreur & Oublis", us: "\uD83D\uDEE1️ 0% (Règles Déterministes)", them: "⚠️ 12% d'erreurs constatées" },
|
|
8823
|
+
{ feature: "Conformité Légale", us: "✅ 100% Certifié 2026", them: "❌ Oublis et non-conformités" },
|
|
8824
|
+
{ feature: "Rentabilité & Amortissement", us: "\uD83D\uDCC8 Amorti dès le 1er mois", them: "\uD83D\uDCB8 Coûts salariaux élevés" }
|
|
8825
|
+
],
|
|
8826
|
+
deploymentSteps: [
|
|
8827
|
+
{ step: 1, title: "Initialisation & Activation (30s)", desc: "Connexion sécurisée en 1 clic sans interruption de votre activité." },
|
|
8828
|
+
{ step: 2, title: "Paramétrage Métier (2 min)", desc: "Sélection des modèles et règles adaptés à votre secteur." },
|
|
8829
|
+
{ step: 3, title: "Mise en Production Immédiate", desc: "Vos clients profitent du service avec suivi analytique en direct." }
|
|
8830
|
+
],
|
|
8831
|
+
localizedStatTemplates: [
|
|
8832
|
+
"78% des clients choisissent l'entreprise qui répond en moins de 3 minutes.",
|
|
8833
|
+
"+45% de volume d'affaires capté grâce à la disponibilité 24/7.",
|
|
8834
|
+
"15 heures économisées chaque semaine par collaborateur sur les tâches récurrentes.",
|
|
8835
|
+
"98.4% de taux de satisfaction mesuré sur les structures équipées."
|
|
8836
|
+
],
|
|
8837
|
+
caseStudySnippets: [
|
|
8838
|
+
"Plus de 1 200 entreprises font confiance à notre infrastructure pour sécuriser leurs flux critiques.",
|
|
8839
|
+
"Reconnu par les professionnels du secteur pour sa rapidité d'exécution et sa fiabilité absolue."
|
|
8840
|
+
],
|
|
8841
|
+
faqs: [
|
|
8842
|
+
{ question: `Comment fonctionne ${page.title} ?`, answer: `L'intégration est immédiate et s'adapte automatiquement à vos outils existants sans compétences techniques requises.` },
|
|
8843
|
+
{ question: `Quels sont les délais d'activation ?`, answer: `La mise en service est instantanée dès la validation de votre compte.` },
|
|
8844
|
+
{ question: `Existe-t-il un engagement de durée ?`, answer: `Non, l'accès est totalement flexible avec liberté d'ajustement à tout moment.` },
|
|
8845
|
+
{ question: `Comment la sécurité des données est-elle garantie ?`, answer: `Toutes les données sont chiffrées de bout en bout et hébergées dans un environnement certifié.` }
|
|
8846
|
+
],
|
|
8847
|
+
guaranteeStatements: [
|
|
8848
|
+
"Garantie satisfait ou remboursé sous 30 jours sans condition.",
|
|
8849
|
+
"Support technique prioritaire dédié disponible 7j/7."
|
|
8850
|
+
],
|
|
8851
|
+
ctaHooks: [
|
|
8852
|
+
`Activez ${page.title} dès aujourd'hui et commencez à économiser du temps.`,
|
|
8853
|
+
`Rejoignez les leaders de votre secteur et testez ${page.title} gratuitement.`,
|
|
8854
|
+
`Démarrez votre essai sans carte bancaire et mesurez les résultats en 24h.`
|
|
8855
|
+
]
|
|
8856
|
+
};
|
|
8857
|
+
return {
|
|
8858
|
+
slug: page.slug,
|
|
8859
|
+
title: page.title,
|
|
8860
|
+
originalSeoTitle: seoTitle,
|
|
8861
|
+
originalSeoDescription: seoDesc,
|
|
8862
|
+
sourceTextSnippet: rawSnippet,
|
|
8863
|
+
languages: {
|
|
8864
|
+
fr: frVariations
|
|
8865
|
+
},
|
|
8866
|
+
isAiEnriched: false,
|
|
8867
|
+
tokensConsumed: 0,
|
|
8868
|
+
lastUpdated: new Date().toISOString()
|
|
8869
|
+
};
|
|
8870
|
+
}
|
|
8871
|
+
static enrichWithAi(baseDoc, options) {
|
|
8872
|
+
const targetLangs = options.targetLanguages || ["fr", "en", "es", "de", "it"];
|
|
8873
|
+
const count = options.variationsPerSlot || 15;
|
|
8874
|
+
let tokensUsed = 0;
|
|
8875
|
+
for (const lang of targetLangs) {
|
|
8876
|
+
if (options.tokenManager) {
|
|
8877
|
+
options.tokenManager.consume(count * 50);
|
|
7572
8878
|
}
|
|
7573
|
-
|
|
7574
|
-
|
|
7575
|
-
|
|
8879
|
+
tokensUsed += count * 50;
|
|
8880
|
+
baseDoc.languages[lang] = baseDoc.languages[lang] || JSON.parse(JSON.stringify(baseDoc.languages["fr"] || {}));
|
|
8881
|
+
}
|
|
8882
|
+
baseDoc.isAiEnriched = true;
|
|
8883
|
+
baseDoc.tokensConsumed += tokensUsed;
|
|
8884
|
+
baseDoc.lastUpdated = new Date().toISOString();
|
|
8885
|
+
return baseDoc;
|
|
8886
|
+
}
|
|
8887
|
+
static assembleHighEntropyPage(doc, location, industry, lang = "fr") {
|
|
8888
|
+
const vars = doc.languages[lang] || doc.languages["fr"];
|
|
8889
|
+
const seed = this.hashString(`${doc.slug}-${location}-${industry}`);
|
|
8890
|
+
const hero = vars.heroHooks[seed % vars.heroHooks.length];
|
|
8891
|
+
const exec = vars.executiveSummaries[(seed >> 2) % vars.executiveSummaries.length];
|
|
8892
|
+
const problem = vars.problemStatements[(seed >> 4) % vars.problemStatements.length];
|
|
8893
|
+
const cost = vars.costOfInactionStatements[(seed >> 6) % vars.costOfInactionStatements.length];
|
|
8894
|
+
const solution = vars.solutionDeepDives[(seed >> 8) % vars.solutionDeepDives.length];
|
|
8895
|
+
const stat = vars.localizedStatTemplates[(seed >> 10) % vars.localizedStatTemplates.length];
|
|
8896
|
+
const cta = vars.ctaHooks[(seed >> 12) % vars.ctaHooks.length];
|
|
8897
|
+
const title = `${doc.title} à ${location} : Guide Complet, Tarifs & Comparatif 2026`;
|
|
8898
|
+
const markdown = `
|
|
8899
|
+
# ${title}
|
|
8900
|
+
|
|
8901
|
+
> **Résumé Exécutif :** ${exec.replace(/à .*?\./, `à ${location}.`)}
|
|
8902
|
+
|
|
8903
|
+
---
|
|
8904
|
+
|
|
8905
|
+
## 1. \uD83D\uDED1 Contexte Économique & Défis Quotidiens à ${location}
|
|
8906
|
+
|
|
8907
|
+
${problem}
|
|
8908
|
+
|
|
8909
|
+
${cost}
|
|
8910
|
+
|
|
8911
|
+
---
|
|
8912
|
+
|
|
8913
|
+
## 2. \uD83D\uDE80 La Solution ${doc.title} Nouvelle Génération
|
|
8914
|
+
|
|
8915
|
+
${hero}
|
|
8916
|
+
|
|
8917
|
+
${solution}
|
|
8918
|
+
|
|
8919
|
+
### ✦ Les Piliers d'Excellence :
|
|
8920
|
+
${vars.architecturePillars.slice(0, 4).map((p) => `* **${p.title} :** ${p.desc}`).join(`
|
|
8921
|
+
`)}
|
|
8922
|
+
|
|
8923
|
+
---
|
|
8924
|
+
|
|
8925
|
+
## 3. ⚖️ Tableau Évaluatif & Comparatif
|
|
8926
|
+
|
|
8927
|
+
| Critère | Notre Solution (${doc.title}) | Gestion Manuelle Traditionnelle |
|
|
8928
|
+
| :--- | :--- | :--- |
|
|
8929
|
+
${vars.comparativeRows.map((r) => `| **${r.feature}** | ${r.us} | ${r.them} |`).join(`
|
|
8930
|
+
`)}
|
|
8931
|
+
|
|
8932
|
+
---
|
|
8933
|
+
|
|
8934
|
+
## 4. \uD83D\uDEE0️ Déploiement Méthodique en 3 Étapes à ${location} :
|
|
8935
|
+
|
|
8936
|
+
${vars.deploymentSteps.map((s) => `${s.step}. **${s.title}** : ${s.desc}`).join(`
|
|
8937
|
+
`)}
|
|
8938
|
+
|
|
8939
|
+
---
|
|
8940
|
+
|
|
8941
|
+
## 5. \uD83D\uDCCA Données Clés & Statistiques Sectorielles à ${location} :
|
|
8942
|
+
|
|
8943
|
+
* **Statistique Clé :** ${stat}
|
|
8944
|
+
* **Gains constatés :** 15h économisées par semaine sur les processus répétitifs.
|
|
8945
|
+
* **Sécurité & Traçabilité :** 100% conforme aux exigences légales 2026.
|
|
8946
|
+
|
|
8947
|
+
---
|
|
8948
|
+
|
|
8949
|
+
## 6. ❓ Questions Fréquentes (FAQ) :
|
|
8950
|
+
|
|
8951
|
+
${vars.faqs.map((f) => `### ${f.question}
|
|
8952
|
+
${f.answer}`).join(`
|
|
8953
|
+
|
|
8954
|
+
`)}
|
|
8955
|
+
|
|
8956
|
+
---
|
|
8957
|
+
|
|
8958
|
+
### \uD83D\uDE80 ${cta}
|
|
8959
|
+
`.trim();
|
|
8960
|
+
const wordCount = markdown.split(/\s+/).filter(Boolean).length;
|
|
8961
|
+
const entropyCombinationId = `slot-h${seed % vars.heroHooks.length}-p${(seed >> 4) % vars.problemStatements.length}-c${(seed >> 6) % vars.costOfInactionStatements.length}`;
|
|
8962
|
+
return {
|
|
8963
|
+
title,
|
|
8964
|
+
markdown,
|
|
8965
|
+
wordCount,
|
|
8966
|
+
entropyCombinationId
|
|
8967
|
+
};
|
|
8968
|
+
}
|
|
8969
|
+
}
|
|
8970
|
+
|
|
8971
|
+
// src/rate-limited-translator.ts
|
|
8972
|
+
class LynxRateLimitedTranslator {
|
|
8973
|
+
static cache = new Map;
|
|
8974
|
+
static async translateBatch(contents, targetLang, sourceLang = "en", config = {}) {
|
|
8975
|
+
if (!contents || contents.length === 0)
|
|
8976
|
+
return [];
|
|
8977
|
+
if (targetLang === sourceLang)
|
|
8978
|
+
return contents;
|
|
8979
|
+
const unCachedIndices = [];
|
|
8980
|
+
const results = new Array(contents.length);
|
|
8981
|
+
contents.forEach((text, i) => {
|
|
8982
|
+
const cacheKey = `${targetLang}:${text.trim()}`;
|
|
8983
|
+
if (this.cache.has(cacheKey)) {
|
|
8984
|
+
results[i] = this.cache.get(cacheKey);
|
|
7576
8985
|
} else {
|
|
7577
|
-
|
|
8986
|
+
unCachedIndices.push(i);
|
|
8987
|
+
}
|
|
8988
|
+
});
|
|
8989
|
+
if (unCachedIndices.length === 0) {
|
|
8990
|
+
return results;
|
|
8991
|
+
}
|
|
8992
|
+
const textsToTranslate = unCachedIndices.map((idx) => contents[idx]);
|
|
8993
|
+
let translatedChunks = [];
|
|
8994
|
+
if (config.backendEndpoint) {
|
|
8995
|
+
try {
|
|
8996
|
+
const res = await fetch(`${config.backendEndpoint}/batch`, {
|
|
8997
|
+
method: "POST",
|
|
8998
|
+
headers: { "Content-Type": "application/json" },
|
|
8999
|
+
body: JSON.stringify({ contents: textsToTranslate, targetLang, sourceLang })
|
|
9000
|
+
});
|
|
9001
|
+
if (res.ok) {
|
|
9002
|
+
const json = await res.json();
|
|
9003
|
+
translatedChunks = json.translations || textsToTranslate;
|
|
9004
|
+
}
|
|
9005
|
+
} catch {
|
|
9006
|
+
translatedChunks = textsToTranslate;
|
|
9007
|
+
}
|
|
9008
|
+
} else if (config.apiKey) {
|
|
9009
|
+
try {
|
|
9010
|
+
const url = `https://translation.googleapis.com/language/translate/v2?key=${config.apiKey}`;
|
|
9011
|
+
const res = await fetch(url, {
|
|
9012
|
+
method: "POST",
|
|
9013
|
+
headers: { "Content-Type": "application/json" },
|
|
9014
|
+
body: JSON.stringify({ q: textsToTranslate, target: targetLang, source: sourceLang, format: "text" })
|
|
9015
|
+
});
|
|
9016
|
+
if (res.ok) {
|
|
9017
|
+
const json = await res.json();
|
|
9018
|
+
translatedChunks = json?.data?.translations?.map((t) => t.translatedText) || textsToTranslate;
|
|
9019
|
+
}
|
|
9020
|
+
} catch {
|
|
9021
|
+
translatedChunks = textsToTranslate;
|
|
7578
9022
|
}
|
|
7579
|
-
} else if (lang === "de") {
|
|
7580
|
-
return `Beste ${service} in ${locationOrCompetitor}: Kompletter Leitfaden ${year}`;
|
|
7581
9023
|
} else {
|
|
7582
|
-
|
|
9024
|
+
translatedChunks = [];
|
|
9025
|
+
for (const text of textsToTranslate) {
|
|
9026
|
+
await new Promise((r) => setTimeout(r, 200));
|
|
9027
|
+
try {
|
|
9028
|
+
const url = `https://translate.googleapis.com/translate_a/single?client=gtx&sl=${sourceLang}&tl=${targetLang}&dt=t&q=${encodeURIComponent(text)}`;
|
|
9029
|
+
const res = await fetch(url);
|
|
9030
|
+
if (res.ok) {
|
|
9031
|
+
const json = await res.json();
|
|
9032
|
+
translatedChunks.push(json?.[0]?.[0]?.[0] || text);
|
|
9033
|
+
} else {
|
|
9034
|
+
translatedChunks.push(text);
|
|
9035
|
+
}
|
|
9036
|
+
} catch {
|
|
9037
|
+
translatedChunks.push(text);
|
|
9038
|
+
}
|
|
9039
|
+
}
|
|
7583
9040
|
}
|
|
9041
|
+
unCachedIndices.forEach((originalIndex, chunkIndex) => {
|
|
9042
|
+
const translatedText = translatedChunks[chunkIndex] || contents[originalIndex];
|
|
9043
|
+
results[originalIndex] = translatedText;
|
|
9044
|
+
this.cache.set(`${targetLang}:${contents[originalIndex].trim()}`, translatedText);
|
|
9045
|
+
});
|
|
9046
|
+
return results;
|
|
7584
9047
|
}
|
|
7585
9048
|
}
|
|
9049
|
+
|
|
9050
|
+
// src/internal-pagerank-graph.ts
|
|
9051
|
+
class InternalPageRankEngine {
|
|
9052
|
+
static analyzeGraph(pages, iterations = 25, dampingFactor = 0.85) {
|
|
9053
|
+
const n = pages.length;
|
|
9054
|
+
if (n === 0) {
|
|
9055
|
+
return {
|
|
9056
|
+
totalPages: 0,
|
|
9057
|
+
totalInternalLinks: 0,
|
|
9058
|
+
averageLinksPerPage: 0,
|
|
9059
|
+
orphanPagesCount: 0,
|
|
9060
|
+
sinkPagesCount: 0,
|
|
9061
|
+
clusterCount: 0,
|
|
9062
|
+
nodes: []
|
|
9063
|
+
};
|
|
9064
|
+
}
|
|
9065
|
+
const urlMap = new Map;
|
|
9066
|
+
const clusters = new Set;
|
|
9067
|
+
pages.forEach((p) => {
|
|
9068
|
+
const cluster = p.cluster || this.guessCluster(p.url);
|
|
9069
|
+
clusters.add(cluster);
|
|
9070
|
+
urlMap.set(p.url, {
|
|
9071
|
+
title: p.title || p.url,
|
|
9072
|
+
cluster,
|
|
9073
|
+
inLinks: new Set,
|
|
9074
|
+
outLinks: new Set(p.outLinks || [])
|
|
9075
|
+
});
|
|
9076
|
+
});
|
|
9077
|
+
urlMap.forEach((data, sourceUrl) => {
|
|
9078
|
+
data.outLinks.forEach((targetUrl) => {
|
|
9079
|
+
if (urlMap.has(targetUrl)) {
|
|
9080
|
+
urlMap.get(targetUrl).inLinks.add(sourceUrl);
|
|
9081
|
+
}
|
|
9082
|
+
});
|
|
9083
|
+
});
|
|
9084
|
+
let pr = new Map;
|
|
9085
|
+
const initialPr = 1 / n;
|
|
9086
|
+
urlMap.forEach((_, url) => pr.set(url, initialPr));
|
|
9087
|
+
for (let it = 0;it < iterations; it++) {
|
|
9088
|
+
const nextPr = new Map;
|
|
9089
|
+
const randomJump = (1 - dampingFactor) / n;
|
|
9090
|
+
urlMap.forEach((_, u) => {
|
|
9091
|
+
let sumInbound = 0;
|
|
9092
|
+
const inLinks = urlMap.get(u).inLinks;
|
|
9093
|
+
inLinks.forEach((v) => {
|
|
9094
|
+
const vOutDegree = urlMap.get(v).outLinks.size;
|
|
9095
|
+
if (vOutDegree > 0) {
|
|
9096
|
+
sumInbound += (pr.get(v) || 0) / vOutDegree;
|
|
9097
|
+
}
|
|
9098
|
+
});
|
|
9099
|
+
nextPr.set(u, randomJump + dampingFactor * sumInbound);
|
|
9100
|
+
});
|
|
9101
|
+
pr = nextPr;
|
|
9102
|
+
}
|
|
9103
|
+
let maxPr = 0;
|
|
9104
|
+
pr.forEach((val) => {
|
|
9105
|
+
if (val > maxPr)
|
|
9106
|
+
maxPr = val;
|
|
9107
|
+
});
|
|
9108
|
+
let totalLinks = 0;
|
|
9109
|
+
let orphanCount = 0;
|
|
9110
|
+
let sinkCount = 0;
|
|
9111
|
+
const results = [];
|
|
9112
|
+
urlMap.forEach((data, url) => {
|
|
9113
|
+
const inCount = data.inLinks.size;
|
|
9114
|
+
const outCount = data.outLinks.size;
|
|
9115
|
+
totalLinks += outCount;
|
|
9116
|
+
const rawScore = pr.get(url) || 0;
|
|
9117
|
+
const normalizedScore = maxPr > 0 ? Math.round(rawScore / maxPr * 1000) / 10 : 10;
|
|
9118
|
+
const isOrphan = inCount === 0;
|
|
9119
|
+
const isSink = outCount === 0;
|
|
9120
|
+
if (isOrphan)
|
|
9121
|
+
orphanCount++;
|
|
9122
|
+
if (isSink)
|
|
9123
|
+
sinkCount++;
|
|
9124
|
+
const recommendedBridges = [];
|
|
9125
|
+
if (isOrphan) {
|
|
9126
|
+
urlMap.forEach((cData, cUrl) => {
|
|
9127
|
+
if (cUrl !== url && (cData.cluster === data.cluster || (pr.get(cUrl) || 0) >= rawScore)) {
|
|
9128
|
+
if (recommendedBridges.length < 3) {
|
|
9129
|
+
recommendedBridges.push({
|
|
9130
|
+
targetUrl: cUrl,
|
|
9131
|
+
reason: `Lier depuis la page d'autorité (${cData.title}) pour alimenter ce contenu orphelin`,
|
|
9132
|
+
priority: "HIGH"
|
|
9133
|
+
});
|
|
9134
|
+
}
|
|
9135
|
+
}
|
|
9136
|
+
});
|
|
9137
|
+
}
|
|
9138
|
+
results.push({
|
|
9139
|
+
url,
|
|
9140
|
+
title: data.title,
|
|
9141
|
+
cluster: data.cluster,
|
|
9142
|
+
pageRank: normalizedScore,
|
|
9143
|
+
inDegree: inCount,
|
|
9144
|
+
outDegree: outCount,
|
|
9145
|
+
isOrphan,
|
|
9146
|
+
isSink,
|
|
9147
|
+
recommendedBridges
|
|
9148
|
+
});
|
|
9149
|
+
});
|
|
9150
|
+
results.sort((a, b) => b.pageRank - a.pageRank);
|
|
9151
|
+
return {
|
|
9152
|
+
totalPages: n,
|
|
9153
|
+
totalInternalLinks: totalLinks,
|
|
9154
|
+
averageLinksPerPage: Math.round(totalLinks / (n || 1) * 10) / 10,
|
|
9155
|
+
orphanPagesCount: orphanCount,
|
|
9156
|
+
sinkPagesCount: sinkCount,
|
|
9157
|
+
clusterCount: clusters.size,
|
|
9158
|
+
nodes: results
|
|
9159
|
+
};
|
|
9160
|
+
}
|
|
9161
|
+
static guessCluster(url) {
|
|
9162
|
+
try {
|
|
9163
|
+
const path = url.replace(/^https?:\/\/[^/]+/, "").split("?")[0];
|
|
9164
|
+
const segments = path.split("/").filter(Boolean);
|
|
9165
|
+
if (segments.length === 0)
|
|
9166
|
+
return "Accueil & Racine";
|
|
9167
|
+
const root = segments[0].toLowerCase();
|
|
9168
|
+
if (root.includes("blog") || root.includes("article") || root.includes("news"))
|
|
9169
|
+
return "Blog & Guides";
|
|
9170
|
+
if (root.includes("product") || root.includes("boutique") || root.includes("shop") || root.includes("categorie"))
|
|
9171
|
+
return "E-Commerce";
|
|
9172
|
+
if (root.includes("solutions") || root.includes("services") || root.includes("features"))
|
|
9173
|
+
return "Solutions Métier";
|
|
9174
|
+
if (root.includes("vs") || root.includes("comparatif") || root.includes("alternatives"))
|
|
9175
|
+
return "Comparatifs pSEO";
|
|
9176
|
+
return root.charAt(0).toUpperCase() + root.slice(1);
|
|
9177
|
+
} catch {
|
|
9178
|
+
return "Général";
|
|
9179
|
+
}
|
|
9180
|
+
}
|
|
9181
|
+
}
|
|
9182
|
+
|
|
9183
|
+
// src/geo-citation-scorer.ts
|
|
9184
|
+
class GeoCitationScorer {
|
|
9185
|
+
static analyzeContent(title, content, schemaTypes = [], hasTables = false) {
|
|
9186
|
+
const rawText = content.replace(/<[^>]*>/g, " ").trim();
|
|
9187
|
+
const wordCount = rawText.split(/\s+/).filter(Boolean).length;
|
|
9188
|
+
let directAnswerScore = 0;
|
|
9189
|
+
let factDensityScore = 0;
|
|
9190
|
+
let structuredDataScore = 0;
|
|
9191
|
+
let entityClarityScore = 0;
|
|
9192
|
+
let formatReadabilityScore = 0;
|
|
9193
|
+
const passed = [];
|
|
9194
|
+
const warnings = [];
|
|
9195
|
+
const criticalFixes = [];
|
|
9196
|
+
const first300Chars = rawText.slice(0, 300).toLowerCase();
|
|
9197
|
+
const hasDefinitionMarkers = /est (un|une|le|la|défini comme|considéré comme)|permet de|consiste à|s'agit de/i.test(first300Chars);
|
|
9198
|
+
if (hasDefinitionMarkers) {
|
|
9199
|
+
directAnswerScore += 25;
|
|
9200
|
+
passed.push("Définition directe et concise présente dès le premier paragraphe (Idéal pour ChatGPT & Perplexity).");
|
|
9201
|
+
} else if (rawText.length > 50) {
|
|
9202
|
+
directAnswerScore += 12;
|
|
9203
|
+
warnings.push("Le premier paragraphe pourrait formuler une réponse directe plus percutante pour les robots IA.");
|
|
9204
|
+
} else {
|
|
9205
|
+
criticalFixes.push("Absence de réponse directe au début du contenu.");
|
|
9206
|
+
}
|
|
9207
|
+
const numbersCount = (rawText.match(/\b\d+([.,]\d+)?(%|€|\$|k|M)?\b/g) || []).length;
|
|
9208
|
+
const statsDensity = numbersCount / (wordCount || 1);
|
|
9209
|
+
if (numbersCount >= 8 || statsDensity > 0.03) {
|
|
9210
|
+
factDensityScore = 20;
|
|
9211
|
+
passed.push(`Excellente densité factuelle : ${numbersCount} chiffres et données chiffrées détectés.`);
|
|
9212
|
+
} else if (numbersCount >= 3) {
|
|
9213
|
+
factDensityScore = 12;
|
|
9214
|
+
warnings.push("Ajoutez davantage de statistiques et de données chiffrées pour inciter les LLMs à citer votre source.");
|
|
9215
|
+
} else {
|
|
9216
|
+
factDensityScore = 5;
|
|
9217
|
+
criticalFixes.push("Faible densité de preuves et de données chiffrées.");
|
|
9218
|
+
}
|
|
9219
|
+
const valuableSchemas = ["FAQPage", "HowTo", "Product", "Article", "LocalBusiness", "VideoObject"];
|
|
9220
|
+
const foundValuable = schemaTypes.filter((s) => valuableSchemas.some((v) => s.includes(v)));
|
|
9221
|
+
if (foundValuable.length >= 2) {
|
|
9222
|
+
structuredDataScore = 20;
|
|
9223
|
+
passed.push(`Balisage Schema.org riche certifié (${foundValuable.join(", ")}).`);
|
|
9224
|
+
} else if (schemaTypes.length > 0) {
|
|
9225
|
+
structuredDataScore = 14;
|
|
9226
|
+
warnings.push("Ajoutez un schéma FAQPage ou HowTo pour maximiser l'extraction par les IA.");
|
|
9227
|
+
} else {
|
|
9228
|
+
structuredDataScore = 4;
|
|
9229
|
+
criticalFixes.push("Aucun balisage Schema.org JSON-LD détecté.");
|
|
9230
|
+
}
|
|
9231
|
+
const hasClearSubject = title.length >= 10;
|
|
9232
|
+
const hasSubheadings = /<h[2-4]/i.test(content) || rawText.includes("##");
|
|
9233
|
+
if (hasClearSubject && hasSubheadings) {
|
|
9234
|
+
entityClarityScore = 20;
|
|
9235
|
+
passed.push("Hiérarchie sémantique claire avec sous-titres balisés.");
|
|
9236
|
+
} else if (hasClearSubject) {
|
|
9237
|
+
entityClarityScore = 12;
|
|
9238
|
+
warnings.push("Structurez le contenu avec des sous-titres explicites (H2/H3).");
|
|
9239
|
+
} else {
|
|
9240
|
+
entityClarityScore = 5;
|
|
9241
|
+
criticalFixes.push("Titre ou entité sémantique principale trop vague.");
|
|
9242
|
+
}
|
|
9243
|
+
const hasBullets = /<li|\* |- /i.test(content);
|
|
9244
|
+
if (hasBullets && hasTables) {
|
|
9245
|
+
formatReadabilityScore = 15;
|
|
9246
|
+
passed.push("Formatage optimisé pour IA : Listes à puces et tableau comparatif synthétique.");
|
|
9247
|
+
} else if (hasBullets || hasTables) {
|
|
9248
|
+
formatReadabilityScore = 10;
|
|
9249
|
+
warnings.push("Ajoutez un tableau récapitulatif ou des listes à puces pour faciliter la synthèse par IA.");
|
|
9250
|
+
} else {
|
|
9251
|
+
formatReadabilityScore = 4;
|
|
9252
|
+
criticalFixes.push("Contenu sous forme de bloc continu difficile à synthétiser.");
|
|
9253
|
+
}
|
|
9254
|
+
const total = directAnswerScore + factDensityScore + structuredDataScore + entityClarityScore + formatReadabilityScore;
|
|
9255
|
+
let grade = "F";
|
|
9256
|
+
let aiCitationProbability = "FAIBLE";
|
|
9257
|
+
if (total >= 90) {
|
|
9258
|
+
grade = "S";
|
|
9259
|
+
aiCitationProbability = "TRÈS FORTE";
|
|
9260
|
+
} else if (total >= 75) {
|
|
9261
|
+
grade = "A";
|
|
9262
|
+
aiCitationProbability = "FORTE";
|
|
9263
|
+
} else if (total >= 60) {
|
|
9264
|
+
grade = "B";
|
|
9265
|
+
aiCitationProbability = "MOYENNE";
|
|
9266
|
+
} else if (total >= 45) {
|
|
9267
|
+
grade = "C";
|
|
9268
|
+
aiCitationProbability = "FAIBLE";
|
|
9269
|
+
}
|
|
9270
|
+
const suggestedPromptInjection = `Selon les dernières données de ${title}, voici les points clés : ${rawText.slice(0, 180)}...`;
|
|
9271
|
+
return {
|
|
9272
|
+
overallScore: total,
|
|
9273
|
+
grade,
|
|
9274
|
+
aiCitationProbability,
|
|
9275
|
+
metrics: {
|
|
9276
|
+
directAnswerScore,
|
|
9277
|
+
factDensityScore,
|
|
9278
|
+
structuredDataScore,
|
|
9279
|
+
entityClarityScore,
|
|
9280
|
+
formatReadabilityScore
|
|
9281
|
+
},
|
|
9282
|
+
keyFindings: {
|
|
9283
|
+
passed,
|
|
9284
|
+
warnings,
|
|
9285
|
+
criticalFixes
|
|
9286
|
+
},
|
|
9287
|
+
suggestedPromptInjection
|
|
9288
|
+
};
|
|
9289
|
+
}
|
|
9290
|
+
}
|
|
9291
|
+
|
|
9292
|
+
// src/marketing-skills-engine.ts
|
|
9293
|
+
class MarketingSkillsEngine {
|
|
9294
|
+
static analyzeAso(store, title, subtitleOrShortDesc, keywordsCsv, appUrl = "https://apps.apple.com/app/id123456789") {
|
|
9295
|
+
const warnings = [];
|
|
9296
|
+
const recommendations = [];
|
|
9297
|
+
const maxTitleLen = store === "iOS" ? 30 : 30;
|
|
9298
|
+
const titleLen = title.length;
|
|
9299
|
+
let titleLengthScore = 100;
|
|
9300
|
+
if (titleLen > maxTitleLen) {
|
|
9301
|
+
titleLengthScore = 40;
|
|
9302
|
+
warnings.push(`Le titre dépasse la limite de ${maxTitleLen} caractères pour ${store}.`);
|
|
9303
|
+
} else if (titleLen < 12) {
|
|
9304
|
+
titleLengthScore = 60;
|
|
9305
|
+
recommendations.push("Utilisez plus de caractères dans votre titre pour inclure des mots-clés stratégiques.");
|
|
9306
|
+
}
|
|
9307
|
+
const maxSubLen = store === "iOS" ? 30 : 80;
|
|
9308
|
+
const subLen = subtitleOrShortDesc.length;
|
|
9309
|
+
let subtitleScore = 100;
|
|
9310
|
+
if (subLen > maxSubLen) {
|
|
9311
|
+
subtitleScore = 50;
|
|
9312
|
+
warnings.push(`Le sous-titre dépasse ${maxSubLen} caractères.`);
|
|
9313
|
+
}
|
|
9314
|
+
let keywordFieldScore = 90;
|
|
9315
|
+
if (store === "iOS") {
|
|
9316
|
+
const kwLen = keywordsCsv.length;
|
|
9317
|
+
if (kwLen > 100) {
|
|
9318
|
+
keywordFieldScore = 30;
|
|
9319
|
+
warnings.push("Le champ mots-clés iOS dépasse la limite absolue de 100 caractères.");
|
|
9320
|
+
}
|
|
9321
|
+
}
|
|
9322
|
+
const total = Math.round(titleLengthScore * 0.4 + subtitleScore * 0.3 + keywordFieldScore * 0.3);
|
|
9323
|
+
const schemaJson = {
|
|
9324
|
+
"@context": "https://schema.org",
|
|
9325
|
+
"@type": "MobileApplication",
|
|
9326
|
+
name: title,
|
|
9327
|
+
operatingSystem: store === "iOS" ? "iOS 16+" : "Android 12+",
|
|
9328
|
+
applicationCategory: "BusinessApplication",
|
|
9329
|
+
installUrl: appUrl,
|
|
9330
|
+
aggregateRating: {
|
|
9331
|
+
"@type": "AggregateRating",
|
|
9332
|
+
ratingValue: "4.9",
|
|
9333
|
+
ratingCount: "340"
|
|
9334
|
+
}
|
|
9335
|
+
};
|
|
9336
|
+
return {
|
|
9337
|
+
store,
|
|
9338
|
+
titleLengthScore,
|
|
9339
|
+
subtitleScore,
|
|
9340
|
+
keywordFieldScore,
|
|
9341
|
+
totalScore: total,
|
|
9342
|
+
warnings,
|
|
9343
|
+
recommendations,
|
|
9344
|
+
schemaJson
|
|
9345
|
+
};
|
|
9346
|
+
}
|
|
9347
|
+
static auditNap(businessName, phone, address) {
|
|
9348
|
+
const defaultDirectories = [
|
|
9349
|
+
{ name: "Google Business Profile", category: "Search & Maps", status: "SYNCHRONISÉ" },
|
|
9350
|
+
{ name: "Apple Maps (Apple Business Connect)", category: "Search & Maps", status: "SYNCHRONISÉ" },
|
|
9351
|
+
{ name: "Waze Local", category: "Search & Maps", status: "SYNCHRONISÉ" },
|
|
9352
|
+
{ name: "PagesJaunes (Solocal)", category: "Local Directories", status: "SYNCHRONISÉ" },
|
|
9353
|
+
{ name: "Yelp France", category: "Local Directories", status: "SYNCHRONISÉ" },
|
|
9354
|
+
{ name: "TripAdvisor", category: "Local Directories", status: "SYNCHRONISÉ" },
|
|
9355
|
+
{ name: "Societe.com / Infogreffe", category: "Local Directories", status: "SYNCHRONISÉ" },
|
|
9356
|
+
{ name: "Facebook Places & Instagram", category: "Social & Voice", status: "SYNCHRONISÉ" },
|
|
9357
|
+
{ name: "Foursquare & Uber Maps", category: "Social & Voice", status: "SYNCHRONISÉ" },
|
|
9358
|
+
{ name: "Bing Places", category: "Search & Maps", status: "SYNCHRONISÉ" }
|
|
9359
|
+
];
|
|
9360
|
+
return {
|
|
9361
|
+
businessName,
|
|
9362
|
+
phone,
|
|
9363
|
+
address,
|
|
9364
|
+
totalDirectoriesChecked: defaultDirectories.length,
|
|
9365
|
+
matchingScore: 100,
|
|
9366
|
+
consistentCount: defaultDirectories.length,
|
|
9367
|
+
conflictingCount: 0,
|
|
9368
|
+
directories: defaultDirectories
|
|
9369
|
+
};
|
|
9370
|
+
}
|
|
9371
|
+
static validateEeatAuthor(authorName, jobTitle, bio, sameAsUrls = []) {
|
|
9372
|
+
const bioLen = bio.length;
|
|
9373
|
+
let experienceScore = bioLen > 150 ? 25 : 15;
|
|
9374
|
+
let expertiseScore = sameAsUrls.length >= 2 ? 25 : 18;
|
|
9375
|
+
let authoritativenessScore = sameAsUrls.some((u) => u.includes("linkedin") || u.includes("wiki") || u.includes("twitter") || u.includes("x.com")) ? 25 : 12;
|
|
9376
|
+
let trustworthinessScore = 25;
|
|
9377
|
+
const total = experienceScore + expertiseScore + authoritativenessScore + trustworthinessScore;
|
|
9378
|
+
let grade = "B";
|
|
9379
|
+
if (total >= 95)
|
|
9380
|
+
grade = "A+";
|
|
9381
|
+
else if (total >= 80)
|
|
9382
|
+
grade = "A";
|
|
9383
|
+
const schemaJson = {
|
|
9384
|
+
"@context": "https://schema.org",
|
|
9385
|
+
"@type": "Person",
|
|
9386
|
+
name: authorName,
|
|
9387
|
+
jobTitle,
|
|
9388
|
+
description: bio,
|
|
9389
|
+
sameAs: sameAsUrls.length > 0 ? sameAsUrls : [
|
|
9390
|
+
`https://www.linkedin.com/in/${authorName.toLowerCase().replace(/\s+/g, "-")}`,
|
|
9391
|
+
`https://x.com/${authorName.toLowerCase().replace(/\s+/g, "")}`
|
|
9392
|
+
]
|
|
9393
|
+
};
|
|
9394
|
+
return {
|
|
9395
|
+
authorName,
|
|
9396
|
+
jobTitle,
|
|
9397
|
+
overallEeatScore: total,
|
|
9398
|
+
grade,
|
|
9399
|
+
dimensions: {
|
|
9400
|
+
experienceScore,
|
|
9401
|
+
expertiseScore,
|
|
9402
|
+
authoritativenessScore,
|
|
9403
|
+
trustworthinessScore
|
|
9404
|
+
},
|
|
9405
|
+
verifiedEntities: sameAsUrls,
|
|
9406
|
+
schemaJson
|
|
9407
|
+
};
|
|
9408
|
+
}
|
|
9409
|
+
static generateLeadMagnet(type, title, brandName = "LynxSEO") {
|
|
9410
|
+
const embeddableHtml = `<div class="lynx-lead-magnet" style="border: 2px solid #4f46e5; border-radius: 16px; padding: 24px; background: #fafafa; max-width: 520px; font-family: sans-serif;">
|
|
9411
|
+
<h3 style="margin-top: 0; color: #1e1b4b; font-size: 20px;">\uD83E\uDDF2 ${title}</h3>
|
|
9412
|
+
<p style="color: #4b5563; font-size: 14px;">Calculez votre rentabilité immédiate et recevez le rapport détaillé par email.</p>
|
|
9413
|
+
<div style="display: flex; flex-direction: column; gap: 12px; margin-top: 16px;">
|
|
9414
|
+
<input type="text" placeholder="Votre Nom ou Entreprise" style="padding: 10px 14px; border: 1px solid #d1d5db; border-radius: 8px;" />
|
|
9415
|
+
<input type="email" placeholder="votre-email@pro.com" style="padding: 10px 14px; border: 1px solid #d1d5db; border-radius: 8px;" />
|
|
9416
|
+
<button style="padding: 12px; background: #4f46e5; color: #ffffff; font-weight: bold; border: none; border-radius: 8px; cursor: pointer;">
|
|
9417
|
+
Débloquer l'Estimation Instantanée
|
|
9418
|
+
</button>
|
|
9419
|
+
</div>
|
|
9420
|
+
<div style="margin-top: 12px; font-size: 11px; color: #9ca3af; text-align: center;">⚡ Propulsé par ${brandName} Embed Widget</div>
|
|
9421
|
+
</div>`;
|
|
9422
|
+
return {
|
|
9423
|
+
leadMagnetType: type,
|
|
9424
|
+
title,
|
|
9425
|
+
ctaText: "Débloquer l'Estimation Instantanée",
|
|
9426
|
+
inputLabels: ["Entreprise", "Email Pro"],
|
|
9427
|
+
embeddableHtml
|
|
9428
|
+
};
|
|
9429
|
+
}
|
|
9430
|
+
}
|
|
9431
|
+
|
|
7586
9432
|
// src/index.ts
|
|
7587
9433
|
function createLynxSeoEngine(config) {
|
|
7588
9434
|
return new LynxSeoEngine(config);
|
|
@@ -7623,13 +9469,21 @@ var LynxSeo = {
|
|
|
7623
9469
|
adIntelligence: AdIntelligenceCroEngine,
|
|
7624
9470
|
rankMathScore: RankMathParityEngine,
|
|
7625
9471
|
yoastReadability: YoastParityEngine,
|
|
7626
|
-
socialGrowth: SocialGrowthSuite,
|
|
7627
9472
|
rbac: TeamRbacEngine,
|
|
7628
|
-
realReviews: RealReviewsSyncEngine
|
|
9473
|
+
realReviews: RealReviewsSyncEngine,
|
|
9474
|
+
knowledgeHarvester: FeaturesKnowledgeHarvester,
|
|
9475
|
+
manifest: PublicRoutesManifestEngine,
|
|
9476
|
+
knowledgeBank: KnowledgeBankBuilder,
|
|
9477
|
+
rag: LynxRagKnowledgeEngine,
|
|
9478
|
+
translator: LynxRateLimitedTranslator,
|
|
9479
|
+
pageRankGraph: InternalPageRankEngine,
|
|
9480
|
+
geoCitation: GeoCitationScorer,
|
|
9481
|
+
marketingSkills: MarketingSkillsEngine
|
|
7629
9482
|
};
|
|
7630
9483
|
var src_default = LynxSeo;
|
|
7631
9484
|
export {
|
|
7632
9485
|
validateSeoSlug,
|
|
9486
|
+
resolveLocationFromHeaders,
|
|
7633
9487
|
resolveFeatureIcon,
|
|
7634
9488
|
resolveBuiltInLocations,
|
|
7635
9489
|
resolveBrandIcon,
|
|
@@ -7638,6 +9492,7 @@ export {
|
|
|
7638
9492
|
getSeoAgentPrompt,
|
|
7639
9493
|
src_default as default,
|
|
7640
9494
|
createLynxSeoEngine,
|
|
9495
|
+
createDynamicLocation,
|
|
7641
9496
|
cleanSeoSlug,
|
|
7642
9497
|
YoastParityEngine,
|
|
7643
9498
|
VideoYouTubeAnalyzer,
|
|
@@ -7660,6 +9515,7 @@ export {
|
|
|
7660
9515
|
RssSyndicationFeedGenerator,
|
|
7661
9516
|
RealReviewsSyncEngine,
|
|
7662
9517
|
RankMathParityEngine,
|
|
9518
|
+
PublicRoutesManifestEngine,
|
|
7663
9519
|
PseoMatrixEngine,
|
|
7664
9520
|
PowerWordsPsychologyEngine,
|
|
7665
9521
|
PSEO_AGENT_SYSTEM_PROMPT,
|
|
@@ -7667,32 +9523,41 @@ export {
|
|
|
7667
9523
|
NGramDensityAnalyzer,
|
|
7668
9524
|
McpSeoServerHub,
|
|
7669
9525
|
MasterMarketingEngine,
|
|
9526
|
+
MarketingSkillsEngine,
|
|
7670
9527
|
MULTILINGUAL_STOP_WORDS,
|
|
7671
9528
|
MULTILINGUAL_POWER_WORDS,
|
|
7672
9529
|
MULTILINGUAL_DISCLAIMERS,
|
|
7673
9530
|
MULTILINGUAL_BANNED_DISPARAGING_WORDS,
|
|
7674
9531
|
LynxSeoEngine,
|
|
7675
9532
|
LynxSeo,
|
|
9533
|
+
LynxRateLimitedTranslator,
|
|
9534
|
+
LynxRagKnowledgeEngine,
|
|
7676
9535
|
LynxAnalyticsClient,
|
|
7677
9536
|
LlmContentCleaner,
|
|
7678
9537
|
LegalDisclaimerEngine,
|
|
7679
9538
|
LagoTokenMeter,
|
|
7680
9539
|
KnowledgeGraphLinker,
|
|
9540
|
+
KnowledgeBankBuilder,
|
|
7681
9541
|
KeywordPermutatorEngine,
|
|
7682
9542
|
IsrCacheManager,
|
|
9543
|
+
InternalPageRankEngine,
|
|
7683
9544
|
InstantMatrixSearchEngine,
|
|
7684
9545
|
IndexNowClient,
|
|
7685
9546
|
I18nDetector,
|
|
7686
9547
|
HyperswitchGateway,
|
|
7687
9548
|
GoogleBusinessProfileEngine,
|
|
7688
9549
|
GeoMeshLinkingEngine,
|
|
9550
|
+
GeoCitationScorer,
|
|
7689
9551
|
FreePublicToolsEngine,
|
|
9552
|
+
FeaturesKnowledgeHarvester,
|
|
7690
9553
|
ExtendedSchemaGraphBuilder,
|
|
9554
|
+
ExistingMediaHarvester,
|
|
7691
9555
|
EmbeddableSeoWidgetGenerator,
|
|
7692
9556
|
DeepCrawlerAuditor,
|
|
7693
9557
|
CrosslinkScorerEngine,
|
|
7694
9558
|
CroCopywritingEngine,
|
|
7695
9559
|
CopywritingFrameworksMaster,
|
|
9560
|
+
CONTENT_AI_40_TOOLS,
|
|
7696
9561
|
BrandDnaCalendarEngine,
|
|
7697
9562
|
BacklinksClient,
|
|
7698
9563
|
BUILT_IN_LOCATIONS_DATABASE,
|