@lynxflow/seo-engine 1.8.30 → 2.0.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.
Files changed (96) hide show
  1. package/README.md +60 -117
  2. package/dist/ai-copilot-client.d.ts +2 -0
  3. package/dist/built-in-locations.d.ts +22 -0
  4. package/dist/content-ai-arsenal.d.ts +28 -0
  5. package/dist/existing-media-harvester.d.ts +33 -0
  6. package/dist/extended-schemas.d.ts +84 -0
  7. package/dist/features-knowledge-harvester.d.ts +56 -0
  8. package/dist/geo-citation-scorer.d.ts +34 -0
  9. package/dist/i18n-detector.d.ts +1 -1
  10. package/dist/index.d.ts +32 -7
  11. package/dist/index.js +567 -83
  12. package/dist/index.mjs +2062 -169
  13. package/dist/internal-pagerank-graph.d.ts +59 -0
  14. package/dist/knowledge-bank-builder.d.ts +91 -0
  15. package/dist/marketing-skills-engine.d.ts +74 -0
  16. package/dist/matrix-engine.d.ts +29 -0
  17. package/dist/power-words-psychology.d.ts +27 -15
  18. package/dist/public-manifest-engine.d.ts +58 -0
  19. package/dist/rag-knowledge-engine.d.ts +50 -0
  20. package/dist/rate-limited-translator.d.ts +19 -0
  21. package/dist/token-quota-manager.d.ts +4 -0
  22. package/package.json +11 -3
  23. package/TUTORIEL_INTEGRATION_SITE.md +0 -283
  24. package/connectors/cloudflare-worker/worker.js +0 -106
  25. package/connectors/laravel/LynxSeoController.php +0 -37
  26. package/connectors/wordpress/lynxseo-admin-app.js +0 -2611
  27. package/connectors/wordpress/lynxseo-connector.php +0 -1712
  28. package/connectors/wordpress/lynxseo-connector.zip +0 -0
  29. package/lynxflow-seo-engine-1.2.0.tgz +0 -0
  30. package/src/ad-intelligence-cro.ts +0 -140
  31. package/src/ai-bots-log-analyzer.ts +0 -127
  32. package/src/ai-copilot-client.ts +0 -99
  33. package/src/analytics-client.ts +0 -315
  34. package/src/auth-key.ts +0 -262
  35. package/src/backlinks-client.ts +0 -85
  36. package/src/brand-dna-calendar.ts +0 -120
  37. package/src/brand-icons.ts +0 -138
  38. package/src/built-in-locations.ts +0 -170
  39. package/src/content-ai-arsenal.ts +0 -268
  40. package/src/copy-frameworks-master.ts +0 -89
  41. package/src/cro-copywriting-engine.ts +0 -105
  42. package/src/crosslink-scorer.ts +0 -101
  43. package/src/embeddable-seo-widget.ts +0 -57
  44. package/src/engine.test.ts +0 -386
  45. package/src/engine.ts +0 -813
  46. package/src/existing-media-harvester.ts +0 -98
  47. package/src/extended-schemas.ts +0 -589
  48. package/src/features-knowledge-harvester.ts +0 -190
  49. package/src/free-public-tools.ts +0 -224
  50. package/src/geo-mesh-linking.ts +0 -93
  51. package/src/google-business-profile.ts +0 -280
  52. package/src/i18n-detector.ts +0 -98
  53. package/src/i18n-dictionary.ts +0 -422
  54. package/src/index.ts +0 -164
  55. package/src/indexnow-client.ts +0 -94
  56. package/src/instant-matrix-search.ts +0 -48
  57. package/src/isr-cache-manager.ts +0 -78
  58. package/src/keyword-permutator.ts +0 -118
  59. package/src/knowledge-bank-builder.ts +0 -299
  60. package/src/knowledge-graph-linker.ts +0 -81
  61. package/src/lago-token-meter.ts +0 -120
  62. package/src/legal-disclaimers.ts +0 -407
  63. package/src/llm-content-cleaner.ts +0 -64
  64. package/src/llm-prompt.ts +0 -323
  65. package/src/master-marketing-engine.ts +0 -314
  66. package/src/matrix-engine.ts +0 -1204
  67. package/src/mcp-seo-server.ts +0 -108
  68. package/src/ngram-density-analyzer.ts +0 -108
  69. package/src/og-image-generator.ts +0 -109
  70. package/src/power-words-psychology.ts +0 -369
  71. package/src/public-manifest-engine.ts +0 -109
  72. package/src/rag-knowledge-engine.ts +0 -163
  73. package/src/rank-math-parity.ts +0 -235
  74. package/src/rate-limited-translator.ts +0 -110
  75. package/src/real-reviews-sync.ts +0 -207
  76. package/src/rss-syndication-feed.ts +0 -56
  77. package/src/schema-builder.ts +0 -134
  78. package/src/seo-opportunities-decay.ts +0 -154
  79. package/src/serp-client.ts +0 -89
  80. package/src/serp-history-alerts.ts +0 -75
  81. package/src/site-auditor.ts +0 -83
  82. package/src/site-crawler.ts +0 -406
  83. package/src/slug-engine.ts +0 -183
  84. package/src/social-ads-seo.ts +0 -211
  85. package/src/social-growth-suite.ts +0 -310
  86. package/src/social-trend-seo.ts +0 -103
  87. package/src/social-video-seo.ts +0 -100
  88. package/src/team-rbac.ts +0 -61
  89. package/src/technical-rules-auditor.ts +0 -171
  90. package/src/token-quota-manager.ts +0 -91
  91. package/src/types.ts +0 -143
  92. package/src/ui-icons.ts +0 -134
  93. package/src/urlytics-engine.ts +0 -97
  94. package/src/video-youtube-analyzer.ts +0 -81
  95. package/src/yoast-parity.ts +0 -161
  96. package/tsconfig.json +0 -15
