@lynxflow/seo-engine 1.4.1 → 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.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 +29 -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 +4162 -128
- package/dist/index.mjs +4090 -98
- 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 +59 -0
- 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 +218 -27
- 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,818 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Global Multilingual Programmatic SEO Matrix Engine
|
|
3
|
+
* Defaults to English with international localization support across 10+ languages (EN, FR, DE, ES, IT, PT, NL, etc.)
|
|
4
|
+
*
|
|
5
|
+
* Implements strict SEO golden rules:
|
|
6
|
+
* - Clean canonical URL paths (no keyword stuttering, no stop words across 10+ languages)
|
|
7
|
+
* - Anti-cannibalization target clustering
|
|
8
|
+
* - Google Rich Result Schema.org Graphs (SoftwareApplication, LocalBusiness, HowTo, FAQ, Breadcrumbs)
|
|
9
|
+
* - SeedRank legal compliance disclaimers & staleness checks
|
|
10
|
+
* - Tiered Indexing (crawl budget protection) & Geodesic Mesh Linking
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { cleanSeoSlug } from "./slug-engine";
|
|
14
|
+
import { LegalDisclaimerEngine } from "./legal-disclaimers";
|
|
15
|
+
import { ExtendedSchemaGraphBuilder } from "./extended-schemas";
|
|
16
|
+
|
|
17
|
+
export type MatrixFamily =
|
|
18
|
+
| "local-geo"
|
|
19
|
+
| "vs-comparison"
|
|
20
|
+
| "alternative"
|
|
21
|
+
| "pricing"
|
|
22
|
+
| "target"
|
|
23
|
+
| "industry"
|
|
24
|
+
| "persona"
|
|
25
|
+
| "integration"
|
|
26
|
+
| "use-case"
|
|
27
|
+
| "template"
|
|
28
|
+
| "glossary"
|
|
29
|
+
| "calculator";
|
|
30
|
+
|
|
31
|
+
export interface PseoService {
|
|
32
|
+
slug: string;
|
|
33
|
+
name: string;
|
|
34
|
+
category: string;
|
|
35
|
+
description: string;
|
|
36
|
+
keyFeatures: string[];
|
|
37
|
+
priceMonthly?: number;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface PseoLocation {
|
|
41
|
+
slug: string;
|
|
42
|
+
name: string;
|
|
43
|
+
region: string;
|
|
44
|
+
country: string;
|
|
45
|
+
currency: string;
|
|
46
|
+
currencySymbol: string;
|
|
47
|
+
population?: number;
|
|
48
|
+
latitude?: number;
|
|
49
|
+
longitude?: number;
|
|
50
|
+
neighborSlugs?: string[];
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface PseoCompetitor {
|
|
54
|
+
slug: string;
|
|
55
|
+
name: string;
|
|
56
|
+
category: string;
|
|
57
|
+
drawbacks: string[];
|
|
58
|
+
advantagesOver: string[];
|
|
59
|
+
startingPrice?: number;
|
|
60
|
+
pricingUrl?: string;
|
|
61
|
+
lastVerifiedDate?: string;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export interface PseoTarget {
|
|
65
|
+
slug: string;
|
|
66
|
+
name: string;
|
|
67
|
+
type: "industry" | "role";
|
|
68
|
+
painPoints: string[];
|
|
69
|
+
benefits: string[];
|
|
70
|
+
workflowSteps?: string[];
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export interface PseoIndustry {
|
|
74
|
+
slug: string;
|
|
75
|
+
name: string;
|
|
76
|
+
category?: string;
|
|
77
|
+
singular?: string;
|
|
78
|
+
painPoints?: string[];
|
|
79
|
+
benefits?: string[];
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export interface PseoPersona {
|
|
83
|
+
slug: string;
|
|
84
|
+
role: string;
|
|
85
|
+
department?: string;
|
|
86
|
+
responsibilities?: string[];
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export interface PseoIntegration {
|
|
90
|
+
slug: string;
|
|
91
|
+
name: string;
|
|
92
|
+
category: string;
|
|
93
|
+
syncFeatures: string[];
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export interface PseoUseCase {
|
|
97
|
+
slug: string;
|
|
98
|
+
title: string;
|
|
99
|
+
problem: string;
|
|
100
|
+
solutionWorkflow: string[];
|
|
101
|
+
expectedRoi: string;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export interface PseoTemplate {
|
|
105
|
+
slug: string;
|
|
106
|
+
title: string;
|
|
107
|
+
topic: string;
|
|
108
|
+
format: "excel" | "sheets" | "notion" | "word" | "pdf";
|
|
109
|
+
downloadUrl?: string;
|
|
110
|
+
benefits: string[];
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export interface PseoGlossaryTerm {
|
|
114
|
+
slug: string;
|
|
115
|
+
term: string;
|
|
116
|
+
shortDefinition: string;
|
|
117
|
+
fullMarkdown?: string;
|
|
118
|
+
relatedTermsSlugs?: string[];
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export interface PseoCalculator {
|
|
122
|
+
slug: string;
|
|
123
|
+
title: string;
|
|
124
|
+
formulaDescription: string;
|
|
125
|
+
savingsMetric: string;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export interface SchemaOrgGraph {
|
|
129
|
+
"@context": "https://schema.org";
|
|
130
|
+
"@graph": Record<string, unknown>[];
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export interface GeneratedPageMeta {
|
|
134
|
+
matrixFamily: MatrixFamily;
|
|
135
|
+
urlPath: string;
|
|
136
|
+
canonicalUrl: string;
|
|
137
|
+
title: string;
|
|
138
|
+
description: string;
|
|
139
|
+
h1: string;
|
|
140
|
+
robots: "index, follow" | "noindex, follow";
|
|
141
|
+
schemaGraph: SchemaOrgGraph;
|
|
142
|
+
disclaimerText?: string;
|
|
143
|
+
neighboringLinks?: { name: string; url: string }[];
|
|
144
|
+
service?: PseoService;
|
|
145
|
+
location?: PseoLocation;
|
|
146
|
+
competitor?: PseoCompetitor;
|
|
147
|
+
target?: PseoTarget;
|
|
148
|
+
integration?: PseoIntegration;
|
|
149
|
+
useCase?: PseoUseCase;
|
|
150
|
+
template?: PseoTemplate;
|
|
151
|
+
glossary?: PseoGlossaryTerm;
|
|
152
|
+
calculator?: PseoCalculator;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export interface MatrixOptions {
|
|
156
|
+
brandName: string;
|
|
157
|
+
language?: string; // Default: "en", supports "fr", "de", "es", "pt", "it", etc.
|
|
158
|
+
minPopulationToIndex?: number;
|
|
159
|
+
defaultCurrency?: string;
|
|
160
|
+
defaultCurrencySymbol?: string;
|
|
161
|
+
organizationSameAs?: string[];
|
|
162
|
+
correctionsEmail?: string;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export class PseoMatrixEngine {
|
|
166
|
+
private legalEngine: LegalDisclaimerEngine;
|
|
167
|
+
|
|
168
|
+
constructor(legalOptions?: { correctionsEmail?: string; companyName?: string; language?: string }) {
|
|
169
|
+
this.legalEngine = new LegalDisclaimerEngine({
|
|
170
|
+
companyName: legalOptions?.companyName || "Our Platform",
|
|
171
|
+
correctionsEmail: legalOptions?.correctionsEmail || "compliance@platform.com",
|
|
172
|
+
language: legalOptions?.language || "en",
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Helper: Resolves route prefixes according to language (English default).
|
|
178
|
+
*/
|
|
179
|
+
private getPrefix(family: string, lang: string = "en"): string {
|
|
180
|
+
if (lang === "fr") {
|
|
181
|
+
const frMap: Record<string, string> = {
|
|
182
|
+
vs: "/comparatif",
|
|
183
|
+
alternative: "/alternatives",
|
|
184
|
+
pricing: "/tarifs",
|
|
185
|
+
target: "/pour",
|
|
186
|
+
integration: "/integrations",
|
|
187
|
+
useCase: "/cas-usage",
|
|
188
|
+
template: "/modeles",
|
|
189
|
+
glossary: "/glossaire",
|
|
190
|
+
calculator: "/outils",
|
|
191
|
+
solutions: "/solutions",
|
|
192
|
+
};
|
|
193
|
+
return frMap[family] || `/${family}`;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (lang === "de") {
|
|
197
|
+
const deMap: Record<string, string> = {
|
|
198
|
+
vs: "/vergleich",
|
|
199
|
+
alternative: "/alternativen",
|
|
200
|
+
pricing: "/preise",
|
|
201
|
+
target: "/fuer",
|
|
202
|
+
integration: "/integrationen",
|
|
203
|
+
useCase: "/anwendungsfaelle",
|
|
204
|
+
template: "/vorlagen",
|
|
205
|
+
glossary: "/glossar",
|
|
206
|
+
calculator: "/rechner",
|
|
207
|
+
solutions: "/loesungen",
|
|
208
|
+
};
|
|
209
|
+
return deMap[family] || `/${family}`;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (lang === "es") {
|
|
213
|
+
const esMap: Record<string, string> = {
|
|
214
|
+
vs: "/comparativa",
|
|
215
|
+
alternative: "/alternativas",
|
|
216
|
+
pricing: "/precios",
|
|
217
|
+
target: "/para",
|
|
218
|
+
integration: "/integraciones",
|
|
219
|
+
useCase: "/casos-uso",
|
|
220
|
+
template: "/plantillas",
|
|
221
|
+
glossary: "/glosario",
|
|
222
|
+
calculator: "/herramientas",
|
|
223
|
+
solutions: "/soluciones",
|
|
224
|
+
};
|
|
225
|
+
return esMap[family] || `/${family}`;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// English Default
|
|
229
|
+
const enMap: Record<string, string> = {
|
|
230
|
+
vs: "/vs",
|
|
231
|
+
alternative: "/alternatives",
|
|
232
|
+
pricing: "/pricing",
|
|
233
|
+
target: "/for",
|
|
234
|
+
integration: "/integrations",
|
|
235
|
+
useCase: "/use-cases",
|
|
236
|
+
template: "/templates",
|
|
237
|
+
glossary: "/glossary",
|
|
238
|
+
calculator: "/tools",
|
|
239
|
+
solutions: "/solutions",
|
|
240
|
+
};
|
|
241
|
+
return enMap[family] || `/${family}`;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* 1. Local & GEO Matrix (`/solutions/{service}/{country}/{city}`)
|
|
246
|
+
*/
|
|
247
|
+
generateLocalGeoMatrix(
|
|
248
|
+
domain: string,
|
|
249
|
+
services: PseoService[],
|
|
250
|
+
locations: PseoLocation[],
|
|
251
|
+
options: MatrixOptions,
|
|
252
|
+
): GeneratedPageMeta[] {
|
|
253
|
+
const cleanDomain = domain.replace(/\/+$/, "");
|
|
254
|
+
const lang = options.language || "en";
|
|
255
|
+
const minPop = options.minPopulationToIndex ?? 15_000;
|
|
256
|
+
const pages: GeneratedPageMeta[] = [];
|
|
257
|
+
const locMap = new Map(locations.map((l) => [l.slug, l]));
|
|
258
|
+
const prefix = this.getPrefix("solutions", lang);
|
|
259
|
+
|
|
260
|
+
for (const s of services) {
|
|
261
|
+
for (const loc of locations) {
|
|
262
|
+
const countryCode = cleanSeoSlug(loc.country, { language: lang });
|
|
263
|
+
const citySlug = cleanSeoSlug(loc.slug, { language: lang });
|
|
264
|
+
const serviceSlug = cleanSeoSlug(s.slug, { language: lang });
|
|
265
|
+
|
|
266
|
+
const urlPath = `${prefix}/${serviceSlug}/${countryCode}/${citySlug}`;
|
|
267
|
+
const fullUrl = `${cleanDomain}${urlPath}`;
|
|
268
|
+
|
|
269
|
+
const isIndexed = (loc.population ?? 20_000) >= minPop;
|
|
270
|
+
const robots: "index, follow" | "noindex, follow" = isIndexed
|
|
271
|
+
? "index, follow"
|
|
272
|
+
: "noindex, follow";
|
|
273
|
+
|
|
274
|
+
const title = `${s.name} in ${loc.name} (${loc.region}) — ${options.brandName}`;
|
|
275
|
+
const description = `Discover ${s.name} software tailored for businesses in ${loc.name}, ${loc.country}. Fast setup, local support in ${loc.currencySymbol}.`;
|
|
276
|
+
const h1 = `${s.name} for Professionals in ${loc.name}`;
|
|
277
|
+
|
|
278
|
+
const neighborLinks = (loc.neighborSlugs || [])
|
|
279
|
+
.map((slug) => locMap.get(slug))
|
|
280
|
+
.filter((n): n is PseoLocation => Boolean(n))
|
|
281
|
+
.map((n) => ({
|
|
282
|
+
name: n.name,
|
|
283
|
+
url: `${cleanDomain}${prefix}/${serviceSlug}/${cleanSeoSlug(n.country, { language: lang })}/${cleanSeoSlug(n.slug, { language: lang })}`,
|
|
284
|
+
}));
|
|
285
|
+
|
|
286
|
+
const schemaGraph: SchemaOrgGraph = {
|
|
287
|
+
"@context": "https://schema.org",
|
|
288
|
+
"@graph": [
|
|
289
|
+
ExtendedSchemaGraphBuilder.buildSoftwareApplication({
|
|
290
|
+
name: `${options.brandName} — ${s.name} ${loc.name}`,
|
|
291
|
+
description,
|
|
292
|
+
url: fullUrl,
|
|
293
|
+
priceMonthly: s.priceMonthly,
|
|
294
|
+
currency: loc.currency,
|
|
295
|
+
}),
|
|
296
|
+
ExtendedSchemaGraphBuilder.buildLocalBusiness({
|
|
297
|
+
name: `${options.brandName} ${loc.name}`,
|
|
298
|
+
url: fullUrl,
|
|
299
|
+
city: loc.name,
|
|
300
|
+
region: loc.region,
|
|
301
|
+
country: loc.country,
|
|
302
|
+
latitude: loc.latitude,
|
|
303
|
+
longitude: loc.longitude,
|
|
304
|
+
}),
|
|
305
|
+
ExtendedSchemaGraphBuilder.buildBreadcrumbs([
|
|
306
|
+
{ name: "Home", url: cleanDomain },
|
|
307
|
+
{ name: "Solutions", url: `${cleanDomain}${prefix}` },
|
|
308
|
+
{ name: s.name, url: `${cleanDomain}${prefix}/${serviceSlug}` },
|
|
309
|
+
{ name: loc.name, url: fullUrl },
|
|
310
|
+
]),
|
|
311
|
+
],
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
pages.push({
|
|
315
|
+
matrixFamily: "local-geo",
|
|
316
|
+
urlPath,
|
|
317
|
+
canonicalUrl: fullUrl,
|
|
318
|
+
title,
|
|
319
|
+
description,
|
|
320
|
+
h1,
|
|
321
|
+
robots,
|
|
322
|
+
schemaGraph,
|
|
323
|
+
neighboringLinks: neighborLinks,
|
|
324
|
+
service: s,
|
|
325
|
+
location: loc,
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
return pages;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* 2. VS Comparisons Matrix (`/vs/{competitor}` in English default)
|
|
334
|
+
*/
|
|
335
|
+
generateVsMatrix(
|
|
336
|
+
domain: string,
|
|
337
|
+
competitors: PseoCompetitor[],
|
|
338
|
+
options: MatrixOptions,
|
|
339
|
+
): GeneratedPageMeta[] {
|
|
340
|
+
const cleanDomain = domain.replace(/\/+$/, "");
|
|
341
|
+
const lang = options.language || "en";
|
|
342
|
+
const prefix = this.getPrefix("vs", lang);
|
|
343
|
+
|
|
344
|
+
return competitors.map((comp) => {
|
|
345
|
+
const compSlug = cleanSeoSlug(comp.slug, { language: lang });
|
|
346
|
+
const urlPath = `${prefix}/${compSlug}`;
|
|
347
|
+
const fullUrl = `${cleanDomain}${urlPath}`;
|
|
348
|
+
const title = `${options.brandName} vs ${comp.name} Comparison & Key Differences`;
|
|
349
|
+
const description = `In-depth comparison between ${options.brandName} and ${comp.name}. Features, pricing transparency, and architectural differences.`;
|
|
350
|
+
const h1 = `${options.brandName} vs ${comp.name} : The Complete Comparison`;
|
|
351
|
+
const disclaimerText = this.legalEngine.getDisclaimer("comparison", comp.lastVerifiedDate, lang);
|
|
352
|
+
|
|
353
|
+
const schemaGraph: SchemaOrgGraph = {
|
|
354
|
+
"@context": "https://schema.org",
|
|
355
|
+
"@graph": [
|
|
356
|
+
ExtendedSchemaGraphBuilder.buildOrganization({
|
|
357
|
+
name: options.brandName,
|
|
358
|
+
url: cleanDomain,
|
|
359
|
+
sameAs: options.organizationSameAs,
|
|
360
|
+
}),
|
|
361
|
+
ExtendedSchemaGraphBuilder.buildBreadcrumbs([
|
|
362
|
+
{ name: "Home", url: cleanDomain },
|
|
363
|
+
{ name: "Comparisons", url: `${cleanDomain}${prefix}` },
|
|
364
|
+
{ name: `vs ${comp.name}`, url: fullUrl },
|
|
365
|
+
]),
|
|
366
|
+
],
|
|
367
|
+
};
|
|
368
|
+
|
|
369
|
+
return {
|
|
370
|
+
matrixFamily: "vs-comparison",
|
|
371
|
+
urlPath,
|
|
372
|
+
canonicalUrl: fullUrl,
|
|
373
|
+
title,
|
|
374
|
+
description,
|
|
375
|
+
h1,
|
|
376
|
+
robots: "index, follow",
|
|
377
|
+
disclaimerText,
|
|
378
|
+
schemaGraph,
|
|
379
|
+
competitor: comp,
|
|
380
|
+
};
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* 3. Alternatives Matrix (`/alternatives/{competitor}`)
|
|
386
|
+
*/
|
|
387
|
+
generateAlternativesMatrix(
|
|
388
|
+
domain: string,
|
|
389
|
+
competitors: PseoCompetitor[],
|
|
390
|
+
options: MatrixOptions,
|
|
391
|
+
): GeneratedPageMeta[] {
|
|
392
|
+
const cleanDomain = domain.replace(/\/+$/, "");
|
|
393
|
+
const lang = options.language || "en";
|
|
394
|
+
const prefix = this.getPrefix("alternative", lang);
|
|
395
|
+
|
|
396
|
+
return competitors.map((comp) => {
|
|
397
|
+
const compSlug = cleanSeoSlug(comp.slug, { language: lang });
|
|
398
|
+
const urlPath = `${prefix}/${compSlug}`;
|
|
399
|
+
const fullUrl = `${cleanDomain}${urlPath}`;
|
|
400
|
+
const title = `Best ${comp.name} Alternative — ${options.brandName}`;
|
|
401
|
+
const description = `Looking for a modern alternative to ${comp.name}? Explore why teams migrate to ${options.brandName}.`;
|
|
402
|
+
const h1 = `Why ${options.brandName} is the Best Alternative to ${comp.name}`;
|
|
403
|
+
const disclaimerText = this.legalEngine.getDisclaimer("alternative", comp.lastVerifiedDate, lang);
|
|
404
|
+
|
|
405
|
+
const schemaGraph: SchemaOrgGraph = {
|
|
406
|
+
"@context": "https://schema.org",
|
|
407
|
+
"@graph": [
|
|
408
|
+
ExtendedSchemaGraphBuilder.buildSoftwareApplication({
|
|
409
|
+
name: `${options.brandName} — Alternative to ${comp.name}`,
|
|
410
|
+
description,
|
|
411
|
+
url: fullUrl,
|
|
412
|
+
}),
|
|
413
|
+
ExtendedSchemaGraphBuilder.buildBreadcrumbs([
|
|
414
|
+
{ name: "Home", url: cleanDomain },
|
|
415
|
+
{ name: "Alternatives", url: `${cleanDomain}${prefix}` },
|
|
416
|
+
{ name: comp.name, url: fullUrl },
|
|
417
|
+
]),
|
|
418
|
+
],
|
|
419
|
+
};
|
|
420
|
+
|
|
421
|
+
return {
|
|
422
|
+
matrixFamily: "alternative",
|
|
423
|
+
urlPath,
|
|
424
|
+
canonicalUrl: fullUrl,
|
|
425
|
+
title,
|
|
426
|
+
description,
|
|
427
|
+
h1,
|
|
428
|
+
robots: "index, follow",
|
|
429
|
+
disclaimerText,
|
|
430
|
+
schemaGraph,
|
|
431
|
+
competitor: comp,
|
|
432
|
+
};
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
/**
|
|
437
|
+
* 4. Pricing Matrix (`/pricing/{competitor}` in English default)
|
|
438
|
+
*/
|
|
439
|
+
generatePricingMatrix(
|
|
440
|
+
domain: string,
|
|
441
|
+
competitors: PseoCompetitor[],
|
|
442
|
+
options: MatrixOptions,
|
|
443
|
+
): GeneratedPageMeta[] {
|
|
444
|
+
const cleanDomain = domain.replace(/\/+$/, "");
|
|
445
|
+
const lang = options.language || "en";
|
|
446
|
+
const prefix = this.getPrefix("pricing", lang);
|
|
447
|
+
|
|
448
|
+
return competitors.map((comp) => {
|
|
449
|
+
const compSlug = cleanSeoSlug(comp.slug, { language: lang });
|
|
450
|
+
const urlPath = `${prefix}/${compSlug}`;
|
|
451
|
+
const fullUrl = `${cleanDomain}${urlPath}`;
|
|
452
|
+
const title = `${comp.name} Pricing & Cost Breakdown — ${options.brandName}`;
|
|
453
|
+
const description = `Detailed ${comp.name} pricing guide. Tiers, hidden fees, add-on costs, and how it compares to ${options.brandName}.`;
|
|
454
|
+
const h1 = `${comp.name} Pricing, Plans & Hidden Costs`;
|
|
455
|
+
const disclaimerText = this.legalEngine.getDisclaimer("pricing", comp.lastVerifiedDate, lang);
|
|
456
|
+
|
|
457
|
+
const schemaGraph: SchemaOrgGraph = {
|
|
458
|
+
"@context": "https://schema.org",
|
|
459
|
+
"@graph": [
|
|
460
|
+
ExtendedSchemaGraphBuilder.buildBreadcrumbs([
|
|
461
|
+
{ name: "Home", url: cleanDomain },
|
|
462
|
+
{ name: "Pricing Guides", url: `${cleanDomain}${prefix}` },
|
|
463
|
+
{ name: `${comp.name} Pricing`, url: fullUrl },
|
|
464
|
+
]),
|
|
465
|
+
],
|
|
466
|
+
};
|
|
467
|
+
|
|
468
|
+
return {
|
|
469
|
+
matrixFamily: "pricing",
|
|
470
|
+
urlPath,
|
|
471
|
+
canonicalUrl: fullUrl,
|
|
472
|
+
title,
|
|
473
|
+
description,
|
|
474
|
+
h1,
|
|
475
|
+
robots: "index, follow",
|
|
476
|
+
disclaimerText,
|
|
477
|
+
schemaGraph,
|
|
478
|
+
competitor: comp,
|
|
479
|
+
};
|
|
480
|
+
});
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
/**
|
|
484
|
+
* 5. Unified Target Audience Matrix (`/for/{target}` in English default)
|
|
485
|
+
*/
|
|
486
|
+
generateTargetMatrix(
|
|
487
|
+
domain: string,
|
|
488
|
+
targets: PseoTarget[],
|
|
489
|
+
options: MatrixOptions,
|
|
490
|
+
): GeneratedPageMeta[] {
|
|
491
|
+
const cleanDomain = domain.replace(/\/+$/, "");
|
|
492
|
+
const lang = options.language || "en";
|
|
493
|
+
const prefix = this.getPrefix("target", lang);
|
|
494
|
+
|
|
495
|
+
return targets.map((t) => {
|
|
496
|
+
const targetSlug = cleanSeoSlug(t.slug, { language: lang });
|
|
497
|
+
const urlPath = `${prefix}/${targetSlug}`;
|
|
498
|
+
const fullUrl = `${cleanDomain}${urlPath}`;
|
|
499
|
+
const title = `${options.brandName} for ${t.name} — Dedicated Solution`;
|
|
500
|
+
const description = `Streamline operations and automate workflows designed specifically for ${t.name}.`;
|
|
501
|
+
const h1 = `The Operating System Designed for ${t.name}`;
|
|
502
|
+
|
|
503
|
+
const schemaGraph: SchemaOrgGraph = {
|
|
504
|
+
"@context": "https://schema.org",
|
|
505
|
+
"@graph": [
|
|
506
|
+
ExtendedSchemaGraphBuilder.buildBreadcrumbs([
|
|
507
|
+
{ name: "Home", url: cleanDomain },
|
|
508
|
+
{ name: "Solutions", url: `${cleanDomain}${prefix}` },
|
|
509
|
+
{ name: t.name, url: fullUrl },
|
|
510
|
+
]),
|
|
511
|
+
],
|
|
512
|
+
};
|
|
513
|
+
|
|
514
|
+
return {
|
|
515
|
+
matrixFamily: "target",
|
|
516
|
+
urlPath,
|
|
517
|
+
canonicalUrl: fullUrl,
|
|
518
|
+
title,
|
|
519
|
+
description,
|
|
520
|
+
h1,
|
|
521
|
+
robots: "index, follow",
|
|
522
|
+
schemaGraph,
|
|
523
|
+
target: t,
|
|
524
|
+
};
|
|
525
|
+
});
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
/**
|
|
529
|
+
* 6. Integrations Matrix (`/integrations/{software}`)
|
|
530
|
+
*/
|
|
531
|
+
generateIntegrationMatrix(
|
|
532
|
+
domain: string,
|
|
533
|
+
integrations: PseoIntegration[],
|
|
534
|
+
options: MatrixOptions,
|
|
535
|
+
): GeneratedPageMeta[] {
|
|
536
|
+
const cleanDomain = domain.replace(/\/+$/, "");
|
|
537
|
+
const lang = options.language || "en";
|
|
538
|
+
const prefix = this.getPrefix("integration", lang);
|
|
539
|
+
|
|
540
|
+
return integrations.map((itg) => {
|
|
541
|
+
const itgSlug = cleanSeoSlug(itg.slug, { language: lang });
|
|
542
|
+
const urlPath = `${prefix}/${itgSlug}`;
|
|
543
|
+
const fullUrl = `${cleanDomain}${urlPath}`;
|
|
544
|
+
const title = `${options.brandName} & ${itg.name} Integration : 2-Way Sync`;
|
|
545
|
+
const description = `Connect ${options.brandName} with ${itg.name} to automate workflows and sync data without code.`;
|
|
546
|
+
const h1 = `${options.brandName} × ${itg.name} Native Integration`;
|
|
547
|
+
|
|
548
|
+
const schemaGraph: SchemaOrgGraph = {
|
|
549
|
+
"@context": "https://schema.org",
|
|
550
|
+
"@graph": [
|
|
551
|
+
ExtendedSchemaGraphBuilder.buildSoftwareApplication({
|
|
552
|
+
name: `${options.brandName} ${itg.name} Connector`,
|
|
553
|
+
description,
|
|
554
|
+
url: fullUrl,
|
|
555
|
+
category: "IntegrationApplication",
|
|
556
|
+
}),
|
|
557
|
+
ExtendedSchemaGraphBuilder.buildBreadcrumbs([
|
|
558
|
+
{ name: "Home", url: cleanDomain },
|
|
559
|
+
{ name: "Integrations", url: `${cleanDomain}${prefix}` },
|
|
560
|
+
{ name: itg.name, url: fullUrl },
|
|
561
|
+
]),
|
|
562
|
+
],
|
|
563
|
+
};
|
|
564
|
+
|
|
565
|
+
return {
|
|
566
|
+
matrixFamily: "integration",
|
|
567
|
+
urlPath,
|
|
568
|
+
canonicalUrl: fullUrl,
|
|
569
|
+
title,
|
|
570
|
+
description,
|
|
571
|
+
h1,
|
|
572
|
+
robots: "index, follow",
|
|
573
|
+
schemaGraph,
|
|
574
|
+
integration: itg,
|
|
575
|
+
};
|
|
576
|
+
});
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
/**
|
|
580
|
+
* 7. Use Cases Matrix (`/use-cases/{useCase}` in English default)
|
|
581
|
+
*/
|
|
582
|
+
generateUseCaseMatrix(
|
|
583
|
+
domain: string,
|
|
584
|
+
useCases: PseoUseCase[],
|
|
585
|
+
options: MatrixOptions,
|
|
586
|
+
): GeneratedPageMeta[] {
|
|
587
|
+
const cleanDomain = domain.replace(/\/+$/, "");
|
|
588
|
+
const lang = options.language || "en";
|
|
589
|
+
const prefix = this.getPrefix("useCase", lang);
|
|
590
|
+
|
|
591
|
+
return useCases.map((uc) => {
|
|
592
|
+
const ucSlug = cleanSeoSlug(uc.slug, { language: lang });
|
|
593
|
+
const urlPath = `${prefix}/${ucSlug}`;
|
|
594
|
+
const fullUrl = `${cleanDomain}${urlPath}`;
|
|
595
|
+
const title = `${uc.title} : Step-by-Step Guide with ${options.brandName}`;
|
|
596
|
+
const description = `Operational playbook: ${uc.problem}. Discover how to solve and automate this with ${options.brandName}.`;
|
|
597
|
+
const h1 = `${uc.title} — Automation Workflow`;
|
|
598
|
+
|
|
599
|
+
const schemaGraph: SchemaOrgGraph = {
|
|
600
|
+
"@context": "https://schema.org",
|
|
601
|
+
"@graph": [
|
|
602
|
+
ExtendedSchemaGraphBuilder.buildHowTo({
|
|
603
|
+
name: uc.title,
|
|
604
|
+
description: uc.problem,
|
|
605
|
+
steps: uc.solutionWorkflow,
|
|
606
|
+
}),
|
|
607
|
+
ExtendedSchemaGraphBuilder.buildBreadcrumbs([
|
|
608
|
+
{ name: "Home", url: cleanDomain },
|
|
609
|
+
{ name: "Use Cases", url: `${cleanDomain}${prefix}` },
|
|
610
|
+
{ name: uc.title, url: fullUrl },
|
|
611
|
+
]),
|
|
612
|
+
],
|
|
613
|
+
};
|
|
614
|
+
|
|
615
|
+
return {
|
|
616
|
+
matrixFamily: "use-case",
|
|
617
|
+
urlPath,
|
|
618
|
+
canonicalUrl: fullUrl,
|
|
619
|
+
title,
|
|
620
|
+
description,
|
|
621
|
+
h1,
|
|
622
|
+
robots: "index, follow",
|
|
623
|
+
schemaGraph,
|
|
624
|
+
useCase: uc,
|
|
625
|
+
};
|
|
626
|
+
});
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
/**
|
|
630
|
+
* 8. Templates & Lead Magnets Matrix (`/templates/{slug}` in English default)
|
|
631
|
+
*/
|
|
632
|
+
generateTemplateMatrix(
|
|
633
|
+
domain: string,
|
|
634
|
+
templates: PseoTemplate[],
|
|
635
|
+
options: MatrixOptions,
|
|
636
|
+
): GeneratedPageMeta[] {
|
|
637
|
+
const cleanDomain = domain.replace(/\/+$/, "");
|
|
638
|
+
const lang = options.language || "en";
|
|
639
|
+
const prefix = this.getPrefix("template", lang);
|
|
640
|
+
|
|
641
|
+
return templates.map((tmpl) => {
|
|
642
|
+
const tmplSlug = cleanSeoSlug(tmpl.slug, { language: lang });
|
|
643
|
+
const urlPath = `${prefix}/${tmplSlug}`;
|
|
644
|
+
const fullUrl = `${cleanDomain}${urlPath}`;
|
|
645
|
+
const title = `Free ${tmpl.title} (${tmpl.format.toUpperCase()}) — ${options.brandName}`;
|
|
646
|
+
const description = `Download our free ${tmpl.title} template for ${tmpl.format.toUpperCase()}. Save time and automate your workflow.`;
|
|
647
|
+
const h1 = `Free ${tmpl.title} Template`;
|
|
648
|
+
|
|
649
|
+
const schemaGraph: SchemaOrgGraph = {
|
|
650
|
+
"@context": "https://schema.org",
|
|
651
|
+
"@graph": [
|
|
652
|
+
ExtendedSchemaGraphBuilder.buildBreadcrumbs([
|
|
653
|
+
{ name: "Home", url: cleanDomain },
|
|
654
|
+
{ name: "Templates", url: `${cleanDomain}${prefix}` },
|
|
655
|
+
{ name: tmpl.title, url: fullUrl },
|
|
656
|
+
]),
|
|
657
|
+
],
|
|
658
|
+
};
|
|
659
|
+
|
|
660
|
+
return {
|
|
661
|
+
matrixFamily: "template",
|
|
662
|
+
urlPath,
|
|
663
|
+
canonicalUrl: fullUrl,
|
|
664
|
+
title,
|
|
665
|
+
description,
|
|
666
|
+
h1,
|
|
667
|
+
robots: "index, follow",
|
|
668
|
+
schemaGraph,
|
|
669
|
+
template: tmpl,
|
|
670
|
+
};
|
|
671
|
+
});
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
/**
|
|
675
|
+
* 9. Glossary Matrix (`/glossary/{term}` in English default)
|
|
676
|
+
*/
|
|
677
|
+
generateGlossaryMatrix(
|
|
678
|
+
domain: string,
|
|
679
|
+
terms: PseoGlossaryTerm[],
|
|
680
|
+
options: MatrixOptions,
|
|
681
|
+
): GeneratedPageMeta[] {
|
|
682
|
+
const cleanDomain = domain.replace(/\/+$/, "");
|
|
683
|
+
const lang = options.language || "en";
|
|
684
|
+
const prefix = this.getPrefix("glossary", lang);
|
|
685
|
+
|
|
686
|
+
return terms.map((g) => {
|
|
687
|
+
const termSlug = cleanSeoSlug(g.slug, { language: lang });
|
|
688
|
+
const urlPath = `${prefix}/${termSlug}`;
|
|
689
|
+
const fullUrl = `${cleanDomain}${urlPath}`;
|
|
690
|
+
const title = `What is ${g.term}? Definition & Best Practices — ${options.brandName}`;
|
|
691
|
+
const description = `${g.shortDefinition.substring(0, 150)}... Learn the core concepts and implementation.`;
|
|
692
|
+
const h1 = `${g.term} : Definition & Strategy`;
|
|
693
|
+
|
|
694
|
+
const schemaGraph: SchemaOrgGraph = {
|
|
695
|
+
"@context": "https://schema.org",
|
|
696
|
+
"@graph": [
|
|
697
|
+
ExtendedSchemaGraphBuilder.buildBreadcrumbs([
|
|
698
|
+
{ name: "Home", url: cleanDomain },
|
|
699
|
+
{ name: "Glossary", url: `${cleanDomain}${prefix}` },
|
|
700
|
+
{ name: g.term, url: fullUrl },
|
|
701
|
+
]),
|
|
702
|
+
],
|
|
703
|
+
};
|
|
704
|
+
|
|
705
|
+
return {
|
|
706
|
+
matrixFamily: "glossary",
|
|
707
|
+
urlPath,
|
|
708
|
+
canonicalUrl: fullUrl,
|
|
709
|
+
title,
|
|
710
|
+
description,
|
|
711
|
+
h1,
|
|
712
|
+
robots: "index, follow",
|
|
713
|
+
schemaGraph,
|
|
714
|
+
glossary: g,
|
|
715
|
+
};
|
|
716
|
+
});
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
/**
|
|
720
|
+
* 10. Tools & Calculators Matrix (`/tools/{calculator}` in English default)
|
|
721
|
+
*/
|
|
722
|
+
generateCalculatorMatrix(
|
|
723
|
+
domain: string,
|
|
724
|
+
calculators: PseoCalculator[],
|
|
725
|
+
options: MatrixOptions,
|
|
726
|
+
): GeneratedPageMeta[] {
|
|
727
|
+
const cleanDomain = domain.replace(/\/+$/, "");
|
|
728
|
+
const lang = options.language || "en";
|
|
729
|
+
const prefix = this.getPrefix("calculator", lang);
|
|
730
|
+
|
|
731
|
+
return calculators.map((calc) => {
|
|
732
|
+
const calcSlug = cleanSeoSlug(calc.slug, { language: lang });
|
|
733
|
+
const urlPath = `${prefix}/${calcSlug}`;
|
|
734
|
+
const fullUrl = `${cleanDomain}${urlPath}`;
|
|
735
|
+
const title = `${calc.title} Online Calculator — ${options.brandName}`;
|
|
736
|
+
const description = `Free calculator: ${calc.formulaDescription}. Estimate your ${calc.savingsMetric} instantly.`;
|
|
737
|
+
const h1 = `${calc.title} — Free Interactive Estimator`;
|
|
738
|
+
|
|
739
|
+
const schemaGraph: SchemaOrgGraph = {
|
|
740
|
+
"@context": "https://schema.org",
|
|
741
|
+
"@graph": [
|
|
742
|
+
ExtendedSchemaGraphBuilder.buildSoftwareApplication({
|
|
743
|
+
name: calc.title,
|
|
744
|
+
description,
|
|
745
|
+
url: fullUrl,
|
|
746
|
+
}),
|
|
747
|
+
ExtendedSchemaGraphBuilder.buildBreadcrumbs([
|
|
748
|
+
{ name: "Home", url: cleanDomain },
|
|
749
|
+
{ name: "Calculators", url: `${cleanDomain}${prefix}` },
|
|
750
|
+
{ name: calc.title, url: fullUrl },
|
|
751
|
+
]),
|
|
752
|
+
],
|
|
753
|
+
};
|
|
754
|
+
|
|
755
|
+
return {
|
|
756
|
+
matrixFamily: "calculator",
|
|
757
|
+
urlPath,
|
|
758
|
+
canonicalUrl: fullUrl,
|
|
759
|
+
title,
|
|
760
|
+
description,
|
|
761
|
+
h1,
|
|
762
|
+
robots: "index, follow",
|
|
763
|
+
schemaGraph,
|
|
764
|
+
calculator: calc,
|
|
765
|
+
};
|
|
766
|
+
});
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
/**
|
|
770
|
+
* Master Generator: Produces all programmatic families simultaneously.
|
|
771
|
+
*/
|
|
772
|
+
generateAllMatrices(
|
|
773
|
+
domain: string,
|
|
774
|
+
data: {
|
|
775
|
+
services?: PseoService[];
|
|
776
|
+
locations?: PseoLocation[];
|
|
777
|
+
competitors?: PseoCompetitor[];
|
|
778
|
+
targets?: PseoTarget[];
|
|
779
|
+
integrations?: PseoIntegration[];
|
|
780
|
+
useCases?: PseoUseCase[];
|
|
781
|
+
templates?: PseoTemplate[];
|
|
782
|
+
glossaryTerms?: PseoGlossaryTerm[];
|
|
783
|
+
calculators?: PseoCalculator[];
|
|
784
|
+
},
|
|
785
|
+
options: MatrixOptions,
|
|
786
|
+
): GeneratedPageMeta[] {
|
|
787
|
+
const allPages: GeneratedPageMeta[] = [];
|
|
788
|
+
|
|
789
|
+
if (data.services && data.locations) {
|
|
790
|
+
allPages.push(...this.generateLocalGeoMatrix(domain, data.services, data.locations, options));
|
|
791
|
+
}
|
|
792
|
+
if (data.competitors) {
|
|
793
|
+
allPages.push(...this.generateVsMatrix(domain, data.competitors, options));
|
|
794
|
+
allPages.push(...this.generateAlternativesMatrix(domain, data.competitors, options));
|
|
795
|
+
allPages.push(...this.generatePricingMatrix(domain, data.competitors, options));
|
|
796
|
+
}
|
|
797
|
+
if (data.targets) {
|
|
798
|
+
allPages.push(...this.generateTargetMatrix(domain, data.targets, options));
|
|
799
|
+
}
|
|
800
|
+
if (data.integrations) {
|
|
801
|
+
allPages.push(...this.generateIntegrationMatrix(domain, data.integrations, options));
|
|
802
|
+
}
|
|
803
|
+
if (data.useCases) {
|
|
804
|
+
allPages.push(...this.generateUseCaseMatrix(domain, data.useCases, options));
|
|
805
|
+
}
|
|
806
|
+
if (data.templates) {
|
|
807
|
+
allPages.push(...this.generateTemplateMatrix(domain, data.templates, options));
|
|
808
|
+
}
|
|
809
|
+
if (data.glossaryTerms) {
|
|
810
|
+
allPages.push(...this.generateGlossaryMatrix(domain, data.glossaryTerms, options));
|
|
811
|
+
}
|
|
812
|
+
if (data.calculators) {
|
|
813
|
+
allPages.push(...this.generateCalculatorMatrix(domain, data.calculators, options));
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
return allPages;
|
|
817
|
+
}
|
|
818
|
+
}
|