@flexireact/core 3.0.1 → 3.0.3

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 (55) hide show
  1. package/dist/cli/index.js +1514 -0
  2. package/dist/cli/index.js.map +1 -0
  3. package/dist/core/client/index.js +373 -0
  4. package/dist/core/client/index.js.map +1 -0
  5. package/dist/core/index.js +6415 -0
  6. package/dist/core/index.js.map +1 -0
  7. package/dist/core/server/index.js +3094 -0
  8. package/dist/core/server/index.js.map +1 -0
  9. package/package.json +80 -80
  10. package/bin/flexireact.js +0 -23
  11. package/cli/generators.ts +0 -616
  12. package/cli/index.ts +0 -1182
  13. package/core/actions/index.ts +0 -364
  14. package/core/api.ts +0 -143
  15. package/core/build/index.ts +0 -425
  16. package/core/cli/logger.ts +0 -353
  17. package/core/client/Link.tsx +0 -345
  18. package/core/client/hydration.ts +0 -147
  19. package/core/client/index.ts +0 -12
  20. package/core/client/islands.ts +0 -143
  21. package/core/client/navigation.ts +0 -212
  22. package/core/client/runtime.ts +0 -52
  23. package/core/config.ts +0 -116
  24. package/core/context.ts +0 -83
  25. package/core/dev.ts +0 -47
  26. package/core/devtools/index.ts +0 -644
  27. package/core/edge/cache.ts +0 -344
  28. package/core/edge/fetch-polyfill.ts +0 -247
  29. package/core/edge/handler.ts +0 -248
  30. package/core/edge/index.ts +0 -81
  31. package/core/edge/ppr.ts +0 -264
  32. package/core/edge/runtime.ts +0 -161
  33. package/core/font/index.ts +0 -306
  34. package/core/helpers.ts +0 -494
  35. package/core/image/index.ts +0 -413
  36. package/core/index.ts +0 -218
  37. package/core/islands/index.ts +0 -293
  38. package/core/loader.ts +0 -111
  39. package/core/logger.ts +0 -242
  40. package/core/metadata/index.ts +0 -622
  41. package/core/middleware/index.ts +0 -416
  42. package/core/plugins/index.ts +0 -373
  43. package/core/render/index.ts +0 -1243
  44. package/core/render.ts +0 -136
  45. package/core/router/index.ts +0 -551
  46. package/core/router.ts +0 -141
  47. package/core/rsc/index.ts +0 -199
  48. package/core/server/index.ts +0 -779
  49. package/core/server.ts +0 -203
  50. package/core/ssg/index.ts +0 -346
  51. package/core/start-dev.ts +0 -6
  52. package/core/start-prod.ts +0 -6
  53. package/core/tsconfig.json +0 -30
  54. package/core/types.ts +0 -239
  55. package/core/utils.ts +0 -176
