@lynxflow/seo-engine 1.3.0 → 1.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -9
- package/connectors/wordpress/lynxseo-connector.php +5 -5
- package/dist/engine.d.ts +5 -0
- package/dist/index.js +43 -6
- package/dist/index.mjs +43 -6
- package/package.json +1 -1
- package/src/auth-key.ts +1 -1
- package/src/engine.ts +56 -5
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
# 🦁 @lynxflow/seo-engine — Guide
|
|
1
|
+
# 🦁 @lynxflow/seo-engine — LynxSEO (Lynxio) Master Guide & Documentation
|
|
2
2
|
|
|
3
|
-
> **Le Moteur Universel de SEO Programmatique (pSEO), Optimisation Moteurs IA (AEO/GEO), Indexation Instantanée & Analytics Embarqués.**
|
|
3
|
+
> **Le Moteur Universel LynxSEO / Lynxio de SEO Programmatique (pSEO), Optimisation Moteurs IA (AEO/GEO), Indexation Instantanée, Crawleur Technique & Analytics Embarqués.**
|
|
4
4
|
|
|
5
|
-
`@lynxflow/seo-engine` est
|
|
5
|
+
`@lynxflow/seo-engine` est le SDK officiel de **LynxSEO (Lynxio)** conçu pour générer, servir et indexer des millions de landing pages dynamiques, des avis 4.9/5★ Schema.org et des réponses directes pour les intelligences artificielles (ChatGPT Search, Perplexity, Google AI Overviews) en **moins de 0.05 milliseconde** avec **0% de charge sur votre base de données SQL**.
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
3. [🏛️ Les 8 Matrices Programmatiques & Multiplicateurs](#3--les-8-matrices-programmatiques--multiplicateurs)
|
|
13
13
|
4. [🚀 Tutoriel d'Intégration Pas-à-Pas (Next.js 15/16 & React 19)](#4--tutoriel-dintégration-pas-à-pas-nextjs-1516--react-19)
|
|
14
14
|
5. [🔌 Intégration sur Autres Frameworks (WordPress, Shopify, Cloudflare, Laravel, Astro)](#5--intégration-sur-autres-frameworks)
|
|
15
|
-
6. [🧰 Les
|
|
15
|
+
6. [🧰 Les Modules Embarqués du SDK (Crawler, Analytics, SERP, Backlinks, IA, IndexNow)](#6--les-modules-embarqués-du-sdk)
|
|
16
16
|
7. [🌍 Internationalisation & Zéro Contenu Dupliqué (10 Langues & Hreflang)](#7--internationalisation--zéro-contenu-dupliqué)
|
|
17
17
|
8. [📘 Référence Complète des Types TypeScript](#8--référence-complète-des-types-typescript)
|
|
18
18
|
|
|
@@ -24,19 +24,19 @@
|
|
|
24
24
|
* ⚡ **Résolution Ultra-Rapide (< 0.05ms) :** Les pages et métadonnées sont résolues en mémoire vive sans requête SQL bloquante.
|
|
25
25
|
* 🤖 **Conçu pour le Search IA (AEO / GEO) :** Génère des blocs de synthèse directe optimisés pour être cités en source n°1 par **ChatGPT Search**, **Perplexity** et **Google AI Overviews**.
|
|
26
26
|
* 🛡️ **Fonctionne à 100% en Local / Sans Dépendance API Bloquante :** La validation cryptographique HMAC de votre clé d'API s'exécute directement sur votre serveur pour garantir un affichage instantané même en cas de panne réseau.
|
|
27
|
-
* 📈 **Suite Complète Embarquée :** Accédez directement à vos analytics de trafic, votre détection de bots IA, vos positions de mots-clés Google et vos backlinks sans quitter votre application.
|
|
27
|
+
* 📈 **Suite Complète Embarquée :** Accédez directement à votre crawleur technique Seonaut/CrawlSEO, vos analytics de trafic, votre détection de bots IA, vos positions de mots-clés Google et vos backlinks sans quitter votre application.
|
|
28
28
|
|
|
29
29
|
---
|
|
30
30
|
|
|
31
31
|
## 2. 🔑 L'API Key Better Auth, Sécurité & Quotas
|
|
32
32
|
|
|
33
33
|
### Étape 1 : Obtenir votre Clé d'API
|
|
34
|
-
Rendez-vous dans votre cockpit
|
|
34
|
+
Rendez-vous dans votre cockpit **LynxSEO (Lynxio)** dans **Paramètres ➔ Clés d'API & Sécurité** et générez votre clé (`ba_key_...` ou `lynx_api_...`).
|
|
35
35
|
|
|
36
36
|
### Étape 2 : Configuration dans votre fichier `.env` ou `.env.local`
|
|
37
37
|
```bash
|
|
38
|
-
# 🔑 Clé d'API Better Auth
|
|
39
|
-
|
|
38
|
+
# 🔑 Clé d'API LynxSEO / Lynxio (Générée via Better Auth)
|
|
39
|
+
LYNXSEO_API_KEY=ba_key_growth_tenant_acme_a8f93e
|
|
40
40
|
|
|
41
41
|
# 🌐 Domaine de votre site web
|
|
42
42
|
NEXT_PUBLIC_SITE_URL=https://mon-entreprise.com
|
|
@@ -98,7 +98,7 @@ bun add @lynxflow/seo-engine
|
|
|
98
98
|
import { createLynxSeoEngine } from "@lynxflow/seo-engine";
|
|
99
99
|
|
|
100
100
|
export const seoEngine = createLynxSeoEngine({
|
|
101
|
-
apiKey: process.env.
|
|
101
|
+
apiKey: process.env.LYNXSEO_API_KEY!,
|
|
102
102
|
domain: process.env.NEXT_PUBLIC_SITE_URL || "https://mon-entreprise.com",
|
|
103
103
|
brandName: "Acme Corp",
|
|
104
104
|
defaultLocale: "fr",
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
<?php
|
|
2
2
|
/**
|
|
3
|
-
* Plugin Name:
|
|
4
|
-
* Plugin URI: https://
|
|
5
|
-
* Description: Moteur pSEO, AEO et Blog 100% autonome pour WordPress. Connexion en 1 clic via Better Auth. Génère et sert instantanément les 8 Matrices Programmatiques chez vous (< 0.05ms) avec sitemap XML automatique et 0% de charge sur votre base MySQL.
|
|
6
|
-
* Version: 2.
|
|
7
|
-
* Author:
|
|
3
|
+
* Plugin Name: LynxSEO (Lynxio) Engine — Moteur Programmatique & Blog Autonome
|
|
4
|
+
* Plugin URI: https://lynxio.io
|
|
5
|
+
* Description: Moteur pSEO, AEO et Blog 100% autonome pour WordPress par LynxSEO (Lynxio). Connexion en 1 clic via Better Auth. Génère et sert instantanément les 8 Matrices Programmatiques chez vous (< 0.05ms) avec sitemap XML automatique et 0% de charge sur votre base MySQL.
|
|
6
|
+
* Version: 2.3.0
|
|
7
|
+
* Author: LynxSEO / Lynxio Technologies
|
|
8
8
|
* License: Proprietary
|
|
9
9
|
*/
|
|
10
10
|
|
package/dist/engine.d.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 🦁 LynxSEO Engine (Lynxio) — Universal Programmatic SEO & AI Search Engine SDK
|
|
3
|
+
*/
|
|
1
4
|
/**
|
|
2
5
|
* ⚡ LynxSEO High-Speed Universal Dynamic Page Engine (Internationalized)
|
|
3
6
|
*
|
|
@@ -63,6 +66,8 @@ export declare class LynxSeoEngine {
|
|
|
63
66
|
private buildIndustryPage;
|
|
64
67
|
private buildRolePage;
|
|
65
68
|
private buildIntegrationPage;
|
|
69
|
+
private buildUseCasePage;
|
|
70
|
+
private buildToolPage;
|
|
66
71
|
private assemblePage;
|
|
67
72
|
/**
|
|
68
73
|
* Returns the exact count of distinct unique programmatic pages currently active.
|
package/dist/index.js
CHANGED
|
@@ -72,7 +72,7 @@ class ApiKeyGuardian {
|
|
|
72
72
|
maxDomains: 1,
|
|
73
73
|
monthlyCreditBudget: 500,
|
|
74
74
|
tokenManager: new TokenQuotaManager("starter"),
|
|
75
|
-
errorMessage: "Missing API Key. Please set
|
|
75
|
+
errorMessage: "Missing API Key. Please set LYNXSEO_API_KEY (or LYNXIO_API_KEY) or pass apiKey in config."
|
|
76
76
|
};
|
|
77
77
|
}
|
|
78
78
|
const cleanKey = apiKey.trim();
|
|
@@ -1307,7 +1307,7 @@ class LynxSeoEngine {
|
|
|
1307
1307
|
...config
|
|
1308
1308
|
};
|
|
1309
1309
|
this.servicesMap = new Map((config.services || []).map((s) => [s.slug, s]));
|
|
1310
|
-
const rawKey = config.apiKey || config.licenseKey || "";
|
|
1310
|
+
const rawKey = config.apiKey || config.licenseKey || typeof process !== "undefined" && process.env && (process.env.LYNXSEO_API_KEY || process.env.LYNXIO_API_KEY || process.env.LYNXFLOW_API_KEY) || "";
|
|
1311
1311
|
this.authStatus = ApiKeyGuardian.validate(rawKey, config.domain);
|
|
1312
1312
|
this.analytics = new LynxAnalyticsClient(rawKey);
|
|
1313
1313
|
this.serp = new SerpClient(rawKey);
|
|
@@ -1453,12 +1453,14 @@ class LynxSeoEngine {
|
|
|
1453
1453
|
return this.buildRolePage(service, remainder.replace(/-/g, " "), domain, t0, locale, cleanPath);
|
|
1454
1454
|
}
|
|
1455
1455
|
if ((pathParts[0] === "cas-usage" || pathParts[0] === "use-cases") && pathParts.length >= 2) {
|
|
1456
|
-
const
|
|
1457
|
-
|
|
1456
|
+
const raw = pathParts[1].replace(/-(pour|for)-/i, "-");
|
|
1457
|
+
const { service, remainder } = detectServiceFromSlug(raw);
|
|
1458
|
+
return this.buildUseCasePage(service, remainder.replace(/-/g, " "), domain, t0, locale, cleanPath);
|
|
1458
1459
|
}
|
|
1459
1460
|
if ((pathParts[0] === "outils" || pathParts[0] === "tools") && pathParts.length >= 2) {
|
|
1460
|
-
const
|
|
1461
|
-
|
|
1461
|
+
const raw = pathParts[1].replace(/^simulateur-roi-/i, "");
|
|
1462
|
+
const { service, remainder } = detectServiceFromSlug(raw);
|
|
1463
|
+
return this.buildToolPage(service, remainder.replace(/-/g, " "), domain, t0, locale, cleanPath);
|
|
1462
1464
|
}
|
|
1463
1465
|
return this.buildGeoPage(defaultService, "FR", "Paris", domain, t0, locale, cleanPath);
|
|
1464
1466
|
}
|
|
@@ -1600,6 +1602,41 @@ class LynxSeoEngine {
|
|
|
1600
1602
|
t0
|
|
1601
1603
|
});
|
|
1602
1604
|
}
|
|
1605
|
+
buildUseCasePage(service, topic, domain, t0, locale = "fr", path = `cas-usage/${topic.toLowerCase().replace(/\s+/g, "-")}`) {
|
|
1606
|
+
const topicName = topic.charAt(0).toUpperCase() + topic.slice(1);
|
|
1607
|
+
const title = `${service.name} pour ${topicName} — ${this.config.brandName}`;
|
|
1608
|
+
const description = `Découvrez comment optimiser vos opérations de ${topicName} avec notre solution ${service.name}. Déploiement en 10 minutes.`;
|
|
1609
|
+
const h1 = `${service.name} : ${topicName}`;
|
|
1610
|
+
const directAnswer = `${this.config.brandName} (${service.name}) répond au cas d'usage "${topicName}" en automatisant le traitement et en réduisant les coûts opérationnels.`;
|
|
1611
|
+
return this.assemblePage({
|
|
1612
|
+
url: `${domain}/${path}`,
|
|
1613
|
+
title,
|
|
1614
|
+
description,
|
|
1615
|
+
h1,
|
|
1616
|
+
directAnswer,
|
|
1617
|
+
service,
|
|
1618
|
+
locale,
|
|
1619
|
+
path,
|
|
1620
|
+
t0
|
|
1621
|
+
});
|
|
1622
|
+
}
|
|
1623
|
+
buildToolPage(service, tool, domain, t0, locale = "fr", path = `outils/${tool.toLowerCase().replace(/\s+/g, "-")}`) {
|
|
1624
|
+
const title = `Simulateur & Calculateur ROI : ${service.name} — ${this.config.brandName}`;
|
|
1625
|
+
const description = `Estimez vos gains de productivité et vos économies annuelles grâce à notre calculateur ROI pour ${service.name}.`;
|
|
1626
|
+
const h1 = `Simulateur de Rentabilité & ROI : ${service.name}`;
|
|
1627
|
+
const directAnswer = `Utilisez le simulateur ROI de ${this.config.brandName} pour calculer précisément la rentabilité de votre investissement dans ${service.name} (amortissement en moins de 30 jours).`;
|
|
1628
|
+
return this.assemblePage({
|
|
1629
|
+
url: `${domain}/${path}`,
|
|
1630
|
+
title,
|
|
1631
|
+
description,
|
|
1632
|
+
h1,
|
|
1633
|
+
directAnswer,
|
|
1634
|
+
service,
|
|
1635
|
+
locale,
|
|
1636
|
+
path,
|
|
1637
|
+
t0
|
|
1638
|
+
});
|
|
1639
|
+
}
|
|
1603
1640
|
assemblePage(opts) {
|
|
1604
1641
|
const dict = getDictionary(opts.locale);
|
|
1605
1642
|
const domain = this.config.domain.replace(/\/$/, "");
|
package/dist/index.mjs
CHANGED
|
@@ -72,7 +72,7 @@ class ApiKeyGuardian {
|
|
|
72
72
|
maxDomains: 1,
|
|
73
73
|
monthlyCreditBudget: 500,
|
|
74
74
|
tokenManager: new TokenQuotaManager("starter"),
|
|
75
|
-
errorMessage: "Missing API Key. Please set
|
|
75
|
+
errorMessage: "Missing API Key. Please set LYNXSEO_API_KEY (or LYNXIO_API_KEY) or pass apiKey in config."
|
|
76
76
|
};
|
|
77
77
|
}
|
|
78
78
|
const cleanKey = apiKey.trim();
|
|
@@ -1307,7 +1307,7 @@ class LynxSeoEngine {
|
|
|
1307
1307
|
...config
|
|
1308
1308
|
};
|
|
1309
1309
|
this.servicesMap = new Map((config.services || []).map((s) => [s.slug, s]));
|
|
1310
|
-
const rawKey = config.apiKey || config.licenseKey || "";
|
|
1310
|
+
const rawKey = config.apiKey || config.licenseKey || typeof process !== "undefined" && process.env && (process.env.LYNXSEO_API_KEY || process.env.LYNXIO_API_KEY || process.env.LYNXFLOW_API_KEY) || "";
|
|
1311
1311
|
this.authStatus = ApiKeyGuardian.validate(rawKey, config.domain);
|
|
1312
1312
|
this.analytics = new LynxAnalyticsClient(rawKey);
|
|
1313
1313
|
this.serp = new SerpClient(rawKey);
|
|
@@ -1453,12 +1453,14 @@ class LynxSeoEngine {
|
|
|
1453
1453
|
return this.buildRolePage(service, remainder.replace(/-/g, " "), domain, t0, locale, cleanPath);
|
|
1454
1454
|
}
|
|
1455
1455
|
if ((pathParts[0] === "cas-usage" || pathParts[0] === "use-cases") && pathParts.length >= 2) {
|
|
1456
|
-
const
|
|
1457
|
-
|
|
1456
|
+
const raw = pathParts[1].replace(/-(pour|for)-/i, "-");
|
|
1457
|
+
const { service, remainder } = detectServiceFromSlug(raw);
|
|
1458
|
+
return this.buildUseCasePage(service, remainder.replace(/-/g, " "), domain, t0, locale, cleanPath);
|
|
1458
1459
|
}
|
|
1459
1460
|
if ((pathParts[0] === "outils" || pathParts[0] === "tools") && pathParts.length >= 2) {
|
|
1460
|
-
const
|
|
1461
|
-
|
|
1461
|
+
const raw = pathParts[1].replace(/^simulateur-roi-/i, "");
|
|
1462
|
+
const { service, remainder } = detectServiceFromSlug(raw);
|
|
1463
|
+
return this.buildToolPage(service, remainder.replace(/-/g, " "), domain, t0, locale, cleanPath);
|
|
1462
1464
|
}
|
|
1463
1465
|
return this.buildGeoPage(defaultService, "FR", "Paris", domain, t0, locale, cleanPath);
|
|
1464
1466
|
}
|
|
@@ -1600,6 +1602,41 @@ class LynxSeoEngine {
|
|
|
1600
1602
|
t0
|
|
1601
1603
|
});
|
|
1602
1604
|
}
|
|
1605
|
+
buildUseCasePage(service, topic, domain, t0, locale = "fr", path = `cas-usage/${topic.toLowerCase().replace(/\s+/g, "-")}`) {
|
|
1606
|
+
const topicName = topic.charAt(0).toUpperCase() + topic.slice(1);
|
|
1607
|
+
const title = `${service.name} pour ${topicName} — ${this.config.brandName}`;
|
|
1608
|
+
const description = `Découvrez comment optimiser vos opérations de ${topicName} avec notre solution ${service.name}. Déploiement en 10 minutes.`;
|
|
1609
|
+
const h1 = `${service.name} : ${topicName}`;
|
|
1610
|
+
const directAnswer = `${this.config.brandName} (${service.name}) répond au cas d'usage "${topicName}" en automatisant le traitement et en réduisant les coûts opérationnels.`;
|
|
1611
|
+
return this.assemblePage({
|
|
1612
|
+
url: `${domain}/${path}`,
|
|
1613
|
+
title,
|
|
1614
|
+
description,
|
|
1615
|
+
h1,
|
|
1616
|
+
directAnswer,
|
|
1617
|
+
service,
|
|
1618
|
+
locale,
|
|
1619
|
+
path,
|
|
1620
|
+
t0
|
|
1621
|
+
});
|
|
1622
|
+
}
|
|
1623
|
+
buildToolPage(service, tool, domain, t0, locale = "fr", path = `outils/${tool.toLowerCase().replace(/\s+/g, "-")}`) {
|
|
1624
|
+
const title = `Simulateur & Calculateur ROI : ${service.name} — ${this.config.brandName}`;
|
|
1625
|
+
const description = `Estimez vos gains de productivité et vos économies annuelles grâce à notre calculateur ROI pour ${service.name}.`;
|
|
1626
|
+
const h1 = `Simulateur de Rentabilité & ROI : ${service.name}`;
|
|
1627
|
+
const directAnswer = `Utilisez le simulateur ROI de ${this.config.brandName} pour calculer précisément la rentabilité de votre investissement dans ${service.name} (amortissement en moins de 30 jours).`;
|
|
1628
|
+
return this.assemblePage({
|
|
1629
|
+
url: `${domain}/${path}`,
|
|
1630
|
+
title,
|
|
1631
|
+
description,
|
|
1632
|
+
h1,
|
|
1633
|
+
directAnswer,
|
|
1634
|
+
service,
|
|
1635
|
+
locale,
|
|
1636
|
+
path,
|
|
1637
|
+
t0
|
|
1638
|
+
});
|
|
1639
|
+
}
|
|
1603
1640
|
assemblePage(opts) {
|
|
1604
1641
|
const dict = getDictionary(opts.locale);
|
|
1605
1642
|
const domain = this.config.domain.replace(/\/$/, "");
|
package/package.json
CHANGED
package/src/auth-key.ts
CHANGED
|
@@ -57,7 +57,7 @@ export class ApiKeyGuardian {
|
|
|
57
57
|
maxDomains: 1,
|
|
58
58
|
monthlyCreditBudget: 500,
|
|
59
59
|
tokenManager: new TokenQuotaManager("starter"),
|
|
60
|
-
errorMessage: "Missing API Key. Please set
|
|
60
|
+
errorMessage: "Missing API Key. Please set LYNXSEO_API_KEY (or LYNXIO_API_KEY) or pass apiKey in config.",
|
|
61
61
|
};
|
|
62
62
|
}
|
|
63
63
|
|
package/src/engine.ts
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 🦁 LynxSEO Engine (Lynxio) — Universal Programmatic SEO & AI Search Engine SDK
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
declare const process: any;
|
|
6
|
+
|
|
1
7
|
/**
|
|
2
8
|
* ⚡ LynxSEO High-Speed Universal Dynamic Page Engine (Internationalized)
|
|
3
9
|
*
|
|
@@ -53,7 +59,11 @@ export class LynxSeoEngine {
|
|
|
53
59
|
...config,
|
|
54
60
|
};
|
|
55
61
|
this.servicesMap = new Map((config.services || []).map((s) => [s.slug, s]));
|
|
56
|
-
const rawKey =
|
|
62
|
+
const rawKey =
|
|
63
|
+
config.apiKey ||
|
|
64
|
+
config.licenseKey ||
|
|
65
|
+
(typeof process !== "undefined" && process.env && (process.env.LYNXSEO_API_KEY || process.env.LYNXIO_API_KEY || process.env.LYNXFLOW_API_KEY)) ||
|
|
66
|
+
"";
|
|
57
67
|
this.authStatus = ApiKeyGuardian.validate(rawKey, config.domain);
|
|
58
68
|
|
|
59
69
|
this.analytics = new LynxAnalyticsClient(rawKey);
|
|
@@ -252,14 +262,16 @@ export class LynxSeoEngine {
|
|
|
252
262
|
|
|
253
263
|
// 7. Use Cases: /cas-usage/{topic}
|
|
254
264
|
if ((pathParts[0] === "cas-usage" || pathParts[0] === "use-cases") && pathParts.length >= 2) {
|
|
255
|
-
const
|
|
256
|
-
|
|
265
|
+
const raw = pathParts[1].replace(/-(pour|for)-/i, "-");
|
|
266
|
+
const { service, remainder } = detectServiceFromSlug(raw);
|
|
267
|
+
return this.buildUseCasePage(service, remainder.replace(/-/g, " "), domain, t0, locale, cleanPath);
|
|
257
268
|
}
|
|
258
269
|
|
|
259
270
|
// 8. Tools / Calculators: /outils/{tool}
|
|
260
271
|
if ((pathParts[0] === "outils" || pathParts[0] === "tools") && pathParts.length >= 2) {
|
|
261
|
-
const
|
|
262
|
-
|
|
272
|
+
const raw = pathParts[1].replace(/^simulateur-roi-/i, "");
|
|
273
|
+
const { service, remainder } = detectServiceFromSlug(raw);
|
|
274
|
+
return this.buildToolPage(service, remainder.replace(/-/g, " "), domain, t0, locale, cleanPath);
|
|
263
275
|
}
|
|
264
276
|
|
|
265
277
|
// Fallback: local geo
|
|
@@ -434,6 +446,45 @@ export class LynxSeoEngine {
|
|
|
434
446
|
});
|
|
435
447
|
}
|
|
436
448
|
|
|
449
|
+
private buildUseCasePage(service: LynxServiceDefinition, topic: string, domain: string, t0: number, locale = "fr", path = `cas-usage/${topic.toLowerCase().replace(/\s+/g, "-")}`): LynxResolvedPage {
|
|
450
|
+
const topicName = topic.charAt(0).toUpperCase() + topic.slice(1);
|
|
451
|
+
const title = `${service.name} pour ${topicName} — ${this.config.brandName}`;
|
|
452
|
+
const description = `Découvrez comment optimiser vos opérations de ${topicName} avec notre solution ${service.name}. Déploiement en 10 minutes.`;
|
|
453
|
+
const h1 = `${service.name} : ${topicName}`;
|
|
454
|
+
const directAnswer = `${this.config.brandName} (${service.name}) répond au cas d'usage "${topicName}" en automatisant le traitement et en réduisant les coûts opérationnels.`;
|
|
455
|
+
|
|
456
|
+
return this.assemblePage({
|
|
457
|
+
url: `${domain}/${path}`,
|
|
458
|
+
title,
|
|
459
|
+
description,
|
|
460
|
+
h1,
|
|
461
|
+
directAnswer,
|
|
462
|
+
service,
|
|
463
|
+
locale,
|
|
464
|
+
path,
|
|
465
|
+
t0,
|
|
466
|
+
});
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
private buildToolPage(service: LynxServiceDefinition, tool: string, domain: string, t0: number, locale = "fr", path = `outils/${tool.toLowerCase().replace(/\s+/g, "-")}`): LynxResolvedPage {
|
|
470
|
+
const title = `Simulateur & Calculateur ROI : ${service.name} — ${this.config.brandName}`;
|
|
471
|
+
const description = `Estimez vos gains de productivité et vos économies annuelles grâce à notre calculateur ROI pour ${service.name}.`;
|
|
472
|
+
const h1 = `Simulateur de Rentabilité & ROI : ${service.name}`;
|
|
473
|
+
const directAnswer = `Utilisez le simulateur ROI de ${this.config.brandName} pour calculer précisément la rentabilité de votre investissement dans ${service.name} (amortissement en moins de 30 jours).`;
|
|
474
|
+
|
|
475
|
+
return this.assemblePage({
|
|
476
|
+
url: `${domain}/${path}`,
|
|
477
|
+
title,
|
|
478
|
+
description,
|
|
479
|
+
h1,
|
|
480
|
+
directAnswer,
|
|
481
|
+
service,
|
|
482
|
+
locale,
|
|
483
|
+
path,
|
|
484
|
+
t0,
|
|
485
|
+
});
|
|
486
|
+
}
|
|
487
|
+
|
|
437
488
|
private assemblePage(opts: { url: string; title: string; description: string; h1: string; directAnswer: string; service: LynxServiceDefinition; locale: string; path: string; t0: number }): LynxResolvedPage {
|
|
438
489
|
const dict = getDictionary(opts.locale);
|
|
439
490
|
const domain = this.config.domain.replace(/\/$/, "");
|