@lynxflow/seo-engine 2.1.0 → 2.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -4,7 +4,7 @@ High-Performance Universal Programmatic SEO & Structured Data Engine for Modern
4
4
 
5
5
  [![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue.svg)](https://www.typescriptlang.org/)
6
6
  [![License](https://img.shields.io/badge/License-Proprietary-green.svg)](LICENSE)
7
- [![Version](https://img.shields.io/badge/Version-2.1.0-orange.svg)](https://www.npmjs.com/package/@lynxflow/seo-engine)
7
+ [![Version](https://img.shields.io/badge/Version-2.1.1-orange.svg)](https://www.npmjs.com/package/@lynxflow/seo-engine)
8
8
 
9
9
  ---
10
10
 
package/dist/index.js CHANGED
@@ -3787,7 +3787,7 @@ class PseoMatrixEngine {
3787
3787
  ])
3788
3788
  ]
3789
3789
  };
3790
- pages.push({
3790
+ const pageItem = {
3791
3791
  matrixFamily: "local-geo",
3792
3792
  urlPath,
3793
3793
  canonicalUrl: fullUrl,
@@ -3799,7 +3799,9 @@ class PseoMatrixEngine {
3799
3799
  neighboringLinks: neighborLinks,
3800
3800
  service: s,
3801
3801
  location: loc
3802
- });
3802
+ };
3803
+ pageItem.ogImageUrl = OgImageGenerator.generateOgImageUrl(cleanDomain, pageItem, options.brandName);
3804
+ pages.push(pageItem);
3803
3805
  }
3804
3806
  }
3805
3807
  return pages;
@@ -4145,7 +4147,7 @@ class PseoMatrixEngine {
4145
4147
  generateAllMatrices(domain, data, options) {
4146
4148
  const allPages = [];
4147
4149
  if (data.services) {
4148
- const locs = data.locations && data.locations.length > 0 ? data.locations : resolveBuiltInLocations(options.countries || options.territories || options.language || "fr");
4150
+ const locs = data.locations && data.locations.length > 0 ? data.locations : resolveBuiltInLocations(options.allowedCountries || options.countries || options.territories || "us");
4149
4151
  allPages.push(...this.generateLocalGeoMatrix(domain, data.services, locs, options));
4150
4152
  }
4151
4153
  if (data.competitors) {
@@ -4212,30 +4214,113 @@ class PseoMatrixEngine {
4212
4214
  }
4213
4215
  resolvePage(slugOrPath, domain, data, options) {
4214
4216
  const path = typeof slugOrPath === "string" ? slugOrPath.startsWith("/") ? slugOrPath : `/${slugOrPath}` : `/${slugOrPath.join("/")}`;
4215
- const pages = this.generateAllMatrices(domain, data, options);
4216
- const existing = pages.find((p) => p.urlPath === path);
4217
- if (existing)
4218
- return existing;
4217
+ const lang = options.language || "en";
4219
4218
  const segments = path.split("/").filter(Boolean);
4220
- if (segments.length >= 2 && data.services && data.services.length > 0) {
4221
- const serviceSlug = segments[0];
4222
- const locationSlug = segments[segments.length - 1];
4223
- const matchedService = data.services.find((s) => cleanSeoSlug(s.slug, { language: options.language }) === serviceSlug || s.slug === serviceSlug);
4224
- if (matchedService) {
4225
- const countryList = options.allowedCountries || options.countries;
4226
- const fallbackCountry = Array.isArray(countryList) ? countryList[0] : typeof countryList === "string" && countryList !== "international" ? countryList : "United States";
4227
- const dynamicLoc = createDynamicLocation(locationSlug, fallbackCountry, {
4228
- currency: options.defaultCurrency,
4229
- currencySymbol: options.defaultCurrencySymbol
4230
- });
4231
- if (options.strictCountryBoundary && countryList) {
4232
- const allowed = (Array.isArray(countryList) ? countryList : [countryList]).map((c) => c.toLowerCase());
4233
- if (!allowed.includes("international") && !allowed.some((c) => dynamicLoc.country.toLowerCase().includes(c))) {
4234
- return;
4219
+ if (segments.length === 0)
4220
+ return;
4221
+ const vsPrefix = this.getPrefix("vs", lang).replace(/^\//, "");
4222
+ if (segments[0] === vsPrefix && segments[1] && data.competitors) {
4223
+ const compSlug = segments[1];
4224
+ const matched = data.competitors.find((c) => cleanSeoSlug(c.slug, { language: lang }) === compSlug || c.slug === compSlug);
4225
+ if (matched) {
4226
+ return this.generateVsMatrix(domain, [matched], options)[0];
4227
+ }
4228
+ }
4229
+ const altPrefix = this.getPrefix("alternative", lang).replace(/^\//, "");
4230
+ if (segments[0] === altPrefix && segments[1] && data.competitors) {
4231
+ const compSlug = segments[1];
4232
+ const matched = data.competitors.find((c) => cleanSeoSlug(c.slug, { language: lang }) === compSlug || c.slug === compSlug);
4233
+ if (matched) {
4234
+ return this.generateAlternativesMatrix(domain, [matched], options)[0];
4235
+ }
4236
+ }
4237
+ const pricingPrefix = this.getPrefix("pricing", lang).replace(/^\//, "");
4238
+ if (segments[0] === pricingPrefix && segments[1] && data.competitors) {
4239
+ const compSlug = segments[1];
4240
+ const matched = data.competitors.find((c) => cleanSeoSlug(c.slug, { language: lang }) === compSlug || c.slug === compSlug);
4241
+ if (matched) {
4242
+ return this.generatePricingMatrix(domain, [matched], options)[0];
4243
+ }
4244
+ }
4245
+ const targetPrefix = this.getPrefix("target", lang).replace(/^\//, "");
4246
+ if (segments[0] === targetPrefix && segments[1] && data.targets) {
4247
+ const targetSlug = segments[1];
4248
+ const matched = data.targets.find((t) => cleanSeoSlug(t.slug, { language: lang }) === targetSlug || t.slug === targetSlug);
4249
+ if (matched) {
4250
+ return this.generateTargetMatrix(domain, [matched], options)[0];
4251
+ }
4252
+ }
4253
+ const integPrefix = this.getPrefix("integration", lang).replace(/^\//, "");
4254
+ if (segments[0] === integPrefix && segments[1] && data.integrations) {
4255
+ const integSlug = segments[1];
4256
+ const matched = data.integrations.find((i) => cleanSeoSlug(i.slug, { language: lang }) === integSlug || i.slug === integSlug);
4257
+ if (matched) {
4258
+ return this.generateIntegrationMatrix(domain, [matched], options)[0];
4259
+ }
4260
+ }
4261
+ const useCasePrefix = this.getPrefix("useCase", lang).replace(/^\//, "");
4262
+ if (segments[0] === useCasePrefix && segments[1] && data.useCases) {
4263
+ const ucSlug = segments[1];
4264
+ const matched = data.useCases.find((u) => cleanSeoSlug(u.slug, { language: lang }) === ucSlug || u.slug === ucSlug);
4265
+ if (matched) {
4266
+ return this.generateUseCaseMatrix(domain, [matched], options)[0];
4267
+ }
4268
+ }
4269
+ const templatePrefix = this.getPrefix("template", lang).replace(/^\//, "");
4270
+ if (segments[0] === templatePrefix && segments[1] && data.templates) {
4271
+ const tSlug = segments[1];
4272
+ const matched = data.templates.find((t) => cleanSeoSlug(t.slug, { language: lang }) === tSlug || t.slug === tSlug);
4273
+ if (matched) {
4274
+ return this.generateTemplateMatrix(domain, [matched], options)[0];
4275
+ }
4276
+ }
4277
+ const glossaryPrefix = this.getPrefix("glossary", lang).replace(/^\//, "");
4278
+ if (segments[0] === glossaryPrefix && segments[1] && data.glossaryTerms) {
4279
+ const gSlug = segments[1];
4280
+ const matched = data.glossaryTerms.find((g) => cleanSeoSlug(g.slug, { language: lang }) === gSlug || g.slug === gSlug);
4281
+ if (matched) {
4282
+ return this.generateGlossaryMatrix(domain, [matched], options)[0];
4283
+ }
4284
+ }
4285
+ const calcPrefix = this.getPrefix("calculator", lang).replace(/^\//, "");
4286
+ if (segments[0] === calcPrefix && segments[1] && data.calculators) {
4287
+ const cSlug = segments[1];
4288
+ const matched = data.calculators.find((c) => cleanSeoSlug(c.slug, { language: lang }) === cSlug || c.slug === cSlug);
4289
+ if (matched) {
4290
+ return this.generateCalculatorMatrix(domain, [matched], options)[0];
4291
+ }
4292
+ }
4293
+ if (data.services && data.services.length > 0) {
4294
+ let serviceSlug = "";
4295
+ let locationSlug = "";
4296
+ if (segments.length === 2 && options.cleanDirectRoutes !== false) {
4297
+ serviceSlug = segments[0];
4298
+ locationSlug = segments[1];
4299
+ } else if (segments.length >= 3 && segments[0] === this.getPrefix("solutions", lang).replace(/^\//, "")) {
4300
+ serviceSlug = segments[1];
4301
+ locationSlug = segments[segments.length - 1];
4302
+ }
4303
+ if (serviceSlug && locationSlug) {
4304
+ const matchedService = data.services.find((s) => cleanSeoSlug(s.slug, { language: lang }) === serviceSlug || s.slug === serviceSlug);
4305
+ if (matchedService) {
4306
+ let resolvedLoc = (data.locations || []).find((l) => cleanSeoSlug(l.slug, { language: lang }) === locationSlug || l.slug === locationSlug);
4307
+ if (!resolvedLoc) {
4308
+ const countryList = options.allowedCountries || options.countries;
4309
+ const fallbackCountry = Array.isArray(countryList) ? countryList[0] : typeof countryList === "string" && countryList !== "international" ? countryList : "United States";
4310
+ resolvedLoc = createDynamicLocation(locationSlug, fallbackCountry, {
4311
+ currency: options.defaultCurrency,
4312
+ currencySymbol: options.defaultCurrencySymbol
4313
+ });
4314
+ if (options.strictCountryBoundary && countryList) {
4315
+ const allowed = (Array.isArray(countryList) ? countryList : [countryList]).map((c) => c.toLowerCase());
4316
+ if (!allowed.includes("international") && !allowed.some((c) => resolvedLoc.country.toLowerCase().includes(c))) {
4317
+ return;
4318
+ }
4319
+ }
4235
4320
  }
4321
+ const dynamicPages = this.generateLocalGeoMatrix(domain, [matchedService], [resolvedLoc], options);
4322
+ return dynamicPages[0];
4236
4323
  }
4237
- const dynamicPages = this.generateLocalGeoMatrix(domain, [matchedService], [dynamicLoc], options);
4238
- return dynamicPages.find((p) => p.urlPath === path) || dynamicPages[0];
4239
4324
  }
4240
4325
  }
4241
4326
  return;
@@ -5045,7 +5130,20 @@ When discovering product modules, features, or services to build the SEO matrice
5045
5130
 
5046
5131
  ---
5047
5132
 
5048
- ### \uD83C\uDF10 2. ARCHITECTURAL RULE: THE WEBSITE OWNS ITS LANGUAGES (i18n)
5133
+ ### \uD83D\uDEA8 2. CRITICAL RULE: ZERO STATIC PRE-COMPILATION AT BUILD TIME (PURE RUNTIME ON-DEMAND)
5134
+
5135
+ - **❌ NEVER PRE-COMPILE CITIES OR PAGES AT BUILD TIME (\`generateStaticParams\` / \`getStaticPaths\`):**
5136
+ - DO NOT create static loops over 365, 61, or any hardcoded list of cities during \`next build\`.
5137
+ - DO NOT freeze pages into static HTML files on disk. That is NOT how high-scale programmatic SEO works.
5138
+ - Doing so breaks infinite dynamic expansion, causes build memory crashes, and falsely caps the website to a tiny subset of pages.
5139
+ - **✅ ALWAYS USE 100% RUNTIME IN-MEMORY RESOLUTION (\`resolvePage\`):**
5140
+ - In Next.js App Router: Always set \`export const dynamic = "force-dynamic"\` and \`export const dynamicParams = true\`.
5141
+ - When an incoming request arrives (e.g. \`/solutions/ac-repair/austin\`, \`/solutions/ac-repair/paris\`, \`/solutions/ac-repair/90210\`), the server executes \`matrixEngine.resolvePage([service, location], ...)\` in RAM in \`< 0.05ms\`.
5142
+ - Build time remains **under 3 seconds** because 0 localized landing pages are built ahead of time. Every page is synthesized on demand.
5143
+
5144
+ ---
5145
+
5146
+ ### \uD83C\uDF10 3. ARCHITECTURAL RULE: THE WEBSITE OWNS ITS LANGUAGES (i18n)
5049
5147
 
5050
5148
  - **The Website (Application) is the sole owner of its languages, routes, and translations:**
5051
5149
  - The website provides its own localized routes (e.g. \`app/[locale]/[...slug]/page.tsx\`) and dictionary files (\`next-intl\`, \`i18next\`, Astro i18n).
@@ -5308,7 +5406,7 @@ The SDK equips applications with 9 enterprise-grade SEO engines:
5308
5406
  - Boosts dwell time, organic backlinks, and visitor-to-customer conversion.
5309
5407
 
5310
5408
  8. **\uD83C\uDFDB️ Google Schema.org Rich Graphs (\`ExtendedSchemaGraphBuilder\`):**
5311
- - \`buildAggregateRating\` (Google Gold Stars 4.9★), \`buildAggregateOffer\` (price ranges), \`buildLocalBusiness\` (GPS & opening hours), \`buildSoftwareApplication\`, \`buildHowTo\`, \`buildFAQPage\`, \`buildBreadcrumbs\`.
5409
+ - \`buildAggregateRating\` (Verified live reviews synced from Google Places / Trustpilot / WooCommerce), \`buildAggregateOffer\` (price ranges), \`buildLocalBusiness\` (GPS & opening hours), \`buildSoftwareApplication\`, \`buildHowTo\`, \`buildFAQPage\`, \`buildBreadcrumbs\`.
5312
5410
 
5313
5411
  9. **\uD83D\uDD0D URL Decomposition & Analysis (\`UrlyticsEngine\`):**
5314
5412
  - Deep structural directory decomposition, depth tracking, query parameter analysis, and slug tokenization (inspired by advertools).
package/dist/index.mjs CHANGED
@@ -3787,7 +3787,7 @@ class PseoMatrixEngine {
3787
3787
  ])
3788
3788
  ]
3789
3789
  };
3790
- pages.push({
3790
+ const pageItem = {
3791
3791
  matrixFamily: "local-geo",
3792
3792
  urlPath,
3793
3793
  canonicalUrl: fullUrl,
@@ -3799,7 +3799,9 @@ class PseoMatrixEngine {
3799
3799
  neighboringLinks: neighborLinks,
3800
3800
  service: s,
3801
3801
  location: loc
3802
- });
3802
+ };
3803
+ pageItem.ogImageUrl = OgImageGenerator.generateOgImageUrl(cleanDomain, pageItem, options.brandName);
3804
+ pages.push(pageItem);
3803
3805
  }
3804
3806
  }
3805
3807
  return pages;
@@ -4145,7 +4147,7 @@ class PseoMatrixEngine {
4145
4147
  generateAllMatrices(domain, data, options) {
4146
4148
  const allPages = [];
4147
4149
  if (data.services) {
4148
- const locs = data.locations && data.locations.length > 0 ? data.locations : resolveBuiltInLocations(options.countries || options.territories || options.language || "fr");
4150
+ const locs = data.locations && data.locations.length > 0 ? data.locations : resolveBuiltInLocations(options.allowedCountries || options.countries || options.territories || "us");
4149
4151
  allPages.push(...this.generateLocalGeoMatrix(domain, data.services, locs, options));
4150
4152
  }
4151
4153
  if (data.competitors) {
@@ -4212,30 +4214,113 @@ class PseoMatrixEngine {
4212
4214
  }
4213
4215
  resolvePage(slugOrPath, domain, data, options) {
4214
4216
  const path = typeof slugOrPath === "string" ? slugOrPath.startsWith("/") ? slugOrPath : `/${slugOrPath}` : `/${slugOrPath.join("/")}`;
4215
- const pages = this.generateAllMatrices(domain, data, options);
4216
- const existing = pages.find((p) => p.urlPath === path);
4217
- if (existing)
4218
- return existing;
4217
+ const lang = options.language || "en";
4219
4218
  const segments = path.split("/").filter(Boolean);
4220
- if (segments.length >= 2 && data.services && data.services.length > 0) {
4221
- const serviceSlug = segments[0];
4222
- const locationSlug = segments[segments.length - 1];
4223
- const matchedService = data.services.find((s) => cleanSeoSlug(s.slug, { language: options.language }) === serviceSlug || s.slug === serviceSlug);
4224
- if (matchedService) {
4225
- const countryList = options.allowedCountries || options.countries;
4226
- const fallbackCountry = Array.isArray(countryList) ? countryList[0] : typeof countryList === "string" && countryList !== "international" ? countryList : "United States";
4227
- const dynamicLoc = createDynamicLocation(locationSlug, fallbackCountry, {
4228
- currency: options.defaultCurrency,
4229
- currencySymbol: options.defaultCurrencySymbol
4230
- });
4231
- if (options.strictCountryBoundary && countryList) {
4232
- const allowed = (Array.isArray(countryList) ? countryList : [countryList]).map((c) => c.toLowerCase());
4233
- if (!allowed.includes("international") && !allowed.some((c) => dynamicLoc.country.toLowerCase().includes(c))) {
4234
- return;
4219
+ if (segments.length === 0)
4220
+ return;
4221
+ const vsPrefix = this.getPrefix("vs", lang).replace(/^\//, "");
4222
+ if (segments[0] === vsPrefix && segments[1] && data.competitors) {
4223
+ const compSlug = segments[1];
4224
+ const matched = data.competitors.find((c) => cleanSeoSlug(c.slug, { language: lang }) === compSlug || c.slug === compSlug);
4225
+ if (matched) {
4226
+ return this.generateVsMatrix(domain, [matched], options)[0];
4227
+ }
4228
+ }
4229
+ const altPrefix = this.getPrefix("alternative", lang).replace(/^\//, "");
4230
+ if (segments[0] === altPrefix && segments[1] && data.competitors) {
4231
+ const compSlug = segments[1];
4232
+ const matched = data.competitors.find((c) => cleanSeoSlug(c.slug, { language: lang }) === compSlug || c.slug === compSlug);
4233
+ if (matched) {
4234
+ return this.generateAlternativesMatrix(domain, [matched], options)[0];
4235
+ }
4236
+ }
4237
+ const pricingPrefix = this.getPrefix("pricing", lang).replace(/^\//, "");
4238
+ if (segments[0] === pricingPrefix && segments[1] && data.competitors) {
4239
+ const compSlug = segments[1];
4240
+ const matched = data.competitors.find((c) => cleanSeoSlug(c.slug, { language: lang }) === compSlug || c.slug === compSlug);
4241
+ if (matched) {
4242
+ return this.generatePricingMatrix(domain, [matched], options)[0];
4243
+ }
4244
+ }
4245
+ const targetPrefix = this.getPrefix("target", lang).replace(/^\//, "");
4246
+ if (segments[0] === targetPrefix && segments[1] && data.targets) {
4247
+ const targetSlug = segments[1];
4248
+ const matched = data.targets.find((t) => cleanSeoSlug(t.slug, { language: lang }) === targetSlug || t.slug === targetSlug);
4249
+ if (matched) {
4250
+ return this.generateTargetMatrix(domain, [matched], options)[0];
4251
+ }
4252
+ }
4253
+ const integPrefix = this.getPrefix("integration", lang).replace(/^\//, "");
4254
+ if (segments[0] === integPrefix && segments[1] && data.integrations) {
4255
+ const integSlug = segments[1];
4256
+ const matched = data.integrations.find((i) => cleanSeoSlug(i.slug, { language: lang }) === integSlug || i.slug === integSlug);
4257
+ if (matched) {
4258
+ return this.generateIntegrationMatrix(domain, [matched], options)[0];
4259
+ }
4260
+ }
4261
+ const useCasePrefix = this.getPrefix("useCase", lang).replace(/^\//, "");
4262
+ if (segments[0] === useCasePrefix && segments[1] && data.useCases) {
4263
+ const ucSlug = segments[1];
4264
+ const matched = data.useCases.find((u) => cleanSeoSlug(u.slug, { language: lang }) === ucSlug || u.slug === ucSlug);
4265
+ if (matched) {
4266
+ return this.generateUseCaseMatrix(domain, [matched], options)[0];
4267
+ }
4268
+ }
4269
+ const templatePrefix = this.getPrefix("template", lang).replace(/^\//, "");
4270
+ if (segments[0] === templatePrefix && segments[1] && data.templates) {
4271
+ const tSlug = segments[1];
4272
+ const matched = data.templates.find((t) => cleanSeoSlug(t.slug, { language: lang }) === tSlug || t.slug === tSlug);
4273
+ if (matched) {
4274
+ return this.generateTemplateMatrix(domain, [matched], options)[0];
4275
+ }
4276
+ }
4277
+ const glossaryPrefix = this.getPrefix("glossary", lang).replace(/^\//, "");
4278
+ if (segments[0] === glossaryPrefix && segments[1] && data.glossaryTerms) {
4279
+ const gSlug = segments[1];
4280
+ const matched = data.glossaryTerms.find((g) => cleanSeoSlug(g.slug, { language: lang }) === gSlug || g.slug === gSlug);
4281
+ if (matched) {
4282
+ return this.generateGlossaryMatrix(domain, [matched], options)[0];
4283
+ }
4284
+ }
4285
+ const calcPrefix = this.getPrefix("calculator", lang).replace(/^\//, "");
4286
+ if (segments[0] === calcPrefix && segments[1] && data.calculators) {
4287
+ const cSlug = segments[1];
4288
+ const matched = data.calculators.find((c) => cleanSeoSlug(c.slug, { language: lang }) === cSlug || c.slug === cSlug);
4289
+ if (matched) {
4290
+ return this.generateCalculatorMatrix(domain, [matched], options)[0];
4291
+ }
4292
+ }
4293
+ if (data.services && data.services.length > 0) {
4294
+ let serviceSlug = "";
4295
+ let locationSlug = "";
4296
+ if (segments.length === 2 && options.cleanDirectRoutes !== false) {
4297
+ serviceSlug = segments[0];
4298
+ locationSlug = segments[1];
4299
+ } else if (segments.length >= 3 && segments[0] === this.getPrefix("solutions", lang).replace(/^\//, "")) {
4300
+ serviceSlug = segments[1];
4301
+ locationSlug = segments[segments.length - 1];
4302
+ }
4303
+ if (serviceSlug && locationSlug) {
4304
+ const matchedService = data.services.find((s) => cleanSeoSlug(s.slug, { language: lang }) === serviceSlug || s.slug === serviceSlug);
4305
+ if (matchedService) {
4306
+ let resolvedLoc = (data.locations || []).find((l) => cleanSeoSlug(l.slug, { language: lang }) === locationSlug || l.slug === locationSlug);
4307
+ if (!resolvedLoc) {
4308
+ const countryList = options.allowedCountries || options.countries;
4309
+ const fallbackCountry = Array.isArray(countryList) ? countryList[0] : typeof countryList === "string" && countryList !== "international" ? countryList : "United States";
4310
+ resolvedLoc = createDynamicLocation(locationSlug, fallbackCountry, {
4311
+ currency: options.defaultCurrency,
4312
+ currencySymbol: options.defaultCurrencySymbol
4313
+ });
4314
+ if (options.strictCountryBoundary && countryList) {
4315
+ const allowed = (Array.isArray(countryList) ? countryList : [countryList]).map((c) => c.toLowerCase());
4316
+ if (!allowed.includes("international") && !allowed.some((c) => resolvedLoc.country.toLowerCase().includes(c))) {
4317
+ return;
4318
+ }
4319
+ }
4235
4320
  }
4321
+ const dynamicPages = this.generateLocalGeoMatrix(domain, [matchedService], [resolvedLoc], options);
4322
+ return dynamicPages[0];
4236
4323
  }
4237
- const dynamicPages = this.generateLocalGeoMatrix(domain, [matchedService], [dynamicLoc], options);
4238
- return dynamicPages.find((p) => p.urlPath === path) || dynamicPages[0];
4239
4324
  }
4240
4325
  }
4241
4326
  return;
@@ -5045,7 +5130,20 @@ When discovering product modules, features, or services to build the SEO matrice
5045
5130
 
5046
5131
  ---
5047
5132
 
5048
- ### \uD83C\uDF10 2. ARCHITECTURAL RULE: THE WEBSITE OWNS ITS LANGUAGES (i18n)
5133
+ ### \uD83D\uDEA8 2. CRITICAL RULE: ZERO STATIC PRE-COMPILATION AT BUILD TIME (PURE RUNTIME ON-DEMAND)
5134
+
5135
+ - **❌ NEVER PRE-COMPILE CITIES OR PAGES AT BUILD TIME (\`generateStaticParams\` / \`getStaticPaths\`):**
5136
+ - DO NOT create static loops over 365, 61, or any hardcoded list of cities during \`next build\`.
5137
+ - DO NOT freeze pages into static HTML files on disk. That is NOT how high-scale programmatic SEO works.
5138
+ - Doing so breaks infinite dynamic expansion, causes build memory crashes, and falsely caps the website to a tiny subset of pages.
5139
+ - **✅ ALWAYS USE 100% RUNTIME IN-MEMORY RESOLUTION (\`resolvePage\`):**
5140
+ - In Next.js App Router: Always set \`export const dynamic = "force-dynamic"\` and \`export const dynamicParams = true\`.
5141
+ - When an incoming request arrives (e.g. \`/solutions/ac-repair/austin\`, \`/solutions/ac-repair/paris\`, \`/solutions/ac-repair/90210\`), the server executes \`matrixEngine.resolvePage([service, location], ...)\` in RAM in \`< 0.05ms\`.
5142
+ - Build time remains **under 3 seconds** because 0 localized landing pages are built ahead of time. Every page is synthesized on demand.
5143
+
5144
+ ---
5145
+
5146
+ ### \uD83C\uDF10 3. ARCHITECTURAL RULE: THE WEBSITE OWNS ITS LANGUAGES (i18n)
5049
5147
 
5050
5148
  - **The Website (Application) is the sole owner of its languages, routes, and translations:**
5051
5149
  - The website provides its own localized routes (e.g. \`app/[locale]/[...slug]/page.tsx\`) and dictionary files (\`next-intl\`, \`i18next\`, Astro i18n).
@@ -5308,7 +5406,7 @@ The SDK equips applications with 9 enterprise-grade SEO engines:
5308
5406
  - Boosts dwell time, organic backlinks, and visitor-to-customer conversion.
5309
5407
 
5310
5408
  8. **\uD83C\uDFDB️ Google Schema.org Rich Graphs (\`ExtendedSchemaGraphBuilder\`):**
5311
- - \`buildAggregateRating\` (Google Gold Stars 4.9★), \`buildAggregateOffer\` (price ranges), \`buildLocalBusiness\` (GPS & opening hours), \`buildSoftwareApplication\`, \`buildHowTo\`, \`buildFAQPage\`, \`buildBreadcrumbs\`.
5409
+ - \`buildAggregateRating\` (Verified live reviews synced from Google Places / Trustpilot / WooCommerce), \`buildAggregateOffer\` (price ranges), \`buildLocalBusiness\` (GPS & opening hours), \`buildSoftwareApplication\`, \`buildHowTo\`, \`buildFAQPage\`, \`buildBreadcrumbs\`.
5312
5410
 
5313
5411
  9. **\uD83D\uDD0D URL Decomposition & Analysis (\`UrlyticsEngine\`):**
5314
5412
  - Deep structural directory decomposition, depth tracking, query parameter analysis, and slug tokenization (inspired by advertools).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lynxflow/seo-engine",
3
- "version": "2.1.0",
3
+ "version": "2.1.1",
4
4
  "description": "High-Performance Universal Programmatic SEO & AI Search Engine SDK",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",