@flexireact/core 3.0.0 → 3.0.2

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 (56) hide show
  1. package/README.md +204 -52
  2. package/dist/cli/index.js +1514 -0
  3. package/dist/cli/index.js.map +1 -0
  4. package/dist/core/client/index.js +373 -0
  5. package/dist/core/client/index.js.map +1 -0
  6. package/dist/core/index.js +6415 -0
  7. package/dist/core/index.js.map +1 -0
  8. package/dist/core/server/index.js +3094 -0
  9. package/dist/core/server/index.js.map +1 -0
  10. package/package.json +80 -80
  11. package/bin/flexireact.js +0 -23
  12. package/cli/generators.ts +0 -616
  13. package/cli/index.ts +0 -1182
  14. package/core/actions/index.ts +0 -364
  15. package/core/api.ts +0 -143
  16. package/core/build/index.ts +0 -425
  17. package/core/cli/logger.ts +0 -353
  18. package/core/client/Link.tsx +0 -345
  19. package/core/client/hydration.ts +0 -147
  20. package/core/client/index.ts +0 -12
  21. package/core/client/islands.ts +0 -143
  22. package/core/client/navigation.ts +0 -212
  23. package/core/client/runtime.ts +0 -52
  24. package/core/config.ts +0 -116
  25. package/core/context.ts +0 -83
  26. package/core/dev.ts +0 -47
  27. package/core/devtools/index.ts +0 -644
  28. package/core/edge/cache.ts +0 -344
  29. package/core/edge/fetch-polyfill.ts +0 -247
  30. package/core/edge/handler.ts +0 -248
  31. package/core/edge/index.ts +0 -81
  32. package/core/edge/ppr.ts +0 -264
  33. package/core/edge/runtime.ts +0 -161
  34. package/core/font/index.ts +0 -306
  35. package/core/helpers.ts +0 -494
  36. package/core/image/index.ts +0 -413
  37. package/core/index.ts +0 -218
  38. package/core/islands/index.ts +0 -293
  39. package/core/loader.ts +0 -111
  40. package/core/logger.ts +0 -242
  41. package/core/metadata/index.ts +0 -622
  42. package/core/middleware/index.ts +0 -416
  43. package/core/plugins/index.ts +0 -373
  44. package/core/render/index.ts +0 -1243
  45. package/core/render.ts +0 -136
  46. package/core/router/index.ts +0 -551
  47. package/core/router.ts +0 -141
  48. package/core/rsc/index.ts +0 -199
  49. package/core/server/index.ts +0 -779
  50. package/core/server.ts +0 -203
  51. package/core/ssg/index.ts +0 -346
  52. package/core/start-dev.ts +0 -6
  53. package/core/start-prod.ts +0 -6
  54. package/core/tsconfig.json +0 -30
  55. package/core/types.ts +0 -239
  56. 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
- };