@@ -1,622 +0,0 @@
1
- /**
2
- * FlexiReact Metadata API
3
- *
4
- * Complete metadata management like Next.js:
5
- * - Static and dynamic metadata
6
- * - Open Graph / Twitter Cards
7
- * - Robots / Sitemap
8
- * - JSON-LD structured data
9
- * - Viewport configuration
10
- * - Icons and manifest
11
- */
12
-
13
- import React from 'react';
14
-
15
- // Base metadata types
16
- export interface Metadata {
17
- // Basic
18
- title?: string | { default: string; template?: string; absolute?: string };
19
- description?: string;
20
- keywords?: string | string[];
21
- authors?: Author | Author[];
22
- creator?: string;
23
- publisher?: string;
24
-
25
- // Robots
26
- robots?: Robots | string;
27
-
28
- // Icons
29
- icons?: Icons;
30
-
31
- // Manifest
32
- manifest?: string;
33
-
34
- // Open Graph
35
- openGraph?: OpenGraph;
36
-
37
- // Twitter
38
- twitter?: Twitter;
39
-
40
- // Verification
41
- verification?: Verification;
42
-
43
- // Alternates
44
- alternates?: Alternates;
45
-
46
- // App Links
47
- appLinks?: AppLinks;
48
-
49
- // Archives
50
- archives?: string | string[];
51
-
52
- // Assets
53
- assets?: string | string[];
54
-
55
- // Bookmarks
56
- bookmarks?: string | string[];
57
-
58
- // Category
59
- category?: string;
60
-
61
- // Classification
62
- classification?: string;
63
-
64
- // Other
65
- other?: Record<string, string | string[]>;
66
-
67
- // Viewport
68
- viewport?: Viewport | string;
69
-
70
- // Theme Color
71
- themeColor?: ThemeColor | ThemeColor[];
72
-
73
- // Color Scheme
74
- colorScheme?: 'normal' | 'light' | 'dark' | 'light dark' | 'dark light';
75
-
76
- // Format Detection
77
- formatDetection?: FormatDetection;
78
-
79
- // Base URL
80
- metadataBase?: URL | string;
81
-
82
- // Generator
83
- generator?: string;
84
-
85
- // Application Name
86
- applicationName?: string;
87
-
88
- // Referrer
89
- referrer?: 'no-referrer' | 'origin' | 'no-referrer-when-downgrade' | 'origin-when-cross-origin' | 'same-origin' | 'strict-origin' | 'strict-origin-when-cross-origin' | 'unsafe-url';
90
- }
91
-
92
- export interface Author {
93
- name?: string;
94
- url?: string;
95
- }
96
-
97
- export interface Robots {
98
- index?: boolean;
99
- follow?: boolean;
100
- noarchive?: boolean;
101
- nosnippet?: boolean;
102
- noimageindex?: boolean;
103
- nocache?: boolean;
104
- googleBot?: Robots | string;
105
- }
106
-
107
- export interface Icons {
108
- icon?: IconDescriptor | IconDescriptor[];
109
- shortcut?: IconDescriptor | IconDescriptor[];
110
- apple?: IconDescriptor | IconDescriptor[];
111
- other?: IconDescriptor[];
112
- }
113
-
114
- export interface IconDescriptor {
115
- url: string;
116
- type?: string;
117
- sizes?: string;
118
- color?: string;
119
- rel?: string;
120
- media?: string;
121
- }
122
-
123
- export interface OpenGraph {
124
- type?: 'website' | 'article' | 'book' | 'profile' | 'music.song' | 'music.album' | 'music.playlist' | 'music.radio_station' | 'video.movie' | 'video.episode' | 'video.tv_show' | 'video.other';
125
- url?: string;
126
- title?: string;
127
- description?: string;
128
- siteName?: string;
129
- locale?: string;
130
- images?: OGImage | OGImage[];
131
- videos?: OGVideo | OGVideo[];
132
- audio?: OGAudio | OGAudio[];
133
- determiner?: 'a' | 'an' | 'the' | 'auto' | '';
134
-
135
- // Article specific
136
- publishedTime?: string;
137
- modifiedTime?: string;
138
- expirationTime?: string;
139
- authors?: string | string[];
140
- section?: string;
141
- tags?: string[];
142
- }
143
-
144
- export interface OGImage {
145
- url: string;
146
- secureUrl?: string;
147
- type?: string;
148
- width?: number;
149
- height?: number;
150
- alt?: string;
151
- }
152
-
153
- export interface OGVideo {
154
- url: string;
155
- secureUrl?: string;
156
- type?: string;
157
- width?: number;
158
- height?: number;
159
- }
160
-
161
- export interface OGAudio {
162
- url: string;
163
- secureUrl?: string;
164
- type?: string;
165
- }
166
-
167
- export interface Twitter {
168
- card?: 'summary' | 'summary_large_image' | 'app' | 'player';
169
- site?: string;
170
- siteId?: string;
171
- creator?: string;
172
- creatorId?: string;
173
- title?: string;
174
- description?: string;
175
- images?: string | TwitterImage | (string | TwitterImage)[];
176
- app?: TwitterApp;
177
- player?: TwitterPlayer;
178
- }
179
-
180
- export interface TwitterImage {
181
- url: string;
182
- alt?: string;
183
- }
184
-
185
- export interface TwitterApp {
186
- id?: { iphone?: string; ipad?: string; googleplay?: string };
187
- name?: string;
188
- url?: { iphone?: string; ipad?: string; googleplay?: string };
189
- }
190
-
191
- export interface TwitterPlayer {
192
- url: string;
193
- width?: number;
194
- height?: number;
195
- stream?: string;
196
- }
197
-
198
- export interface Verification {
199
- google?: string | string[];
200
- yahoo?: string | string[];
201
- yandex?: string | string[];
202
- me?: string | string[];
203
- other?: Record<string, string | string[]>;
204
- }
205
-
206
- export interface Alternates {
207
- canonical?: string;
208
- languages?: Record<string, string>;
209
- media?: Record<string, string>;
210
- types?: Record<string, string>;
211
- }
212
-
213
- export interface AppLinks {
214
- ios?: AppLink | AppLink[];
215
- iphone?: AppLink | AppLink[];
216
- ipad?: AppLink | AppLink[];
217
- android?: AppLink | AppLink[];
218
- windows_phone?: AppLink | AppLink[];
219
- windows?: AppLink | AppLink[];
220
- windows_universal?: AppLink | AppLink[];
221
- web?: AppLink | AppLink[];
222
- }
223
-
224
- export interface AppLink {
225
- url: string;
226
- app_store_id?: string;
227
- app_name?: string;
228
- }
229
-
230
- export interface Viewport {
231
- width?: number | 'device-width';
232
- height?: number | 'device-height';
233
- initialScale?: number;
234
- minimumScale?: number;
235
- maximumScale?: number;
236
- userScalable?: boolean;
237
- viewportFit?: 'auto' | 'cover' | 'contain';
238
- interactiveWidget?: 'resizes-visual' | 'resizes-content' | 'overlays-content';
239
- }
240
-
241
- export interface ThemeColor {
242
- color: string;
243
- media?: string;
244
- }
245
-
246
- export interface FormatDetection {
247
- telephone?: boolean;
248
- date?: boolean;
249
- address?: boolean;
250
- email?: boolean;
251
- url?: boolean;
252
- }
253
-
254
- // Generate HTML head tags from metadata
255
- export function generateMetadataTags(metadata: Metadata, baseUrl?: string): string {
256
- const tags: string[] = [];
257
- const base = baseUrl || metadata.metadataBase?.toString() || '';
258
-
259
- // Title
260
- if (metadata.title) {
261
- const title = typeof metadata.title === 'string'
262
- ? metadata.title
263
- : metadata.title.absolute || (metadata.title.template
264
- ? metadata.title.template.replace('%s', metadata.title.default)
265
- : metadata.title.default);
266
- tags.push(`<title>${escapeHtml(title)}</title>`);
267
- }
268
-
269
- // Description
270
- if (metadata.description) {
271
- tags.push(`<meta name="description" content="${escapeHtml(metadata.description)}">`);
272
- }
273
-
274
- // Keywords
275
- if (metadata.keywords) {
276
- const keywords = Array.isArray(metadata.keywords) ? metadata.keywords.join(', ') : metadata.keywords;
277
- tags.push(`<meta name="keywords" content="${escapeHtml(keywords)}">`);
278
- }
279
-
280
- // Authors
281
- if (metadata.authors) {
282
- const authors = Array.isArray(metadata.authors) ? metadata.authors : [metadata.authors];
283
- authors.forEach(author => {
284
- if (author.name) tags.push(`<meta name="author" content="${escapeHtml(author.name)}">`);
285
- if (author.url) tags.push(`<link rel="author" href="${author.url}">`);
286
- });
287
- }
288
-
289
- // Generator
290
- if (metadata.generator) {
291
- tags.push(`<meta name="generator" content="${escapeHtml(metadata.generator)}">`);
292
- }
293
-
294
- // Application Name
295
- if (metadata.applicationName) {
296
- tags.push(`<meta name="application-name" content="${escapeHtml(metadata.applicationName)}">`);
297
- }
298
-
299
- // Referrer
300
- if (metadata.referrer) {
301
- tags.push(`<meta name="referrer" content="${metadata.referrer}">`);
302
- }
303
-
304
- // Robots
305
- if (metadata.robots) {
306
- if (typeof metadata.robots === 'string') {
307
- tags.push(`<meta name="robots" content="${metadata.robots}">`);
308
- } else {
309
- const robotsContent = generateRobotsContent(metadata.robots);
310
- tags.push(`<meta name="robots" content="${robotsContent}">`);
311
- if (metadata.robots.googleBot) {
312
- const googleBotContent = typeof metadata.robots.googleBot === 'string'
313
- ? metadata.robots.googleBot
314
- : generateRobotsContent(metadata.robots.googleBot);
315
- tags.push(`<meta name="googlebot" content="${googleBotContent}">`);
316
- }
317
- }
318
- }
319
-
320
- // Viewport
321
- if (metadata.viewport) {
322
- const viewportContent = typeof metadata.viewport === 'string'
323
- ? metadata.viewport
324
- : generateViewportContent(metadata.viewport);
325
- tags.push(`<meta name="viewport" content="${viewportContent}">`);
326
- }
327
-
328
- // Theme Color
329
- if (metadata.themeColor) {
330
- const themeColors = Array.isArray(metadata.themeColor) ? metadata.themeColor : [metadata.themeColor];
331
- themeColors.forEach(tc => {
332
- if (typeof tc === 'string') {
333
- tags.push(`<meta name="theme-color" content="${tc}">`);
334
- } else {
335
- const mediaAttr = tc.media ? ` media="${tc.media}"` : '';
336
- tags.push(`<meta name="theme-color" content="${tc.color}"${mediaAttr}>`);
337
- }
338
- });
339
- }
340
-
341
- // Color Scheme
342
- if (metadata.colorScheme) {
343
- tags.push(`<meta name="color-scheme" content="${metadata.colorScheme}">`);
344
- }
345
-
346
- // Format Detection
347
- if (metadata.formatDetection) {
348
- const fd = metadata.formatDetection;
349
- const parts: string[] = [];
350
- if (fd.telephone === false) parts.push('telephone=no');
351
- if (fd.date === false) parts.push('date=no');
352
- if (fd.address === false) parts.push('address=no');
353
- if (fd.email === false) parts.push('email=no');
354
- if (parts.length > 0) {
355
- tags.push(`<meta name="format-detection" content="${parts.join(', ')}">`);
356
- }
357
- }
358
-
359
- // Icons
360
- if (metadata.icons) {
361
- const addIcon = (icon: IconDescriptor, defaultRel: string) => {
362
- const rel = icon.rel || defaultRel;
363
- const type = icon.type ? ` type="${icon.type}"` : '';
364
- const sizes = icon.sizes ? ` sizes="${icon.sizes}"` : '';
365
- const color = icon.color ? ` color="${icon.color}"` : '';
366
- const media = icon.media ? ` media="${icon.media}"` : '';
367
- tags.push(`<link rel="${rel}" href="${resolveUrl(icon.url, base)}"${type}${sizes}${color}${media}>`);
368
- };
369
-
370
- if (metadata.icons.icon) {
371
- const icons = Array.isArray(metadata.icons.icon) ? metadata.icons.icon : [metadata.icons.icon];
372
- icons.forEach(icon => addIcon(icon, 'icon'));
373
- }
374
- if (metadata.icons.shortcut) {
375
- const icons = Array.isArray(metadata.icons.shortcut) ? metadata.icons.shortcut : [metadata.icons.shortcut];
376
- icons.forEach(icon => addIcon(icon, 'shortcut icon'));
377
- }
378
- if (metadata.icons.apple) {
379
- const icons = Array.isArray(metadata.icons.apple) ? metadata.icons.apple : [metadata.icons.apple];
380
- icons.forEach(icon => addIcon(icon, 'apple-touch-icon'));
381
- }
382
- }
383
-
384
- // Manifest
385
- if (metadata.manifest) {
386
- tags.push(`<link rel="manifest" href="${resolveUrl(metadata.manifest, base)}">`);
387
- }
388
-
389
- // Open Graph
390
- if (metadata.openGraph) {
391
- const og = metadata.openGraph;
392
- if (og.type) tags.push(`<meta property="og:type" content="${og.type}">`);
393
- if (og.title) tags.push(`<meta property="og:title" content="${escapeHtml(og.title)}">`);
394
- if (og.description) tags.push(`<meta property="og:description" content="${escapeHtml(og.description)}">`);
395
- if (og.url) tags.push(`<meta property="og:url" content="${resolveUrl(og.url, base)}">`);
396
- if (og.siteName) tags.push(`<meta property="og:site_name" content="${escapeHtml(og.siteName)}">`);
397
- if (og.locale) tags.push(`<meta property="og:locale" content="${og.locale}">`);
398
- if (og.determiner) tags.push(`<meta property="og:determiner" content="${og.determiner}">`);
399
-
400
- // Images
401
- if (og.images) {
402
- const images = Array.isArray(og.images) ? og.images : [og.images];
403
- images.forEach(img => {
404
- tags.push(`<meta property="og:image" content="${resolveUrl(img.url, base)}">`);
405
- if (img.secureUrl) tags.push(`<meta property="og:image:secure_url" content="${img.secureUrl}">`);
406
- if (img.type) tags.push(`<meta property="og:image:type" content="${img.type}">`);
407
- if (img.width) tags.push(`<meta property="og:image:width" content="${img.width}">`);
408
- if (img.height) tags.push(`<meta property="og:image:height" content="${img.height}">`);
409
- if (img.alt) tags.push(`<meta property="og:image:alt" content="${escapeHtml(img.alt)}">`);
410
- });
411
- }
412
-
413
- // Article specific
414
- if (og.type === 'article') {
415
- if (og.publishedTime) tags.push(`<meta property="article:published_time" content="${og.publishedTime}">`);
416
- if (og.modifiedTime) tags.push(`<meta property="article:modified_time" content="${og.modifiedTime}">`);
417
- if (og.expirationTime) tags.push(`<meta property="article:expiration_time" content="${og.expirationTime}">`);
418
- if (og.section) tags.push(`<meta property="article:section" content="${escapeHtml(og.section)}">`);
419
- if (og.tags) {
420
- og.tags.forEach(tag => tags.push(`<meta property="article:tag" content="${escapeHtml(tag)}">`));
421
- }
422
- if (og.authors) {
423
- const authors = Array.isArray(og.authors) ? og.authors : [og.authors];
424
- authors.forEach(author => tags.push(`<meta property="article:author" content="${escapeHtml(author)}">`));
425
- }
426
- }
427
- }
428
-
429
- // Twitter
430
- if (metadata.twitter) {
431
- const tw = metadata.twitter;
432
- if (tw.card) tags.push(`<meta name="twitter:card" content="${tw.card}">`);
433
- if (tw.site) tags.push(`<meta name="twitter:site" content="${tw.site}">`);
434
- if (tw.siteId) tags.push(`<meta name="twitter:site:id" content="${tw.siteId}">`);
435
- if (tw.creator) tags.push(`<meta name="twitter:creator" content="${tw.creator}">`);
436
- if (tw.creatorId) tags.push(`<meta name="twitter:creator:id" content="${tw.creatorId}">`);
437
- if (tw.title) tags.push(`<meta name="twitter:title" content="${escapeHtml(tw.title)}">`);
438
- if (tw.description) tags.push(`<meta name="twitter:description" content="${escapeHtml(tw.description)}">`);
439
-
440
- if (tw.images) {
441
- const images = Array.isArray(tw.images) ? tw.images : [tw.images];
442
- images.forEach(img => {
443
- const url = typeof img === 'string' ? img : img.url;
444
- tags.push(`<meta name="twitter:image" content="${resolveUrl(url, base)}">`);
445
- if (typeof img !== 'string' && img.alt) {
446
- tags.push(`<meta name="twitter:image:alt" content="${escapeHtml(img.alt)}">`);
447
- }
448
- });
449
- }
450
- }
451
-
452
- // Verification
453
- if (metadata.verification) {
454
- const v = metadata.verification;
455
- if (v.google) {
456
- const values = Array.isArray(v.google) ? v.google : [v.google];
457
- values.forEach(val => tags.push(`<meta name="google-site-verification" content="${val}">`));
458
- }
459
- if (v.yandex) {
460
- const values = Array.isArray(v.yandex) ? v.yandex : [v.yandex];
461
- values.forEach(val => tags.push(`<meta name="yandex-verification" content="${val}">`));
462
- }
463
- }
464
-
465
- // Alternates
466
- if (metadata.alternates) {
467
- const alt = metadata.alternates;
468
- if (alt.canonical) {
469
- tags.push(`<link rel="canonical" href="${resolveUrl(alt.canonical, base)}">`);
470
- }
471
- if (alt.languages) {
472
- Object.entries(alt.languages).forEach(([lang, url]) => {
473
- tags.push(`<link rel="alternate" hreflang="${lang}" href="${resolveUrl(url, base)}">`);
474
- });
475
- }
476
- }
477
-
478
- return tags.join('\n ');
479
- }
480
-
481
- // Helper functions
482
- function escapeHtml(str: string): string {
483
- return str
484
- .replace(/&/g, '&amp;')
485
- .replace(/</g, '&lt;')
486
- .replace(/>/g, '&gt;')
487
- .replace(/"/g, '&quot;')
488
- .replace(/'/g, '&#039;');
489
- }
490
-
491
- function resolveUrl(url: string, base: string): string {
492
- if (url.startsWith('http://') || url.startsWith('https://') || url.startsWith('//')) {
493
- return url;
494
- }
495
- return base ? `${base.replace(/\/$/, '')}${url.startsWith('/') ? '' : '/'}${url}` : url;
496
- }
497
-
498
- function generateRobotsContent(robots: Robots): string {
499
- const parts: string[] = [];
500
- if (robots.index !== undefined) parts.push(robots.index ? 'index' : 'noindex');
501
- if (robots.follow !== undefined) parts.push(robots.follow ? 'follow' : 'nofollow');
502
- if (robots.noarchive) parts.push('noarchive');
503
- if (robots.nosnippet) parts.push('nosnippet');
504
- if (robots.noimageindex) parts.push('noimageindex');
505
- if (robots.nocache) parts.push('nocache');
506
- return parts.join(', ') || 'index, follow';
507
- }
508
-
509
- function generateViewportContent(viewport: Viewport): string {
510
- const parts: string[] = [];
511
- if (viewport.width) parts.push(`width=${viewport.width}`);
512
- if (viewport.height) parts.push(`height=${viewport.height}`);
513
- if (viewport.initialScale !== undefined) parts.push(`initial-scale=${viewport.initialScale}`);
514
- if (viewport.minimumScale !== undefined) parts.push(`minimum-scale=${viewport.minimumScale}`);
515
- if (viewport.maximumScale !== undefined) parts.push(`maximum-scale=${viewport.maximumScale}`);
516
- if (viewport.userScalable !== undefined) parts.push(`user-scalable=${viewport.userScalable ? 'yes' : 'no'}`);
517
- if (viewport.viewportFit) parts.push(`viewport-fit=${viewport.viewportFit}`);
518
- return parts.join(', ') || 'width=device-width, initial-scale=1';
519
- }
520
-
521
- // Merge metadata (child overrides parent)
522
- export function mergeMetadata(parent: Metadata, child: Metadata): Metadata {
523
- return {
524
- ...parent,
525
- ...child,
526
- // Deep merge for nested objects
527
- openGraph: child.openGraph ? { ...parent.openGraph, ...child.openGraph } : parent.openGraph,
528
- twitter: child.twitter ? { ...parent.twitter, ...child.twitter } : parent.twitter,
529
- icons: child.icons ? { ...parent.icons, ...child.icons } : parent.icons,
530
- verification: child.verification ? { ...parent.verification, ...child.verification } : parent.verification,
531
- alternates: child.alternates ? { ...parent.alternates, ...child.alternates } : parent.alternates
532
- };
533
- }
534
-
535
- // Generate JSON-LD structured data
536
- export function generateJsonLd(data: Record<string, any>): string {
537
- return `<script type="application/ld+json">${JSON.stringify(data)}</script>`;
538
- }
539
-
540
- // Common JSON-LD schemas
541
- export const jsonLd = {
542
- website: (config: { name: string; url: string; description?: string }) => ({
543
- '@context': 'https://schema.org',
544
- '@type': 'WebSite',
545
- name: config.name,
546
- url: config.url,
547
- description: config.description
548
- }),
549
-
550
- article: (config: {
551
- headline: string;
552
- description?: string;
553
- image?: string | string[];
554
- datePublished: string;
555
- dateModified?: string;
556
- author: { name: string; url?: string } | { name: string; url?: string }[];
557
- }) => ({
558
- '@context': 'https://schema.org',
559
- '@type': 'Article',
560
- headline: config.headline,
561
- description: config.description,
562
- image: config.image,
563
- datePublished: config.datePublished,
564
- dateModified: config.dateModified || config.datePublished,
565
- author: Array.isArray(config.author)
566
- ? config.author.map(a => ({ '@type': 'Person', ...a }))
567
- : { '@type': 'Person', ...config.author }
568
- }),
569
-
570
- organization: (config: {
571
- name: string;
572
- url: string;
573
- logo?: string;
574
- sameAs?: string[];
575
- }) => ({
576
- '@context': 'https://schema.org',
577
- '@type': 'Organization',
578
- name: config.name,
579
- url: config.url,
580
- logo: config.logo,
581
- sameAs: config.sameAs
582
- }),
583
-
584
- product: (config: {
585
- name: string;
586
- description?: string;
587
- image?: string | string[];
588
- brand?: string;
589
- offers?: { price: number; priceCurrency: string; availability?: string };
590
- }) => ({
591
- '@context': 'https://schema.org',
592
- '@type': 'Product',
593
- name: config.name,
594
- description: config.description,
595
- image: config.image,
596
- brand: config.brand ? { '@type': 'Brand', name: config.brand } : undefined,
597
- offers: config.offers ? {
598
- '@type': 'Offer',
599
- price: config.offers.price,
600
- priceCurrency: config.offers.priceCurrency,
601
- availability: config.offers.availability || 'https://schema.org/InStock'
602
- } : undefined
603
- }),
604
-
605
- breadcrumb: (items: { name: string; url: string }[]) => ({
606
- '@context': 'https://schema.org',
607
- '@type': 'BreadcrumbList',
608
- itemListElement: items.map((item, index) => ({
609
- '@type': 'ListItem',
610
- position: index + 1,
611
- name: item.name,
612
- item: item.url
613
- }))
614
- })
615
- };
616
-
617
- export default {
618
- generateMetadataTags,
619
- mergeMetadata,
620
- generateJsonLd,
621
- jsonLd
622
- };