@lynxflow/seo-engine 1.5.7 → 1.5.9

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 CHANGED
@@ -57,47 +57,101 @@ The SDK structures programmatic content across **10 canonical root pillars** exp
57
57
 
58
58
  ## Quickstart
59
59
 
60
- ### 1. Initialize Engine
60
+ ### 1. Initialize Engine & Configure Minimal Inputs
61
+
62
+ The SDK runs **in-memory inside the client application** (< 0.05ms) and automatically provisions verified cities, populations, GPS coordinates, and currencies from its built-in global demographic database.
61
63
 
62
64
  ```typescript
63
65
  import { PseoMatrixEngine } from "@lynxflow/seo-engine";
64
66
 
65
- export const matrixEngine = new PseoMatrixEngine({
66
- companyName: "Acme Cloud",
67
- language: "en", // "en" default, supports "fr", "de", "es", "it", "pt", etc.
68
- correctionsEmail: "compliance@acme.com",
69
- });
70
- ```
67
+ export const matrixEngine = new PseoMatrixEngine();
71
68
 
72
- ### 2. Resolve Programmatic Pages
69
+ export const SEO_CONFIG = {
70
+ brandName: "Acme Cloud",
71
+ languages: ["fr", "en", "es"], // Supported site languages
72
+ countries: ["france", "espagne", "belgique"], // Or "europe" / "international" — SDK auto-provisions verified cities!
73
+ cleanDirectRoutes: true, // Eliminates parasite words (/solutions/, etc.) for direct /{service}/{city}
74
+ };
73
75
 
74
- ```typescript
75
- const pages = matrixEngine.generateAllMatrices("https://example.com", {
76
+ export const PSEO_DATASET = {
76
77
  services: [
77
- { slug: "crm-pipeline", name: "Sales CRM", category: "Sales", keyFeatures: ["Kanban"], description: "CRM" }
78
+ {
79
+ slug: "autopost-facebook",
80
+ name: "Autopost Facebook & Instagram",
81
+ category: "Social Media",
82
+ keyFeatures: ["AI Scheduling", "Visual Calendar", "Auto-Hashtags"],
83
+ description: "Automated social media posting engine"
84
+ },
85
+ {
86
+ slug: "crm-pipeline",
87
+ name: "Visual CRM Pipeline",
88
+ category: "Sales",
89
+ keyFeatures: ["Kanban", "Lead scoring"],
90
+ description: "Visual deal tracking software"
91
+ },
78
92
  ],
79
93
  targets: [
80
- { slug: "law-firms", name: "Law Firms", type: "industry", painPoints: ["Billing"], benefits: ["Security"] }
94
+ { slug: "agences", name: "Marketing Agencies", type: "industry", painPoints: ["Manual Posting"], benefits: ["10x Output"] }
81
95
  ],
82
96
  integrations: [
83
- { slug: "slack", name: "Slack", category: "Communication", syncFeatures: ["Alerts"] }
97
+ { slug: "shopify", name: "Shopify", category: "E-Commerce", syncFeatures: ["Catalog sync"] }
84
98
  ],
85
99
  templates: [
86
- { slug: "pipeline-excel", title: "Sales Pipeline", topic: "Sales", format: "excel", benefits: ["Free"] }
100
+ { slug: "social-calendar-excel", title: "Social Media Calendar", topic: "Social Media", format: "excel", benefits: ["Free download"] }
87
101
  ],
88
102
  glossaryTerms: [
89
- { slug: "mrr", term: "MRR", shortDefinition: "Monthly Recurring Revenue" }
103
+ { slug: "engagement-rate", term: "Engagement Rate", shortDefinition: "Percentage of audience interacting with content" }
90
104
  ]
91
- }, { brandName: "Acme" });
105
+ };
106
+ ```
92
107
 
93
- console.log(pages[0].urlPath); // "/for/law-firms"
94
- console.log(pages[0].schemaGraph); // Injected Schema.org JSON-LD graph
108
+ ### 2. Next.js Universal Dynamic Catch-All (`app/[...slug]/page.tsx`)
109
+
110
+ ```tsx
111
+ import { notFound } from "next/navigation";
112
+ import { matrixEngine, SEO_CONFIG, PSEO_DATASET } from "@/lib/seo";
113
+
114
+ export async function generateMetadata({ params }: { params: { slug: string[] } }) {
115
+ const domain = process.env.NEXT_PUBLIC_SITE_URL || "https://example.com";
116
+ const page = matrixEngine.resolvePage(params.slug, domain, PSEO_DATASET, SEO_CONFIG);
117
+ if (!page) return {};
118
+ return { title: page.title, description: page.description, alternates: { canonical: page.canonicalUrl }, robots: page.robots };
119
+ }
120
+
121
+ export default async function ProgrammaticPage({ params }: { params: { slug: string[] } }) {
122
+ const domain = process.env.NEXT_PUBLIC_SITE_URL || "https://example.com";
123
+ // ⚡ Resolved mathematically in local RAM in < 0.05ms
124
+ const page = matrixEngine.resolvePage(params.slug, domain, PSEO_DATASET, SEO_CONFIG);
125
+ if (!page) notFound();
126
+
127
+ return (
128
+ <article className="min-h-screen bg-background text-foreground py-12 px-6 max-w-5xl mx-auto space-y-8">
129
+ <script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(page.schemaGraph) }} />
130
+ <h1 className="text-4xl font-extrabold">{page.h1}</h1>
131
+ <p className="text-lg text-muted-foreground">{page.description}</p>
132
+ {page.neighboringLinks && (
133
+ <nav className="flex flex-wrap gap-2 pt-6">
134
+ {page.neighboringLinks.map((l) => (
135
+ <a key={l.url} href={l.url} className="px-3 py-1 bg-secondary rounded text-xs">{l.name}</a>
136
+ ))}
137
+ </nav>
138
+ )}
139
+ </article>
140
+ );
141
+ }
95
142
  ```
96
143
 
97
- ### 3. URL Slug Cleansing (6 Golden Rules)
144
+ ### 3. XML Sitemap & `/llms.txt` Feeds (1 Line of Code)
98
145
 
99
146
  ```typescript
100
- import { cleanSeoSlug, validateSeoSlug } from "@lynxflow/seo-engine";
147
+ // app/sitemap.ts
148
+ export default function sitemap() {
149
+ const domain = process.env.NEXT_PUBLIC_SITE_URL || "https://example.com";
150
+ return matrixEngine.generateAllMatrices(domain, PSEO_DATASET, SEO_CONFIG)
151
+ .filter(p => p.robots.includes("index"))
152
+ .map(p => ({ url: p.canonicalUrl, lastModified: new Date() }));
153
+ }
154
+ ```
101
155
 
102
156
  const slug = cleanSeoSlug("The best software for managing client invoices in 2026", { language: "en" });
103
157
  console.log(slug); // "best-software-managing-client-invoices"
@@ -0,0 +1,12 @@
1
+ /**
2
+ * 🌍 Built-in Global Geographic & Demographic Database
3
+ * Automatically provisions verified cities, populations, currencies, and coordinates
4
+ * across Europe, North America, and international markets without requiring the user to list cities manually.
5
+ */
6
+ import { PseoLocation } from "./matrix-engine";
7
+ export declare const BUILT_IN_LOCATIONS_DATABASE: Record<string, PseoLocation[]>;
8
+ /**
9
+ * Resolves built-in locations by country code or territory keyword.
10
+ * Supports: "fr", "france", "es", "spain", "de", "germany", "uk", "us", "be", "ch", "it", "ca", "europe", "international", "all"
11
+ */
12
+ export declare function resolveBuiltInLocations(territories?: string[] | string): PseoLocation[];
package/dist/index.d.ts CHANGED
@@ -13,6 +13,7 @@ export * from "./engine";
13
13
  export * from "./slug-engine";
14
14
  export * from "./legal-disclaimers";
15
15
  export * from "./matrix-engine";
16
+ export * from "./built-in-locations";
16
17
  export * from "./brand-icons";
17
18
  export * from "./urlytics-engine";
18
19
  export * from "./keyword-permutator";
