@getvitops/astro 0.1.0 → 0.2.0

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,92 @@
1
+ ---
2
+ export interface Movie {
3
+ name: string;
4
+ url: string;
5
+ image?: string;
6
+ dateCreated?: string;
7
+ director?: string | string[];
8
+ actor?: string[];
9
+ description?: string;
10
+ duration?: string;
11
+ aggregateRating?: {
12
+ ratingValue: number;
13
+ ratingCount?: number;
14
+ bestRating?: number;
15
+ worstRating?: number;
16
+ };
17
+ review?: {
18
+ author: string;
19
+ datePublished: string;
20
+ reviewBody: string;
21
+ reviewRating?: {
22
+ ratingValue: number;
23
+ bestRating?: number;
24
+ worstRating?: number;
25
+ };
26
+ }[];
27
+ }
28
+
29
+ export interface Props {
30
+ movies: Movie[];
31
+ }
32
+
33
+ const { movies } = Astro.props;
34
+
35
+ const jsonLd = {
36
+ '@context': 'https://schema.org',
37
+ '@type': 'ItemList',
38
+ itemListElement: movies.map((movie, index) => ({
39
+ '@type': 'ListItem',
40
+ position: index + 1,
41
+ item: {
42
+ '@type': 'Movie',
43
+ name: movie.name,
44
+ url: movie.url,
45
+ ...(movie.image && { image: movie.image }),
46
+ ...(movie.dateCreated && { dateCreated: movie.dateCreated }),
47
+ ...(movie.director && {
48
+ director: (Array.isArray(movie.director) ? movie.director : [movie.director]).map(d => ({
49
+ '@type': 'Person',
50
+ name: d,
51
+ })),
52
+ }),
53
+ ...(movie.actor && {
54
+ actor: movie.actor.map(a => ({
55
+ '@type': 'Person',
56
+ name: a,
57
+ })),
58
+ }),
59
+ ...(movie.description && { description: movie.description }),
60
+ ...(movie.duration && { duration: movie.duration }),
61
+ ...(movie.aggregateRating && {
62
+ aggregateRating: {
63
+ '@type': 'AggregateRating',
64
+ ratingValue: movie.aggregateRating.ratingValue,
65
+ ...(movie.aggregateRating.ratingCount && { ratingCount: movie.aggregateRating.ratingCount }),
66
+ bestRating: movie.aggregateRating.bestRating ?? 10,
67
+ worstRating: movie.aggregateRating.worstRating ?? 1,
68
+ },
69
+ }),
70
+ ...(movie.review && {
71
+ review: movie.review.map(r => ({
72
+ '@type': 'Review',
73
+ author: { '@type': 'Person', name: r.author },
74
+ datePublished: r.datePublished,
75
+ reviewBody: r.reviewBody,
76
+ ...(r.reviewRating && {
77
+ reviewRating: {
78
+ '@type': 'Rating',
79
+ ratingValue: r.reviewRating.ratingValue,
80
+ bestRating: r.reviewRating.bestRating ?? 10,
81
+ worstRating: r.reviewRating.worstRating ?? 1,
82
+ },
83
+ }),
84
+ })),
85
+ }),
86
+ },
87
+ })),
88
+ };
89
+ ---
90
+
91
+ <script is:inline type="application/ld+json" set:html={JSON.stringify(jsonLd)} />
92
+ <slot />
@@ -0,0 +1,140 @@
1
+ ---
2
+ export interface Props {
3
+ name: string;
4
+ url?: string;
5
+ logo?: string;
6
+ description?: string;
7
+ email?: string;
8
+ telephone?: string;
9
+ address?: {
10
+ streetAddress?: string;
11
+ addressLocality?: string;
12
+ addressRegion?: string;
13
+ postalCode?: string;
14
+ addressCountry?: string;
15
+ };
16
+ sameAs?: string[];
17
+ foundingDate?: string;
18
+ founders?: string[];
19
+ numberOfEmployees?: number | { minValue: number; maxValue: number };
20
+ parentOrganization?: {
21
+ name: string;
22
+ url?: string;
23
+ };
24
+ subOrganization?: {
25
+ name: string;
26
+ url?: string;
27
+ }[];
28
+ contactPoint?: {
29
+ type?: 'customer service' | 'technical support' | 'sales' | 'billing support' | 'reservations';
30
+ telephone: string;
31
+ email?: string;
32
+ contactType: string;
33
+ availableLanguage?: string[];
34
+ areaServed?: string[];
35
+ }[];
36
+ areaServed?: string[];
37
+ brand?: string;
38
+ award?: string[];
39
+ memberOf?: {
40
+ name: string;
41
+ url?: string;
42
+ }[];
43
+ }
44
+
45
+ const {
46
+ name,
47
+ url,
48
+ logo,
49
+ description,
50
+ email,
51
+ telephone,
52
+ address,
53
+ sameAs,
54
+ foundingDate,
55
+ founders,
56
+ numberOfEmployees,
57
+ parentOrganization,
58
+ subOrganization,
59
+ contactPoint,
60
+ areaServed,
61
+ brand,
62
+ award,
63
+ memberOf,
64
+ } = Astro.props;
65
+
66
+ const jsonLd = {
67
+ '@context': 'https://schema.org',
68
+ '@type': 'Organization',
69
+ name,
70
+ ...(url && { url }),
71
+ ...(logo && { logo }),
72
+ ...(description && { description }),
73
+ ...(email && { email }),
74
+ ...(telephone && { telephone }),
75
+ ...(address && {
76
+ address: {
77
+ '@type': 'PostalAddress',
78
+ ...(address.streetAddress && { streetAddress: address.streetAddress }),
79
+ ...(address.addressLocality && { addressLocality: address.addressLocality }),
80
+ ...(address.addressRegion && { addressRegion: address.addressRegion }),
81
+ ...(address.postalCode && { postalCode: address.postalCode }),
82
+ ...(address.addressCountry && { addressCountry: address.addressCountry }),
83
+ },
84
+ }),
85
+ ...(sameAs && { sameAs }),
86
+ ...(foundingDate && { foundingDate }),
87
+ ...(founders && {
88
+ founders: founders.map(f => ({
89
+ '@type': 'Person',
90
+ name: f,
91
+ })),
92
+ }),
93
+ ...(numberOfEmployees && {
94
+ numberOfEmployees: typeof numberOfEmployees === 'number'
95
+ ? { '@type': 'QuantitativeValue', value: numberOfEmployees }
96
+ : {
97
+ '@type': 'QuantitativeValue',
98
+ minValue: numberOfEmployees.minValue,
99
+ maxValue: numberOfEmployees.maxValue,
100
+ },
101
+ }),
102
+ ...(parentOrganization && {
103
+ parentOrganization: {
104
+ '@type': 'Organization',
105
+ name: parentOrganization.name,
106
+ ...(parentOrganization.url && { url: parentOrganization.url }),
107
+ },
108
+ }),
109
+ ...(subOrganization && {
110
+ subOrganization: subOrganization.map(sub => ({
111
+ '@type': 'Organization',
112
+ name: sub.name,
113
+ ...(sub.url && { url: sub.url }),
114
+ })),
115
+ }),
116
+ ...(contactPoint && {
117
+ contactPoint: contactPoint.map(cp => ({
118
+ '@type': 'ContactPoint',
119
+ telephone: cp.telephone,
120
+ contactType: cp.contactType,
121
+ ...(cp.email && { email: cp.email }),
122
+ ...(cp.availableLanguage && { availableLanguage: cp.availableLanguage }),
123
+ ...(cp.areaServed && { areaServed: cp.areaServed }),
124
+ })),
125
+ }),
126
+ ...(areaServed && { areaServed }),
127
+ ...(brand && { brand: { '@type': 'Brand', name: brand } }),
128
+ ...(award && { award }),
129
+ ...(memberOf && {
130
+ memberOf: memberOf.map(m => ({
131
+ '@type': 'Organization',
132
+ name: m.name,
133
+ ...(m.url && { url: m.url }),
134
+ })),
135
+ }),
136
+ };
137
+ ---
138
+
139
+ <script is:inline type="application/ld+json" set:html={JSON.stringify(jsonLd)} />
140
+ <slot />
@@ -0,0 +1,250 @@
1
+ ---
2
+ export interface Props {
3
+ name: string;
4
+ description?: string;
5
+ image?: string | string[];
6
+ sku?: string;
7
+ mpn?: string;
8
+ gtin?: string;
9
+ gtin8?: string;
10
+ gtin13?: string;
11
+ gtin14?: string;
12
+ brand?: {
13
+ name: string;
14
+ };
15
+ offers?: {
16
+ url?: string;
17
+ price: number | string;
18
+ priceCurrency: string;
19
+ priceValidUntil?: string;
20
+ availability?: 'InStock' | 'OutOfStock' | 'PreOrder' | 'SoldOut' | 'BackOrder' | 'Discontinued' | 'OnlineOnly' | 'InStoreOnly' | 'LimitedAvailability';
21
+ itemCondition?: 'NewCondition' | 'UsedCondition' | 'RefurbishedCondition' | 'DamagedCondition';
22
+ seller?: {
23
+ name: string;
24
+ };
25
+ shippingDetails?: {
26
+ shippingRate?: {
27
+ value: number;
28
+ currency: string;
29
+ };
30
+ shippingDestination?: {
31
+ addressCountry: string;
32
+ };
33
+ deliveryTime?: {
34
+ handlingTime?: { minValue: number; maxValue: number };
35
+ transitTime?: { minValue: number; maxValue: number };
36
+ };
37
+ };
38
+ hasMerchantReturnPolicy?: {
39
+ applicableCountry: string;
40
+ returnPolicyCategory: 'MerchantReturnFiniteReturnWindow' | 'MerchantReturnNotPermitted' | 'MerchantReturnUnlimitedWindow';
41
+ merchantReturnDays?: number;
42
+ returnMethod?: 'ReturnByMail' | 'ReturnInStore';
43
+ returnFees?: 'FreeReturn' | 'ReturnFeesCustomerResponsibility';
44
+ };
45
+ } | {
46
+ url?: string;
47
+ price: number | string;
48
+ priceCurrency: string;
49
+ priceValidUntil?: string;
50
+ availability?: 'InStock' | 'OutOfStock' | 'PreOrder' | 'SoldOut' | 'BackOrder' | 'Discontinued' | 'OnlineOnly' | 'InStoreOnly' | 'LimitedAvailability';
51
+ itemCondition?: 'NewCondition' | 'UsedCondition' | 'RefurbishedCondition' | 'DamagedCondition';
52
+ seller?: {
53
+ name: string;
54
+ };
55
+ }[];
56
+ aggregateRating?: {
57
+ ratingValue: number;
58
+ reviewCount?: number;
59
+ ratingCount?: number;
60
+ bestRating?: number;
61
+ worstRating?: number;
62
+ };
63
+ review?: {
64
+ author: {
65
+ name: string;
66
+ };
67
+ datePublished: string;
68
+ reviewBody?: string;
69
+ reviewRating?: {
70
+ ratingValue: number;
71
+ bestRating?: number;
72
+ worstRating?: number;
73
+ };
74
+ }[];
75
+ color?: string;
76
+ material?: string;
77
+ size?: string;
78
+ weight?: {
79
+ value: number;
80
+ unitCode: string;
81
+ };
82
+ category?: string;
83
+ productID?: string;
84
+ model?: string;
85
+ manufacturer?: {
86
+ name: string;
87
+ };
88
+ }
89
+
90
+ const {
91
+ name,
92
+ description,
93
+ image,
94
+ sku,
95
+ mpn,
96
+ gtin,
97
+ gtin8,
98
+ gtin13,
99
+ gtin14,
100
+ brand,
101
+ offers,
102
+ aggregateRating,
103
+ review,
104
+ color,
105
+ material,
106
+ size,
107
+ weight,
108
+ category,
109
+ productID,
110
+ model,
111
+ manufacturer,
112
+ } = Astro.props;
113
+
114
+ const formatOffer = (offer: any) => ({
115
+ '@type': 'Offer',
116
+ ...(offer.url && { url: offer.url }),
117
+ price: offer.price,
118
+ priceCurrency: offer.priceCurrency,
119
+ ...(offer.priceValidUntil && { priceValidUntil: offer.priceValidUntil }),
120
+ ...(offer.availability && { availability: `https://schema.org/${offer.availability}` }),
121
+ ...(offer.itemCondition && { itemCondition: `https://schema.org/${offer.itemCondition}` }),
122
+ ...(offer.seller && {
123
+ seller: {
124
+ '@type': 'Organization',
125
+ name: offer.seller.name,
126
+ },
127
+ }),
128
+ ...(offer.shippingDetails && {
129
+ shippingDetails: {
130
+ '@type': 'OfferShippingDetails',
131
+ ...(offer.shippingDetails.shippingRate && {
132
+ shippingRate: {
133
+ '@type': 'MonetaryAmount',
134
+ value: offer.shippingDetails.shippingRate.value,
135
+ currency: offer.shippingDetails.shippingRate.currency,
136
+ },
137
+ }),
138
+ ...(offer.shippingDetails.shippingDestination && {
139
+ shippingDestination: {
140
+ '@type': 'DefinedRegion',
141
+ addressCountry: offer.shippingDetails.shippingDestination.addressCountry,
142
+ },
143
+ }),
144
+ ...(offer.shippingDetails.deliveryTime && {
145
+ deliveryTime: {
146
+ '@type': 'ShippingDeliveryTime',
147
+ ...(offer.shippingDetails.deliveryTime.handlingTime && {
148
+ handlingTime: {
149
+ '@type': 'QuantitativeValue',
150
+ minValue: offer.shippingDetails.deliveryTime.handlingTime.minValue,
151
+ maxValue: offer.shippingDetails.deliveryTime.handlingTime.maxValue,
152
+ unitCode: 'DAY',
153
+ },
154
+ }),
155
+ ...(offer.shippingDetails.deliveryTime.transitTime && {
156
+ transitTime: {
157
+ '@type': 'QuantitativeValue',
158
+ minValue: offer.shippingDetails.deliveryTime.transitTime.minValue,
159
+ maxValue: offer.shippingDetails.deliveryTime.transitTime.maxValue,
160
+ unitCode: 'DAY',
161
+ },
162
+ }),
163
+ },
164
+ }),
165
+ },
166
+ }),
167
+ ...(offer.hasMerchantReturnPolicy && {
168
+ hasMerchantReturnPolicy: {
169
+ '@type': 'MerchantReturnPolicy',
170
+ applicableCountry: offer.hasMerchantReturnPolicy.applicableCountry,
171
+ returnPolicyCategory: `https://schema.org/${offer.hasMerchantReturnPolicy.returnPolicyCategory}`,
172
+ ...(offer.hasMerchantReturnPolicy.merchantReturnDays && {
173
+ merchantReturnDays: offer.hasMerchantReturnPolicy.merchantReturnDays,
174
+ }),
175
+ ...(offer.hasMerchantReturnPolicy.returnMethod && {
176
+ returnMethod: `https://schema.org/${offer.hasMerchantReturnPolicy.returnMethod}`,
177
+ }),
178
+ ...(offer.hasMerchantReturnPolicy.returnFees && {
179
+ returnFees: `https://schema.org/${offer.hasMerchantReturnPolicy.returnFees}`,
180
+ }),
181
+ },
182
+ }),
183
+ });
184
+
185
+ const jsonLd = {
186
+ '@context': 'https://schema.org',
187
+ '@type': 'Product',
188
+ name,
189
+ ...(description && { description }),
190
+ ...(image && { image: Array.isArray(image) ? image : [image] }),
191
+ ...(sku && { sku }),
192
+ ...(mpn && { mpn }),
193
+ ...(gtin && { gtin }),
194
+ ...(gtin8 && { gtin8 }),
195
+ ...(gtin13 && { gtin13 }),
196
+ ...(gtin14 && { gtin14 }),
197
+ ...(brand && { brand: { '@type': 'Brand', name: brand.name } }),
198
+ ...(offers && {
199
+ offers: Array.isArray(offers) ? offers.map(formatOffer) : formatOffer(offers),
200
+ }),
201
+ ...(aggregateRating && {
202
+ aggregateRating: {
203
+ '@type': 'AggregateRating',
204
+ ratingValue: aggregateRating.ratingValue,
205
+ ...(aggregateRating.reviewCount && { reviewCount: aggregateRating.reviewCount }),
206
+ ...(aggregateRating.ratingCount && { ratingCount: aggregateRating.ratingCount }),
207
+ bestRating: aggregateRating.bestRating ?? 5,
208
+ worstRating: aggregateRating.worstRating ?? 1,
209
+ },
210
+ }),
211
+ ...(review && {
212
+ review: review.map(r => ({
213
+ '@type': 'Review',
214
+ author: { '@type': 'Person', name: r.author.name },
215
+ datePublished: r.datePublished,
216
+ ...(r.reviewBody && { reviewBody: r.reviewBody }),
217
+ ...(r.reviewRating && {
218
+ reviewRating: {
219
+ '@type': 'Rating',
220
+ ratingValue: r.reviewRating.ratingValue,
221
+ bestRating: r.reviewRating.bestRating ?? 5,
222
+ worstRating: r.reviewRating.worstRating ?? 1,
223
+ },
224
+ }),
225
+ })),
226
+ }),
227
+ ...(color && { color }),
228
+ ...(material && { material }),
229
+ ...(size && { size }),
230
+ ...(weight && {
231
+ weight: {
232
+ '@type': 'QuantitativeValue',
233
+ value: weight.value,
234
+ unitCode: weight.unitCode,
235
+ },
236
+ }),
237
+ ...(category && { category }),
238
+ ...(productID && { productID }),
239
+ ...(model && { model }),
240
+ ...(manufacturer && {
241
+ manufacturer: {
242
+ '@type': 'Organization',
243
+ name: manufacturer.name,
244
+ },
245
+ }),
246
+ };
247
+ ---
248
+
249
+ <script is:inline type="application/ld+json" set:html={JSON.stringify(jsonLd)} />
250
+ <slot />
@@ -0,0 +1,147 @@
1
+ ---
2
+ export interface Props {
3
+ name: string;
4
+ alternateName?: string;
5
+ description?: string;
6
+ image?: string;
7
+ url?: string;
8
+ sameAs?: string[];
9
+ mainEntity: {
10
+ type: 'Person' | 'Organization';
11
+ name: string;
12
+ alternateName?: string;
13
+ description?: string;
14
+ image?: string;
15
+ url?: string;
16
+ sameAs?: string[];
17
+ jobTitle?: string;
18
+ worksFor?: {
19
+ name: string;
20
+ url?: string;
21
+ };
22
+ knowsAbout?: string[];
23
+ knowsLanguage?: string[];
24
+ nationality?: string;
25
+ birthDate?: string;
26
+ email?: string;
27
+ telephone?: string;
28
+ address?: {
29
+ addressLocality?: string;
30
+ addressRegion?: string;
31
+ addressCountry?: string;
32
+ };
33
+ alumniOf?: {
34
+ name: string;
35
+ url?: string;
36
+ }[];
37
+ award?: string[];
38
+ memberOf?: {
39
+ name: string;
40
+ url?: string;
41
+ }[];
42
+ interactionStatistic?: {
43
+ type: 'FollowAction' | 'LikeAction' | 'ShareAction' | 'WriteAction';
44
+ count: number;
45
+ }[];
46
+ agentInteractionStatistic?: {
47
+ type: 'FollowAction' | 'LikeAction' | 'ShareAction' | 'WriteAction';
48
+ count: number;
49
+ }[];
50
+ };
51
+ dateCreated?: string;
52
+ dateModified?: string;
53
+ }
54
+
55
+ const {
56
+ name,
57
+ alternateName,
58
+ description,
59
+ image,
60
+ url,
61
+ sameAs,
62
+ mainEntity,
63
+ dateCreated,
64
+ dateModified,
65
+ } = Astro.props;
66
+
67
+ const jsonLd = {
68
+ '@context': 'https://schema.org',
69
+ '@type': 'ProfilePage',
70
+ name,
71
+ ...(alternateName && { alternateName }),
72
+ ...(description && { description }),
73
+ ...(image && { image }),
74
+ ...(url && { url }),
75
+ ...(sameAs && { sameAs }),
76
+ ...(dateCreated && { dateCreated }),
77
+ ...(dateModified && { dateModified }),
78
+ mainEntity: {
79
+ '@type': mainEntity.type,
80
+ name: mainEntity.name,
81
+ ...(mainEntity.alternateName && { alternateName: mainEntity.alternateName }),
82
+ ...(mainEntity.description && { description: mainEntity.description }),
83
+ ...(mainEntity.image && { image: mainEntity.image }),
84
+ ...(mainEntity.url && { url: mainEntity.url }),
85
+ ...(mainEntity.sameAs && { sameAs: mainEntity.sameAs }),
86
+ ...(mainEntity.jobTitle && { jobTitle: mainEntity.jobTitle }),
87
+ ...(mainEntity.worksFor && {
88
+ worksFor: {
89
+ '@type': 'Organization',
90
+ name: mainEntity.worksFor.name,
91
+ ...(mainEntity.worksFor.url && { url: mainEntity.worksFor.url }),
92
+ },
93
+ }),
94
+ ...(mainEntity.knowsAbout && { knowsAbout: mainEntity.knowsAbout }),
95
+ ...(mainEntity.knowsLanguage && { knowsLanguage: mainEntity.knowsLanguage }),
96
+ ...(mainEntity.nationality && {
97
+ nationality: {
98
+ '@type': 'Country',
99
+ name: mainEntity.nationality,
100
+ },
101
+ }),
102
+ ...(mainEntity.birthDate && { birthDate: mainEntity.birthDate }),
103
+ ...(mainEntity.email && { email: mainEntity.email }),
104
+ ...(mainEntity.telephone && { telephone: mainEntity.telephone }),
105
+ ...(mainEntity.address && {
106
+ address: {
107
+ '@type': 'PostalAddress',
108
+ ...(mainEntity.address.addressLocality && { addressLocality: mainEntity.address.addressLocality }),
109
+ ...(mainEntity.address.addressRegion && { addressRegion: mainEntity.address.addressRegion }),
110
+ ...(mainEntity.address.addressCountry && { addressCountry: mainEntity.address.addressCountry }),
111
+ },
112
+ }),
113
+ ...(mainEntity.alumniOf && {
114
+ alumniOf: mainEntity.alumniOf.map(a => ({
115
+ '@type': 'EducationalOrganization',
116
+ name: a.name,
117
+ ...(a.url && { url: a.url }),
118
+ })),
119
+ }),
120
+ ...(mainEntity.award && { award: mainEntity.award }),
121
+ ...(mainEntity.memberOf && {
122
+ memberOf: mainEntity.memberOf.map(m => ({
123
+ '@type': 'Organization',
124
+ name: m.name,
125
+ ...(m.url && { url: m.url }),
126
+ })),
127
+ }),
128
+ ...(mainEntity.interactionStatistic && {
129
+ interactionStatistic: mainEntity.interactionStatistic.map(stat => ({
130
+ '@type': 'InteractionCounter',
131
+ interactionType: { '@type': stat.type },
132
+ userInteractionCount: stat.count,
133
+ })),
134
+ }),
135
+ ...(mainEntity.agentInteractionStatistic && {
136
+ agentInteractionStatistic: mainEntity.agentInteractionStatistic.map(stat => ({
137
+ '@type': 'InteractionCounter',
138
+ interactionType: { '@type': stat.type },
139
+ userInteractionCount: stat.count,
140
+ })),
141
+ }),
142
+ },
143
+ };
144
+ ---
145
+
146
+ <script is:inline type="application/ld+json" set:html={JSON.stringify(jsonLd)} />
147
+ <slot />
@@ -0,0 +1,71 @@
1
+ ---
2
+ export interface Answer {
3
+ text: string;
4
+ dateCreated: string;
5
+ author: {
6
+ name: string;
7
+ type?: 'Person' | 'Organization';
8
+ };
9
+ upvoteCount?: number;
10
+ url?: string;
11
+ }
12
+
13
+ export interface Props {
14
+ question: string;
15
+ questionDetail?: string;
16
+ dateCreated: string;
17
+ author: {
18
+ name: string;
19
+ type?: 'Person' | 'Organization';
20
+ };
21
+ answerCount?: number;
22
+ upvoteCount?: number;
23
+ acceptedAnswer?: Answer;
24
+ suggestedAnswer?: Answer[];
25
+ }
26
+
27
+ const {
28
+ question,
29
+ questionDetail,
30
+ dateCreated,
31
+ author,
32
+ answerCount,
33
+ upvoteCount,
34
+ acceptedAnswer,
35
+ suggestedAnswer,
36
+ } = Astro.props;
37
+
38
+ const formatAnswer = (answer: Answer) => ({
39
+ '@type': 'Answer',
40
+ text: answer.text,
41
+ dateCreated: answer.dateCreated,
42
+ author: {
43
+ '@type': answer.author.type || 'Person',
44
+ name: answer.author.name,
45
+ },
46
+ ...(answer.upvoteCount !== undefined && { upvoteCount: answer.upvoteCount }),
47
+ ...(answer.url && { url: answer.url }),
48
+ });
49
+
50
+ const jsonLd = {
51
+ '@context': 'https://schema.org',
52
+ '@type': 'QAPage',
53
+ mainEntity: {
54
+ '@type': 'Question',
55
+ name: question,
56
+ ...(questionDetail && { text: questionDetail }),
57
+ dateCreated,
58
+ author: {
59
+ '@type': author.type || 'Person',
60
+ name: author.name,
61
+ },
62
+ ...(answerCount !== undefined && { answerCount }),
63
+ ...(upvoteCount !== undefined && { upvoteCount }),
64
+ ...(acceptedAnswer && { acceptedAnswer: formatAnswer(acceptedAnswer) }),
65
+ ...(suggestedAnswer && { suggestedAnswer: suggestedAnswer.map(formatAnswer) }),
66
+ },
67
+ };
68
+ ---
69
+
70
+ <script is:inline type="application/ld+json" set:html={JSON.stringify(jsonLd)} />
71
+ <slot />