@@ -1,98 +0,0 @@
1
- /**
2
- * 📸 Existing Media Harvester & Asset Pool Engine
3
- *
4
- * Reuses real, pre-existing photos from the client's website & WordPress Media Library:
5
- * 1. 0 € GPU Cost & 0s Compute (Zero VRAM Saturation Guarantee)
6
- * 2. Maximum Google E-E-A-T Authenticity (Real brand photos > Generic AI images)
7
- * 3. Rotates existing photos across the 6 page variations
8
- * 4. Dynamically injects localized <img alt="..."> tags across 40+ languages in RAM (< 0.01ms)
9
- */
10
-
11
- export interface HarvestedMediaAsset {
12
- id?: string | number;
13
- url: string;
14
- originalAlt?: string;
15
- matchedKeywords?: string[];
16
- width?: number;
17
- height?: number;
18
- }
19
-
20
- export class ExistingMediaHarvester {
21
- /**
22
- * Extracts all existing image URLs from HTML pages or WordPress Media API
23
- */
24
- static harvestFromHtml(htmlContent: string, baseUrl = ""): HarvestedMediaAsset[] {
25
- const assets: HarvestedMediaAsset[] = [];
26
- const imgRegex = /<img\s+[^>]*src=["']([^"']+)["'][^>]*>/gi;
27
- const altRegex = /alt=["']([^"']*)["']/i;
28
-
29
- let match: RegExpExecArray | null;
30
- const seenUrls = new Set<string>();
31
-
32
- while ((match = imgRegex.exec(htmlContent)) !== null) {
33
- const fullImgTag = match[0];
34
- let src = match[1];
35
-
36
- // Exclude tiny tracking pixels, icons or avatars
37
- if (
38
- src.includes("gravatar.com") ||
39
- src.includes("icon-") ||
40
- src.includes(".svg") ||
41
- src.includes("1x1")
42
- ) {
43
- continue;
44
- }
45
-
46
- if (src.startsWith("/") && baseUrl) {
47
- src = `${baseUrl.replace(/\/$/, "")}${src}`;
48
- }
49
-
50
- if (!seenUrls.has(src)) {
51
- seenUrls.add(src);
52
- const altMatch = altRegex.exec(fullImgTag);
53
- const originalAlt = altMatch ? altMatch[1] : "";
54
-
55
- assets.push({
56
- url: src,
57
- originalAlt,
58
- matchedKeywords: this.extractKeywordsFromUrl(src, originalAlt),
59
- });
60
- }
61
- }
62
-
63
- return assets;
64
- }
65
-
66
- /**
67
- * Matches the best existing photo from the pool for a given service & variation
68
- */
69
- static selectBestImageForVariant(params: {
70
- serviceName: string;
71
- variantIndex: number;
72
- mediaPool: HarvestedMediaAsset[];
73
- fallbackPlaceholderUrl?: string;
74
- }): string {
75
- if (!params.mediaPool || params.mediaPool.length === 0) {
76
- return params.fallbackPlaceholderUrl || "https://images.unsplash.com/photo-1581091226825-a6a2a5aee158?w=1200&q=80";
77
- }
78
-
79
- const serviceLower = params.serviceName.toLowerCase();
80
-
81
- // 1. Try to find a photo whose filename or original alt matches the service
82
- const matchingPhotos = params.mediaPool.filter((asset) =>
83
- asset.matchedKeywords?.some((kw) => serviceLower.includes(kw))
84
- );
85
-
86
- const candidatePool = matchingPhotos.length > 0 ? matchingPhotos : params.mediaPool;
87
-
88
- // 2. Rotate deterministically across variations so each variant gets a different photo
89
- const selectedIndex = params.variantIndex % candidatePool.length;
90
- return candidatePool[selectedIndex].url;
91
- }
92
-
93
- private static extractKeywordsFromUrl(url: string, alt: string): string[] {
94
- const filename = url.split("/").pop()?.split("?")[0] || "";
95
- const cleanText = `${filename} ${alt}`.toLowerCase().replace(/[-_.]/g, " ");
96
- return cleanText.split(/\s+/).filter((w) => w.length > 3);
97
- }
98
- }
@@ -1,589 +0,0 @@
1
- /**
2
- * Extended Universal Schema.org Rich Graph Builder
3
- * Supports 50+ Schema.org official types for Google Search Rich Results:
4
- * - SoftwareApplication, WebApplication, MobileApplication
5
- * - 35+ LocalBusiness Subtypes (Plumber, HVACBusiness, Electrician, Dentist, LegalService, AccountingService, etc.)
6
- * - Organization with Entity SEO sameAs links (Wikidata, Crunchbase, LinkedIn)
7
- * - Product, Offer, MerchantReturnPolicy
8
- * - Article, TechArticle, NewsArticle
9
- * - HowTo, HowToStep
10
- * - FAQPage
11
- * - BreadcrumbList
12
- */
13
-
14
- export const SCHEMA_LOCAL_BUSINESS_MAP: Record<string, string> = {
15
- // Trades & Construction
16
- plumber: "Plumber",
17
- plumbing: "Plumber",
18
- hvac: "HVACBusiness",
19
- electrician: "Electrician",
20
- locksmith: "Locksmith",
21
- roofing: "RoofingContractor",
22
- painting: "HousePainter",
23
- painter: "HousePainter",
24
- carpenter: "HomeAndConstructionBusiness",
25
- masonry: "GeneralContractor",
26
- contractor: "GeneralContractor",
27
- landscaping: "LandscapeContractor",
28
- gardener: "LandscapeContractor",
29
- pestcontrol: "PestControlService",
30
- cleaning: "CleaningService",
31
- towing: "TowingService",
32
-
33
- // Healthcare & Wellness
34
- dentist: "Dentist",
35
- doctor: "MedicalBusiness",
36
- physician: "MedicalBusiness",
37
- medical: "MedicalBusiness",
38
- psychologist: "MedicalBusiness",
39
- osteopath: "MedicalBusiness",
40
- physiotherapist: "MedicalBusiness",
41
- veterinarian: "VeterinaryCare",
42
- pharmacy: "Pharmacy",
43
- dayspa: "DaySpa",
44
- spa: "DaySpa",
45
- hairsalon: "HairSalon",
46
- barber: "BarberShop",
47
- beautysalon: "BeautySalon",
48
- healthclub: "HealthClub",
49
- gym: "HealthClub",
50
-
51
- // Professional Services
52
- lawyer: "LegalService",
53
- attorney: "LegalService",
54
- legalservice: "LegalService",
55
- accountant: "AccountingService",
56
- accounting: "AccountingService",
57
- insurance: "InsuranceAgency",
58
- realestate: "RealEstateAgent",
59
- realtor: "RealEstateAgent",
60
- notary: "Notary",
61
- moving: "MovingCompany",
62
- education: "EducationalOrganization",
63
- school: "EducationalOrganization",
64
-
65
- // Automotive & Hospitality
66
- autorepair: "AutoRepair",
67
- mechanic: "AutoRepair",
68
- carrental: "AutoRental",
69
- restaurant: "Restaurant",
70
- hotel: "Hotel",
71
- store: "Store",
72
- };
73
-
74
- export interface SchemaOrgOrganizationOpts {
75
- name: string;
76
- url: string;
77
- logo?: string;
78
- sameAs?: string[];
79
- contactPoint?: {
80
- telephone: string;
81
- contactType: string;
82
- availableLanguage: string[];
83
- };
84
- }
85
-
86
- export interface SchemaOrgLocalBusinessOpts {
87
- name: string;
88
- url: string;
89
- schemaType?: string;
90
- telephone?: string;
91
- email?: string;
92
- streetAddress?: string;
93
- city: string;
94
- region?: string;
95
- postalCode?: string;
96
- country: string;
97
- latitude?: number;
98
- longitude?: number;
99
- openingHours?: string[];
100
- priceRange?: string;
101
- }
102
-
103
- export interface SchemaOrgProductOpts {
104
- name: string;
105
- description: string;
106
- url: string;
107
- brandName: string;
108
- price: number;
109
- currency: string;
110
- image?: string;
111
- }
112
-
113
- export interface SchemaOrgArticleOpts {
114
- headline: string;
115
- description: string;
116
- url: string;
117
- datePublished?: string;
118
- dateModified?: string;
119
- authorName: string;
120
- publisherName: string;
121
- }
122
-
123
- export class ExtendedSchemaGraphBuilder {
124
- /**
125
- * Resolves the most accurate Schema.org type for a business sector or keyword.
126
- */
127
- static resolveBusinessType(industryOrSectorSlug: string): string {
128
- const clean = industryOrSectorSlug.toLowerCase().replace(/[^a-z0-9]/g, "");
129
- return SCHEMA_LOCAL_BUSINESS_MAP[clean] || "LocalBusiness";
130
- }
131
-
132
- /**
133
- * 1. Organization with Entity SEO sameAs links
134
- */
135
- static buildOrganization(opts: SchemaOrgOrganizationOpts): Record<string, unknown> {
136
- return {
137
- "@context": "https://schema.org",
138
- "@type": "Organization",
139
- name: opts.name,
140
- url: opts.url,
141
- logo: opts.logo,
142
- sameAs: opts.sameAs || [],
143
- contactPoint: opts.contactPoint
144
- ? {
145
- "@type": "ContactPoint",
146
- telephone: opts.contactPoint.telephone,
147
- contactType: opts.contactPoint.contactType,
148
- availableLanguage: opts.contactPoint.availableLanguage,
149
- }
150
- : undefined,
151
- };
152
- }
153
-
154
- /**
155
- * 2. LocalBusiness with GeoCoordinates & Address
156
- */
157
- static buildLocalBusiness(opts: SchemaOrgLocalBusinessOpts): Record<string, unknown> {
158
- const businessType = opts.schemaType || this.resolveBusinessType(opts.name);
159
-
160
- return {
161
- "@context": "https://schema.org",
162
- "@type": businessType,
163
- name: opts.name,
164
- url: opts.url,
165
- telephone: opts.telephone,
166
- email: opts.email,
167
- priceRange: opts.priceRange || "$$",
168
- address: {
169
- "@type": "PostalAddress",
170
- streetAddress: opts.streetAddress,
171
- addressLocality: opts.city,
172
- addressRegion: opts.region,
173
- postalCode: opts.postalCode,
174
- addressCountry: opts.country,
175
- },
176
- geo:
177
- opts.latitude && opts.longitude
178
- ? {
179
- "@type": "GeoCoordinates",
180
- latitude: opts.latitude,
181
- longitude: opts.longitude,
182
- }
183
- : undefined,
184
- openingHoursSpecification: opts.openingHours
185
- ? opts.openingHours.map((hours) => ({
186
- "@type": "OpeningHoursSpecification",
187
- dayOfWeek: ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"],
188
- opens: hours.split("-")[0] || "09:00",
189
- closes: hours.split("-")[1] || "18:00",
190
- }))
191
- : undefined,
192
- };
193
- }
194
-
195
- /**
196
- * 3. SoftwareApplication & WebApplication
197
- */
198
- static buildSoftwareApplication(opts: {
199
- name: string;
200
- description: string;
201
- url: string;
202
- priceMonthly?: number;
203
- currency?: string;
204
- operatingSystem?: string;
205
- category?: string;
206
- }): Record<string, unknown> {
207
- return {
208
- "@context": "https://schema.org",
209
- "@type": "SoftwareApplication",
210
- name: opts.name,
211
- description: opts.description,
212
- url: opts.url,
213
- applicationCategory: opts.category || "BusinessApplication",
214
- operatingSystem: opts.operatingSystem || "Web, Cloud, iOS, Android",
215
- offers: {
216
- "@type": "Offer",
217
- price: opts.priceMonthly !== undefined ? String(opts.priceMonthly) : "0",
218
- priceCurrency: opts.currency || "USD",
219
- description: "14-day free trial, no credit card required",
220
- },
221
- };
222
- }
223
-
224
- /**
225
- * 4. FAQPage
226
- */
227
- static buildFAQPage(faqs: { question: string; answer: string }[]): Record<string, unknown> {
228
- return {
229
- "@context": "https://schema.org",
230
- "@type": "FAQPage",
231
- mainEntity: faqs.map((faq) => ({
232
- "@type": "Question",
233
- name: faq.question,
234
- acceptedAnswer: {
235
- "@type": "Answer",
236
- text: faq.answer,
237
- },
238
- })),
239
- };
240
- }
241
-
242
- /**
243
- * 5. HowTo & HowToStep
244
- */
245
- static buildHowTo(opts: {
246
- name: string;
247
- description: string;
248
- steps: string[];
249
- }): Record<string, unknown> {
250
- return {
251
- "@context": "https://schema.org",
252
- "@type": "HowTo",
253
- name: opts.name,
254
- description: opts.description,
255
- step: opts.steps.map((text, idx) => ({
256
- "@type": "HowToStep",
257
- position: idx + 1,
258
- name: `Step ${idx + 1}`,
259
- text,
260
- })),
261
- };
262
- }
263
-
264
- /**
265
- * 6. BreadcrumbList
266
- */
267
- static buildBreadcrumbs(items: { name: string; url: string }[]): Record<string, unknown> {
268
- return {
269
- "@context": "https://schema.org",
270
- "@type": "BreadcrumbList",
271
- itemListElement: items.map((item, idx) => ({
272
- "@type": "ListItem",
273
- position: idx + 1,
274
- name: item.name,
275
- item: item.url,
276
- })),
277
- };
278
- }
279
-
280
- /**
281
- * 7. AggregateRating (Google Gold Stars in SERPs) - Strictly Dynamic (No Fake Defaults)
282
- */
283
- static buildAggregateRating(opts: {
284
- ratingValue?: number;
285
- reviewCount?: number;
286
- bestRating?: number;
287
- worstRating?: number;
288
- itemReviewedName: string;
289
- }): Record<string, unknown> | null {
290
- // Google Strict Compliance: Never emit fake or default hardcoded reviews
291
- if (!opts.ratingValue || !opts.reviewCount || opts.reviewCount <= 0) {
292
- return null;
293
- }
294
- return {
295
- "@type": "AggregateRating",
296
- ratingValue: opts.ratingValue,
297
- reviewCount: opts.reviewCount,
298
- bestRating: opts.bestRating ?? 5,
299
- worstRating: opts.worstRating ?? 1,
300
- itemReviewed: {
301
- "@type": "Thing",
302
- name: opts.itemReviewedName,
303
- },
304
- };
305
- }
306
-
307
- /**
308
- * 8. AggregateOffer (Price Ranges & Tiers) - Inspired by santifer-irepair
309
- */
310
- static buildAggregateOffer(opts: {
311
- lowPrice: number;
312
- highPrice: number;
313
- currency: string;
314
- offerCount?: number;
315
- description?: string;
316
- }): Record<string, unknown> {
317
- return {
318
- "@type": "AggregateOffer",
319
- lowPrice: String(opts.lowPrice),
320
- highPrice: String(opts.highPrice),
321
- priceCurrency: opts.currency,
322
- offerCount: String(opts.offerCount ?? 3),
323
- description: opts.description || "Transparent pricing with zero lock-in",
324
- };
325
- }
326
-
327
- /**
328
- * 9. Service with ServiceArea & Providers
329
- */
330
- static buildService(opts: {
331
- name: string;
332
- description: string;
333
- providerName: string;
334
- areaServed?: string[];
335
- serviceType?: string;
336
- }): Record<string, unknown> {
337
- return {
338
- "@context": "https://schema.org",
339
- "@type": "Service",
340
- name: opts.name,
341
- description: opts.description,
342
- serviceType: opts.serviceType || "SoftwareService",
343
- provider: {
344
- "@type": "Organization",
345
- name: opts.providerName,
346
- },
347
- areaServed: opts.areaServed
348
- ? opts.areaServed.map((city) => ({
349
- "@type": "City",
350
- name: city,
351
- }))
352
- : undefined,
353
- };
354
- }
355
-
356
- /**
357
- * 10. WebSite with Google Sitelinks SearchBox
358
- */
359
- static buildWebSite(opts: {
360
- name: string;
361
- url: string;
362
- searchUrlTemplate?: string;
363
- }): Record<string, unknown> {
364
- return {
365
- "@context": "https://schema.org",
366
- "@type": "WebSite",
367
- name: opts.name,
368
- url: opts.url,
369
- potentialAction: opts.searchUrlTemplate
370
- ? {
371
- "@type": "SearchAction",
372
- target: {
373
- "@type": "EntryPoint",
374
- urlTemplate: opts.searchUrlTemplate,
375
- },
376
- "query-input": "required name=search_term_string",
377
- }
378
- : undefined,
379
- };
380
- }
381
-
382
- /**
383
- * 11. Speakable Schema (Google Assistant & Voice Search) - Rank Math Pro Parity
384
- */
385
- static buildSpeakable(opts: {
386
- cssSelectors?: string[];
387
- xpaths?: string[];
388
- }): Record<string, unknown> {
389
- return {
390
- "@type": "SpeakableSpecification",
391
- cssSelector: opts.cssSelectors || [".headline", ".summary", ".direct-answer", "article h1", "article p:first-of-type"],
392
- xpath: opts.xpaths,
393
- };
394
- }
395
-
396
- /**
397
- * 12. Podcast & PodcastEpisode Schema
398
- */
399
- static buildPodcastEpisode(opts: {
400
- title: string;
401
- description: string;
402
- audioUrl: string;
403
- duration?: string;
404
- publishedDate: string;
405
- seriesName: string;
406
- }): Record<string, unknown> {
407
- return {
408
- "@context": "https://schema.org",
409
- "@type": "PodcastEpisode",
410
- name: opts.title,
411
- description: opts.description,
412
- datePublished: opts.publishedDate,
413
- timeRequired: opts.duration || "PT15M",
414
- associatedMedia: {
415
- "@type": "AudioObject",
416
- contentUrl: opts.audioUrl,
417
- },
418
- partOfSeries: {
419
- "@type": "PodcastSeries",
420
- name: opts.seriesName,
421
- },
422
- };
423
- }
424
-
425
- /**
426
- * 13. Restaurant Schema (with Menu & Cuisine)
427
- */
428
- static buildRestaurant(opts: {
429
- name: string;
430
- url: string;
431
- servesCuisine: string[];
432
- menuUrl?: string;
433
- priceRange?: string;
434
- telephone?: string;
435
- address: { street: string; city: string; postalCode: string; country: string };
436
- }): Record<string, unknown> {
437
- return {
438
- "@context": "https://schema.org",
439
- "@type": "Restaurant",
440
- name: opts.name,
441
- url: opts.url,
442
- servesCuisine: opts.servesCuisine,
443
- hasMenu: opts.menuUrl,
444
- priceRange: opts.priceRange || "€€",
445
- telephone: opts.telephone,
446
- address: {
447
- "@type": "PostalAddress",
448
- streetAddress: opts.address.street,
449
- addressLocality: opts.address.city,
450
- postalCode: opts.address.postalCode,
451
- addressCountry: opts.address.country,
452
- },
453
- };
454
- }
455
-
456
- /**
457
- * 14. Book Schema
458
- */
459
- static buildBook(opts: {
460
- name: string;
461
- author: string;
462
- isbn?: string;
463
- bookFormat?: string;
464
- url?: string;
465
- }): Record<string, unknown> {
466
- return {
467
- "@context": "https://schema.org",
468
- "@type": "Book",
469
- name: opts.name,
470
- author: {
471
- "@type": "Person",
472
- name: opts.author,
473
- },
474
- isbn: opts.isbn,
475
- bookFormat: opts.bookFormat || "https://schema.org/EBook",
476
- url: opts.url,
477
- };
478
- }
479
-
480
- /**
481
- * 15. Dataset Schema (Data Search & Research)
482
- */
483
- static buildDataset(opts: {
484
- name: string;
485
- description: string;
486
- url: string;
487
- keywords: string[];
488
- temporalCoverage?: string;
489
- distributionUrl?: string;
490
- }): Record<string, unknown> {
491
- return {
492
- "@context": "https://schema.org",
493
- "@type": "Dataset",
494
- name: opts.name,
495
- description: opts.description,
496
- url: opts.url,
497
- keywords: opts.keywords,
498
- temporalCoverage: opts.temporalCoverage || "2024/2026",
499
- distribution: opts.distributionUrl
500
- ? [
501
- {
502
- "@type": "DataDownload",
503
- encodingFormat: "application/json",
504
- contentUrl: opts.distributionUrl,
505
- },
506
- ]
507
- : undefined,
508
- };
509
- }
510
-
511
- /**
512
- * 16. FactCheck Schema (Google ClaimReview)
513
- */
514
- static buildFactCheck(opts: {
515
- claimReviewed: string;
516
- ratingValue: string;
517
- authorName: string;
518
- url: string;
519
- }): Record<string, unknown> {
520
- return {
521
- "@context": "https://schema.org",
522
- "@type": "ClaimReview",
523
- claimReviewed: opts.claimReviewed,
524
- url: opts.url,
525
- reviewRating: {
526
- "@type": "Rating",
527
- ratingValue: opts.ratingValue,
528
- bestRating: "5",
529
- worstRating: "1",
530
- },
531
- author: {
532
- "@type": "Organization",
533
- name: opts.authorName,
534
- },
535
- };
536
- }
537
-
538
- /**
539
- * 17. Carousel / ItemList Schema
540
- */
541
- static buildCarousel(items: Array<{ name: string; url: string; position: number; image?: string }>): Record<string, unknown> {
542
- return {
543
- "@context": "https://schema.org",
544
- "@type": "ItemList",
545
- itemListElement: items.map((item) => ({
546
- "@type": "ListItem",
547
- position: item.position,
548
- url: item.url,
549
- name: item.name,
550
- image: item.image,
551
- })),
552
- };
553
- }
554
-
555
- /**
556
- * 18. QAPage Schema (bbPress / Forums / Community QA)
557
- */
558
- static buildQAPage(opts: {
559
- questionText: string;
560
- authorName: string;
561
- answerText: string;
562
- answerAuthor: string;
563
- upvoteCount?: number;
564
- }): Record<string, unknown> {
565
- return {
566
- "@context": "https://schema.org",
567
- "@type": "QAPage",
568
- mainEntity: {
569
- "@type": "Question",
570
- name: opts.questionText,
571
- text: opts.questionText,
572
- author: {
573
- "@type": "Person",
574
- name: opts.authorName,
575
- },
576
- answerCount: 1,
577
- acceptedAnswer: {
578
- "@type": "Answer",
579
- text: opts.answerText,
580
- upvoteCount: opts.upvoteCount ?? 12,
581
- author: {
582
- "@type": "Person",
583
- name: opts.answerAuthor,
584
- },
585
- },
586
- },
587
- };
588
- }
589
- }