package/dist/index.js CHANGED
@@ -40,6 +40,7 @@ var __export = (target, all) => {
40
40
  var exports_src = {};
41
41
  __export(exports_src, {
42
42
  validateSeoSlug: () => validateSeoSlug,
43
+ resolveBuiltInLocations: () => resolveBuiltInLocations,
43
44
  resolveBrandIcon: () => resolveBrandIcon,
44
45
  renderBrandIconSvg: () => renderBrandIconSvg,
45
46
  getSeoAgentPrompt: () => getSeoAgentPrompt,
@@ -93,6 +94,7 @@ __export(exports_src, {
93
94
  CopywritingFrameworksMaster: () => CopywritingFrameworksMaster,
94
95
  BrandDnaCalendarEngine: () => BrandDnaCalendarEngine,
95
96
  BacklinksClient: () => BacklinksClient,
97
+ BUILT_IN_LOCATIONS_DATABASE: () => BUILT_IN_LOCATIONS_DATABASE,
96
98
  BRAND_ICONS: () => BRAND_ICONS,
97
99
  ApiKeyGuardian: () => ApiKeyGuardian,
98
100
  AiCopilotClient: () => AiCopilotClient,
@@ -3190,6 +3192,150 @@ class ExtendedSchemaGraphBuilder {
3190
3192
  }
3191
3193
  }
3192
3194
 
3195
+ // src/built-in-locations.ts
3196
+ var BUILT_IN_LOCATIONS_DATABASE = {
3197
+ fr: [
3198
+ { slug: "paris", name: "Paris", region: "Île-de-France", country: "France", population: 2161000, currency: "EUR", currencySymbol: "€", latitude: 48.8566, longitude: 2.3522, neighborSlugs: ["boulogne-billancourt", "saint-denis", "montreuil"] },
3199
+ { slug: "marseille", name: "Marseille", region: "Provence-Alpes-Côte d'Azur", country: "France", population: 873000, currency: "EUR", currencySymbol: "€", latitude: 43.2965, longitude: 5.3698, neighborSlugs: ["aix-en-provence", "aubagne", "toulon"] },
3200
+ { slug: "lyon", name: "Lyon", region: "Auvergne-Rhône-Alpes", country: "France", population: 522000, currency: "EUR", currencySymbol: "€", latitude: 45.764, longitude: 4.8357, neighborSlugs: ["villeurbanne", "venissieux", "saint-etienne"] },
3201
+ { slug: "toulouse", name: "Toulouse", region: "Occitanie", country: "France", population: 498000, currency: "EUR", currencySymbol: "€", latitude: 43.6047, longitude: 1.4442, neighborSlugs: ["montauban", "albi", "colomiers"] },
3202
+ { slug: "nice", name: "Nice", region: "Provence-Alpes-Côte d'Azur", country: "France", population: 343000, currency: "EUR", currencySymbol: "€", latitude: 43.7102, longitude: 7.262, neighborSlugs: ["cannes", "antibes", "monaco"] },
3203
+ { slug: "nantes", name: "Nantes", region: "Pays de la Loire", country: "France", population: 320000, currency: "EUR", currencySymbol: "€", latitude: 47.2184, longitude: -1.5536, neighborSlugs: ["saint-nazaire", "angers", "rennes"] },
3204
+ { slug: "montpellier", name: "Montpellier", region: "Occitanie", country: "France", population: 299000, currency: "EUR", currencySymbol: "€", latitude: 43.6108, longitude: 3.8767, neighborSlugs: ["nimes", "beziers", "sete"] },
3205
+ { slug: "strasbourg", name: "Strasbourg", region: "Grand Est", country: "France", population: 290000, currency: "EUR", currencySymbol: "€", latitude: 48.5734, longitude: 7.7521, neighborSlugs: ["mulhouse", "colmar", "metz"] },
3206
+ { slug: "bordeaux", name: "Bordeaux", region: "Nouvelle-Aquitaine", country: "France", population: 260000, currency: "EUR", currencySymbol: "€", latitude: 44.8378, longitude: -0.5792, neighborSlugs: ["merignac", "pessac", "arcachon"] },
3207
+ { slug: "lille", name: "Lille", region: "Hauts-de-France", country: "France", population: 236000, currency: "EUR", currencySymbol: "€", latitude: 50.6292, longitude: 3.0573, neighborSlugs: ["roubaix", "tourcoing", "villeneuve-d-ascq"] },
3208
+ { slug: "rennes", name: "Rennes", region: "Bretagne", country: "France", population: 222000, currency: "EUR", currencySymbol: "€", latitude: 48.1173, longitude: -1.6778, neighborSlugs: ["saint-malo", "brest", "nantes"] },
3209
+ { slug: "toulon", name: "Toulon", region: "Provence-Alpes-Côte d'Azur", country: "France", population: 179000, currency: "EUR", currencySymbol: "€", latitude: 43.1242, longitude: 5.928, neighborSlugs: ["hyeres", "la-seyne-sur-mer", "marseille"] },
3210
+ { slug: "reims", name: "Reims", region: "Grand Est", country: "France", population: 179000, currency: "EUR", currencySymbol: "€", latitude: 49.2583, longitude: 4.0317, neighborSlugs: ["chalons-en-champagne", "troyes", "metz"] },
3211
+ { slug: "saint-etienne", name: "Saint-Étienne", region: "Auvergne-Rhône-Alpes", country: "France", population: 174000, currency: "EUR", currencySymbol: "€", latitude: 45.4397, longitude: 4.3872, neighborSlugs: ["lyon", "roanne", "saint-chamond"] },
3212
+ { slug: "le-havre", name: "Le Havre", region: "Normandie", country: "France", population: 166000, currency: "EUR", currencySymbol: "€", latitude: 49.4944, longitude: 0.1079, neighborSlugs: ["rouen", "caen", "honfleur"] },
3213
+ { slug: "grenoble", name: "Grenoble", region: "Auvergne-Rhône-Alpes", country: "France", population: 158000, currency: "EUR", currencySymbol: "€", latitude: 45.1885, longitude: 5.7245, neighborSlugs: ["echirolles", "chambery", "annecy"] },
3214
+ { slug: "dijon", name: "Dijon", region: "Bourgogne-Franche-Comté", country: "France", population: 159000, currency: "EUR", currencySymbol: "€", latitude: 47.322, longitude: 5.0415, neighborSlugs: ["besancon", "chalon-sur-saone", "beaune"] },
3215
+ { slug: "angers", name: "Angers", region: "Pays de la Loire", country: "France", population: 155000, currency: "EUR", currencySymbol: "€", latitude: 47.4784, longitude: -0.5632, neighborSlugs: ["saumur", "cholet", "nantes"] },
3216
+ { slug: "villeurbanne", name: "Villeurbanne", region: "Auvergne-Rhône-Alpes", country: "France", population: 154000, currency: "EUR", currencySymbol: "€", latitude: 45.7667, longitude: 4.8833, neighborSlugs: ["lyon", "bron", "caluire-et-cuire"] },
3217
+ { slug: "nimes", name: "Nîmes", region: "Occitanie", country: "France", population: 148000, currency: "EUR", currencySymbol: "€", latitude: 43.8367, longitude: 4.3601, neighborSlugs: ["montpellier", "avignon", "arles"] },
3218
+ { slug: "aix-en-provence", name: "Aix-en-Provence", region: "Provence-Alpes-Côte d'Azur", country: "France", population: 147000, currency: "EUR", currencySymbol: "€", latitude: 43.5297, longitude: 5.4474, neighborSlugs: ["marseille", "gardanne", "salon-de-provence"] },
3219
+ { slug: "clermont-ferrand", name: "Clermont-Ferrand", region: "Auvergne-Rhône-Alpes", country: "France", population: 147000, currency: "EUR", currencySymbol: "€", latitude: 45.7772, longitude: 3.087, neighborSlugs: ["riom", "cournon-d-auvergne", "vichy"] },
3220
+ { slug: "brest", name: "Brest", region: "Bretagne", country: "France", population: 139000, currency: "EUR", currencySymbol: "€", latitude: 48.3904, longitude: -4.4861, neighborSlugs: ["quimper", "morlaix", "rennes"] },
3221
+ { slug: "tours", name: "Tours", region: "Centre-Val de Loire", country: "France", population: 137000, currency: "EUR", currencySymbol: "€", latitude: 47.3941, longitude: 0.6848, neighborSlugs: ["orleans", "blois", "angers"] },
3222
+ { slug: "amiens", name: "Amiens", region: "Hauts-de-France", country: "France", population: 134000, currency: "EUR", currencySymbol: "€", latitude: 49.8941, longitude: 2.2958, neighborSlugs: ["beauvais", "compiegne", "lille"] },
3223
+ { slug: "annecy", name: "Annecy", region: "Auvergne-Rhône-Alpes", country: "France", population: 131000, currency: "EUR", currencySymbol: "€", latitude: 45.8992, longitude: 6.1294, neighborSlugs: ["chambery", "geneve", "grenoble"] }
3224
+ ],
3225
+ es: [
3226
+ { slug: "madrid", name: "Madrid", region: "Comunidad de Madrid", country: "Spain", population: 3300000, currency: "EUR", currencySymbol: "€", latitude: 40.4168, longitude: -3.7038, neighborSlugs: ["alcala-de-henares", "mostoles", "getafe"] },
3227
+ { slug: "barcelona", name: "Barcelona", region: "Catalunya", country: "Spain", population: 1636000, currency: "EUR", currencySymbol: "€", latitude: 41.3851, longitude: 2.1734, neighborSlugs: ["hospitalet-de-llobregat", "badalona", "terrassa"] },
3228
+ { slug: "valencia", name: "Valencia", region: "Comunitat Valenciana", country: "Spain", population: 800000, currency: "EUR", currencySymbol: "€", latitude: 39.4699, longitude: -0.3763, neighborSlugs: ["alicante", "castellon-de-la-plana", "gandia"] },
3229
+ { slug: "sevilla", name: "Sevilla", region: "Andalucía", country: "Spain", population: 688000, currency: "EUR", currencySymbol: "€", latitude: 37.3891, longitude: -5.9845, neighborSlugs: ["cordoba", "huelva", "cadiz"] },
3230
+ { slug: "zaragoza", name: "Zaragoza", region: "Aragón", country: "Spain", population: 675000, currency: "EUR", currencySymbol: "€", latitude: 41.6488, longitude: -0.8891, neighborSlugs: ["huesca", "teruel", "madrid"] },
3231
+ { slug: "malaga", name: "Málaga", region: "Andalucía", country: "Spain", population: 579000, currency: "EUR", currencySymbol: "€", latitude: 36.7213, longitude: -4.4214, neighborSlugs: ["marbella", "torremolinos", "granada"] },
3232
+ { slug: "bilbao", name: "Bilbao", region: "País Vasco", country: "Spain", population: 346000, currency: "EUR", currencySymbol: "€", latitude: 43.263, longitude: -2.935, neighborSlugs: ["san-sebastian", "vitoria-gasteiz", "santander"] }
3233
+ ],
3234
+ de: [
3235
+ { slug: "berlin", name: "Berlin", region: "Berlin", country: "Germany", population: 3645000, currency: "EUR", currencySymbol: "€", latitude: 52.52, longitude: 13.405, neighborSlugs: ["potsdam", "cottbus", "brandenburg"] },
3236
+ { slug: "hamburg", name: "Hamburg", region: "Hamburg", country: "Germany", population: 1841000, currency: "EUR", currencySymbol: "€", latitude: 53.5511, longitude: 9.9937, neighborSlugs: ["luebeck", "bremen", "kiel"] },
3237
+ { slug: "muenchen", name: "München", region: "Bayern", country: "Germany", population: 1472000, currency: "EUR", currencySymbol: "€", latitude: 48.1351, longitude: 11.582, neighborSlugs: ["augsburg", "ingolstadt", "rosenheim"] },
3238
+ { slug: "koeln", name: "Köln", region: "Nordrhein-Westfalen", country: "Germany", population: 1086000, currency: "EUR", currencySymbol: "€", latitude: 50.9375, longitude: 6.9603, neighborSlugs: ["duesseldorf", "bonn", "leverkusen"] },
3239
+ { slug: "frankfurt", name: "Frankfurt am Main", region: "Hessen", country: "Germany", population: 753000, currency: "EUR", currencySymbol: "€", latitude: 50.1109, longitude: 8.6821, neighborSlugs: ["wiesbaden", "mainz", "darmstadt"] },
3240
+ { slug: "stuttgart", name: "Stuttgart", region: "Baden-Württemberg", country: "Germany", population: 635000, currency: "EUR", currencySymbol: "€", latitude: 48.7758, longitude: 9.1829, neighborSlugs: ["esslingen", "ludwigsburg", "karlsruhe"] }
3241
+ ],
3242
+ uk: [
3243
+ { slug: "london", name: "London", region: "Greater London", country: "United Kingdom", population: 8982000, currency: "GBP", currencySymbol: "£", latitude: 51.5074, longitude: -0.1278, neighborSlugs: ["croydon", "watford", "reading"] },
3244
+ { slug: "birmingham", name: "Birmingham", region: "West Midlands", country: "United Kingdom", population: 1149000, currency: "GBP", currencySymbol: "£", latitude: 52.4862, longitude: -1.8904, neighborSlugs: ["coventry", "wolverhampton", "solihull"] },
3245
+ { slug: "manchester", name: "Manchester", region: "Greater Manchester", country: "United Kingdom", population: 553000, currency: "GBP", currencySymbol: "£", latitude: 53.4808, longitude: -2.2426, neighborSlugs: ["salford", "bolton", "stockport"] },
3246
+ { slug: "edinburgh", name: "Edinburgh", region: "Scotland", country: "United Kingdom", population: 527000, currency: "GBP", currencySymbol: "£", latitude: 55.9533, longitude: -3.1883, neighborSlugs: ["glasgow", "livingston", "dundee"] }
3247
+ ],
3248
+ us: [
3249
+ { slug: "new-york", name: "New York", region: "NY", country: "United States", population: 8336000, currency: "USD", currencySymbol: "$", latitude: 40.7128, longitude: -74.006, neighborSlugs: ["brooklyn", "queens", "jersey-city"] },
3250
+ { slug: "los-angeles", name: "Los Angeles", region: "CA", country: "United States", population: 3979000, currency: "USD", currencySymbol: "$", latitude: 34.0522, longitude: -118.2437, neighborSlugs: ["long-beach", "pasadena", "glendale"] },
3251
+ { slug: "chicago", name: "Chicago", region: "IL", country: "United States", population: 2693000, currency: "USD", currencySymbol: "$", latitude: 41.8781, longitude: -87.6298, neighborSlugs: ["evanston", "naperville", "aurora"] },
3252
+ { slug: "houston", name: "Houston", region: "TX", country: "United States", population: 2320000, currency: "USD", currencySymbol: "$", latitude: 29.7604, longitude: -95.3698, neighborSlugs: ["the-woodlands", "sugar-land", "katy"] },
3253
+ { slug: "miami", name: "Miami", region: "FL", country: "United States", population: 442000, currency: "USD", currencySymbol: "$", latitude: 25.7617, longitude: -80.1918, neighborSlugs: ["fort-lauderdale", "miami-beach", "hialeah"] },
3254
+ { slug: "san-francisco", name: "San Francisco", region: "CA", country: "United States", population: 873000, currency: "USD", currencySymbol: "$", latitude: 37.7749, longitude: -122.4194, neighborSlugs: ["oakland", "san-jose", "berkeley"] }
3255
+ ],
3256
+ be: [
3257
+ { slug: "bruxelles", name: "Bruxelles", region: "Bruxelles-Capitale", country: "Belgium", population: 1220000, currency: "EUR", currencySymbol: "€", latitude: 50.8503, longitude: 4.3517, neighborSlugs: ["anvers", "gand", "liege"] },
3258
+ { slug: "anvers", name: "Anvers", region: "Flandre", country: "Belgium", population: 530000, currency: "EUR", currencySymbol: "€", latitude: 51.2194, longitude: 4.4025, neighborSlugs: ["bruxelles", "malines", "gand"] },
3259
+ { slug: "liege", name: "Liège", region: "Wallonie", country: "Belgium", population: 197000, currency: "EUR", currencySymbol: "€", latitude: 50.6326, longitude: 5.5797, neighborSlugs: ["namur", "charleroi", "bruxelles"] }
3260
+ ],
3261
+ ch: [
3262
+ { slug: "zurich", name: "Zürich", region: "Zürich", country: "Switzerland", population: 434000, currency: "CHF", currencySymbol: "CHF", latitude: 47.3769, longitude: 8.5417, neighborSlugs: ["bale", "berne", "lucerne"] },
3263
+ { slug: "geneve", name: "Genève", region: "Genève", country: "Switzerland", population: 203000, currency: "EUR", currencySymbol: "CHF", latitude: 46.2044, longitude: 6.1432, neighborSlugs: ["lausanne", "annecy", "nyon"] },
3264
+ { slug: "lausanne", name: "Lausanne", region: "Vaud", country: "Switzerland", population: 140000, currency: "CHF", currencySymbol: "CHF", latitude: 46.5197, longitude: 6.6323, neighborSlugs: ["geneve", "montreux", "vevey"] }
3265
+ ],
3266
+ it: [
3267
+ { slug: "roma", name: "Roma", region: "Lazio", country: "Italy", population: 2873000, currency: "EUR", currencySymbol: "€", latitude: 41.9028, longitude: 12.4964, neighborSlugs: ["latina", "fiumicino", "tivoli"] },
3268
+ { slug: "milano", name: "Milano", region: "Lombardia", country: "Italy", population: 1366000, currency: "EUR", currencySymbol: "€", latitude: 45.4642, longitude: 9.19, neighborSlugs: ["monza", "bergamo", "brescia"] },
3269
+ { slug: "torino", name: "Torino", region: "Piemonte", country: "Italy", population: 870000, currency: "EUR", currencySymbol: "€", latitude: 45.0703, longitude: 7.6869, neighborSlugs: ["novara", "alessandria", "asti"] }
3270
+ ],
3271
+ ca: [
3272
+ { slug: "toronto", name: "Toronto", region: "ON", country: "Canada", population: 2794000, currency: "CAD", currencySymbol: "$", latitude: 43.6532, longitude: -79.3832, neighborSlugs: ["mississauga", "brampton", "markham"] },
3273
+ { slug: "montreal", name: "Montréal", region: "QC", country: "Canada", population: 1762000, currency: "CAD", currencySymbol: "$", latitude: 45.5017, longitude: -73.5673, neighborSlugs: ["laval", "longueuil", "quebec"] },
3274
+ { slug: "vancouver", name: "Vancouver", region: "BC", country: "Canada", population: 662000, currency: "CAD", currencySymbol: "$", latitude: 49.2827, longitude: -123.1207, neighborSlugs: ["burnaby", "richmond", "surrey"] }
3275
+ ]
3276
+ };
3277
+ function resolveBuiltInLocations(territories) {
3278
+ if (!territories) {
3279
+ return [...BUILT_IN_LOCATIONS_DATABASE.fr];
3280
+ }
3281
+ const requested = (Array.isArray(territories) ? territories : [territories]).map((t) => t.toLowerCase().trim());
3282
+ const results = [];
3283
+ const addedSlugs = new Set;
3284
+ const isAllOrEurope = requested.some((t) => ["europe", "international", "all", "global", "world"].includes(t));
3285
+ if (isAllOrEurope) {
3286
+ for (const list of Object.values(BUILT_IN_LOCATIONS_DATABASE)) {
3287
+ for (const loc of list) {
3288
+ if (!addedSlugs.has(loc.slug)) {
3289
+ addedSlugs.add(loc.slug);
3290
+ results.push(loc);
3291
+ }
3292
+ }
3293
+ }
3294
+ return results;
3295
+ }
3296
+ const countryAliasMap = {
3297
+ france: "fr",
3298
+ fr: "fr",
3299
+ spain: "es",
3300
+ espagne: "es",
3301
+ es: "es",
3302
+ germany: "de",
3303
+ allemagne: "de",
3304
+ de: "de",
3305
+ uk: "uk",
3306
+ gb: "uk",
3307
+ "united kingdom": "uk",
3308
+ angleterre: "uk",
3309
+ us: "us",
3310
+ usa: "us",
3311
+ "united states": "us",
3312
+ etatsunis: "us",
3313
+ belgium: "be",
3314
+ belgique: "be",
3315
+ be: "be",
3316
+ switzerland: "ch",
3317
+ suisse: "ch",
3318
+ ch: "ch",
3319
+ italy: "it",
3320
+ italie: "it",
3321
+ it: "it",
3322
+ canada: "ca",
3323
+ ca: "ca"
3324
+ };
3325
+ for (const req of requested) {
3326
+ const code = countryAliasMap[req.replace(/[^a-z0-9]/g, "")];
3327
+ if (code && BUILT_IN_LOCATIONS_DATABASE[code]) {
3328
+ for (const loc of BUILT_IN_LOCATIONS_DATABASE[code]) {
3329
+ if (!addedSlugs.has(loc.slug)) {
3330
+ addedSlugs.add(loc.slug);
3331
+ results.push(loc);
3332
+ }
3333
+ }
3334
+ }
3335
+ }
3336
+ return results.length > 0 ? results : [...BUILT_IN_LOCATIONS_DATABASE.fr];
3337
+ }
3338
+
3193
3339
  // src/matrix-engine.ts
3194
3340
  class PseoMatrixEngine {
3195
3341
  legalEngine;
@@ -3667,8 +3813,9 @@ class PseoMatrixEngine {
3667
3813
  }
3668
3814
  generateAllMatrices(domain, data, options) {
3669
3815
  const allPages = [];
3670
- if (data.services && data.locations) {
3671
- allPages.push(...this.generateLocalGeoMatrix(domain, data.services, data.locations, options));
3816
+ if (data.services) {
3817
+ const locs = data.locations && data.locations.length > 0 ? data.locations : resolveBuiltInLocations(options.countries || options.territories || options.language || "fr");
3818
+ allPages.push(...this.generateLocalGeoMatrix(domain, data.services, locs, options));
3672
3819
  }
3673
3820
  if (data.competitors) {
3674
3821
  allPages.push(...this.generateVsMatrix(domain, data.competitors, options));
@@ -4067,21 +4214,19 @@ export const matrixEngine = new PseoMatrixEngine();
4067
4214
 
4068
4215
  export const SEO_CONFIG = {
4069
4216
  brandName: "Acme",
4070
- language: "en",
4217
+ language: "fr",
4218
+ countries: ["france", "belgique", "suisse"], // Or "europe" / "international". The SDK automatically provides verified cities!
4071
4219
  cleanDirectRoutes: true, // Eliminates parasite words (/solutions/, etc.)
4072
4220
  minPopulationToIndex: 15_000,
4073
4221
  };
4074
4222
 
4075
4223
  // Discovered EXCLUSIVELY from Frontend UI components
4224
+ // NOTE: DO NOT list cities manually! The SDK auto-provisions them based on your target countries/scope.
4076
4225
  export const PSEO_DATASET = {
4077
4226
  services: [
4078
4227
  // 1 per major frontend feature branch
4079
4228
  { slug: "autopost-facebook", name: "Autopost Facebook & Instagram", category: "Social Media", keyFeatures: ["AI Scheduling", "Visual Calendar", "Auto-Hashtags"], description: "Automated social media posting engine" },
4080
4229
  ],
4081
- locations: [
4082
- { slug: "paris", name: "Paris", region: "Île-de-France", country: "France", population: 2_161_000, currency: "EUR", currencySymbol: "€" },
4083
- { slug: "lyon", name: "Lyon", region: "Rhône-Alpes", country: "France", population: 522_000, currency: "EUR", currencySymbol: "€" },
4084
- ],
4085
4230
  targets: [
4086
4231
  { slug: "agences", name: "Marketing Agencies", type: "industry", painPoints: ["Manual Posting"], benefits: ["10x Output"] },
4087
4232
  ],
package/dist/index.mjs CHANGED
@@ -3087,6 +3087,150 @@ class ExtendedSchemaGraphBuilder {
3087
3087
  }
3088
3088
  }
3089
3089
 
3090
+ // src/built-in-locations.ts
3091
+ var BUILT_IN_LOCATIONS_DATABASE = {
3092
+ fr: [
3093
+ { slug: "paris", name: "Paris", region: "Île-de-France", country: "France", population: 2161000, currency: "EUR", currencySymbol: "€", latitude: 48.8566, longitude: 2.3522, neighborSlugs: ["boulogne-billancourt", "saint-denis", "montreuil"] },
3094
+ { slug: "marseille", name: "Marseille", region: "Provence-Alpes-Côte d'Azur", country: "France", population: 873000, currency: "EUR", currencySymbol: "€", latitude: 43.2965, longitude: 5.3698, neighborSlugs: ["aix-en-provence", "aubagne", "toulon"] },
3095
+ { slug: "lyon", name: "Lyon", region: "Auvergne-Rhône-Alpes", country: "France", population: 522000, currency: "EUR", currencySymbol: "€", latitude: 45.764, longitude: 4.8357, neighborSlugs: ["villeurbanne", "venissieux", "saint-etienne"] },
3096
+ { slug: "toulouse", name: "Toulouse", region: "Occitanie", country: "France", population: 498000, currency: "EUR", currencySymbol: "€", latitude: 43.6047, longitude: 1.4442, neighborSlugs: ["montauban", "albi", "colomiers"] },
3097
+ { slug: "nice", name: "Nice", region: "Provence-Alpes-Côte d'Azur", country: "France", population: 343000, currency: "EUR", currencySymbol: "€", latitude: 43.7102, longitude: 7.262, neighborSlugs: ["cannes", "antibes", "monaco"] },
3098
+ { slug: "nantes", name: "Nantes", region: "Pays de la Loire", country: "France", population: 320000, currency: "EUR", currencySymbol: "€", latitude: 47.2184, longitude: -1.5536, neighborSlugs: ["saint-nazaire", "angers", "rennes"] },
3099
+ { slug: "montpellier", name: "Montpellier", region: "Occitanie", country: "France", population: 299000, currency: "EUR", currencySymbol: "€", latitude: 43.6108, longitude: 3.8767, neighborSlugs: ["nimes", "beziers", "sete"] },
3100
+ { slug: "strasbourg", name: "Strasbourg", region: "Grand Est", country: "France", population: 290000, currency: "EUR", currencySymbol: "€", latitude: 48.5734, longitude: 7.7521, neighborSlugs: ["mulhouse", "colmar", "metz"] },
3101
+ { slug: "bordeaux", name: "Bordeaux", region: "Nouvelle-Aquitaine", country: "France", population: 260000, currency: "EUR", currencySymbol: "€", latitude: 44.8378, longitude: -0.5792, neighborSlugs: ["merignac", "pessac", "arcachon"] },
3102
+ { slug: "lille", name: "Lille", region: "Hauts-de-France", country: "France", population: 236000, currency: "EUR", currencySymbol: "€", latitude: 50.6292, longitude: 3.0573, neighborSlugs: ["roubaix", "tourcoing", "villeneuve-d-ascq"] },
3103
+ { slug: "rennes", name: "Rennes", region: "Bretagne", country: "France", population: 222000, currency: "EUR", currencySymbol: "€", latitude: 48.1173, longitude: -1.6778, neighborSlugs: ["saint-malo", "brest", "nantes"] },
3104
+ { slug: "toulon", name: "Toulon", region: "Provence-Alpes-Côte d'Azur", country: "France", population: 179000, currency: "EUR", currencySymbol: "€", latitude: 43.1242, longitude: 5.928, neighborSlugs: ["hyeres", "la-seyne-sur-mer", "marseille"] },
3105
+ { slug: "reims", name: "Reims", region: "Grand Est", country: "France", population: 179000, currency: "EUR", currencySymbol: "€", latitude: 49.2583, longitude: 4.0317, neighborSlugs: ["chalons-en-champagne", "troyes", "metz"] },
3106
+ { slug: "saint-etienne", name: "Saint-Étienne", region: "Auvergne-Rhône-Alpes", country: "France", population: 174000, currency: "EUR", currencySymbol: "€", latitude: 45.4397, longitude: 4.3872, neighborSlugs: ["lyon", "roanne", "saint-chamond"] },
3107
+ { slug: "le-havre", name: "Le Havre", region: "Normandie", country: "France", population: 166000, currency: "EUR", currencySymbol: "€", latitude: 49.4944, longitude: 0.1079, neighborSlugs: ["rouen", "caen", "honfleur"] },
3108
+ { slug: "grenoble", name: "Grenoble", region: "Auvergne-Rhône-Alpes", country: "France", population: 158000, currency: "EUR", currencySymbol: "€", latitude: 45.1885, longitude: 5.7245, neighborSlugs: ["echirolles", "chambery", "annecy"] },
3109
+ { slug: "dijon", name: "Dijon", region: "Bourgogne-Franche-Comté", country: "France", population: 159000, currency: "EUR", currencySymbol: "€", latitude: 47.322, longitude: 5.0415, neighborSlugs: ["besancon", "chalon-sur-saone", "beaune"] },
3110
+ { slug: "angers", name: "Angers", region: "Pays de la Loire", country: "France", population: 155000, currency: "EUR", currencySymbol: "€", latitude: 47.4784, longitude: -0.5632, neighborSlugs: ["saumur", "cholet", "nantes"] },
3111
+ { slug: "villeurbanne", name: "Villeurbanne", region: "Auvergne-Rhône-Alpes", country: "France", population: 154000, currency: "EUR", currencySymbol: "€", latitude: 45.7667, longitude: 4.8833, neighborSlugs: ["lyon", "bron", "caluire-et-cuire"] },
3112
+ { slug: "nimes", name: "Nîmes", region: "Occitanie", country: "France", population: 148000, currency: "EUR", currencySymbol: "€", latitude: 43.8367, longitude: 4.3601, neighborSlugs: ["montpellier", "avignon", "arles"] },
3113
+ { slug: "aix-en-provence", name: "Aix-en-Provence", region: "Provence-Alpes-Côte d'Azur", country: "France", population: 147000, currency: "EUR", currencySymbol: "€", latitude: 43.5297, longitude: 5.4474, neighborSlugs: ["marseille", "gardanne", "salon-de-provence"] },
3114
+ { slug: "clermont-ferrand", name: "Clermont-Ferrand", region: "Auvergne-Rhône-Alpes", country: "France", population: 147000, currency: "EUR", currencySymbol: "€", latitude: 45.7772, longitude: 3.087, neighborSlugs: ["riom", "cournon-d-auvergne", "vichy"] },
3115
+ { slug: "brest", name: "Brest", region: "Bretagne", country: "France", population: 139000, currency: "EUR", currencySymbol: "€", latitude: 48.3904, longitude: -4.4861, neighborSlugs: ["quimper", "morlaix", "rennes"] },
3116
+ { slug: "tours", name: "Tours", region: "Centre-Val de Loire", country: "France", population: 137000, currency: "EUR", currencySymbol: "€", latitude: 47.3941, longitude: 0.6848, neighborSlugs: ["orleans", "blois", "angers"] },
3117
+ { slug: "amiens", name: "Amiens", region: "Hauts-de-France", country: "France", population: 134000, currency: "EUR", currencySymbol: "€", latitude: 49.8941, longitude: 2.2958, neighborSlugs: ["beauvais", "compiegne", "lille"] },
3118
+ { slug: "annecy", name: "Annecy", region: "Auvergne-Rhône-Alpes", country: "France", population: 131000, currency: "EUR", currencySymbol: "€", latitude: 45.8992, longitude: 6.1294, neighborSlugs: ["chambery", "geneve", "grenoble"] }
3119
+ ],
3120
+ es: [
3121
+ { slug: "madrid", name: "Madrid", region: "Comunidad de Madrid", country: "Spain", population: 3300000, currency: "EUR", currencySymbol: "€", latitude: 40.4168, longitude: -3.7038, neighborSlugs: ["alcala-de-henares", "mostoles", "getafe"] },
3122
+ { slug: "barcelona", name: "Barcelona", region: "Catalunya", country: "Spain", population: 1636000, currency: "EUR", currencySymbol: "€", latitude: 41.3851, longitude: 2.1734, neighborSlugs: ["hospitalet-de-llobregat", "badalona", "terrassa"] },
3123
+ { slug: "valencia", name: "Valencia", region: "Comunitat Valenciana", country: "Spain", population: 800000, currency: "EUR", currencySymbol: "€", latitude: 39.4699, longitude: -0.3763, neighborSlugs: ["alicante", "castellon-de-la-plana", "gandia"] },
3124
+ { slug: "sevilla", name: "Sevilla", region: "Andalucía", country: "Spain", population: 688000, currency: "EUR", currencySymbol: "€", latitude: 37.3891, longitude: -5.9845, neighborSlugs: ["cordoba", "huelva", "cadiz"] },
3125
+ { slug: "zaragoza", name: "Zaragoza", region: "Aragón", country: "Spain", population: 675000, currency: "EUR", currencySymbol: "€", latitude: 41.6488, longitude: -0.8891, neighborSlugs: ["huesca", "teruel", "madrid"] },
3126
+ { slug: "malaga", name: "Málaga", region: "Andalucía", country: "Spain", population: 579000, currency: "EUR", currencySymbol: "€", latitude: 36.7213, longitude: -4.4214, neighborSlugs: ["marbella", "torremolinos", "granada"] },
3127
+ { slug: "bilbao", name: "Bilbao", region: "País Vasco", country: "Spain", population: 346000, currency: "EUR", currencySymbol: "€", latitude: 43.263, longitude: -2.935, neighborSlugs: ["san-sebastian", "vitoria-gasteiz", "santander"] }
3128
+ ],
3129
+ de: [
3130
+ { slug: "berlin", name: "Berlin", region: "Berlin", country: "Germany", population: 3645000, currency: "EUR", currencySymbol: "€", latitude: 52.52, longitude: 13.405, neighborSlugs: ["potsdam", "cottbus", "brandenburg"] },
3131
+ { slug: "hamburg", name: "Hamburg", region: "Hamburg", country: "Germany", population: 1841000, currency: "EUR", currencySymbol: "€", latitude: 53.5511, longitude: 9.9937, neighborSlugs: ["luebeck", "bremen", "kiel"] },
3132
+ { slug: "muenchen", name: "München", region: "Bayern", country: "Germany", population: 1472000, currency: "EUR", currencySymbol: "€", latitude: 48.1351, longitude: 11.582, neighborSlugs: ["augsburg", "ingolstadt", "rosenheim"] },
3133
+ { slug: "koeln", name: "Köln", region: "Nordrhein-Westfalen", country: "Germany", population: 1086000, currency: "EUR", currencySymbol: "€", latitude: 50.9375, longitude: 6.9603, neighborSlugs: ["duesseldorf", "bonn", "leverkusen"] },
3134
+ { slug: "frankfurt", name: "Frankfurt am Main", region: "Hessen", country: "Germany", population: 753000, currency: "EUR", currencySymbol: "€", latitude: 50.1109, longitude: 8.6821, neighborSlugs: ["wiesbaden", "mainz", "darmstadt"] },
3135
+ { slug: "stuttgart", name: "Stuttgart", region: "Baden-Württemberg", country: "Germany", population: 635000, currency: "EUR", currencySymbol: "€", latitude: 48.7758, longitude: 9.1829, neighborSlugs: ["esslingen", "ludwigsburg", "karlsruhe"] }
3136
+ ],
3137
+ uk: [
3138
+ { slug: "london", name: "London", region: "Greater London", country: "United Kingdom", population: 8982000, currency: "GBP", currencySymbol: "£", latitude: 51.5074, longitude: -0.1278, neighborSlugs: ["croydon", "watford", "reading"] },
3139
+ { slug: "birmingham", name: "Birmingham", region: "West Midlands", country: "United Kingdom", population: 1149000, currency: "GBP", currencySymbol: "£", latitude: 52.4862, longitude: -1.8904, neighborSlugs: ["coventry", "wolverhampton", "solihull"] },
3140
+ { slug: "manchester", name: "Manchester", region: "Greater Manchester", country: "United Kingdom", population: 553000, currency: "GBP", currencySymbol: "£", latitude: 53.4808, longitude: -2.2426, neighborSlugs: ["salford", "bolton", "stockport"] },
3141
+ { slug: "edinburgh", name: "Edinburgh", region: "Scotland", country: "United Kingdom", population: 527000, currency: "GBP", currencySymbol: "£", latitude: 55.9533, longitude: -3.1883, neighborSlugs: ["glasgow", "livingston", "dundee"] }
3142
+ ],
3143
+ us: [
3144
+ { slug: "new-york", name: "New York", region: "NY", country: "United States", population: 8336000, currency: "USD", currencySymbol: "$", latitude: 40.7128, longitude: -74.006, neighborSlugs: ["brooklyn", "queens", "jersey-city"] },
3145
+ { slug: "los-angeles", name: "Los Angeles", region: "CA", country: "United States", population: 3979000, currency: "USD", currencySymbol: "$", latitude: 34.0522, longitude: -118.2437, neighborSlugs: ["long-beach", "pasadena", "glendale"] },
3146
+ { slug: "chicago", name: "Chicago", region: "IL", country: "United States", population: 2693000, currency: "USD", currencySymbol: "$", latitude: 41.8781, longitude: -87.6298, neighborSlugs: ["evanston", "naperville", "aurora"] },
3147
+ { slug: "houston", name: "Houston", region: "TX", country: "United States", population: 2320000, currency: "USD", currencySymbol: "$", latitude: 29.7604, longitude: -95.3698, neighborSlugs: ["the-woodlands", "sugar-land", "katy"] },
3148
+ { slug: "miami", name: "Miami", region: "FL", country: "United States", population: 442000, currency: "USD", currencySymbol: "$", latitude: 25.7617, longitude: -80.1918, neighborSlugs: ["fort-lauderdale", "miami-beach", "hialeah"] },
3149
+ { slug: "san-francisco", name: "San Francisco", region: "CA", country: "United States", population: 873000, currency: "USD", currencySymbol: "$", latitude: 37.7749, longitude: -122.4194, neighborSlugs: ["oakland", "san-jose", "berkeley"] }
3150
+ ],
3151
+ be: [
3152
+ { slug: "bruxelles", name: "Bruxelles", region: "Bruxelles-Capitale", country: "Belgium", population: 1220000, currency: "EUR", currencySymbol: "€", latitude: 50.8503, longitude: 4.3517, neighborSlugs: ["anvers", "gand", "liege"] },
3153
+ { slug: "anvers", name: "Anvers", region: "Flandre", country: "Belgium", population: 530000, currency: "EUR", currencySymbol: "€", latitude: 51.2194, longitude: 4.4025, neighborSlugs: ["bruxelles", "malines", "gand"] },
3154
+ { slug: "liege", name: "Liège", region: "Wallonie", country: "Belgium", population: 197000, currency: "EUR", currencySymbol: "€", latitude: 50.6326, longitude: 5.5797, neighborSlugs: ["namur", "charleroi", "bruxelles"] }
3155
+ ],
3156
+ ch: [
3157
+ { slug: "zurich", name: "Zürich", region: "Zürich", country: "Switzerland", population: 434000, currency: "CHF", currencySymbol: "CHF", latitude: 47.3769, longitude: 8.5417, neighborSlugs: ["bale", "berne", "lucerne"] },
3158
+ { slug: "geneve", name: "Genève", region: "Genève", country: "Switzerland", population: 203000, currency: "EUR", currencySymbol: "CHF", latitude: 46.2044, longitude: 6.1432, neighborSlugs: ["lausanne", "annecy", "nyon"] },
3159
+ { slug: "lausanne", name: "Lausanne", region: "Vaud", country: "Switzerland", population: 140000, currency: "CHF", currencySymbol: "CHF", latitude: 46.5197, longitude: 6.6323, neighborSlugs: ["geneve", "montreux", "vevey"] }
3160
+ ],
3161
+ it: [
3162
+ { slug: "roma", name: "Roma", region: "Lazio", country: "Italy", population: 2873000, currency: "EUR", currencySymbol: "€", latitude: 41.9028, longitude: 12.4964, neighborSlugs: ["latina", "fiumicino", "tivoli"] },
3163
+ { slug: "milano", name: "Milano", region: "Lombardia", country: "Italy", population: 1366000, currency: "EUR", currencySymbol: "€", latitude: 45.4642, longitude: 9.19, neighborSlugs: ["monza", "bergamo", "brescia"] },
3164
+ { slug: "torino", name: "Torino", region: "Piemonte", country: "Italy", population: 870000, currency: "EUR", currencySymbol: "€", latitude: 45.0703, longitude: 7.6869, neighborSlugs: ["novara", "alessandria", "asti"] }
3165
+ ],
3166
+ ca: [
3167
+ { slug: "toronto", name: "Toronto", region: "ON", country: "Canada", population: 2794000, currency: "CAD", currencySymbol: "$", latitude: 43.6532, longitude: -79.3832, neighborSlugs: ["mississauga", "brampton", "markham"] },
3168
+ { slug: "montreal", name: "Montréal", region: "QC", country: "Canada", population: 1762000, currency: "CAD", currencySymbol: "$", latitude: 45.5017, longitude: -73.5673, neighborSlugs: ["laval", "longueuil", "quebec"] },
3169
+ { slug: "vancouver", name: "Vancouver", region: "BC", country: "Canada", population: 662000, currency: "CAD", currencySymbol: "$", latitude: 49.2827, longitude: -123.1207, neighborSlugs: ["burnaby", "richmond", "surrey"] }
3170
+ ]
3171
+ };
3172
+ function resolveBuiltInLocations(territories) {
3173
+ if (!territories) {
3174
+ return [...BUILT_IN_LOCATIONS_DATABASE.fr];
3175
+ }
3176
+ const requested = (Array.isArray(territories) ? territories : [territories]).map((t) => t.toLowerCase().trim());
3177
+ const results = [];
3178
+ const addedSlugs = new Set;
3179
+ const isAllOrEurope = requested.some((t) => ["europe", "international", "all", "global", "world"].includes(t));
3180
+ if (isAllOrEurope) {
3181
+ for (const list of Object.values(BUILT_IN_LOCATIONS_DATABASE)) {
3182
+ for (const loc of list) {
3183
+ if (!addedSlugs.has(loc.slug)) {
3184
+ addedSlugs.add(loc.slug);
3185
+ results.push(loc);
3186
+ }
3187
+ }
3188
+ }
3189
+ return results;
3190
+ }
3191
+ const countryAliasMap = {
3192
+ france: "fr",
3193
+ fr: "fr",
3194
+ spain: "es",
3195
+ espagne: "es",
3196
+ es: "es",
3197
+ germany: "de",
3198
+ allemagne: "de",
3199
+ de: "de",
3200
+ uk: "uk",
3201
+ gb: "uk",
3202
+ "united kingdom": "uk",
3203
+ angleterre: "uk",
3204
+ us: "us",
3205
+ usa: "us",
3206
+ "united states": "us",
3207
+ etatsunis: "us",
3208
+ belgium: "be",
3209
+ belgique: "be",
3210
+ be: "be",
3211
+ switzerland: "ch",
3212
+ suisse: "ch",
3213
+ ch: "ch",
3214
+ italy: "it",
3215
+ italie: "it",
3216
+ it: "it",
3217
+ canada: "ca",
3218
+ ca: "ca"
3219
+ };
3220
+ for (const req of requested) {
3221
+ const code = countryAliasMap[req.replace(/[^a-z0-9]/g, "")];
3222
+ if (code && BUILT_IN_LOCATIONS_DATABASE[code]) {
3223
+ for (const loc of BUILT_IN_LOCATIONS_DATABASE[code]) {
3224
+ if (!addedSlugs.has(loc.slug)) {
3225
+ addedSlugs.add(loc.slug);
3226
+ results.push(loc);
3227
+ }
3228
+ }
3229
+ }
3230
+ }
3231
+ return results.length > 0 ? results : [...BUILT_IN_LOCATIONS_DATABASE.fr];
3232
+ }
3233
+
3090
3234
  // src/matrix-engine.ts
3091
3235
  class PseoMatrixEngine {
3092
3236
  legalEngine;
@@ -3564,8 +3708,9 @@ class PseoMatrixEngine {
3564
3708
  }
3565
3709
  generateAllMatrices(domain, data, options) {
3566
3710
  const allPages = [];
3567
- if (data.services && data.locations) {
3568
- allPages.push(...this.generateLocalGeoMatrix(domain, data.services, data.locations, options));
3711
+ if (data.services) {
3712
+ const locs = data.locations && data.locations.length > 0 ? data.locations : resolveBuiltInLocations(options.countries || options.territories || options.language || "fr");
3713
+ allPages.push(...this.generateLocalGeoMatrix(domain, data.services, locs, options));
3569
3714
  }
3570
3715
  if (data.competitors) {
3571
3716
  allPages.push(...this.generateVsMatrix(domain, data.competitors, options));
@@ -3964,21 +4109,19 @@ export const matrixEngine = new PseoMatrixEngine();
3964
4109
 
3965
4110
  export const SEO_CONFIG = {
3966
4111
  brandName: "Acme",
3967
- language: "en",
4112
+ language: "fr",
4113
+ countries: ["france", "belgique", "suisse"], // Or "europe" / "international". The SDK automatically provides verified cities!
3968
4114
  cleanDirectRoutes: true, // Eliminates parasite words (/solutions/, etc.)
3969
4115
  minPopulationToIndex: 15_000,
3970
4116
  };
3971
4117
 
3972
4118
  // Discovered EXCLUSIVELY from Frontend UI components
4119
+ // NOTE: DO NOT list cities manually! The SDK auto-provisions them based on your target countries/scope.
3973
4120
  export const PSEO_DATASET = {
3974
4121
  services: [
3975
4122
  // 1 per major frontend feature branch
3976
4123
  { slug: "autopost-facebook", name: "Autopost Facebook & Instagram", category: "Social Media", keyFeatures: ["AI Scheduling", "Visual Calendar", "Auto-Hashtags"], description: "Automated social media posting engine" },
3977
4124
  ],
3978
- locations: [
3979
- { slug: "paris", name: "Paris", region: "Île-de-France", country: "France", population: 2_161_000, currency: "EUR", currencySymbol: "€" },
3980
- { slug: "lyon", name: "Lyon", region: "Rhône-Alpes", country: "France", population: 522_000, currency: "EUR", currencySymbol: "€" },
3981
- ],
3982
4125
  targets: [
3983
4126
  { slug: "agences", name: "Marketing Agencies", type: "industry", painPoints: ["Manual Posting"], benefits: ["10x Output"] },
3984
4127
  ],
@@ -6300,6 +6443,7 @@ var LynxSeo = {
6300
6443
  var src_default = LynxSeo;
6301
6444
  export {
6302
6445
  validateSeoSlug,
6446
+ resolveBuiltInLocations,
6303
6447
  resolveBrandIcon,
6304
6448
  renderBrandIconSvg,
6305
6449
  getSeoAgentPrompt,
@@ -6353,6 +6497,7 @@ export {
6353
6497
  CopywritingFrameworksMaster,
6354
6498
  BrandDnaCalendarEngine,
6355
6499
  BacklinksClient,
6500
+ BUILT_IN_LOCATIONS_DATABASE,
6356
6501
  BRAND_ICONS,
6357
6502
  ApiKeyGuardian,
6358
6503
  AiCopilotClient,
@@ -127,6 +127,9 @@ export interface GeneratedPageMeta {
127
127
  export interface MatrixOptions {
128
128
  brandName: string;
129
129
  language?: string;
130
+ languages?: string[];
131
+ countries?: string[] | string;
132
+ territories?: string[] | string;
130
133
  cleanDirectRoutes?: boolean;
131
134
  minPopulationToIndex?: number;
132
135
  defaultCurrency?: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lynxflow/seo-engine",
3
- "version": "1.5.7",
3
+ "version": "1.5.9",
4
4
  "description": "High-Performance Multilingual Programmatic SEO & AI Search Engine SDK",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -0,0 +1,170 @@
1
+ /**
2
+ * 🌍 Built-in Global Geographic & Demographic Database
3
+ * Automatically provisions verified cities, populations, currencies, and coordinates
4
+ * across Europe, North America, and international markets without requiring the user to list cities manually.
5
+ */
6
+
7
+ import { PseoLocation } from "./matrix-engine";
8
+
9
+ export const BUILT_IN_LOCATIONS_DATABASE: Record<string, PseoLocation[]> = {
10
+ fr: [
11
+ { slug: "paris", name: "Paris", region: "Île-de-France", country: "France", population: 2_161_000, currency: "EUR", currencySymbol: "€", latitude: 48.8566, longitude: 2.3522, neighborSlugs: ["boulogne-billancourt", "saint-denis", "montreuil"] },
12
+ { slug: "marseille", name: "Marseille", region: "Provence-Alpes-Côte d'Azur", country: "France", population: 873_000, currency: "EUR", currencySymbol: "€", latitude: 43.2965, longitude: 5.3698, neighborSlugs: ["aix-en-provence", "aubagne", "toulon"] },
13
+ { slug: "lyon", name: "Lyon", region: "Auvergne-Rhône-Alpes", country: "France", population: 522_000, currency: "EUR", currencySymbol: "€", latitude: 45.764, longitude: 4.8357, neighborSlugs: ["villeurbanne", "venissieux", "saint-etienne"] },
14
+ { slug: "toulouse", name: "Toulouse", region: "Occitanie", country: "France", population: 498_000, currency: "EUR", currencySymbol: "€", latitude: 43.6047, longitude: 1.4442, neighborSlugs: ["montauban", "albi", "colomiers"] },
15
+ { slug: "nice", name: "Nice", region: "Provence-Alpes-Côte d'Azur", country: "France", population: 343_000, currency: "EUR", currencySymbol: "€", latitude: 43.7102, longitude: 7.262, neighborSlugs: ["cannes", "antibes", "monaco"] },
16
+ { slug: "nantes", name: "Nantes", region: "Pays de la Loire", country: "France", population: 320_000, currency: "EUR", currencySymbol: "€", latitude: 47.2184, longitude: -1.5536, neighborSlugs: ["saint-nazaire", "angers", "rennes"] },
17
+ { slug: "montpellier", name: "Montpellier", region: "Occitanie", country: "France", population: 299_000, currency: "EUR", currencySymbol: "€", latitude: 43.6108, longitude: 3.8767, neighborSlugs: ["nimes", "beziers", "sete"] },
18
+ { slug: "strasbourg", name: "Strasbourg", region: "Grand Est", country: "France", population: 290_000, currency: "EUR", currencySymbol: "€", latitude: 48.5734, longitude: 7.7521, neighborSlugs: ["mulhouse", "colmar", "metz"] },
19
+ { slug: "bordeaux", name: "Bordeaux", region: "Nouvelle-Aquitaine", country: "France", population: 260_000, currency: "EUR", currencySymbol: "€", latitude: 44.8378, longitude: -0.5792, neighborSlugs: ["merignac", "pessac", "arcachon"] },
20
+ { slug: "lille", name: "Lille", region: "Hauts-de-France", country: "France", population: 236_000, currency: "EUR", currencySymbol: "€", latitude: 50.6292, longitude: 3.0573, neighborSlugs: ["roubaix", "tourcoing", "villeneuve-d-ascq"] },
21
+ { slug: "rennes", name: "Rennes", region: "Bretagne", country: "France", population: 222_000, currency: "EUR", currencySymbol: "€", latitude: 48.1173, longitude: -1.6778, neighborSlugs: ["saint-malo", "brest", "nantes"] },
22
+ { slug: "toulon", name: "Toulon", region: "Provence-Alpes-Côte d'Azur", country: "France", population: 179_000, currency: "EUR", currencySymbol: "€", latitude: 43.1242, longitude: 5.928, neighborSlugs: ["hyeres", "la-seyne-sur-mer", "marseille"] },
23
+ { slug: "reims", name: "Reims", region: "Grand Est", country: "France", population: 179_000, currency: "EUR", currencySymbol: "€", latitude: 49.2583, longitude: 4.0317, neighborSlugs: ["chalons-en-champagne", "troyes", "metz"] },
24
+ { slug: "saint-etienne", name: "Saint-Étienne", region: "Auvergne-Rhône-Alpes", country: "France", population: 174_000, currency: "EUR", currencySymbol: "€", latitude: 45.4397, longitude: 4.3872, neighborSlugs: ["lyon", "roanne", "saint-chamond"] },
25
+ { slug: "le-havre", name: "Le Havre", region: "Normandie", country: "France", population: 166_000, currency: "EUR", currencySymbol: "€", latitude: 49.4944, longitude: 0.1079, neighborSlugs: ["rouen", "caen", "honfleur"] },
26
+ { slug: "grenoble", name: "Grenoble", region: "Auvergne-Rhône-Alpes", country: "France", population: 158_000, currency: "EUR", currencySymbol: "€", latitude: 45.1885, longitude: 5.7245, neighborSlugs: ["echirolles", "chambery", "annecy"] },
27
+ { slug: "dijon", name: "Dijon", region: "Bourgogne-Franche-Comté", country: "France", population: 159_000, currency: "EUR", currencySymbol: "€", latitude: 47.322, longitude: 5.0415, neighborSlugs: ["besancon", "chalon-sur-saone", "beaune"] },
28
+ { slug: "angers", name: "Angers", region: "Pays de la Loire", country: "France", population: 155_000, currency: "EUR", currencySymbol: "€", latitude: 47.4784, longitude: -0.5632, neighborSlugs: ["saumur", "cholet", "nantes"] },
29
+ { slug: "villeurbanne", name: "Villeurbanne", region: "Auvergne-Rhône-Alpes", country: "France", population: 154_000, currency: "EUR", currencySymbol: "€", latitude: 45.7667, longitude: 4.8833, neighborSlugs: ["lyon", "bron", "caluire-et-cuire"] },
30
+ { slug: "nimes", name: "Nîmes", region: "Occitanie", country: "France", population: 148_000, currency: "EUR", currencySymbol: "€", latitude: 43.8367, longitude: 4.3601, neighborSlugs: ["montpellier", "avignon", "arles"] },
31
+ { slug: "aix-en-provence", name: "Aix-en-Provence", region: "Provence-Alpes-Côte d'Azur", country: "France", population: 147_000, currency: "EUR", currencySymbol: "€", latitude: 43.5297, longitude: 5.4474, neighborSlugs: ["marseille", "gardanne", "salon-de-provence"] },
32
+ { slug: "clermont-ferrand", name: "Clermont-Ferrand", region: "Auvergne-Rhône-Alpes", country: "France", population: 147_000, currency: "EUR", currencySymbol: "€", latitude: 45.7772, longitude: 3.087, neighborSlugs: ["riom", "cournon-d-auvergne", "vichy"] },
33
+ { slug: "brest", name: "Brest", region: "Bretagne", country: "France", population: 139_000, currency: "EUR", currencySymbol: "€", latitude: 48.3904, longitude: -4.4861, neighborSlugs: ["quimper", "morlaix", "rennes"] },
34
+ { slug: "tours", name: "Tours", region: "Centre-Val de Loire", country: "France", population: 137_000, currency: "EUR", currencySymbol: "€", latitude: 47.3941, longitude: 0.6848, neighborSlugs: ["orleans", "blois", "angers"] },
35
+ { slug: "amiens", name: "Amiens", region: "Hauts-de-France", country: "France", population: 134_000, currency: "EUR", currencySymbol: "€", latitude: 49.8941, longitude: 2.2958, neighborSlugs: ["beauvais", "compiegne", "lille"] },
36
+ { slug: "annecy", name: "Annecy", region: "Auvergne-Rhône-Alpes", country: "France", population: 131_000, currency: "EUR", currencySymbol: "€", latitude: 45.8992, longitude: 6.1294, neighborSlugs: ["chambery", "geneve", "grenoble"] },
37
+ ],
38
+
39
+ es: [
40
+ { slug: "madrid", name: "Madrid", region: "Comunidad de Madrid", country: "Spain", population: 3_300_000, currency: "EUR", currencySymbol: "€", latitude: 40.4168, longitude: -3.7038, neighborSlugs: ["alcala-de-henares", "mostoles", "getafe"] },
41
+ { slug: "barcelona", name: "Barcelona", region: "Catalunya", country: "Spain", population: 1_636_000, currency: "EUR", currencySymbol: "€", latitude: 41.3851, longitude: 2.1734, neighborSlugs: ["hospitalet-de-llobregat", "badalona", "terrassa"] },
42
+ { slug: "valencia", name: "Valencia", region: "Comunitat Valenciana", country: "Spain", population: 800_000, currency: "EUR", currencySymbol: "€", latitude: 39.4699, longitude: -0.3763, neighborSlugs: ["alicante", "castellon-de-la-plana", "gandia"] },
43
+ { slug: "sevilla", name: "Sevilla", region: "Andalucía", country: "Spain", population: 688_000, currency: "EUR", currencySymbol: "€", latitude: 37.3891, longitude: -5.9845, neighborSlugs: ["cordoba", "huelva", "cadiz"] },
44
+ { slug: "zaragoza", name: "Zaragoza", region: "Aragón", country: "Spain", population: 675_000, currency: "EUR", currencySymbol: "€", latitude: 41.6488, longitude: -0.8891, neighborSlugs: ["huesca", "teruel", "madrid"] },
45
+ { slug: "malaga", name: "Málaga", region: "Andalucía", country: "Spain", population: 579_000, currency: "EUR", currencySymbol: "€", latitude: 36.7213, longitude: -4.4214, neighborSlugs: ["marbella", "torremolinos", "granada"] },
46
+ { slug: "bilbao", name: "Bilbao", region: "País Vasco", country: "Spain", population: 346_000, currency: "EUR", currencySymbol: "€", latitude: 43.263, longitude: -2.935, neighborSlugs: ["san-sebastian", "vitoria-gasteiz", "santander"] },
47
+ ],
48
+
49
+ de: [
50
+ { slug: "berlin", name: "Berlin", region: "Berlin", country: "Germany", population: 3_645_000, currency: "EUR", currencySymbol: "€", latitude: 52.52, longitude: 13.405, neighborSlugs: ["potsdam", "cottbus", "brandenburg"] },
51
+ { slug: "hamburg", name: "Hamburg", region: "Hamburg", country: "Germany", population: 1_841_000, currency: "EUR", currencySymbol: "€", latitude: 53.5511, longitude: 9.9937, neighborSlugs: ["luebeck", "bremen", "kiel"] },
52
+ { slug: "muenchen", name: "München", region: "Bayern", country: "Germany", population: 1_472_000, currency: "EUR", currencySymbol: "€", latitude: 48.1351, longitude: 11.582, neighborSlugs: ["augsburg", "ingolstadt", "rosenheim"] },
53
+ { slug: "koeln", name: "Köln", region: "Nordrhein-Westfalen", country: "Germany", population: 1_086_000, currency: "EUR", currencySymbol: "€", latitude: 50.9375, longitude: 6.9603, neighborSlugs: ["duesseldorf", "bonn", "leverkusen"] },
54
+ { slug: "frankfurt", name: "Frankfurt am Main", region: "Hessen", country: "Germany", population: 753_000, currency: "EUR", currencySymbol: "€", latitude: 50.1109, longitude: 8.6821, neighborSlugs: ["wiesbaden", "mainz", "darmstadt"] },
55
+ { slug: "stuttgart", name: "Stuttgart", region: "Baden-Württemberg", country: "Germany", population: 635_000, currency: "EUR", currencySymbol: "€", latitude: 48.7758, longitude: 9.1829, neighborSlugs: ["esslingen", "ludwigsburg", "karlsruhe"] },
56
+ ],
57
+
58
+ uk: [
59
+ { slug: "london", name: "London", region: "Greater London", country: "United Kingdom", population: 8_982_000, currency: "GBP", currencySymbol: "£", latitude: 51.5074, longitude: -0.1278, neighborSlugs: ["croydon", "watford", "reading"] },
60
+ { slug: "birmingham", name: "Birmingham", region: "West Midlands", country: "United Kingdom", population: 1_149_000, currency: "GBP", currencySymbol: "£", latitude: 52.4862, longitude: -1.8904, neighborSlugs: ["coventry", "wolverhampton", "solihull"] },
61
+ { slug: "manchester", name: "Manchester", region: "Greater Manchester", country: "United Kingdom", population: 553_000, currency: "GBP", currencySymbol: "£", latitude: 53.4808, longitude: -2.2426, neighborSlugs: ["salford", "bolton", "stockport"] },
62
+ { slug: "edinburgh", name: "Edinburgh", region: "Scotland", country: "United Kingdom", population: 527_000, currency: "GBP", currencySymbol: "£", latitude: 55.9533, longitude: -3.1883, neighborSlugs: ["glasgow", "livingston", "dundee"] },
63
+ ],
64
+
65
+ us: [
66
+ { slug: "new-york", name: "New York", region: "NY", country: "United States", population: 8_336_000, currency: "USD", currencySymbol: "$", latitude: 40.7128, longitude: -74.006, neighborSlugs: ["brooklyn", "queens", "jersey-city"] },
67
+ { slug: "los-angeles", name: "Los Angeles", region: "CA", country: "United States", population: 3_979_000, currency: "USD", currencySymbol: "$", latitude: 34.0522, longitude: -118.2437, neighborSlugs: ["long-beach", "pasadena", "glendale"] },
68
+ { slug: "chicago", name: "Chicago", region: "IL", country: "United States", population: 2_693_000, currency: "USD", currencySymbol: "$", latitude: 41.8781, longitude: -87.6298, neighborSlugs: ["evanston", "naperville", "aurora"] },
69
+ { slug: "houston", name: "Houston", region: "TX", country: "United States", population: 2_320_000, currency: "USD", currencySymbol: "$", latitude: 29.7604, longitude: -95.3698, neighborSlugs: ["the-woodlands", "sugar-land", "katy"] },
70
+ { slug: "miami", name: "Miami", region: "FL", country: "United States", population: 442_000, currency: "USD", currencySymbol: "$", latitude: 25.7617, longitude: -80.1918, neighborSlugs: ["fort-lauderdale", "miami-beach", "hialeah"] },
71
+ { slug: "san-francisco", name: "San Francisco", region: "CA", country: "United States", population: 873_000, currency: "USD", currencySymbol: "$", latitude: 37.7749, longitude: -122.4194, neighborSlugs: ["oakland", "san-jose", "berkeley"] },
72
+ ],
73
+
74
+ be: [
75
+ { slug: "bruxelles", name: "Bruxelles", region: "Bruxelles-Capitale", country: "Belgium", population: 1_220_000, currency: "EUR", currencySymbol: "€", latitude: 50.8503, longitude: 4.3517, neighborSlugs: ["anvers", "gand", "liege"] },
76
+ { slug: "anvers", name: "Anvers", region: "Flandre", country: "Belgium", population: 530_000, currency: "EUR", currencySymbol: "€", latitude: 51.2194, longitude: 4.4025, neighborSlugs: ["bruxelles", "malines", "gand"] },
77
+ { slug: "liege", name: "Liège", region: "Wallonie", country: "Belgium", population: 197_000, currency: "EUR", currencySymbol: "€", latitude: 50.6326, longitude: 5.5797, neighborSlugs: ["namur", "charleroi", "bruxelles"] },
78
+ ],
79
+
80
+ ch: [
81
+ { slug: "zurich", name: "Zürich", region: "Zürich", country: "Switzerland", population: 434_000, currency: "CHF", currencySymbol: "CHF", latitude: 47.3769, longitude: 8.5417, neighborSlugs: ["bale", "berne", "lucerne"] },
82
+ { slug: "geneve", name: "Genève", region: "Genève", country: "Switzerland", population: 203_000, currency: "EUR", currencySymbol: "CHF", latitude: 46.2044, longitude: 6.1432, neighborSlugs: ["lausanne", "annecy", "nyon"] },
83
+ { slug: "lausanne", name: "Lausanne", region: "Vaud", country: "Switzerland", population: 140_000, currency: "CHF", currencySymbol: "CHF", latitude: 46.5197, longitude: 6.6323, neighborSlugs: ["geneve", "montreux", "vevey"] },
84
+ ],
85
+
86
+ it: [
87
+ { slug: "roma", name: "Roma", region: "Lazio", country: "Italy", population: 2_873_000, currency: "EUR", currencySymbol: "€", latitude: 41.9028, longitude: 12.4964, neighborSlugs: ["latina", "fiumicino", "tivoli"] },
88
+ { slug: "milano", name: "Milano", region: "Lombardia", country: "Italy", population: 1_366_000, currency: "EUR", currencySymbol: "€", latitude: 45.4642, longitude: 9.19, neighborSlugs: ["monza", "bergamo", "brescia"] },
89
+ { slug: "torino", name: "Torino", region: "Piemonte", country: "Italy", population: 870_000, currency: "EUR", currencySymbol: "€", latitude: 45.0703, longitude: 7.6869, neighborSlugs: ["novara", "alessandria", "asti"] },
90
+ ],
91
+
92
+ ca: [
93
+ { slug: "toronto", name: "Toronto", region: "ON", country: "Canada", population: 2_794_000, currency: "CAD", currencySymbol: "$", latitude: 43.6532, longitude: -79.3832, neighborSlugs: ["mississauga", "brampton", "markham"] },
94
+ { slug: "montreal", name: "Montréal", region: "QC", country: "Canada", population: 1_762_000, currency: "CAD", currencySymbol: "$", latitude: 45.5017, longitude: -73.5673, neighborSlugs: ["laval", "longueuil", "quebec"] },
95
+ { slug: "vancouver", name: "Vancouver", region: "BC", country: "Canada", population: 662_000, currency: "CAD", currencySymbol: "$", latitude: 49.2827, longitude: -123.1207, neighborSlugs: ["burnaby", "richmond", "surrey"] },
96
+ ],
97
+ };
98
+
99
+ /**
100
+ * Resolves built-in locations by country code or territory keyword.
101
+ * Supports: "fr", "france", "es", "spain", "de", "germany", "uk", "us", "be", "ch", "it", "ca", "europe", "international", "all"
102
+ */
103
+ export function resolveBuiltInLocations(territories?: string[] | string): PseoLocation[] {
104
+ if (!territories) {
105
+ // Default: France + Belgium + Switzerland if unspecified
106
+ return [...BUILT_IN_LOCATIONS_DATABASE.fr];
107
+ }
108
+
109
+ const requested = (Array.isArray(territories) ? territories : [territories]).map((t) => t.toLowerCase().trim());
110
+ const results: PseoLocation[] = [];
111
+ const addedSlugs = new Set<string>();
112
+
113
+ const isAllOrEurope = requested.some((t) => ["europe", "international", "all", "global", "world"].includes(t));
114
+
115
+ if (isAllOrEurope) {
116
+ for (const list of Object.values(BUILT_IN_LOCATIONS_DATABASE)) {
117
+ for (const loc of list) {
118
+ if (!addedSlugs.has(loc.slug)) {
119
+ addedSlugs.add(loc.slug);
120
+ results.push(loc);
121
+ }
122
+ }
123
+ }
124
+ return results;
125
+ }
126
+
127
+ const countryAliasMap: Record<string, string> = {
128
+ france: "fr",
129
+ fr: "fr",
130
+ spain: "es",
131
+ espagne: "es",
132
+ es: "es",
133
+ germany: "de",
134
+ allemagne: "de",
135
+ de: "de",
136
+ uk: "uk",
137
+ gb: "uk",
138
+ "united kingdom": "uk",
139
+ angleterre: "uk",
140
+ us: "us",
141
+ usa: "us",
142
+ "united states": "us",
143
+ etatsunis: "us",
144
+ belgium: "be",
145
+ belgique: "be",
146
+ be: "be",
147
+ switzerland: "ch",
148
+ suisse: "ch",
149
+ ch: "ch",
150
+ italy: "it",
151
+ italie: "it",
152
+ it: "it",
153
+ canada: "ca",
154
+ ca: "ca",
155
+ };
156
+
157
+ for (const req of requested) {
158
+ const code = countryAliasMap[req.replace(/[^a-z0-9]/g, "")];
159
+ if (code && BUILT_IN_LOCATIONS_DATABASE[code]) {
160
+ for (const loc of BUILT_IN_LOCATIONS_DATABASE[code]) {
161
+ if (!addedSlugs.has(loc.slug)) {
162
+ addedSlugs.add(loc.slug);
163
+ results.push(loc);
164
+ }
165
+ }
166
+ }
167
+ }
168
+
169
+ return results.length > 0 ? results : [...BUILT_IN_LOCATIONS_DATABASE.fr];
170
+ }
@@ -217,4 +217,21 @@ describe("Audited Reference Engines (Advertools, Santifer, Seonaut)", () => {
217
217
  expect(llmsTxt).toContain("# Acme — AI Knowledge Graph");
218
218
  expect(llmsTxt).toContain("https://acme.com/autopost/paris");
219
219
  });
220
+
221
+ it("Built-in Locations: Auto-provisions cities without manual entry", () => {
222
+ const engine = new PseoMatrixEngine();
223
+ const pages = engine.generateAllMatrices(
224
+ "https://acme.com",
225
+ {
226
+ services: [{ slug: "autopost-facebook", name: "Autopost Facebook", category: "Social" }],
227
+ },
228
+ { brandName: "Acme", countries: ["france", "spain"] },
229
+ );
230
+
231
+ const paths = pages.map((p) => p.urlPath);
232
+ expect(paths).toContain("/autopost-facebook/paris");
233
+ expect(paths).toContain("/autopost-facebook/lyon");
234
+ expect(paths).toContain("/autopost-facebook/madrid");
235
+ expect(paths).toContain("/autopost-facebook/barcelona");
236
+ });
220
237
  });
package/src/index.ts CHANGED
@@ -14,6 +14,7 @@ export * from "./engine";
14
14
  export * from "./slug-engine";
15
15
  export * from "./legal-disclaimers";
16
16
  export * from "./matrix-engine";
17
+ export * from "./built-in-locations";
17
18
  export * from "./brand-icons";
18
19
  export * from "./urlytics-engine";
19
20
  export * from "./keyword-permutator";
package/src/llm-prompt.ts CHANGED
@@ -86,21 +86,19 @@ export const matrixEngine = new PseoMatrixEngine();
86
86
 
87
87
  export const SEO_CONFIG = {
88
88
  brandName: "Acme",
89
- language: "en",
89
+ language: "fr",
90
+ countries: ["france", "belgique", "suisse"], // Or "europe" / "international". The SDK automatically provides verified cities!
90
91
  cleanDirectRoutes: true, // Eliminates parasite words (/solutions/, etc.)
91
92
  minPopulationToIndex: 15_000,
92
93
  };
93
94
 
94
95
  // Discovered EXCLUSIVELY from Frontend UI components
96
+ // NOTE: DO NOT list cities manually! The SDK auto-provisions them based on your target countries/scope.
95
97
  export const PSEO_DATASET = {
96
98
  services: [
97
99
  // 1 per major frontend feature branch
98
100
  { slug: "autopost-facebook", name: "Autopost Facebook & Instagram", category: "Social Media", keyFeatures: ["AI Scheduling", "Visual Calendar", "Auto-Hashtags"], description: "Automated social media posting engine" },
99
101
  ],
100
- locations: [
101
- { slug: "paris", name: "Paris", region: "Île-de-France", country: "France", population: 2_161_000, currency: "EUR", currencySymbol: "€" },
102
- { slug: "lyon", name: "Lyon", region: "Rhône-Alpes", country: "France", population: 522_000, currency: "EUR", currencySymbol: "€" },
103
- ],
104
102
  targets: [
105
103
  { slug: "agences", name: "Marketing Agencies", type: "industry", painPoints: ["Manual Posting"], benefits: ["10x Output"] },
106
104
  ],
@@ -13,6 +13,7 @@
13
13
  import { cleanSeoSlug } from "./slug-engine";
14
14
  import { LegalDisclaimerEngine } from "./legal-disclaimers";
15
15
  import { ExtendedSchemaGraphBuilder } from "./extended-schemas";
16
+ import { resolveBuiltInLocations } from "./built-in-locations";
16
17
 
17
18
  export type MatrixFamily =
18
19
  | "local-geo"
@@ -155,6 +156,9 @@ export interface GeneratedPageMeta {
155
156
  export interface MatrixOptions {
156
157
  brandName: string;
157
158
  language?: string; // Default: "en", supports "fr", "de", "es", "pt", "it", etc.
159
+ languages?: string[]; // Array of supported site languages (e.g. ["fr", "en", "es"])
160
+ countries?: string[] | string; // e.g. ["france", "spain"] or "europe" or "international"
161
+ territories?: string[] | string; // Alias for countries
158
162
  cleanDirectRoutes?: boolean; // When true (default), eliminates parasite words (/solutions/, etc.) for direct /{service}/{city}
159
163
  minPopulationToIndex?: number;
160
164
  defaultCurrency?: string;
@@ -790,8 +794,11 @@ export class PseoMatrixEngine {
790
794
  ): GeneratedPageMeta[] {
791
795
  const allPages: GeneratedPageMeta[] = [];
792
796
 
793
- if (data.services && data.locations) {
794
- allPages.push(...this.generateLocalGeoMatrix(domain, data.services, data.locations, options));
797
+ if (data.services) {
798
+ const locs = data.locations && data.locations.length > 0
799
+ ? data.locations
800
+ : resolveBuiltInLocations(options.countries || options.territories || options.language || "fr");
801
+ allPages.push(...this.generateLocalGeoMatrix(domain, data.services, locs, options));
795
802
  }
796
803
  if (data.competitors) {
797
804
  allPages.push(...this.generateVsMatrix(domain, data.competitors, options));