@masters-ws/react-seo 1.2.1 → 1.4.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.
@@ -0,0 +1,721 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+
3
+ interface SiteConfig {
4
+ name: string;
5
+ description: string;
6
+ url: string;
7
+ logo?: string;
8
+ publisher?: string;
9
+ twitterHandle?: string;
10
+ facebookAppId?: string;
11
+ language?: string;
12
+ socialLinks?: string[];
13
+ googleAnalyticsId?: string;
14
+ gtmId?: string;
15
+ facebookPixelId?: string;
16
+ yandexMetricaId?: string;
17
+ themeColor?: string;
18
+ manifest?: string;
19
+ }
20
+ interface SEOData {
21
+ title?: string;
22
+ description?: string;
23
+ image?: string;
24
+ canonical?: string;
25
+ type?: 'website' | 'article' | 'product' | 'profile' | 'video' | 'faq';
26
+ robots?: string;
27
+ noindex?: boolean;
28
+ keywords?: string[];
29
+ prev?: string;
30
+ next?: string;
31
+ alternates?: Array<{
32
+ hreflang: string;
33
+ href: string;
34
+ }>;
35
+ ogTitle?: string;
36
+ ogDescription?: string;
37
+ ogImage?: string;
38
+ ogImageWidth?: number;
39
+ ogImageHeight?: number;
40
+ ogImageAlt?: string;
41
+ ogType?: string;
42
+ ogLocale?: string;
43
+ twitterCard?: 'summary' | 'summary_large_image' | 'app' | 'player';
44
+ twitterTitle?: string;
45
+ twitterDescription?: string;
46
+ twitterImage?: string;
47
+ twitterImageAlt?: string;
48
+ publishedTime?: string;
49
+ modifiedTime?: string;
50
+ author?: {
51
+ name: string;
52
+ url?: string;
53
+ image?: string;
54
+ };
55
+ tags?: string[];
56
+ section?: string;
57
+ readingTime?: number;
58
+ product?: {
59
+ sku?: string;
60
+ brand?: string;
61
+ price?: number;
62
+ currency?: string;
63
+ availability?: string;
64
+ rating?: number;
65
+ reviewCount?: number;
66
+ };
67
+ dnsPrefetch?: string[];
68
+ preconnect?: string[];
69
+ prefetch?: string[];
70
+ preload?: Array<{
71
+ href: string;
72
+ as: string;
73
+ type?: string;
74
+ }>;
75
+ whatsappImage?: string;
76
+ whatsappDescription?: string;
77
+ schema?: any;
78
+ }
79
+ interface BreadcrumbItem {
80
+ name: string;
81
+ item: string;
82
+ }
83
+
84
+ /**
85
+ * Generate Organization Schema
86
+ */
87
+ declare function generateOrganizationSchema(config: SiteConfig): {
88
+ "@context": string;
89
+ "@type": string;
90
+ name: string;
91
+ url: string;
92
+ logo: string | undefined;
93
+ sameAs: string[];
94
+ };
95
+ /**
96
+ * Generate WebSite Schema with SearchAction
97
+ */
98
+ declare function generateWebSiteSchema(config: SiteConfig): {
99
+ "@context": string;
100
+ "@type": string;
101
+ name: string;
102
+ url: string;
103
+ potentialAction: {
104
+ "@type": string;
105
+ target: string;
106
+ "query-input": string;
107
+ };
108
+ };
109
+ /**
110
+ * Generate NewsArticle Schema
111
+ */
112
+ declare function generateArticleSchema(data: {
113
+ title: string;
114
+ description: string;
115
+ image?: string;
116
+ publishedTime?: string;
117
+ modifiedTime?: string;
118
+ author?: {
119
+ name: string;
120
+ url?: string;
121
+ };
122
+ url?: string;
123
+ }, config: SiteConfig): {
124
+ "@context": string;
125
+ "@type": string;
126
+ headline: string;
127
+ description: string;
128
+ image: string | undefined;
129
+ datePublished: string | undefined;
130
+ dateModified: string | undefined;
131
+ mainEntityOfPage: string | undefined;
132
+ author: {
133
+ "@context": string;
134
+ "@type": string;
135
+ name: string;
136
+ url: string;
137
+ logo: string | undefined;
138
+ sameAs: string[];
139
+ } | {
140
+ "@type": string;
141
+ name: string;
142
+ url: string | undefined;
143
+ };
144
+ publisher: {
145
+ "@context": string;
146
+ "@type": string;
147
+ name: string;
148
+ url: string;
149
+ logo: string | undefined;
150
+ sameAs: string[];
151
+ };
152
+ };
153
+ /**
154
+ * Generate Product Schema
155
+ */
156
+ declare function generateProductSchema(data: {
157
+ name: string;
158
+ description: string;
159
+ image?: string;
160
+ sku?: string;
161
+ brand?: string;
162
+ price?: number;
163
+ currency?: string;
164
+ availability?: string;
165
+ rating?: number;
166
+ reviewCount?: number;
167
+ url?: string;
168
+ }): {
169
+ "@context": string;
170
+ "@type": string;
171
+ name: string;
172
+ description: string;
173
+ image: string | undefined;
174
+ sku: string | undefined;
175
+ brand: {
176
+ "@type": string;
177
+ name: string;
178
+ } | undefined;
179
+ offers: {
180
+ "@type": string;
181
+ url: string | undefined;
182
+ priceCurrency: string;
183
+ price: number | undefined;
184
+ availability: string;
185
+ };
186
+ aggregateRating: {
187
+ "@type": string;
188
+ ratingValue: number;
189
+ reviewCount: number;
190
+ } | undefined;
191
+ };
192
+ /**
193
+ * Generate FAQ Schema
194
+ */
195
+ declare function generateFAQSchema(questions: Array<{
196
+ q: string;
197
+ a: string;
198
+ }>): {
199
+ "@context": string;
200
+ "@type": string;
201
+ mainEntity: {
202
+ "@type": string;
203
+ name: string;
204
+ acceptedAnswer: {
205
+ "@type": string;
206
+ text: string;
207
+ };
208
+ }[];
209
+ };
210
+ /**
211
+ * Generate Breadcrumb Schema
212
+ */
213
+ declare function generateBreadcrumbSchema(items: Array<{
214
+ name: string;
215
+ item: string;
216
+ }>): {
217
+ "@context": string;
218
+ "@type": string;
219
+ itemListElement: {
220
+ "@type": string;
221
+ position: number;
222
+ name: string;
223
+ item: string;
224
+ }[];
225
+ };
226
+ /**
227
+ * Generate Video Schema
228
+ */
229
+ declare function generateVideoSchema(data: {
230
+ name: string;
231
+ description: string;
232
+ thumbnailUrl: string;
233
+ uploadDate: string;
234
+ duration?: string;
235
+ contentUrl?: string;
236
+ embedUrl?: string;
237
+ }): {
238
+ "@context": string;
239
+ "@type": string;
240
+ name: string;
241
+ description: string;
242
+ thumbnailUrl: string;
243
+ uploadDate: string;
244
+ duration: string | undefined;
245
+ contentUrl: string | undefined;
246
+ embedUrl: string | undefined;
247
+ };
248
+ /**
249
+ * Generate Event Schema
250
+ */
251
+ declare function generateEventSchema(data: {
252
+ name: string;
253
+ description: string;
254
+ startDate: string;
255
+ endDate?: string;
256
+ location?: {
257
+ name: string;
258
+ address: string;
259
+ } | {
260
+ url: string;
261
+ };
262
+ image?: string;
263
+ offers?: {
264
+ price: number;
265
+ currency: string;
266
+ url: string;
267
+ };
268
+ }): {
269
+ "@context": string;
270
+ "@type": string;
271
+ name: string;
272
+ description: string;
273
+ startDate: string;
274
+ endDate: string | undefined;
275
+ eventAttendanceMode: string;
276
+ location: {
277
+ "@type": string;
278
+ url: string;
279
+ name?: undefined;
280
+ address?: undefined;
281
+ } | {
282
+ "@type": string;
283
+ name: string;
284
+ address: string;
285
+ url?: undefined;
286
+ } | undefined;
287
+ image: string | undefined;
288
+ offers: {
289
+ "@type": string;
290
+ price: number;
291
+ priceCurrency: string;
292
+ url: string;
293
+ } | undefined;
294
+ };
295
+ /**
296
+ * Generate LocalBusiness Schema
297
+ */
298
+ declare function generateLocalBusinessSchema(data: {
299
+ name: string;
300
+ description: string;
301
+ image?: string;
302
+ telephone?: string;
303
+ address: {
304
+ street: string;
305
+ city: string;
306
+ region?: string;
307
+ postalCode?: string;
308
+ country: string;
309
+ };
310
+ geo?: {
311
+ lat: number;
312
+ lng: number;
313
+ };
314
+ openingHours?: string[];
315
+ priceRange?: string;
316
+ }): {
317
+ "@context": string;
318
+ "@type": string;
319
+ name: string;
320
+ description: string;
321
+ image: string | undefined;
322
+ telephone: string | undefined;
323
+ address: {
324
+ "@type": string;
325
+ streetAddress: string;
326
+ addressLocality: string;
327
+ addressRegion: string | undefined;
328
+ postalCode: string | undefined;
329
+ addressCountry: string;
330
+ };
331
+ geo: {
332
+ "@type": string;
333
+ latitude: number;
334
+ longitude: number;
335
+ } | undefined;
336
+ openingHours: string[] | undefined;
337
+ priceRange: string | undefined;
338
+ };
339
+ /**
340
+ * Generate SoftwareApplication Schema
341
+ */
342
+ declare function generateSoftwareSchema(data: {
343
+ name: string;
344
+ description: string;
345
+ operatingSystem?: string;
346
+ applicationCategory?: string;
347
+ offers?: {
348
+ price: number;
349
+ currency: string;
350
+ };
351
+ rating?: number;
352
+ reviewCount?: number;
353
+ downloadUrl?: string;
354
+ screenshot?: string;
355
+ }): {
356
+ "@context": string;
357
+ "@type": string;
358
+ name: string;
359
+ description: string;
360
+ operatingSystem: string | undefined;
361
+ applicationCategory: string | undefined;
362
+ offers: {
363
+ "@type": string;
364
+ price: number;
365
+ priceCurrency: string;
366
+ } | undefined;
367
+ aggregateRating: {
368
+ "@type": string;
369
+ ratingValue: number;
370
+ reviewCount: number;
371
+ } | undefined;
372
+ downloadUrl: string | undefined;
373
+ screenshot: string | undefined;
374
+ };
375
+ /**
376
+ * Generate Book Schema
377
+ */
378
+ declare function generateBookSchema(data: {
379
+ name: string;
380
+ author: string | {
381
+ name: string;
382
+ url?: string;
383
+ };
384
+ description: string;
385
+ isbn?: string;
386
+ numberOfPages?: number;
387
+ publisher?: string;
388
+ datePublished?: string;
389
+ image?: string;
390
+ inLanguage?: string;
391
+ genre?: string;
392
+ }): {
393
+ "@context": string;
394
+ "@type": string;
395
+ name: string;
396
+ author: {
397
+ "@type": string;
398
+ name: string;
399
+ url?: undefined;
400
+ } | {
401
+ "@type": string;
402
+ name: string;
403
+ url: string | undefined;
404
+ };
405
+ description: string;
406
+ isbn: string | undefined;
407
+ numberOfPages: number | undefined;
408
+ publisher: {
409
+ "@type": string;
410
+ name: string;
411
+ } | undefined;
412
+ datePublished: string | undefined;
413
+ image: string | undefined;
414
+ inLanguage: string | undefined;
415
+ genre: string | undefined;
416
+ };
417
+ /**
418
+ * Generate Movie Schema
419
+ */
420
+ declare function generateMovieSchema(data: {
421
+ name: string;
422
+ description: string;
423
+ image?: string;
424
+ director?: string;
425
+ actor?: string[];
426
+ dateCreated?: string;
427
+ duration?: string;
428
+ genre?: string[];
429
+ rating?: number;
430
+ reviewCount?: number;
431
+ }): {
432
+ "@context": string;
433
+ "@type": string;
434
+ name: string;
435
+ description: string;
436
+ image: string | undefined;
437
+ director: {
438
+ "@type": string;
439
+ name: string;
440
+ } | undefined;
441
+ actor: {
442
+ "@type": string;
443
+ name: string;
444
+ }[] | undefined;
445
+ dateCreated: string | undefined;
446
+ duration: string | undefined;
447
+ genre: string[] | undefined;
448
+ aggregateRating: {
449
+ "@type": string;
450
+ ratingValue: number;
451
+ reviewCount: number;
452
+ } | undefined;
453
+ };
454
+ /**
455
+ * Generate Podcast Schema (PodcastSeries)
456
+ */
457
+ declare function generatePodcastSchema(data: {
458
+ name: string;
459
+ description: string;
460
+ image?: string;
461
+ author?: string;
462
+ webFeed?: string;
463
+ url?: string;
464
+ genre?: string;
465
+ }): {
466
+ "@context": string;
467
+ "@type": string;
468
+ name: string;
469
+ description: string;
470
+ image: string | undefined;
471
+ author: {
472
+ "@type": string;
473
+ name: string;
474
+ } | undefined;
475
+ webFeed: string | undefined;
476
+ url: string | undefined;
477
+ genre: string | undefined;
478
+ };
479
+ /**
480
+ * Generate PodcastEpisode Schema
481
+ */
482
+ declare function generatePodcastEpisodeSchema(data: {
483
+ name: string;
484
+ description: string;
485
+ datePublished: string;
486
+ duration?: string;
487
+ url?: string;
488
+ audio?: string;
489
+ partOfSeries?: {
490
+ name: string;
491
+ url: string;
492
+ };
493
+ }): {
494
+ "@context": string;
495
+ "@type": string;
496
+ name: string;
497
+ description: string;
498
+ datePublished: string;
499
+ timeRequired: string | undefined;
500
+ url: string | undefined;
501
+ associatedMedia: {
502
+ "@type": string;
503
+ contentUrl: string;
504
+ } | undefined;
505
+ partOfSeries: {
506
+ "@type": string;
507
+ name: string;
508
+ url: string;
509
+ } | undefined;
510
+ };
511
+
512
+ /**
513
+ * Converts SEOData and SiteConfig to a Next.js compatible Metadata object.
514
+ * This is designed to be used in Server Components (App Router).
515
+ *
516
+ * @example
517
+ * // In page.tsx (Server Component - no 'use client')
518
+ * import { toNextMetadata } from '@masters-ws/react-seo/core';
519
+ *
520
+ * export async function generateMetadata({ params }) {
521
+ * const post = await getPost(params.id);
522
+ * return toNextMetadata({ title: post.title, ... }, siteConfig);
523
+ * }
524
+ */
525
+ declare function toNextMetadata(props: SEOData, config: SiteConfig): any;
526
+ /**
527
+ * Generates pagination links for category/tag pages.
528
+ * Returns { prev, next, canonical } URLs.
529
+ */
530
+ declare function generatePaginationLinks(baseUrl: string, currentPage: number, totalPages: number): {
531
+ next: string | undefined;
532
+ prev: string | undefined;
533
+ canonical: string;
534
+ };
535
+ /**
536
+ * Generates title with page number suffix for paginated pages.
537
+ */
538
+ declare function generatePaginatedTitle(title: string, page: number, suffix?: string): string;
539
+
540
+ /**
541
+ * Server-safe JSON-LD component for Next.js App Router.
542
+ * Renders schema markup as <script type="application/ld+json"> in server-rendered HTML.
543
+ *
544
+ * Unlike Helmet-based components, this works in Server Components,
545
+ * so Google sees the schemas on first crawl without JavaScript execution.
546
+ *
547
+ * @example
548
+ * // In a Server Component (page.tsx without 'use client')
549
+ * import { JsonLd, generateProductSchema } from '@masters-ws/react-seo/core';
550
+ *
551
+ * export default function ProductPage() {
552
+ * const schema = generateProductSchema({ name: 'Product', ... });
553
+ * return (
554
+ * <>
555
+ * <JsonLd schema={schema} />
556
+ * <main>...</main>
557
+ * </>
558
+ * );
559
+ * }
560
+ *
561
+ * // Multiple schemas
562
+ * <JsonLd schema={[productSchema, breadcrumbSchema, orgSchema]} />
563
+ */
564
+ interface JsonLdProps {
565
+ /** A single schema object or an array of schema objects */
566
+ schema: Record<string, any> | Record<string, any>[];
567
+ }
568
+ declare function JsonLd({ schema }: JsonLdProps): react_jsx_runtime.JSX.Element;
569
+
570
+ /**
571
+ * Product metadata input for convenience helper
572
+ */
573
+ interface ProductMetadataInput {
574
+ name: string;
575
+ description: string;
576
+ image?: string;
577
+ images?: string[];
578
+ sku?: string;
579
+ brand?: string;
580
+ price?: number;
581
+ currency?: string;
582
+ availability?: string;
583
+ rating?: number;
584
+ reviewCount?: number;
585
+ url: string;
586
+ metaTitle?: string;
587
+ metaDescription?: string;
588
+ canonical?: string;
589
+ noindex?: boolean;
590
+ breadcrumbs?: Array<{
591
+ name: string;
592
+ item: string;
593
+ }>;
594
+ category?: string;
595
+ ogImage?: string;
596
+ ogImageWidth?: number;
597
+ ogImageHeight?: number;
598
+ }
599
+ /**
600
+ * Result from generateProductMetadata
601
+ */
602
+ interface ProductMetadataResult {
603
+ /** Next.js Metadata object - use as return from generateMetadata() */
604
+ metadata: any;
605
+ /** All JSON-LD schemas for the product page */
606
+ schemas: Record<string, any>[];
607
+ /** Individual schemas for granular control */
608
+ productSchema: Record<string, any>;
609
+ breadcrumbSchema: Record<string, any>;
610
+ organizationSchema: Record<string, any>;
611
+ websiteSchema: Record<string, any>;
612
+ }
613
+ /**
614
+ * All-in-one helper for product pages in Next.js App Router.
615
+ * Generates both the Metadata object AND all JSON-LD schemas in one call.
616
+ *
617
+ * @example
618
+ * ```tsx
619
+ * // app/products/[slug]/page.tsx (Server Component)
620
+ * import { generateProductMetadata, JsonLd } from '@masters-ws/react-seo/core';
621
+ *
622
+ * const siteConfig = { name: 'My Store', url: 'https://store.com', ... };
623
+ *
624
+ * export async function generateMetadata({ params }) {
625
+ * const product = await fetchProduct(params.slug);
626
+ * const { metadata } = generateProductMetadata(product, siteConfig);
627
+ * return metadata;
628
+ * }
629
+ *
630
+ * export default async function ProductPage({ params }) {
631
+ * const product = await fetchProduct(params.slug);
632
+ * const { schemas } = generateProductMetadata(product, siteConfig);
633
+ *
634
+ * return (
635
+ * <>
636
+ * <JsonLd schema={schemas} />
637
+ * <ProductDetailClient product={product} />
638
+ * </>
639
+ * );
640
+ * }
641
+ * ```
642
+ */
643
+ declare function generateProductMetadata(product: ProductMetadataInput, config: SiteConfig): ProductMetadataResult;
644
+
645
+ /**
646
+ * Article metadata input for convenience helper
647
+ */
648
+ interface ArticleMetadataInput {
649
+ title: string;
650
+ description: string;
651
+ image?: string;
652
+ publishedTime: string;
653
+ modifiedTime?: string;
654
+ author?: {
655
+ name: string;
656
+ url?: string;
657
+ image?: string;
658
+ };
659
+ url: string;
660
+ category?: string;
661
+ tags?: string[];
662
+ readingTime?: number;
663
+ metaTitle?: string;
664
+ metaDescription?: string;
665
+ canonical?: string;
666
+ noindex?: boolean;
667
+ breadcrumbs?: Array<{
668
+ name: string;
669
+ item: string;
670
+ }>;
671
+ ogImage?: string;
672
+ ogImageWidth?: number;
673
+ ogImageHeight?: number;
674
+ }
675
+ /**
676
+ * Result from generateArticleMetadata
677
+ */
678
+ interface ArticleMetadataResult {
679
+ /** Next.js Metadata object - use as return from generateMetadata() */
680
+ metadata: any;
681
+ /** All JSON-LD schemas for the article page */
682
+ schemas: Record<string, any>[];
683
+ /** Individual schemas for granular control */
684
+ articleSchema: Record<string, any>;
685
+ breadcrumbSchema: Record<string, any>;
686
+ organizationSchema: Record<string, any>;
687
+ websiteSchema: Record<string, any>;
688
+ }
689
+ /**
690
+ * All-in-one helper for article pages in Next.js App Router.
691
+ * Generates both the Metadata object AND all JSON-LD schemas in one call.
692
+ *
693
+ * @example
694
+ * ```tsx
695
+ * // app/news/[slug]/page.tsx (Server Component)
696
+ * import { generateArticleMetadata, JsonLd } from '@masters-ws/react-seo/core';
697
+ *
698
+ * const siteConfig = { name: 'My Site', url: 'https://mysite.com', ... };
699
+ *
700
+ * export async function generateMetadata({ params }) {
701
+ * const article = await fetchArticle(params.slug);
702
+ * const { metadata } = generateArticleMetadata(article, siteConfig);
703
+ * return metadata;
704
+ * }
705
+ *
706
+ * export default async function ArticlePage({ params }) {
707
+ * const article = await fetchArticle(params.slug);
708
+ * const { schemas } = generateArticleMetadata(article, siteConfig);
709
+ *
710
+ * return (
711
+ * <>
712
+ * <JsonLd schema={schemas} />
713
+ * <article>...</article>
714
+ * </>
715
+ * );
716
+ * }
717
+ * ```
718
+ */
719
+ declare function generateArticleMetadata(article: ArticleMetadataInput, config: SiteConfig): ArticleMetadataResult;
720
+
721
+ export { type ArticleMetadataInput as A, type BreadcrumbItem as B, JsonLd as J, type ProductMetadataInput as P, type SiteConfig as S, type SEOData as a, type ArticleMetadataResult as b, type JsonLdProps as c, type ProductMetadataResult as d, generateArticleSchema as e, generateBookSchema as f, generateArticleMetadata as g, generateBreadcrumbSchema as h, generateEventSchema as i, generateFAQSchema as j, generateLocalBusinessSchema as k, generateMovieSchema as l, generateOrganizationSchema as m, generatePaginatedTitle as n, generatePaginationLinks as o, generatePodcastEpisodeSchema as p, generatePodcastSchema as q, generateProductMetadata as r, generateProductSchema as s, generateSoftwareSchema as t, generateVideoSchema as u, generateWebSiteSchema as v, toNextMetadata as w };