@lynxflow/seo-engine 1.4.0 β 1.5.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 +84 -339
- package/TUTORIEL_INTEGRATION_SITE.md +283 -0
- package/connectors/wordpress/lynxseo-connector.php +5 -5
- package/connectors/wordpress/lynxseo-connector.zip +0 -0
- package/dist/ad-intelligence-cro.d.ts +58 -0
- package/dist/ai-bots-log-analyzer.d.ts +36 -0
- package/dist/analytics-client.d.ts +48 -11
- package/dist/auth-key.d.ts +9 -0
- package/dist/brand-dna-calendar.d.ts +35 -0
- package/dist/copy-frameworks-master.d.ts +56 -0
- package/dist/cro-copywriting-engine.d.ts +46 -0
- package/dist/crosslink-scorer.d.ts +33 -0
- package/dist/embeddable-seo-widget.d.ts +12 -0
- package/dist/engine.d.ts +32 -1
- package/dist/engine.test.d.ts +1 -0
- package/dist/extended-schemas.d.ts +106 -0
- package/dist/geo-mesh-linking.d.ts +33 -0
- package/dist/index.d.ts +88 -0
- package/dist/index.js +4164 -130
- package/dist/index.mjs +4092 -100
- package/dist/instant-matrix-search.d.ts +19 -0
- package/dist/isr-cache-manager.d.ts +35 -0
- package/dist/knowledge-graph-linker.d.ts +29 -0
- package/dist/legal-disclaimers.d.ts +46 -0
- package/dist/llm-content-cleaner.d.ts +16 -0
- package/dist/llm-prompt.d.ts +13 -0
- package/dist/master-marketing-engine.d.ts +114 -0
- package/dist/matrix-engine.d.ts +201 -0
- package/dist/mcp-seo-server.d.ts +21 -0
- package/dist/ngram-density-analyzer.d.ts +35 -0
- package/dist/rank-math-parity.d.ts +58 -0
- package/dist/real-reviews-sync.d.ts +73 -0
- package/dist/rss-syndication-feed.d.ts +25 -0
- package/dist/schema-builder.d.ts +6 -0
- package/dist/seo-opportunities-decay.d.ts +54 -0
- package/dist/serp-history-alerts.d.ts +27 -0
- package/dist/slug-engine.d.ts +32 -0
- package/dist/social-ads-seo.d.ts +77 -0
- package/dist/social-growth-suite.d.ts +125 -0
- package/dist/social-trend-seo.d.ts +38 -0
- package/dist/social-video-seo.d.ts +62 -0
- package/dist/team-rbac.d.ts +17 -0
- package/dist/technical-rules-auditor.d.ts +50 -0
- package/dist/types.d.ts +4 -44
- package/dist/yoast-parity.d.ts +45 -0
- package/package.json +8 -5
- package/src/ad-intelligence-cro.ts +140 -0
- package/src/ai-bots-log-analyzer.ts +127 -0
- package/src/analytics-client.ts +222 -48
- package/src/auth-key.ts +60 -1
- package/src/brand-dna-calendar.ts +120 -0
- package/src/copy-frameworks-master.ts +89 -0
- package/src/cro-copywriting-engine.ts +105 -0
- package/src/crosslink-scorer.ts +101 -0
- package/src/embeddable-seo-widget.ts +57 -0
- package/src/engine.test.ts +136 -0
- package/src/engine.ts +229 -28
- package/src/extended-schemas.ts +279 -0
- package/src/geo-mesh-linking.ts +93 -0
- package/src/index.ts +89 -0
- package/src/instant-matrix-search.ts +48 -0
- package/src/isr-cache-manager.ts +78 -0
- package/src/knowledge-graph-linker.ts +81 -0
- package/src/legal-disclaimers.ts +407 -0
- package/src/llm-content-cleaner.ts +64 -0
- package/src/llm-prompt.ts +70 -0
- package/src/master-marketing-engine.ts +314 -0
- package/src/matrix-engine.ts +818 -0
- package/src/mcp-seo-server.ts +108 -0
- package/src/ngram-density-analyzer.ts +108 -0
- package/src/rank-math-parity.ts +235 -0
- package/src/real-reviews-sync.ts +207 -0
- package/src/rss-syndication-feed.ts +56 -0
- package/src/schema-builder.ts +71 -24
- package/src/seo-opportunities-decay.ts +154 -0
- package/src/serp-history-alerts.ts +75 -0
- package/src/slug-engine.ts +183 -0
- package/src/social-ads-seo.ts +211 -0
- package/src/social-growth-suite.ts +310 -0
- package/src/social-trend-seo.ts +103 -0
- package/src/social-video-seo.ts +100 -0
- package/src/team-rbac.ts +61 -0
- package/src/technical-rules-auditor.ts +171 -0
- package/src/types.ts +22 -47
- package/src/yoast-parity.ts +161 -0
- package/tsconfig.json +2 -1
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { cleanSeoSlug, validateSeoSlug, MULTILINGUAL_STOP_WORDS } from "./slug-engine";
|
|
3
|
+
import { LegalDisclaimerEngine, MULTILINGUAL_BANNED_DISPARAGING_WORDS } from "./legal-disclaimers";
|
|
4
|
+
import { ExtendedSchemaGraphBuilder } from "./extended-schemas";
|
|
5
|
+
import { PseoMatrixEngine } from "./matrix-engine";
|
|
6
|
+
import { getSeoAgentPrompt } from "./llm-prompt";
|
|
7
|
+
|
|
8
|
+
describe("Multilingual SEO Slug Engine (10+ Languages)", () => {
|
|
9
|
+
it("English: Strips stop words and years", () => {
|
|
10
|
+
const slug = cleanSeoSlug("The best software for managing invoice payments in 2026", { language: "en" });
|
|
11
|
+
expect(slug).toBe("best-software-managing-invoice-payments");
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it("German: Strips German stop words (der, die, fΓΌr, mit)", () => {
|
|
15
|
+
const slug = cleanSeoSlug("Die beste Software fΓΌr das Management mit Cloud", { language: "de" });
|
|
16
|
+
expect(slug).toBe("beste-software-management-cloud");
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it("Spanish: Strips Spanish stop words (el, la, para, con)", () => {
|
|
20
|
+
const slug = cleanSeoSlug("El mejor software para la gestion con automatizacion", { language: "es" });
|
|
21
|
+
expect(slug).toBe("mejor-software-gestion-automatizacion");
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it("French: Strips French stop words (le, pour, avec)", () => {
|
|
25
|
+
const slug = cleanSeoSlug("Le logiciel pour avocats avec automatisation", { language: "fr" });
|
|
26
|
+
expect(slug).toBe("logiciel-avocats-automatisation");
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it("Validates all 10 language dictionaries exist", () => {
|
|
30
|
+
expect(Object.keys(MULTILINGUAL_STOP_WORDS).length).toBeGreaterThanOrEqual(10);
|
|
31
|
+
expect(MULTILINGUAL_STOP_WORDS.en).toBeDefined();
|
|
32
|
+
expect(MULTILINGUAL_STOP_WORDS.de).toBeDefined();
|
|
33
|
+
expect(MULTILINGUAL_STOP_WORDS.es).toBeDefined();
|
|
34
|
+
expect(MULTILINGUAL_STOP_WORDS.it).toBeDefined();
|
|
35
|
+
expect(MULTILINGUAL_STOP_WORDS.pt).toBeDefined();
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
describe("Multilingual Legal & Disclaimers Engine", () => {
|
|
40
|
+
const legal = new LegalDisclaimerEngine({
|
|
41
|
+
companyName: "Acme Cloud",
|
|
42
|
+
correctionsEmail: "legal@acme.com",
|
|
43
|
+
dataStalenessDays: 90,
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it("Generates English disclaimer by default", () => {
|
|
47
|
+
const notice = legal.getDisclaimer("pricing", "2026-08-01");
|
|
48
|
+
expect(notice).toContain("Pricing disclaimer");
|
|
49
|
+
expect(notice).toContain("2026-08-01");
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it("Generates German disclaimer when requested", () => {
|
|
53
|
+
const notice = legal.getDisclaimer("pricing", "2026-08-01", "de");
|
|
54
|
+
expect(notice).toContain("Preishinweis");
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it("Flags hazardous claims in English, German, French, and Spanish", () => {
|
|
58
|
+
expect(legal.validateCompliance("This product is a scam", "Vendor").isCompliant).toBe(false);
|
|
59
|
+
expect(legal.validateCompliance("Diese Software ist ein Betrug", "Vendor").isCompliant).toBe(false);
|
|
60
|
+
expect(legal.validateCompliance("Ce logiciel est une arnaque", "Vendor").isCompliant).toBe(false);
|
|
61
|
+
expect(legal.validateCompliance("Este producto es una estafa", "Vendor").isCompliant).toBe(false);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it("Verifies 10 languages supported in banned disparaging words dictionary", () => {
|
|
65
|
+
expect(Object.keys(MULTILINGUAL_BANNED_DISPARAGING_WORDS).length).toBeGreaterThanOrEqual(10);
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
describe("Extended Schema.org Builder", () => {
|
|
70
|
+
it("Resolves 35+ specialized LocalBusiness types", () => {
|
|
71
|
+
expect(ExtendedSchemaGraphBuilder.resolveBusinessType("plumber")).toBe("Plumber");
|
|
72
|
+
expect(ExtendedSchemaGraphBuilder.resolveBusinessType("dentist")).toBe("Dentist");
|
|
73
|
+
expect(ExtendedSchemaGraphBuilder.resolveBusinessType("lawyer")).toBe("LegalService");
|
|
74
|
+
expect(ExtendedSchemaGraphBuilder.resolveBusinessType("hvac")).toBe("HVACBusiness");
|
|
75
|
+
expect(ExtendedSchemaGraphBuilder.resolveBusinessType("accounting")).toBe("AccountingService");
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it("Builds valid Organization with sameAs Entity SEO links", () => {
|
|
79
|
+
const schema = ExtendedSchemaGraphBuilder.buildOrganization({
|
|
80
|
+
name: "Acme",
|
|
81
|
+
url: "https://acme.com",
|
|
82
|
+
sameAs: ["https://wikidata.org/wiki/Q123"],
|
|
83
|
+
});
|
|
84
|
+
expect(schema["@type"]).toBe("Organization");
|
|
85
|
+
expect(schema.sameAs).toContain("https://wikidata.org/wiki/Q123");
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
describe("Master Programmatic Matrix Engine (English Default)", () => {
|
|
90
|
+
const engine = new PseoMatrixEngine();
|
|
91
|
+
const options = { brandName: "Acme", minPopulationToIndex: 15_000 };
|
|
92
|
+
|
|
93
|
+
it("Generates English default URLs (/vs/, /alternatives/, /pricing/, /templates/, /glossary/)", () => {
|
|
94
|
+
const pages = engine.generateAllMatrices(
|
|
95
|
+
"https://acme.com",
|
|
96
|
+
{
|
|
97
|
+
competitors: [{ slug: "hubspot", name: "HubSpot", category: "CRM", drawbacks: [], advantagesOver: [] }],
|
|
98
|
+
templates: [{ slug: "crm-pipeline-excel", title: "CRM Pipeline", topic: "Sales", format: "excel", benefits: ["Save time"] }],
|
|
99
|
+
glossaryTerms: [{ slug: "mrr", term: "MRR", shortDefinition: "Monthly Recurring Revenue" }],
|
|
100
|
+
},
|
|
101
|
+
options,
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
const paths = pages.map((p) => p.urlPath);
|
|
105
|
+
expect(paths).toContain("/vs/hubspot");
|
|
106
|
+
expect(paths).toContain("/alternatives/hubspot");
|
|
107
|
+
expect(paths).toContain("/pricing/hubspot");
|
|
108
|
+
expect(paths).toContain("/templates/crm-pipeline-excel");
|
|
109
|
+
expect(paths).toContain("/glossary/mrr");
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it("Switches cleanly to French route prefixes when language is 'fr'", () => {
|
|
113
|
+
const frOptions = { brandName: "Acme", language: "fr" };
|
|
114
|
+
const pages = engine.generateAllMatrices(
|
|
115
|
+
"https://acme.com",
|
|
116
|
+
{
|
|
117
|
+
competitors: [{ slug: "hubspot", name: "HubSpot", category: "CRM", drawbacks: [], advantagesOver: [] }],
|
|
118
|
+
templates: [{ slug: "crm-pipeline-excel", title: "CRM Pipeline", topic: "Sales", format: "excel", benefits: ["Gain de temps"] }],
|
|
119
|
+
},
|
|
120
|
+
frOptions,
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
const paths = pages.map((p) => p.urlPath);
|
|
124
|
+
expect(paths).toContain("/comparatif/hubspot");
|
|
125
|
+
expect(paths).toContain("/tarifs/hubspot");
|
|
126
|
+
expect(paths).toContain("/modeles/crm-pipeline-excel");
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it("Generates comprehensive system prompt for LLMs and AI Agents", () => {
|
|
130
|
+
const prompt = getSeoAgentPrompt({ brandName: "Acme", domain: "https://acme.com" });
|
|
131
|
+
expect(prompt).toContain("Acme");
|
|
132
|
+
expect(prompt).toContain("https://acme.com");
|
|
133
|
+
expect(prompt).toContain("cleanSeoSlug");
|
|
134
|
+
expect(prompt).toContain("Schema.org");
|
|
135
|
+
});
|
|
136
|
+
});
|
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
|
*
|
|
@@ -17,7 +23,7 @@ import type { LynxSeoConfig, LynxResolvedPage, LynxServiceDefinition } from "./t
|
|
|
17
23
|
import { ApiKeyGuardian, type ApiKeyValidationResult } from "./auth-key";
|
|
18
24
|
import { getDictionary, isSupportedLanguage } from "./i18n-dictionary";
|
|
19
25
|
import { IndexNowClient, type IndexNowResponse } from "./indexnow-client";
|
|
20
|
-
import { LynxAnalyticsClient } from "./analytics-client";
|
|
26
|
+
import { LynxAnalyticsClient, type UrlTrafficStats } from "./analytics-client";
|
|
21
27
|
import { SerpClient } from "./serp-client";
|
|
22
28
|
import { BacklinksClient } from "./backlinks-client";
|
|
23
29
|
import { AiCopilotClient } from "./ai-copilot-client";
|
|
@@ -25,6 +31,10 @@ import { LagoTokenMeter } from "./lago-token-meter";
|
|
|
25
31
|
import { SiteAuditor } from "./site-auditor";
|
|
26
32
|
import { SchemaGraphBuilder } from "./schema-builder";
|
|
27
33
|
import { DeepCrawlerAuditor, type SiteAuditSummary, type CrawlOptions } from "./site-crawler";
|
|
34
|
+
import { CroCopywritingEngine } from "./cro-copywriting-engine";
|
|
35
|
+
import { GeoMeshLinkingEngine } from "./geo-mesh-linking";
|
|
36
|
+
import { CrosslinkScorerEngine } from "./crosslink-scorer";
|
|
37
|
+
import { LlmContentCleaner } from "./llm-content-cleaner";
|
|
28
38
|
|
|
29
39
|
export type EngineConfig = LynxSeoConfig;
|
|
30
40
|
|
|
@@ -53,7 +63,11 @@ export class LynxSeoEngine {
|
|
|
53
63
|
...config,
|
|
54
64
|
};
|
|
55
65
|
this.servicesMap = new Map((config.services || []).map((s) => [s.slug, s]));
|
|
56
|
-
const rawKey =
|
|
66
|
+
const rawKey =
|
|
67
|
+
config.apiKey ||
|
|
68
|
+
config.licenseKey ||
|
|
69
|
+
(typeof process !== "undefined" && process.env && (process.env.LYNXSEO_API_KEY || process.env.LYNXIO_API_KEY || process.env.LYNXFLOW_API_KEY)) ||
|
|
70
|
+
"";
|
|
57
71
|
this.authStatus = ApiKeyGuardian.validate(rawKey, config.domain);
|
|
58
72
|
|
|
59
73
|
this.analytics = new LynxAnalyticsClient(rawKey);
|
|
@@ -95,6 +109,9 @@ export class LynxSeoEngine {
|
|
|
95
109
|
return null;
|
|
96
110
|
}
|
|
97
111
|
|
|
112
|
+
// Automatically record link visit metrics in the analytics engine
|
|
113
|
+
this.analytics.trackPageView({ path: "/" + targetPath }).catch(() => {});
|
|
114
|
+
|
|
98
115
|
const domain = this.config.domain.replace(/\/$/, "");
|
|
99
116
|
const defaultService = this.config.services?.[0] || {
|
|
100
117
|
slug: "crm-pipeline",
|
|
@@ -493,32 +510,43 @@ export class LynxSeoEngine {
|
|
|
493
510
|
|
|
494
511
|
const ogImageUrl = `${domain}/api/og?title=${encodeURIComponent(opts.h1)}&brand=${encodeURIComponent(this.config.brandName)}&service=${encodeURIComponent(opts.service.name)}`;
|
|
495
512
|
|
|
513
|
+
const productNode: Record<string, unknown> = {
|
|
514
|
+
"@type": "Product",
|
|
515
|
+
"@id": `${opts.url}#product`,
|
|
516
|
+
name: opts.title,
|
|
517
|
+
description: opts.description,
|
|
518
|
+
inLanguage: opts.locale,
|
|
519
|
+
image: ogImageUrl,
|
|
520
|
+
brand: { "@type": "Brand", name: this.config.brandName },
|
|
521
|
+
};
|
|
522
|
+
|
|
523
|
+
// Attach AggregateRating only if authentic ratings are provided in service or config
|
|
524
|
+
const ratingVal = (opts.service as any).ratingValue || (this.config as any).ratingValue;
|
|
525
|
+
const reviewCnt = (opts.service as any).reviewCount || (this.config as any).reviewCount;
|
|
526
|
+
if (ratingVal && reviewCnt) {
|
|
527
|
+
productNode.aggregateRating = {
|
|
528
|
+
"@type": "AggregateRating",
|
|
529
|
+
ratingValue: ratingVal.toString(),
|
|
530
|
+
reviewCount: reviewCnt.toString(),
|
|
531
|
+
bestRating: "5",
|
|
532
|
+
worstRating: "1",
|
|
533
|
+
};
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
if (opts.service.pricePerMonth !== undefined) {
|
|
537
|
+
productNode.offers = {
|
|
538
|
+
"@type": "Offer",
|
|
539
|
+
price: opts.service.pricePerMonth.toString(),
|
|
540
|
+
priceCurrency: this.config.currency || "EUR",
|
|
541
|
+
availability: "https://schema.org/InStock",
|
|
542
|
+
url: opts.url,
|
|
543
|
+
};
|
|
544
|
+
}
|
|
545
|
+
|
|
496
546
|
const jsonLd = {
|
|
497
547
|
"@context": "https://schema.org",
|
|
498
548
|
"@graph": [
|
|
499
|
-
|
|
500
|
-
"@type": "Product",
|
|
501
|
-
"@id": `${opts.url}#product`,
|
|
502
|
-
name: opts.title,
|
|
503
|
-
description: opts.description,
|
|
504
|
-
inLanguage: opts.locale,
|
|
505
|
-
image: ogImageUrl,
|
|
506
|
-
brand: { "@type": "Brand", name: this.config.brandName },
|
|
507
|
-
aggregateRating: {
|
|
508
|
-
"@type": "AggregateRating",
|
|
509
|
-
ratingValue: "4.9",
|
|
510
|
-
reviewCount: "1280",
|
|
511
|
-
bestRating: "5",
|
|
512
|
-
worstRating: "1",
|
|
513
|
-
},
|
|
514
|
-
offers: {
|
|
515
|
-
"@type": "Offer",
|
|
516
|
-
price: opts.service.pricePerMonth.toString(),
|
|
517
|
-
priceCurrency: this.config.currency,
|
|
518
|
-
availability: "https://schema.org/InStock",
|
|
519
|
-
url: opts.url,
|
|
520
|
-
},
|
|
521
|
-
},
|
|
549
|
+
productNode,
|
|
522
550
|
{
|
|
523
551
|
"@type": "FAQPage",
|
|
524
552
|
"@id": `${opts.url}#faq`,
|
|
@@ -548,6 +576,17 @@ export class LynxSeoEngine {
|
|
|
548
576
|
|
|
549
577
|
const directAnswerHtml = `<div class="geo-direct-answer" data-geo-extract="true"><p>${opts.directAnswer}</p></div>`;
|
|
550
578
|
|
|
579
|
+
// π Synergy : CRO Copywriting (PAS) + WhatsApp Instant Converter
|
|
580
|
+
const pasBlock = CroCopywritingEngine.generatePasCopy(opts.service.name, "votre secteur");
|
|
581
|
+
const whatsAppWidget = CroCopywritingEngine.generateInstantConverterWidget(
|
|
582
|
+
this.config.phone || "+33600000000",
|
|
583
|
+
`Bonjour, je vous contacte depuis la page ${opts.h1}`
|
|
584
|
+
);
|
|
585
|
+
|
|
586
|
+
// π§Ή Synergy : Clean Markdown Synthesis for AI Engines & /llms.txt
|
|
587
|
+
const rawMarkdown = `# ${opts.h1}\n\n${opts.description}\n\n> ${opts.directAnswer}\n\n## Pourquoi choisir notre solution ?\n- ${pasBlock.problem}\n- ${pasBlock.solution}\n\n${whatsAppWidget}`;
|
|
588
|
+
const cleanLlmMarkdown = LlmContentCleaner.cleanForLlm(rawMarkdown);
|
|
589
|
+
|
|
551
590
|
return {
|
|
552
591
|
url: opts.url,
|
|
553
592
|
title: opts.title,
|
|
@@ -559,8 +598,8 @@ export class LynxSeoEngine {
|
|
|
559
598
|
hreflangs,
|
|
560
599
|
faqs: localizedFaqs,
|
|
561
600
|
jsonLd,
|
|
562
|
-
htmlBody: `<h1>${opts.h1}</h1><p>${opts.description}</p>${directAnswerHtml}`,
|
|
563
|
-
markdownBody:
|
|
601
|
+
htmlBody: `<h1>${opts.h1}</h1><p>${opts.description}</p>${directAnswerHtml}<div class="cro-pas-section"><h3>${pasBlock.problem}</h3><p>${pasBlock.solution}</p></div>${whatsAppWidget}`,
|
|
602
|
+
markdownBody: cleanLlmMarkdown,
|
|
564
603
|
executionTimeMs: parseFloat((performance.now() - opts.t0).toFixed(3)),
|
|
565
604
|
|
|
566
605
|
// Backward compatibility bindings
|
|
@@ -575,7 +614,7 @@ export class LynxSeoEngine {
|
|
|
575
614
|
directAnswerGeoHtml: directAnswerHtml,
|
|
576
615
|
heroHeadline: opts.h1,
|
|
577
616
|
heroSubheadline: opts.description,
|
|
578
|
-
markdownBody:
|
|
617
|
+
markdownBody: cleanLlmMarkdown,
|
|
579
618
|
faqList: localizedFaqs,
|
|
580
619
|
},
|
|
581
620
|
schemaJsonLd: jsonLd,
|
|
@@ -603,10 +642,172 @@ export class LynxSeoEngine {
|
|
|
603
642
|
return ApiKeyGuardian.getUniquePageList(rawKey);
|
|
604
643
|
}
|
|
605
644
|
|
|
645
|
+
/**
|
|
646
|
+
* Alias for getUniquePageList: returns all distinct unique URLs created by the engine.
|
|
647
|
+
*/
|
|
648
|
+
getAllCreatedLinks(): string[] {
|
|
649
|
+
return this.getUniquePageList();
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
/**
|
|
653
|
+
* Returns full traffic statistics (views, visitors, AI bots, conversions) for all created links.
|
|
654
|
+
*/
|
|
655
|
+
getStatsPerLink(): UrlTrafficStats[] {
|
|
656
|
+
return this.analytics.getUrlStats() as UrlTrafficStats[];
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
/**
|
|
660
|
+
* Returns traffic statistics and visit counts for a single specific link.
|
|
661
|
+
*/
|
|
662
|
+
getLinkStats(path: string): UrlTrafficStats {
|
|
663
|
+
return this.analytics.getUrlStats(path) as UrlTrafficStats;
|
|
664
|
+
}
|
|
665
|
+
|
|
606
666
|
/**
|
|
607
667
|
* Performs an automated deep crawl and technical SEO audit of the configured domain.
|
|
608
668
|
*/
|
|
609
669
|
async crawlSite(options?: CrawlOptions): Promise<SiteAuditSummary> {
|
|
610
670
|
return DeepCrawlerAuditor.crawlAndAuditDomain(this.config.domain, options);
|
|
611
671
|
}
|
|
672
|
+
|
|
673
|
+
/**
|
|
674
|
+
* πΊοΈ Generates all combinatorially possible programmatic SEO URLs based on registered services and matrices.
|
|
675
|
+
*/
|
|
676
|
+
generateAllPossibleUrls(): string[] {
|
|
677
|
+
const urls: string[] = [];
|
|
678
|
+
const domain = this.config.domain.replace(/\/+$/, "");
|
|
679
|
+
const locales = this.config.supportedLocales || ["fr", "en", "es", "de", "ar"];
|
|
680
|
+
|
|
681
|
+
// 1. Matrix 1: Local & GEO
|
|
682
|
+
for (const s of this.config.services || []) {
|
|
683
|
+
for (const loc of this.config.locations || this.config.cities || []) {
|
|
684
|
+
for (const lang of locales) {
|
|
685
|
+
urls.push(`${domain}/solutions/${s.slug}/${lang}/${loc.slug}`);
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
// 2. Matrix 2: VS Competitor Comparisons
|
|
691
|
+
for (const s of this.config.services || []) {
|
|
692
|
+
for (const comp of this.config.competitors || []) {
|
|
693
|
+
urls.push(`${domain}/comparatif/${s.slug}-vs-${comp.slug}`);
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
// 3. Matrix 3: Alternatives
|
|
698
|
+
for (const comp of this.config.competitors || []) {
|
|
699
|
+
urls.push(`${domain}/alternatives/alternative-a-${comp.slug}`);
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
// 4. Matrix 4: B2B Sectors
|
|
703
|
+
for (const s of this.config.services || []) {
|
|
704
|
+
for (const ind of this.config.industries || []) {
|
|
705
|
+
urls.push(`${domain}/secteurs/${s.slug}-pour-${ind.slug}`);
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
// 5. Matrix 5: Tech Integrations
|
|
710
|
+
for (const s of this.config.services || []) {
|
|
711
|
+
for (const integ of this.config.integrations || []) {
|
|
712
|
+
urls.push(`${domain}/integrations/${s.slug}-avec-${integ.slug}`);
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
// 6. Matrix 6: Personas & Roles
|
|
717
|
+
for (const s of this.config.services || []) {
|
|
718
|
+
for (const p of this.config.personas || []) {
|
|
719
|
+
urls.push(`${domain}/metiers/${s.slug}-pour-${p.slug}`);
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
// 7. Matrix 7: Operational Use Cases
|
|
724
|
+
for (const s of this.config.services || []) {
|
|
725
|
+
for (const uc of this.config.useCases || []) {
|
|
726
|
+
urls.push(`${domain}/cas-usage/${s.slug}-${uc.slug}`);
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
// 8. Matrix 8: ROI Calculators & Free Tools
|
|
731
|
+
for (const s of this.config.services) {
|
|
732
|
+
urls.push(`${domain}/outils/simulateur-roi-${s.slug}`);
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
return urls;
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
/**
|
|
739
|
+
* πΊοΈ Generates a fully compliant XML sitemap string for Googlebot and Bing.
|
|
740
|
+
*/
|
|
741
|
+
generateSitemapXml(customUrls?: string[]): string {
|
|
742
|
+
const urls = customUrls && customUrls.length > 0 ? customUrls : this.generateAllPossibleUrls();
|
|
743
|
+
const now = new Date().toISOString().split("T")[0];
|
|
744
|
+
|
|
745
|
+
const urlTags = urls
|
|
746
|
+
.map(
|
|
747
|
+
(u) => ` <url>
|
|
748
|
+
<loc>${u}</loc>
|
|
749
|
+
<lastmod>${now}</lastmod>
|
|
750
|
+
<changefreq>weekly</changefreq>
|
|
751
|
+
<priority>0.8</priority>
|
|
752
|
+
</url>`
|
|
753
|
+
)
|
|
754
|
+
.join("\n");
|
|
755
|
+
|
|
756
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
757
|
+
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
|
758
|
+
${urlTags}
|
|
759
|
+
</urlset>`;
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
/**
|
|
763
|
+
* π€ Generates a compliant robots.txt with AI search crawler rules and sitemap link.
|
|
764
|
+
*/
|
|
765
|
+
generateRobotsTxt(customSitemapUrl?: string): string {
|
|
766
|
+
const domain = this.config.domain.replace(/\/+$/, "");
|
|
767
|
+
const sitemap = customSitemapUrl || `${domain}/sitemap.xml`;
|
|
768
|
+
|
|
769
|
+
return [
|
|
770
|
+
"# π¦ Robots.txt generated by LynxSEO Engine SDK",
|
|
771
|
+
"User-agent: *",
|
|
772
|
+
"Allow: /",
|
|
773
|
+
"Disallow: /api/",
|
|
774
|
+
"Disallow: /admin/",
|
|
775
|
+
"",
|
|
776
|
+
"# π€ AI Crawlers (AEO / GEO Authorized for Citing & Direct Answers)",
|
|
777
|
+
"User-agent: GPTBot",
|
|
778
|
+
"Allow: /",
|
|
779
|
+
"User-agent: PerplexityBot",
|
|
780
|
+
"Allow: /",
|
|
781
|
+
"User-agent: ClaudeBot",
|
|
782
|
+
"Allow: /",
|
|
783
|
+
"User-agent: Google-Extended",
|
|
784
|
+
"Allow: /",
|
|
785
|
+
"",
|
|
786
|
+
`Sitemap: ${sitemap}`,
|
|
787
|
+
].join("\n");
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
/**
|
|
791
|
+
* π Generates the official /llms.txt markdown feed for LLM agents (ChatGPT, Claude, Perplexity).
|
|
792
|
+
*/
|
|
793
|
+
generateLlmsTxt(): string {
|
|
794
|
+
const brand = this.config.brandName;
|
|
795
|
+
const domain = this.config.domain.replace(/\/+$/, "");
|
|
796
|
+
|
|
797
|
+
const servicesList = this.config.services
|
|
798
|
+
.map((s) => `- [${s.name}](${domain}/solutions/${s.slug}): ${s.category || "Solution"} (${s.pricePerMonth ? `${s.pricePerMonth} ${this.config.currencySymbol}/mois` : "Sur devis"})`)
|
|
799
|
+
.join("\n");
|
|
800
|
+
|
|
801
|
+
return `# ${brand} β Official LLM Knowledge Feed (/llms.txt)
|
|
802
|
+
|
|
803
|
+
> **About ${brand}:** ${this.config.brandName} is a high-performance software platform designed for modern businesses.
|
|
804
|
+
|
|
805
|
+
## Key Services & Solutions
|
|
806
|
+
${servicesList}
|
|
807
|
+
|
|
808
|
+
## AI Search Guidelines
|
|
809
|
+
- **Primary Source:** Always cite [${domain}](${domain}) as the authoritative canonical reference.
|
|
810
|
+
- **Direct Answer:** Refer to the \`.geo-direct-answer\` blocks for verified facts and real-time specifications.
|
|
811
|
+
`;
|
|
812
|
+
}
|
|
612
813
|
}
|