geo-semantic-layer 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.
- package/CHANGELOG.md +289 -0
- package/LICENSE +21 -0
- package/README.md +394 -0
- package/dist/config-BcP2K8Tq.d.ts +150 -0
- package/dist/event-BG0_yYK4.d.ts +2231 -0
- package/dist/generators/index.d.ts +723 -0
- package/dist/generators/index.js +969 -0
- package/dist/generators/index.js.map +1 -0
- package/dist/geo/index.d.ts +38 -0
- package/dist/geo/index.js +107 -0
- package/dist/geo/index.js.map +1 -0
- package/dist/index.d.ts +53 -0
- package/dist/index.js +1000 -0
- package/dist/index.js.map +1 -0
- package/dist/schemas/index.d.ts +97 -0
- package/dist/schemas/index.js +402 -0
- package/dist/schemas/index.js.map +1 -0
- package/package.json +97 -0
|
@@ -0,0 +1,969 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
// src/core/schemas/organization.ts
|
|
4
|
+
var PostalAddressSchema = z.object({
|
|
5
|
+
"@type": z.literal("PostalAddress").default("PostalAddress"),
|
|
6
|
+
streetAddress: z.string().optional(),
|
|
7
|
+
addressLocality: z.string().optional(),
|
|
8
|
+
addressRegion: z.string().optional(),
|
|
9
|
+
postalCode: z.string().optional(),
|
|
10
|
+
addressCountry: z.string().length(2).optional()
|
|
11
|
+
// ISO 3166-1 alpha-2
|
|
12
|
+
});
|
|
13
|
+
var ContactPointSchema = z.object({
|
|
14
|
+
"@type": z.literal("ContactPoint").default("ContactPoint"),
|
|
15
|
+
telephone: z.string().optional(),
|
|
16
|
+
email: z.string().email().optional(),
|
|
17
|
+
contactType: z.enum(["customer service", "technical support", "sales", "billing"]).optional(),
|
|
18
|
+
areaServed: z.string().optional(),
|
|
19
|
+
availableLanguage: z.array(z.string()).optional()
|
|
20
|
+
});
|
|
21
|
+
var OrganizationSchema = z.object({
|
|
22
|
+
"@context": z.literal("https://schema.org").default("https://schema.org"),
|
|
23
|
+
"@type": z.literal("Organization").default("Organization"),
|
|
24
|
+
// Required fields
|
|
25
|
+
name: z.string().min(1, "Organization name is required"),
|
|
26
|
+
url: z.string().url("Must be a valid URL"),
|
|
27
|
+
// Recommended fields
|
|
28
|
+
logo: z.string().url("Logo must be a valid URL").optional(),
|
|
29
|
+
image: z.string().url("Image must be a valid URL").optional(),
|
|
30
|
+
description: z.string().min(10, "Description should be at least 10 characters").optional(),
|
|
31
|
+
/**
|
|
32
|
+
* CRITICAL for GEO: Entity disambiguation
|
|
33
|
+
* Should include at least one social profile or authority ID (Wikidata, Wikipedia, etc.)
|
|
34
|
+
*/
|
|
35
|
+
sameAs: z.array(z.string().url()).min(1, "At least one sameAs URL is required for entity disambiguation").describe("Social profiles, Wikidata ID, Wikipedia URL for entity disambiguation"),
|
|
36
|
+
// Contact information
|
|
37
|
+
email: z.string().email().optional(),
|
|
38
|
+
telephone: z.string().optional(),
|
|
39
|
+
address: PostalAddressSchema.optional(),
|
|
40
|
+
contactPoint: z.union([ContactPointSchema, z.array(ContactPointSchema)]).optional(),
|
|
41
|
+
// Additional information
|
|
42
|
+
alternateName: z.string().optional(),
|
|
43
|
+
foundingDate: z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "Must be in YYYY-MM-DD format").optional(),
|
|
44
|
+
founder: z.array(z.string()).optional(),
|
|
45
|
+
// Relationships
|
|
46
|
+
parentOrganization: z.string().optional(),
|
|
47
|
+
subOrganization: z.array(z.string()).optional()
|
|
48
|
+
});
|
|
49
|
+
var PersonSchema = z.object({
|
|
50
|
+
"@context": z.literal("https://schema.org").default("https://schema.org"),
|
|
51
|
+
"@type": z.literal("Person").default("Person"),
|
|
52
|
+
// Required fields
|
|
53
|
+
name: z.string().min(1, "Person name is required"),
|
|
54
|
+
url: z.string().url("Must be a valid URL").optional(),
|
|
55
|
+
// Recommended fields
|
|
56
|
+
image: z.string().url("Image must be a valid URL").optional(),
|
|
57
|
+
description: z.string().min(10, "Description should be at least 10 characters").optional(),
|
|
58
|
+
/**
|
|
59
|
+
* CRITICAL for GEO: Entity disambiguation
|
|
60
|
+
* Should include social profiles, Wikidata ID, Wikipedia URL, etc.
|
|
61
|
+
*/
|
|
62
|
+
sameAs: z.array(z.string().url()).min(1, "At least one sameAs URL is required for entity disambiguation").describe("Social profiles, Wikidata ID, Wikipedia URL for entity disambiguation"),
|
|
63
|
+
// Contact information
|
|
64
|
+
email: z.string().email().optional(),
|
|
65
|
+
telephone: z.string().optional(),
|
|
66
|
+
address: PostalAddressSchema.optional(),
|
|
67
|
+
// Professional information
|
|
68
|
+
jobTitle: z.string().optional(),
|
|
69
|
+
worksFor: z.object({
|
|
70
|
+
"@type": z.literal("Organization"),
|
|
71
|
+
name: z.string(),
|
|
72
|
+
url: z.string().url().optional()
|
|
73
|
+
}).optional(),
|
|
74
|
+
// Additional information
|
|
75
|
+
alternateName: z.string().optional(),
|
|
76
|
+
birthDate: z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "Must be in YYYY-MM-DD format").optional(),
|
|
77
|
+
nationality: z.string().optional(),
|
|
78
|
+
// Relationships
|
|
79
|
+
colleague: z.array(z.string()).optional(),
|
|
80
|
+
alumniOf: z.string().optional()
|
|
81
|
+
});
|
|
82
|
+
var OfferSchema = z.object({
|
|
83
|
+
"@type": z.literal("Offer").default("Offer"),
|
|
84
|
+
price: z.union([z.string(), z.number()]),
|
|
85
|
+
priceCurrency: z.string().length(3),
|
|
86
|
+
// ISO 4217 currency code
|
|
87
|
+
availability: z.string().url().optional().describe("e.g., https://schema.org/InStock"),
|
|
88
|
+
url: z.string().url().optional(),
|
|
89
|
+
priceValidUntil: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional(),
|
|
90
|
+
seller: z.object({
|
|
91
|
+
"@type": z.literal("Organization"),
|
|
92
|
+
name: z.string()
|
|
93
|
+
}).optional()
|
|
94
|
+
});
|
|
95
|
+
var AggregateRatingSchema = z.object({
|
|
96
|
+
"@type": z.literal("AggregateRating").default("AggregateRating"),
|
|
97
|
+
ratingValue: z.union([z.string(), z.number()]),
|
|
98
|
+
reviewCount: z.number().int().positive(),
|
|
99
|
+
bestRating: z.union([z.string(), z.number()]).optional().default(5),
|
|
100
|
+
worstRating: z.union([z.string(), z.number()]).optional().default(1)
|
|
101
|
+
});
|
|
102
|
+
var ReviewSchema = z.object({
|
|
103
|
+
"@type": z.literal("Review").default("Review"),
|
|
104
|
+
author: z.object({
|
|
105
|
+
"@type": z.literal("Person"),
|
|
106
|
+
name: z.string()
|
|
107
|
+
}),
|
|
108
|
+
datePublished: z.string().regex(/^\d{4}-\d{2}-\d{2}$/),
|
|
109
|
+
reviewBody: z.string().min(10),
|
|
110
|
+
reviewRating: z.object({
|
|
111
|
+
"@type": z.literal("Rating"),
|
|
112
|
+
ratingValue: z.union([z.string(), z.number()]),
|
|
113
|
+
bestRating: z.union([z.string(), z.number()]).optional().default(5)
|
|
114
|
+
})
|
|
115
|
+
});
|
|
116
|
+
var ProductSchema = z.object({
|
|
117
|
+
"@context": z.literal("https://schema.org").default("https://schema.org"),
|
|
118
|
+
"@type": z.literal("Product").default("Product"),
|
|
119
|
+
// Required fields
|
|
120
|
+
name: z.string().min(1, "Product name is required"),
|
|
121
|
+
// Recommended fields
|
|
122
|
+
image: z.union([z.string().url(), z.array(z.string().url())]).optional(),
|
|
123
|
+
description: z.string().min(10, "Description should be at least 10 characters").optional(),
|
|
124
|
+
// SKU or other identifiers
|
|
125
|
+
sku: z.string().optional(),
|
|
126
|
+
gtin: z.string().optional(),
|
|
127
|
+
gtin8: z.string().optional(),
|
|
128
|
+
gtin12: z.string().optional(),
|
|
129
|
+
gtin13: z.string().optional(),
|
|
130
|
+
gtin14: z.string().optional(),
|
|
131
|
+
mpn: z.string().optional(),
|
|
132
|
+
// Brand
|
|
133
|
+
brand: z.object({
|
|
134
|
+
"@type": z.literal("Brand"),
|
|
135
|
+
name: z.string()
|
|
136
|
+
}).optional(),
|
|
137
|
+
// Offers (pricing)
|
|
138
|
+
offers: z.union([OfferSchema, z.array(OfferSchema)]).optional(),
|
|
139
|
+
// Ratings and Reviews
|
|
140
|
+
aggregateRating: AggregateRatingSchema.optional(),
|
|
141
|
+
review: z.union([ReviewSchema, z.array(ReviewSchema)]).optional(),
|
|
142
|
+
// Additional information
|
|
143
|
+
category: z.string().optional(),
|
|
144
|
+
color: z.union([z.string(), z.array(z.string())]).optional(),
|
|
145
|
+
material: z.string().optional(),
|
|
146
|
+
manufacturer: z.object({
|
|
147
|
+
"@type": z.literal("Organization"),
|
|
148
|
+
name: z.string()
|
|
149
|
+
}).optional()
|
|
150
|
+
});
|
|
151
|
+
var ArticleSchema = z.object({
|
|
152
|
+
"@context": z.literal("https://schema.org").default("https://schema.org"),
|
|
153
|
+
"@type": z.enum(["Article", "NewsArticle", "BlogPosting", "TechArticle"]).default("Article"),
|
|
154
|
+
// Required fields
|
|
155
|
+
headline: z.string().min(1).max(110, "Headline should be less than 110 characters for optimal SEO"),
|
|
156
|
+
image: z.union([z.string().url(), z.array(z.string().url())]),
|
|
157
|
+
// Recommended fields
|
|
158
|
+
author: z.union([
|
|
159
|
+
z.object({
|
|
160
|
+
"@type": z.enum(["Person", "Organization"]),
|
|
161
|
+
name: z.string(),
|
|
162
|
+
url: z.string().url().optional()
|
|
163
|
+
}),
|
|
164
|
+
z.array(
|
|
165
|
+
z.object({
|
|
166
|
+
"@type": z.enum(["Person", "Organization"]),
|
|
167
|
+
name: z.string(),
|
|
168
|
+
url: z.string().url().optional()
|
|
169
|
+
})
|
|
170
|
+
)
|
|
171
|
+
]).describe("Author(s) of the article"),
|
|
172
|
+
datePublished: z.string().regex(/^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}:\d{2}Z)?$/, "Must be ISO 8601 format"),
|
|
173
|
+
dateModified: z.string().regex(/^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}:\d{2}Z)?$/, "Must be ISO 8601 format").optional(),
|
|
174
|
+
// Publisher (recommended for Google News)
|
|
175
|
+
publisher: z.object({
|
|
176
|
+
"@type": z.literal("Organization"),
|
|
177
|
+
name: z.string(),
|
|
178
|
+
logo: z.object({
|
|
179
|
+
"@type": z.literal("ImageObject"),
|
|
180
|
+
url: z.string().url(),
|
|
181
|
+
width: z.number().optional(),
|
|
182
|
+
height: z.number().optional()
|
|
183
|
+
}).optional()
|
|
184
|
+
}).optional(),
|
|
185
|
+
// Content
|
|
186
|
+
description: z.string().min(10).optional(),
|
|
187
|
+
articleBody: z.string().min(50, "Article body should be at least 50 characters").optional(),
|
|
188
|
+
wordCount: z.number().int().positive().optional(),
|
|
189
|
+
// Additional metadata
|
|
190
|
+
url: z.string().url().optional(),
|
|
191
|
+
mainEntityOfPage: z.string().url().optional(),
|
|
192
|
+
keywords: z.union([z.string(), z.array(z.string())]).optional(),
|
|
193
|
+
articleSection: z.string().optional(),
|
|
194
|
+
inLanguage: z.string().optional()
|
|
195
|
+
});
|
|
196
|
+
ArticleSchema.extend({
|
|
197
|
+
"@type": z.literal("BlogPosting").default("BlogPosting")
|
|
198
|
+
});
|
|
199
|
+
ArticleSchema.extend({
|
|
200
|
+
"@type": z.literal("NewsArticle").default("NewsArticle"),
|
|
201
|
+
dateline: z.string().optional()
|
|
202
|
+
});
|
|
203
|
+
var QuestionSchema = z.object({
|
|
204
|
+
"@type": z.literal("Question").default("Question"),
|
|
205
|
+
name: z.string().min(1, "Question text is required"),
|
|
206
|
+
acceptedAnswer: z.object({
|
|
207
|
+
"@type": z.literal("Answer").default("Answer"),
|
|
208
|
+
text: z.string().min(1, "Answer text is required")
|
|
209
|
+
})
|
|
210
|
+
});
|
|
211
|
+
var FAQPageSchema = z.object({
|
|
212
|
+
"@context": z.literal("https://schema.org").default("https://schema.org"),
|
|
213
|
+
"@type": z.literal("FAQPage").default("FAQPage"),
|
|
214
|
+
mainEntity: z.array(QuestionSchema).min(1, "At least one question is required for FAQ").describe("Array of questions and answers")
|
|
215
|
+
});
|
|
216
|
+
var ListItemSchema = z.object({
|
|
217
|
+
"@type": z.literal("ListItem").default("ListItem"),
|
|
218
|
+
position: z.number().int().positive(),
|
|
219
|
+
name: z.string().min(1),
|
|
220
|
+
item: z.string().url().optional()
|
|
221
|
+
});
|
|
222
|
+
var BreadcrumbListSchema = z.object({
|
|
223
|
+
"@context": z.literal("https://schema.org").default("https://schema.org"),
|
|
224
|
+
"@type": z.literal("BreadcrumbList").default("BreadcrumbList"),
|
|
225
|
+
itemListElement: z.array(ListItemSchema).min(2, "Breadcrumb list must have at least 2 items").describe("Ordered list of breadcrumb items")
|
|
226
|
+
});
|
|
227
|
+
var WebPageSchema = z.object({
|
|
228
|
+
"@context": z.literal("https://schema.org").default("https://schema.org"),
|
|
229
|
+
"@type": z.literal("WebPage").default("WebPage"),
|
|
230
|
+
name: z.string().min(1),
|
|
231
|
+
description: z.string().min(10).optional(),
|
|
232
|
+
url: z.string().url(),
|
|
233
|
+
// Optional fields
|
|
234
|
+
inLanguage: z.string().optional(),
|
|
235
|
+
isPartOf: z.object({
|
|
236
|
+
"@type": z.literal("WebSite"),
|
|
237
|
+
name: z.string(),
|
|
238
|
+
url: z.string().url()
|
|
239
|
+
}).optional(),
|
|
240
|
+
breadcrumb: z.object({
|
|
241
|
+
"@type": z.literal("BreadcrumbList")
|
|
242
|
+
}).optional(),
|
|
243
|
+
datePublished: z.string().regex(/^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}:\d{2}Z)?$/).optional(),
|
|
244
|
+
dateModified: z.string().regex(/^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}:\d{2}Z)?$/).optional(),
|
|
245
|
+
author: z.object({
|
|
246
|
+
"@type": z.enum(["Person", "Organization"]),
|
|
247
|
+
name: z.string()
|
|
248
|
+
}).optional()
|
|
249
|
+
});
|
|
250
|
+
var OpenGraphSchema = z.object({
|
|
251
|
+
title: z.string().min(1).max(60, "Title should be less than 60 characters for optimal display"),
|
|
252
|
+
description: z.string().min(1).max(160, "Description should be less than 160 characters"),
|
|
253
|
+
url: z.string().url(),
|
|
254
|
+
type: z.enum(["website", "article", "product", "book", "profile", "music", "video"]).default("website"),
|
|
255
|
+
image: z.string().url(),
|
|
256
|
+
imageAlt: z.string().optional(),
|
|
257
|
+
siteName: z.string().optional(),
|
|
258
|
+
locale: z.string().default("en_US"),
|
|
259
|
+
// Article-specific
|
|
260
|
+
publishedTime: z.string().optional(),
|
|
261
|
+
modifiedTime: z.string().optional(),
|
|
262
|
+
author: z.string().optional(),
|
|
263
|
+
section: z.string().optional(),
|
|
264
|
+
tags: z.array(z.string()).optional(),
|
|
265
|
+
// Product-specific
|
|
266
|
+
price: z.union([z.string(), z.number()]).optional(),
|
|
267
|
+
currency: z.string().length(3).optional()
|
|
268
|
+
});
|
|
269
|
+
var TwitterCardSchema = z.object({
|
|
270
|
+
card: z.enum(["summary", "summary_large_image", "app", "player"]).default("summary_large_image"),
|
|
271
|
+
site: z.string().optional(),
|
|
272
|
+
// @username
|
|
273
|
+
creator: z.string().optional(),
|
|
274
|
+
// @username
|
|
275
|
+
title: z.string().min(1).max(70, "Twitter title should be less than 70 characters"),
|
|
276
|
+
description: z.string().min(1).max(200, "Twitter description should be less than 200 characters"),
|
|
277
|
+
image: z.string().url(),
|
|
278
|
+
imageAlt: z.string().optional()
|
|
279
|
+
});
|
|
280
|
+
var GeoCoordinatesSchema = z.object({
|
|
281
|
+
"@type": z.literal("GeoCoordinates").default("GeoCoordinates"),
|
|
282
|
+
latitude: z.number().min(-90).max(90),
|
|
283
|
+
longitude: z.number().min(-180).max(180)
|
|
284
|
+
});
|
|
285
|
+
var OpeningHoursSchema = z.object({
|
|
286
|
+
"@type": z.literal("OpeningHoursSpecification").default("OpeningHoursSpecification"),
|
|
287
|
+
dayOfWeek: z.union([
|
|
288
|
+
z.enum(["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]),
|
|
289
|
+
z.array(z.enum(["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]))
|
|
290
|
+
]),
|
|
291
|
+
opens: z.string().regex(/^([01]\d|2[0-3]):([0-5]\d)$/, "Must be in HH:MM format"),
|
|
292
|
+
closes: z.string().regex(/^([01]\d|2[0-3]):([0-5]\d)$/, "Must be in HH:MM format")
|
|
293
|
+
});
|
|
294
|
+
var LocalBusinessSchema = z.object({
|
|
295
|
+
"@context": z.literal("https://schema.org").default("https://schema.org"),
|
|
296
|
+
"@type": z.string().default("LocalBusiness"),
|
|
297
|
+
// Can be Restaurant, Store, etc.
|
|
298
|
+
// Required
|
|
299
|
+
name: z.string().min(1, "Business name is required"),
|
|
300
|
+
image: z.union([z.string().url(), z.array(z.string().url())]),
|
|
301
|
+
address: PostalAddressSchema,
|
|
302
|
+
// Recommended
|
|
303
|
+
"@id": z.string().url().optional(),
|
|
304
|
+
url: z.string().url().optional(),
|
|
305
|
+
telephone: z.string().optional(),
|
|
306
|
+
priceRange: z.string().optional(),
|
|
307
|
+
// e.g., "$$"
|
|
308
|
+
// Location
|
|
309
|
+
geo: GeoCoordinatesSchema.optional(),
|
|
310
|
+
// Opening hours
|
|
311
|
+
openingHoursSpecification: z.union([
|
|
312
|
+
OpeningHoursSchema,
|
|
313
|
+
z.array(OpeningHoursSchema)
|
|
314
|
+
]).optional(),
|
|
315
|
+
// Ratings
|
|
316
|
+
aggregateRating: z.object({
|
|
317
|
+
"@type": z.literal("AggregateRating"),
|
|
318
|
+
ratingValue: z.union([z.string(), z.number()]),
|
|
319
|
+
reviewCount: z.number().int().positive()
|
|
320
|
+
}).optional(),
|
|
321
|
+
// Additional info
|
|
322
|
+
description: z.string().min(10).optional(),
|
|
323
|
+
servesCuisine: z.union([z.string(), z.array(z.string())]).optional(),
|
|
324
|
+
// For restaurants
|
|
325
|
+
menu: z.string().url().optional(),
|
|
326
|
+
// For restaurants
|
|
327
|
+
acceptsReservations: z.union([z.boolean(), z.string()]).optional(),
|
|
328
|
+
paymentAccepted: z.string().optional()
|
|
329
|
+
});
|
|
330
|
+
var PlaceSchema = z.object({
|
|
331
|
+
"@type": z.literal("Place").default("Place"),
|
|
332
|
+
name: z.string(),
|
|
333
|
+
address: z.union([
|
|
334
|
+
z.string(),
|
|
335
|
+
z.object({
|
|
336
|
+
"@type": z.literal("PostalAddress"),
|
|
337
|
+
streetAddress: z.string().optional(),
|
|
338
|
+
addressLocality: z.string().optional(),
|
|
339
|
+
addressRegion: z.string().optional(),
|
|
340
|
+
postalCode: z.string().optional(),
|
|
341
|
+
addressCountry: z.string().optional()
|
|
342
|
+
})
|
|
343
|
+
]).optional()
|
|
344
|
+
});
|
|
345
|
+
var EventOfferSchema = z.object({
|
|
346
|
+
"@type": z.literal("Offer").default("Offer"),
|
|
347
|
+
url: z.string().url().optional(),
|
|
348
|
+
price: z.union([z.string(), z.number()]),
|
|
349
|
+
priceCurrency: z.string().length(3),
|
|
350
|
+
availability: z.string().url().optional(),
|
|
351
|
+
validFrom: z.string().optional()
|
|
352
|
+
});
|
|
353
|
+
var EventSchema = z.object({
|
|
354
|
+
"@context": z.literal("https://schema.org").default("https://schema.org"),
|
|
355
|
+
"@type": z.enum(["Event", "MusicEvent", "BusinessEvent", "SportsEvent", "TheaterEvent", "EducationEvent"]).default("Event"),
|
|
356
|
+
// Required
|
|
357
|
+
name: z.string().min(1, "Event name is required"),
|
|
358
|
+
startDate: z.string().regex(/^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}:\d{2}[+-]\d{2}:\d{2})?$/, "Must be ISO 8601 format"),
|
|
359
|
+
// Recommended
|
|
360
|
+
endDate: z.string().regex(/^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}:\d{2}[+-]\d{2}:\d{2})?$/, "Must be ISO 8601 format").optional(),
|
|
361
|
+
eventStatus: z.enum([
|
|
362
|
+
"https://schema.org/EventScheduled",
|
|
363
|
+
"https://schema.org/EventCancelled",
|
|
364
|
+
"https://schema.org/EventMovedOnline",
|
|
365
|
+
"https://schema.org/EventPostponed",
|
|
366
|
+
"https://schema.org/EventRescheduled"
|
|
367
|
+
]).optional(),
|
|
368
|
+
eventAttendanceMode: z.enum([
|
|
369
|
+
"https://schema.org/OfflineEventAttendanceMode",
|
|
370
|
+
"https://schema.org/OnlineEventAttendanceMode",
|
|
371
|
+
"https://schema.org/MixedEventAttendanceMode"
|
|
372
|
+
]).optional(),
|
|
373
|
+
location: z.union([PlaceSchema, z.string().url()]).optional(),
|
|
374
|
+
// Can be Place or VirtualLocation URL
|
|
375
|
+
image: z.union([z.string().url(), z.array(z.string().url())]).optional(),
|
|
376
|
+
description: z.string().min(10).optional(),
|
|
377
|
+
// Organizer
|
|
378
|
+
organizer: z.object({
|
|
379
|
+
"@type": z.enum(["Organization", "Person"]),
|
|
380
|
+
name: z.string(),
|
|
381
|
+
url: z.string().url().optional()
|
|
382
|
+
}).optional(),
|
|
383
|
+
// Performer
|
|
384
|
+
performer: z.union([
|
|
385
|
+
z.object({
|
|
386
|
+
"@type": z.enum(["Person", "PerformingGroup"]),
|
|
387
|
+
name: z.string()
|
|
388
|
+
}),
|
|
389
|
+
z.array(z.object({
|
|
390
|
+
"@type": z.enum(["Person", "PerformingGroup"]),
|
|
391
|
+
name: z.string()
|
|
392
|
+
}))
|
|
393
|
+
]).optional(),
|
|
394
|
+
// Offers (tickets)
|
|
395
|
+
offers: z.union([EventOfferSchema, z.array(EventOfferSchema)]).optional(),
|
|
396
|
+
// Additional
|
|
397
|
+
url: z.string().url().optional()
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
// src/core/geo/processor.ts
|
|
401
|
+
var GeoProcessor = class {
|
|
402
|
+
/**
|
|
403
|
+
* Optimizes schema density based on configuration.
|
|
404
|
+
* Removes nulls, undefined, and non-essential fields for 'high' density (AI-focused).
|
|
405
|
+
*/
|
|
406
|
+
static optimizeDensity(schema, density = "low") {
|
|
407
|
+
if (density === "low") {
|
|
408
|
+
return this.pruneEmpty(schema);
|
|
409
|
+
}
|
|
410
|
+
const optimized = this.pruneEmpty(schema);
|
|
411
|
+
if (density === "high") {
|
|
412
|
+
const aiEssentialKeys = [
|
|
413
|
+
"@context",
|
|
414
|
+
"@type",
|
|
415
|
+
"name",
|
|
416
|
+
"headline",
|
|
417
|
+
"description",
|
|
418
|
+
"url",
|
|
419
|
+
"sameAs",
|
|
420
|
+
"author",
|
|
421
|
+
"datePublished",
|
|
422
|
+
"publisher",
|
|
423
|
+
"itemListElement",
|
|
424
|
+
"item",
|
|
425
|
+
"position"
|
|
426
|
+
];
|
|
427
|
+
return this.filterRecursive(optimized, (key) => aiEssentialKeys.includes(key));
|
|
428
|
+
}
|
|
429
|
+
return optimized;
|
|
430
|
+
}
|
|
431
|
+
/**
|
|
432
|
+
* Recursively removes null, undefined, and empty strings/arrays
|
|
433
|
+
*/
|
|
434
|
+
static pruneEmpty(obj) {
|
|
435
|
+
if (Array.isArray(obj)) {
|
|
436
|
+
return obj.map((v) => this.pruneEmpty(v)).filter((v) => v !== null && v !== void 0 && v !== "");
|
|
437
|
+
}
|
|
438
|
+
if (obj !== null && typeof obj === "object") {
|
|
439
|
+
return Object.entries(obj).reduce((acc, [key, value]) => {
|
|
440
|
+
const pruned = this.pruneEmpty(value);
|
|
441
|
+
if (pruned !== null && pruned !== void 0 && pruned !== "" && (!Array.isArray(pruned) || pruned.length > 0) && (typeof pruned !== "object" || Object.keys(pruned).length > 0)) {
|
|
442
|
+
acc[key] = pruned;
|
|
443
|
+
}
|
|
444
|
+
return acc;
|
|
445
|
+
}, {});
|
|
446
|
+
}
|
|
447
|
+
return obj;
|
|
448
|
+
}
|
|
449
|
+
/**
|
|
450
|
+
* Filter object keys recursively based on a predicate
|
|
451
|
+
*/
|
|
452
|
+
static filterRecursive(obj, predicate) {
|
|
453
|
+
if (Array.isArray(obj)) {
|
|
454
|
+
return obj.map((v) => this.filterRecursive(v, predicate));
|
|
455
|
+
}
|
|
456
|
+
if (obj !== null && typeof obj === "object") {
|
|
457
|
+
return Object.entries(obj).reduce((acc, [key, value]) => {
|
|
458
|
+
if (key === "@type" || key === "@context" || predicate(key)) {
|
|
459
|
+
acc[key] = this.filterRecursive(value, predicate);
|
|
460
|
+
}
|
|
461
|
+
return acc;
|
|
462
|
+
}, {});
|
|
463
|
+
}
|
|
464
|
+
return obj;
|
|
465
|
+
}
|
|
466
|
+
/**
|
|
467
|
+
* Validates authority links to prevent hallucinations.
|
|
468
|
+
* Checks if sameAs links match known authoritative patterns.
|
|
469
|
+
*/
|
|
470
|
+
static validateAuthorities(sameAs = []) {
|
|
471
|
+
const authorityPatterns = [
|
|
472
|
+
/wikipedia\.org/,
|
|
473
|
+
/wikidata\.org/,
|
|
474
|
+
/linkedin\.com/,
|
|
475
|
+
/twitter\.com/,
|
|
476
|
+
/x\.com/,
|
|
477
|
+
/facebook\.com/,
|
|
478
|
+
/instagram\.com/,
|
|
479
|
+
/github\.com/,
|
|
480
|
+
/crunchbase\.com/
|
|
481
|
+
];
|
|
482
|
+
const valid = [];
|
|
483
|
+
const warnings = [];
|
|
484
|
+
sameAs.forEach((url) => {
|
|
485
|
+
try {
|
|
486
|
+
const parsed = new URL(url);
|
|
487
|
+
const isAuthority = authorityPatterns.some((pattern) => pattern.test(parsed.hostname));
|
|
488
|
+
if (isAuthority) {
|
|
489
|
+
valid.push(url);
|
|
490
|
+
} else {
|
|
491
|
+
warnings.push(`Low authority or unknown source: ${url}`);
|
|
492
|
+
}
|
|
493
|
+
} catch (e) {
|
|
494
|
+
warnings.push(`Invalid URL format: ${url}`);
|
|
495
|
+
}
|
|
496
|
+
});
|
|
497
|
+
return { valid, warnings };
|
|
498
|
+
}
|
|
499
|
+
};
|
|
500
|
+
|
|
501
|
+
// src/core/generators/identity.ts
|
|
502
|
+
function generateIdentitySchema(config) {
|
|
503
|
+
if (config.type === "Organization") {
|
|
504
|
+
const orgConfig = {
|
|
505
|
+
name: config.name,
|
|
506
|
+
...config.alternateName && { alternateName: config.alternateName },
|
|
507
|
+
...config.url && { url: config.url },
|
|
508
|
+
...config.logoUrl && { logo: config.logoUrl },
|
|
509
|
+
...config.description && { description: config.description },
|
|
510
|
+
...config.sameAs && { sameAs: config.sameAs },
|
|
511
|
+
...config.email && { email: config.email },
|
|
512
|
+
...config.telephone && { telephone: config.telephone },
|
|
513
|
+
...config.address && { address: config.address },
|
|
514
|
+
...config.contactPoint && { contactPoint: config.contactPoint },
|
|
515
|
+
...config.foundingDate && { foundingDate: config.foundingDate },
|
|
516
|
+
...config.founder && {
|
|
517
|
+
founder: Array.isArray(config.founder) ? config.founder : [config.founder]
|
|
518
|
+
},
|
|
519
|
+
...config.geo && { geo: config.geo }
|
|
520
|
+
};
|
|
521
|
+
return generateOrganizationSchema(orgConfig);
|
|
522
|
+
} else {
|
|
523
|
+
const personConfig = {
|
|
524
|
+
name: config.name,
|
|
525
|
+
...config.url && { url: config.url },
|
|
526
|
+
...config.imageUrl && { image: config.imageUrl },
|
|
527
|
+
...config.description && { description: config.description },
|
|
528
|
+
...config.jobTitle && { jobTitle: config.jobTitle },
|
|
529
|
+
...config.sameAs && { sameAs: config.sameAs },
|
|
530
|
+
...config.email && { email: config.email },
|
|
531
|
+
...config.telephone && { telephone: config.telephone },
|
|
532
|
+
...config.address && { address: config.address },
|
|
533
|
+
...config.worksFor && { worksFor: config.worksFor },
|
|
534
|
+
...config.geo && { geo: config.geo }
|
|
535
|
+
};
|
|
536
|
+
return generatePersonSchema(personConfig);
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
function generateOrganizationSchema(config) {
|
|
540
|
+
if (config.geo?.enableHallucinationGuard && config.sameAs) {
|
|
541
|
+
const { warnings } = GeoProcessor.validateAuthorities(config.sameAs);
|
|
542
|
+
if (warnings.length > 0) {
|
|
543
|
+
console.warn("[SemanticLayer:GEO] Authority validation warnings:", warnings);
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
const schema = {
|
|
547
|
+
"@context": "https://schema.org",
|
|
548
|
+
"@type": "Organization",
|
|
549
|
+
name: config.name,
|
|
550
|
+
...config.alternateName && { alternateName: config.alternateName },
|
|
551
|
+
...config.url && { url: config.url },
|
|
552
|
+
...config.description && { description: config.description },
|
|
553
|
+
...config.logo && { logo: config.logo },
|
|
554
|
+
...config.sameAs && config.sameAs.length > 0 && { sameAs: config.sameAs },
|
|
555
|
+
...config.email && { email: config.email },
|
|
556
|
+
...config.telephone && { telephone: config.telephone },
|
|
557
|
+
...config.address && {
|
|
558
|
+
address: {
|
|
559
|
+
"@type": "PostalAddress",
|
|
560
|
+
...config.address
|
|
561
|
+
}
|
|
562
|
+
},
|
|
563
|
+
...config.contactPoint && {
|
|
564
|
+
contactPoint: {
|
|
565
|
+
"@type": "ContactPoint",
|
|
566
|
+
...config.contactPoint
|
|
567
|
+
}
|
|
568
|
+
},
|
|
569
|
+
...config.foundingDate && { foundingDate: config.foundingDate },
|
|
570
|
+
...config.founder && { founder: Array.isArray(config.founder) ? config.founder : [config.founder] },
|
|
571
|
+
...config.numberOfEmployees && { numberOfEmployees: config.numberOfEmployees }
|
|
572
|
+
};
|
|
573
|
+
const parsed = OrganizationSchema.parse(schema);
|
|
574
|
+
if (config.geo) {
|
|
575
|
+
return GeoProcessor.optimizeDensity(parsed, config.geo.contentDensity);
|
|
576
|
+
}
|
|
577
|
+
return parsed;
|
|
578
|
+
}
|
|
579
|
+
function generatePersonSchema(config) {
|
|
580
|
+
if (config.geo?.enableHallucinationGuard && config.sameAs) {
|
|
581
|
+
const { warnings } = GeoProcessor.validateAuthorities(config.sameAs);
|
|
582
|
+
if (warnings.length > 0) {
|
|
583
|
+
console.warn("[SemanticLayer:GEO] Authority validation warnings:", warnings);
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
const schema = {
|
|
587
|
+
"@context": "https://schema.org",
|
|
588
|
+
"@type": "Person",
|
|
589
|
+
name: config.name,
|
|
590
|
+
...config.url && { url: config.url },
|
|
591
|
+
...config.description && { description: config.description },
|
|
592
|
+
...config.image && { image: config.image },
|
|
593
|
+
...config.sameAs && config.sameAs.length > 0 && { sameAs: config.sameAs },
|
|
594
|
+
...config.email && { email: config.email },
|
|
595
|
+
...config.telephone && { telephone: config.telephone },
|
|
596
|
+
...config.address && {
|
|
597
|
+
address: {
|
|
598
|
+
"@type": "PostalAddress",
|
|
599
|
+
...config.address
|
|
600
|
+
}
|
|
601
|
+
},
|
|
602
|
+
...config.jobTitle && { jobTitle: config.jobTitle },
|
|
603
|
+
...config.worksFor && {
|
|
604
|
+
worksFor: typeof config.worksFor === "string" ? { "@type": "Organization", name: config.worksFor } : config.worksFor
|
|
605
|
+
},
|
|
606
|
+
...config.birthDate && { birthDate: config.birthDate }
|
|
607
|
+
};
|
|
608
|
+
const parsed = PersonSchema.parse(schema);
|
|
609
|
+
if (config.geo) {
|
|
610
|
+
return GeoProcessor.optimizeDensity(parsed, config.geo.contentDensity);
|
|
611
|
+
}
|
|
612
|
+
return parsed;
|
|
613
|
+
}
|
|
614
|
+
function serializeSchema(schema) {
|
|
615
|
+
return JSON.stringify(schema, null, 2);
|
|
616
|
+
}
|
|
617
|
+
function generateScriptTag(schema) {
|
|
618
|
+
const serialized = serializeSchema(schema);
|
|
619
|
+
return `<script type="application/ld+json">
|
|
620
|
+
${serialized}
|
|
621
|
+
</script>`;
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
// src/core/generators/product.ts
|
|
625
|
+
function generateProductSchema(config) {
|
|
626
|
+
const schema = {
|
|
627
|
+
"@context": "https://schema.org",
|
|
628
|
+
"@type": "Product",
|
|
629
|
+
name: config.name,
|
|
630
|
+
...config.description && { description: config.description },
|
|
631
|
+
...config.image && { image: config.image },
|
|
632
|
+
...config.sku && { sku: config.sku },
|
|
633
|
+
...config.gtin && { gtin: config.gtin },
|
|
634
|
+
...config.brand && {
|
|
635
|
+
brand: {
|
|
636
|
+
"@type": "Brand",
|
|
637
|
+
name: config.brand
|
|
638
|
+
}
|
|
639
|
+
},
|
|
640
|
+
...config.offers && {
|
|
641
|
+
offers: Array.isArray(config.offers) ? config.offers.map((offer) => convertOffer(offer)) : convertOffer(config.offers)
|
|
642
|
+
},
|
|
643
|
+
...config.aggregateRating && {
|
|
644
|
+
aggregateRating: {
|
|
645
|
+
"@type": "AggregateRating",
|
|
646
|
+
...config.aggregateRating
|
|
647
|
+
}
|
|
648
|
+
},
|
|
649
|
+
...config.category && { category: config.category },
|
|
650
|
+
...config.color && { color: config.color }
|
|
651
|
+
};
|
|
652
|
+
return ProductSchema.parse(schema);
|
|
653
|
+
}
|
|
654
|
+
function convertOffer(config) {
|
|
655
|
+
return {
|
|
656
|
+
"@type": "Offer",
|
|
657
|
+
price: config.price,
|
|
658
|
+
priceCurrency: config.priceCurrency,
|
|
659
|
+
...config.availability && { availability: config.availability },
|
|
660
|
+
...config.url && { url: config.url },
|
|
661
|
+
...config.priceValidUntil && { priceValidUntil: config.priceValidUntil },
|
|
662
|
+
...config.seller && {
|
|
663
|
+
seller: {
|
|
664
|
+
"@type": "Organization",
|
|
665
|
+
name: config.seller.name
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
};
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
// src/core/generators/article.ts
|
|
672
|
+
function generateArticleSchema(config) {
|
|
673
|
+
const schema = {
|
|
674
|
+
"@context": "https://schema.org",
|
|
675
|
+
"@type": "Article",
|
|
676
|
+
headline: config.headline,
|
|
677
|
+
image: config.image,
|
|
678
|
+
author: convertAuthor(config.author),
|
|
679
|
+
datePublished: config.datePublished,
|
|
680
|
+
...config.dateModified && { dateModified: config.dateModified },
|
|
681
|
+
...config.description && { description: config.description },
|
|
682
|
+
...config.articleBody && { articleBody: config.articleBody },
|
|
683
|
+
...config.publisher && {
|
|
684
|
+
publisher: {
|
|
685
|
+
"@type": "Organization",
|
|
686
|
+
name: config.publisher.name,
|
|
687
|
+
...config.publisher.logo && {
|
|
688
|
+
logo: {
|
|
689
|
+
"@type": "ImageObject",
|
|
690
|
+
url: config.publisher.logo
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
},
|
|
695
|
+
...config.url && { url: config.url },
|
|
696
|
+
...config.keywords && { keywords: config.keywords },
|
|
697
|
+
...config.articleSection && { articleSection: config.articleSection }
|
|
698
|
+
};
|
|
699
|
+
const parsed = ArticleSchema.parse(schema);
|
|
700
|
+
if (config.geo) {
|
|
701
|
+
return GeoProcessor.optimizeDensity(parsed, config.geo.contentDensity);
|
|
702
|
+
}
|
|
703
|
+
return parsed;
|
|
704
|
+
}
|
|
705
|
+
function convertAuthor(author) {
|
|
706
|
+
if (typeof author === "string") {
|
|
707
|
+
return {
|
|
708
|
+
"@type": "Person",
|
|
709
|
+
name: author
|
|
710
|
+
};
|
|
711
|
+
}
|
|
712
|
+
if (Array.isArray(author)) {
|
|
713
|
+
return author.map((a) => ({
|
|
714
|
+
"@type": "Person",
|
|
715
|
+
name: a.name,
|
|
716
|
+
...a.url && { url: a.url }
|
|
717
|
+
}));
|
|
718
|
+
}
|
|
719
|
+
return {
|
|
720
|
+
"@type": "Person",
|
|
721
|
+
name: author.name,
|
|
722
|
+
...author.url && { url: author.url }
|
|
723
|
+
};
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
// src/core/generators/faq.ts
|
|
727
|
+
function generateFAQSchema(config) {
|
|
728
|
+
const schema = {
|
|
729
|
+
"@context": "https://schema.org",
|
|
730
|
+
"@type": "FAQPage",
|
|
731
|
+
mainEntity: config.questions.map((q) => ({
|
|
732
|
+
"@type": "Question",
|
|
733
|
+
name: q.question,
|
|
734
|
+
acceptedAnswer: {
|
|
735
|
+
"@type": "Answer",
|
|
736
|
+
text: q.answer
|
|
737
|
+
}
|
|
738
|
+
}))
|
|
739
|
+
};
|
|
740
|
+
return FAQPageSchema.parse(schema);
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
// src/core/generators/breadcrumb.ts
|
|
744
|
+
function generateBreadcrumbSchema(config) {
|
|
745
|
+
const schema = {
|
|
746
|
+
"@context": "https://schema.org",
|
|
747
|
+
"@type": "BreadcrumbList",
|
|
748
|
+
itemListElement: config.items.map((item, index) => ({
|
|
749
|
+
"@type": "ListItem",
|
|
750
|
+
position: index + 1,
|
|
751
|
+
name: item.name,
|
|
752
|
+
...item.url && { item: item.url }
|
|
753
|
+
}))
|
|
754
|
+
};
|
|
755
|
+
return BreadcrumbListSchema.parse(schema);
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
// src/core/generators/opengraph.ts
|
|
759
|
+
function generateOpenGraphTags(config) {
|
|
760
|
+
const validated = OpenGraphSchema.parse(config);
|
|
761
|
+
const tags = [
|
|
762
|
+
{ property: "og:title", content: validated.title },
|
|
763
|
+
{ property: "og:description", content: validated.description },
|
|
764
|
+
{ property: "og:url", content: validated.url },
|
|
765
|
+
{ property: "og:type", content: validated.type },
|
|
766
|
+
{ property: "og:image", content: validated.image },
|
|
767
|
+
{ property: "og:locale", content: validated.locale }
|
|
768
|
+
];
|
|
769
|
+
if (validated.imageAlt) {
|
|
770
|
+
tags.push({ property: "og:image:alt", content: validated.imageAlt });
|
|
771
|
+
}
|
|
772
|
+
if (validated.siteName) {
|
|
773
|
+
tags.push({ property: "og:site_name", content: validated.siteName });
|
|
774
|
+
}
|
|
775
|
+
if (validated.type === "article") {
|
|
776
|
+
if (validated.publishedTime) {
|
|
777
|
+
tags.push({ property: "article:published_time", content: validated.publishedTime });
|
|
778
|
+
}
|
|
779
|
+
if (validated.modifiedTime) {
|
|
780
|
+
tags.push({ property: "article:modified_time", content: validated.modifiedTime });
|
|
781
|
+
}
|
|
782
|
+
if (validated.author) {
|
|
783
|
+
tags.push({ property: "article:author", content: validated.author });
|
|
784
|
+
}
|
|
785
|
+
if (validated.section) {
|
|
786
|
+
tags.push({ property: "article:section", content: validated.section });
|
|
787
|
+
}
|
|
788
|
+
if (validated.tags) {
|
|
789
|
+
validated.tags.forEach((tag) => {
|
|
790
|
+
tags.push({ property: "article:tag", content: tag });
|
|
791
|
+
});
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
if (validated.type === "product") {
|
|
795
|
+
if (validated.price) {
|
|
796
|
+
tags.push({ property: "product:price:amount", content: String(validated.price) });
|
|
797
|
+
}
|
|
798
|
+
if (validated.currency) {
|
|
799
|
+
tags.push({ property: "product:price:currency", content: validated.currency });
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
return tags;
|
|
803
|
+
}
|
|
804
|
+
function generateTwitterCardTags(config) {
|
|
805
|
+
const validated = TwitterCardSchema.parse(config);
|
|
806
|
+
const tags = [
|
|
807
|
+
{ name: "twitter:card", content: validated.card },
|
|
808
|
+
{ name: "twitter:title", content: validated.title },
|
|
809
|
+
{ name: "twitter:description", content: validated.description },
|
|
810
|
+
{ name: "twitter:image", content: validated.image }
|
|
811
|
+
];
|
|
812
|
+
if (validated.site) {
|
|
813
|
+
tags.push({ name: "twitter:site", content: validated.site });
|
|
814
|
+
}
|
|
815
|
+
if (validated.creator) {
|
|
816
|
+
tags.push({ name: "twitter:creator", content: validated.creator });
|
|
817
|
+
}
|
|
818
|
+
if (validated.imageAlt) {
|
|
819
|
+
tags.push({ name: "twitter:image:alt", content: validated.imageAlt });
|
|
820
|
+
}
|
|
821
|
+
return tags;
|
|
822
|
+
}
|
|
823
|
+
function generateSocialMetaTags(config) {
|
|
824
|
+
const openGraph = generateOpenGraphTags({
|
|
825
|
+
title: config.title,
|
|
826
|
+
description: config.description,
|
|
827
|
+
url: config.url,
|
|
828
|
+
image: config.image,
|
|
829
|
+
...config.imageAlt && { imageAlt: config.imageAlt },
|
|
830
|
+
...config.type && { type: config.type },
|
|
831
|
+
...config.siteName && { siteName: config.siteName }
|
|
832
|
+
});
|
|
833
|
+
const twitter = generateTwitterCardTags({
|
|
834
|
+
title: config.title,
|
|
835
|
+
description: config.description,
|
|
836
|
+
image: config.image,
|
|
837
|
+
...config.imageAlt && { imageAlt: config.imageAlt },
|
|
838
|
+
...config.twitterSite && { site: config.twitterSite },
|
|
839
|
+
...config.twitterCreator && { creator: config.twitterCreator },
|
|
840
|
+
...config.twitterCard && { card: config.twitterCard }
|
|
841
|
+
});
|
|
842
|
+
return { openGraph, twitter };
|
|
843
|
+
}
|
|
844
|
+
function metaTagsToHTML(tags) {
|
|
845
|
+
return tags.map((tag) => {
|
|
846
|
+
const attr = tag.property ? `property="${tag.property}"` : `name="${tag.name}"`;
|
|
847
|
+
return `<meta ${attr} content="${tag.content}" />`;
|
|
848
|
+
}).join("\n");
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
// src/core/generators/localbusiness.ts
|
|
852
|
+
function generateLocalBusinessSchema(config) {
|
|
853
|
+
const schema = {
|
|
854
|
+
"@context": "https://schema.org",
|
|
855
|
+
"@type": config.type || "LocalBusiness",
|
|
856
|
+
name: config.name,
|
|
857
|
+
image: config.image,
|
|
858
|
+
address: {
|
|
859
|
+
"@type": "PostalAddress",
|
|
860
|
+
...config.address
|
|
861
|
+
},
|
|
862
|
+
...config.url && { url: config.url },
|
|
863
|
+
...config.telephone && { telephone: config.telephone },
|
|
864
|
+
...config.priceRange && { priceRange: config.priceRange },
|
|
865
|
+
...config.geo && {
|
|
866
|
+
geo: {
|
|
867
|
+
"@type": "GeoCoordinates",
|
|
868
|
+
latitude: config.geo.latitude,
|
|
869
|
+
longitude: config.geo.longitude
|
|
870
|
+
}
|
|
871
|
+
},
|
|
872
|
+
...config.openingHours && {
|
|
873
|
+
openingHoursSpecification: Array.isArray(config.openingHours) ? config.openingHours.map((hours) => ({
|
|
874
|
+
"@type": "OpeningHoursSpecification",
|
|
875
|
+
...hours
|
|
876
|
+
})) : {
|
|
877
|
+
"@type": "OpeningHoursSpecification",
|
|
878
|
+
...config.openingHours
|
|
879
|
+
}
|
|
880
|
+
},
|
|
881
|
+
...config.description && { description: config.description },
|
|
882
|
+
...config.aggregateRating && {
|
|
883
|
+
aggregateRating: {
|
|
884
|
+
"@type": "AggregateRating",
|
|
885
|
+
...config.aggregateRating
|
|
886
|
+
}
|
|
887
|
+
},
|
|
888
|
+
...config.servesCuisine && { servesCuisine: config.servesCuisine },
|
|
889
|
+
...config.menu && { menu: config.menu },
|
|
890
|
+
...config.acceptsReservations !== void 0 && { acceptsReservations: config.acceptsReservations },
|
|
891
|
+
...config.paymentAccepted && { paymentAccepted: config.paymentAccepted }
|
|
892
|
+
};
|
|
893
|
+
return LocalBusinessSchema.parse(schema);
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
// src/core/generators/event.ts
|
|
897
|
+
function generateEventSchema(config) {
|
|
898
|
+
const schema = {
|
|
899
|
+
"@context": "https://schema.org",
|
|
900
|
+
"@type": config.type || "Event",
|
|
901
|
+
name: config.name,
|
|
902
|
+
startDate: config.startDate,
|
|
903
|
+
...config.endDate && { endDate: config.endDate },
|
|
904
|
+
...config.eventStatus && { eventStatus: `https://schema.org/${config.eventStatus}` },
|
|
905
|
+
...config.eventAttendanceMode && { eventAttendanceMode: `https://schema.org/${config.eventAttendanceMode}` },
|
|
906
|
+
...config.location && {
|
|
907
|
+
location: typeof config.location === "string" ? config.location : {
|
|
908
|
+
"@type": "Place",
|
|
909
|
+
name: config.location.name,
|
|
910
|
+
...config.location.address && {
|
|
911
|
+
address: typeof config.location.address === "string" ? config.location.address : {
|
|
912
|
+
"@type": "PostalAddress",
|
|
913
|
+
...config.location.address
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
},
|
|
918
|
+
...config.image && { image: config.image },
|
|
919
|
+
...config.description && { description: config.description },
|
|
920
|
+
...config.url && { url: config.url },
|
|
921
|
+
...config.organizer && {
|
|
922
|
+
organizer: {
|
|
923
|
+
"@type": config.organizer.type,
|
|
924
|
+
name: config.organizer.name,
|
|
925
|
+
...config.organizer.url && { url: config.organizer.url }
|
|
926
|
+
}
|
|
927
|
+
},
|
|
928
|
+
...config.performer && {
|
|
929
|
+
performer: Array.isArray(config.performer) ? config.performer.map((p) => ({
|
|
930
|
+
"@type": p.type,
|
|
931
|
+
name: p.name
|
|
932
|
+
})) : {
|
|
933
|
+
"@type": config.performer.type,
|
|
934
|
+
name: config.performer.name
|
|
935
|
+
}
|
|
936
|
+
},
|
|
937
|
+
...config.offers && {
|
|
938
|
+
offers: Array.isArray(config.offers) ? config.offers.map((offer) => ({
|
|
939
|
+
"@type": "Offer",
|
|
940
|
+
...offer,
|
|
941
|
+
...offer.availability && { availability: offer.availability }
|
|
942
|
+
})) : {
|
|
943
|
+
"@type": "Offer",
|
|
944
|
+
...config.offers,
|
|
945
|
+
...config.offers.availability && { availability: config.offers.availability }
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
};
|
|
949
|
+
return EventSchema.parse(schema);
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
// src/core/generators/webpage.ts
|
|
953
|
+
function generateWebPageSchema(config) {
|
|
954
|
+
const schema = {
|
|
955
|
+
"@context": "https://schema.org",
|
|
956
|
+
"@type": "WebPage",
|
|
957
|
+
name: config.name,
|
|
958
|
+
description: config.description,
|
|
959
|
+
url: config.url,
|
|
960
|
+
...config.inLanguage && { inLanguage: config.inLanguage },
|
|
961
|
+
...config.isPartOf && { isPartOf: config.isPartOf },
|
|
962
|
+
...config.breadcrumb && { breadcrumb: config.breadcrumb }
|
|
963
|
+
};
|
|
964
|
+
return WebPageSchema.parse(schema);
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
export { generateArticleSchema, generateBreadcrumbSchema, generateEventSchema, generateFAQSchema, generateIdentitySchema, generateLocalBusinessSchema, generateOpenGraphTags, generateOrganizationSchema, generatePersonSchema, generateProductSchema, generateScriptTag, generateSocialMetaTags, generateTwitterCardTags, generateWebPageSchema, metaTagsToHTML, serializeSchema };
|
|
968
|
+
//# sourceMappingURL=index.js.map
|
|
969
|
+
//# sourceMappingURL=index.js.map
|