@schemasentry/core 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.
package/dist/index.cjs CHANGED
@@ -23,12 +23,15 @@ __export(index_exports, {
23
23
  Article: () => Article,
24
24
  BlogPosting: () => BlogPosting,
25
25
  BreadcrumbList: () => BreadcrumbList,
26
+ Event: () => Event,
26
27
  FAQPage: () => FAQPage,
27
28
  HowTo: () => HowTo,
29
+ LocalBusiness: () => LocalBusiness,
28
30
  Location: () => Location,
29
31
  Organization: () => Organization,
30
32
  Person: () => Person,
31
33
  Product: () => Product,
34
+ Review: () => Review,
32
35
  SCHEMA_CONTEXT: () => SCHEMA_CONTEXT,
33
36
  WebPage: () => WebPage,
34
37
  WebSite: () => WebSite,
@@ -53,6 +56,7 @@ var withBase = (type, input) => {
53
56
  var Organization = (input) => withBase("Organization", input);
54
57
  var Person = (input) => withBase("Person", input);
55
58
  var Location = (input) => withBase("Place", input);
59
+ var LocalBusiness = (input) => withBase("LocalBusiness", input);
56
60
  var WebSite = (input) => withBase("WebSite", input);
57
61
  var WebPage = (input) => withBase("WebPage", input);
58
62
  var Article = (input) => {
@@ -87,6 +91,59 @@ var Product = (input) => {
87
91
  } : {}
88
92
  });
89
93
  };
94
+ var Event = (input) => {
95
+ const {
96
+ locationName,
97
+ locationAddress,
98
+ locationUrl,
99
+ organizerName,
100
+ ...rest
101
+ } = input;
102
+ return withBase("Event", {
103
+ ...rest,
104
+ ...locationName || locationAddress || locationUrl ? {
105
+ location: {
106
+ "@type": "Place",
107
+ ...locationName ? { name: locationName } : {},
108
+ ...locationAddress ? { address: locationAddress } : {},
109
+ ...locationUrl ? { url: locationUrl } : {}
110
+ }
111
+ } : {},
112
+ ...organizerName ? {
113
+ organizer: {
114
+ "@type": "Organization",
115
+ name: organizerName
116
+ }
117
+ } : {}
118
+ });
119
+ };
120
+ var Review = (input) => {
121
+ const {
122
+ itemName,
123
+ authorName,
124
+ ratingValue,
125
+ bestRating,
126
+ worstRating,
127
+ ...rest
128
+ } = input;
129
+ return withBase("Review", {
130
+ ...rest,
131
+ itemReviewed: {
132
+ "@type": "Thing",
133
+ name: itemName
134
+ },
135
+ author: {
136
+ "@type": "Person",
137
+ name: authorName
138
+ },
139
+ reviewRating: {
140
+ "@type": "Rating",
141
+ ratingValue,
142
+ ...bestRating !== void 0 ? { bestRating } : {},
143
+ ...worstRating !== void 0 ? { worstRating } : {}
144
+ }
145
+ });
146
+ };
90
147
  var FAQPage = (input) => withBase("FAQPage", {
91
148
  mainEntity: input.questions.map((item) => ({
92
149
  "@type": "Question",
@@ -113,8 +170,9 @@ var BreadcrumbList = (input) => withBase("BreadcrumbList", {
113
170
  item: item.useItemObject ? { "@id": item.url, name: item.name } : item.url
114
171
  }))
115
172
  });
116
- var validateSchema = (nodes) => {
173
+ var validateSchema = (nodes, options = {}) => {
117
174
  const issues = [];
175
+ const { recommended = true } = options;
118
176
  if (!nodes.length) {
119
177
  issues.push({
120
178
  path: "root",
@@ -205,6 +263,20 @@ var validateSchema = (nodes) => {
205
263
  });
206
264
  }
207
265
  }
266
+ if (recommended) {
267
+ const recommendedFields = RECOMMENDED_FIELDS[type] ?? [];
268
+ for (const field of recommendedFields) {
269
+ const value = node[field];
270
+ if (isEmpty(value)) {
271
+ issues.push({
272
+ path: `${pathPrefix}.${field}`,
273
+ message: `Recommended field '${field}' is missing`,
274
+ severity: "warn",
275
+ ruleId: `schema.recommended.${field}`
276
+ });
277
+ }
278
+ }
279
+ }
208
280
  if (type === "BreadcrumbList") {
209
281
  validateBreadcrumbList(node, pathPrefix, issues);
210
282
  }
@@ -222,15 +294,34 @@ var REQUIRED_FIELDS = {
222
294
  Organization: ["name"],
223
295
  Person: ["name"],
224
296
  Place: ["name"],
297
+ LocalBusiness: ["name"],
225
298
  WebSite: ["name", "url"],
226
299
  WebPage: ["name", "url"],
227
300
  Article: ["headline", "author", "datePublished", "url"],
228
301
  BlogPosting: ["headline", "author", "datePublished", "url"],
229
302
  Product: ["name", "description", "url"],
303
+ Event: ["name", "startDate"],
304
+ Review: ["itemReviewed", "reviewRating", "author"],
230
305
  FAQPage: ["mainEntity"],
231
306
  HowTo: ["name", "step"],
232
307
  BreadcrumbList: ["itemListElement"]
233
308
  };
309
+ var RECOMMENDED_FIELDS = {
310
+ Organization: ["url", "logo", "sameAs"],
311
+ Person: ["url", "sameAs", "jobTitle"],
312
+ Place: ["address", "url"],
313
+ LocalBusiness: ["address", "telephone", "url"],
314
+ WebSite: ["description"],
315
+ WebPage: ["description", "isPartOf"],
316
+ Article: ["description", "image", "dateModified"],
317
+ BlogPosting: ["description", "image", "dateModified"],
318
+ Product: ["image", "brand", "sku"],
319
+ Event: ["description", "location", "organizer", "url"],
320
+ Review: ["reviewBody", "datePublished", "url"],
321
+ FAQPage: [],
322
+ HowTo: [],
323
+ BreadcrumbList: []
324
+ };
234
325
  var isSchemaType = (value) => typeof value === "string" && value in REQUIRED_FIELDS;
235
326
  var isEmpty = (value) => {
236
327
  if (value === null || value === void 0) {
@@ -344,12 +435,15 @@ var stableStringify = (value) => JSON.stringify(sortKeys(value));
344
435
  Article,
345
436
  BlogPosting,
346
437
  BreadcrumbList,
438
+ Event,
347
439
  FAQPage,
348
440
  HowTo,
441
+ LocalBusiness,
349
442
  Location,
350
443
  Organization,
351
444
  Person,
352
445
  Product,
446
+ Review,
353
447
  SCHEMA_CONTEXT,
354
448
  WebPage,
355
449
  WebSite,
package/dist/index.d.cts CHANGED
@@ -3,7 +3,7 @@ type JsonLdValue = string | number | boolean | null | JsonLdObject | JsonLdValue
3
3
  type JsonLdObject = {
4
4
  [key: string]: JsonLdValue;
5
5
  };
6
- type SchemaTypeName = "Organization" | "Person" | "Place" | "WebSite" | "WebPage" | "Article" | "BlogPosting" | "Product" | "FAQPage" | "HowTo" | "BreadcrumbList";
6
+ type SchemaTypeName = "Organization" | "Person" | "Place" | "LocalBusiness" | "WebSite" | "WebPage" | "Article" | "BlogPosting" | "Product" | "Event" | "Review" | "FAQPage" | "HowTo" | "BreadcrumbList";
7
7
  type SchemaNode = JsonLdObject & {
8
8
  "@context": typeof SCHEMA_CONTEXT;
9
9
  "@type": SchemaTypeName;
@@ -33,6 +33,17 @@ type LocationInput = BaseInput & {
33
33
  url?: string;
34
34
  };
35
35
  declare const Location: (input: LocationInput) => SchemaNode;
36
+ type LocalBusinessInput = BaseInput & {
37
+ name: string;
38
+ address?: string;
39
+ url?: string;
40
+ telephone?: string;
41
+ image?: string;
42
+ priceRange?: string;
43
+ sameAs?: string[];
44
+ description?: string;
45
+ };
46
+ declare const LocalBusiness: (input: LocalBusinessInput) => SchemaNode;
36
47
  type WebSiteInput = BaseInput & {
37
48
  name: string;
38
49
  url: string;
@@ -67,6 +78,29 @@ type ProductInput = BaseInput & {
67
78
  sku?: string;
68
79
  };
69
80
  declare const Product: (input: ProductInput) => SchemaNode;
81
+ type EventInput = BaseInput & {
82
+ name: string;
83
+ startDate: string;
84
+ endDate?: string;
85
+ url?: string;
86
+ description?: string;
87
+ locationName?: string;
88
+ locationAddress?: string;
89
+ locationUrl?: string;
90
+ organizerName?: string;
91
+ };
92
+ declare const Event: (input: EventInput) => SchemaNode;
93
+ type ReviewInput = BaseInput & {
94
+ itemName: string;
95
+ authorName: string;
96
+ ratingValue: number;
97
+ bestRating?: number;
98
+ worstRating?: number;
99
+ reviewBody?: string;
100
+ datePublished?: string;
101
+ url?: string;
102
+ };
103
+ declare const Review: (input: ReviewInput) => SchemaNode;
70
104
  type FAQItem = {
71
105
  question: string;
72
106
  answer: string;
@@ -103,12 +137,15 @@ type ValidationIssue = {
103
137
  severity: ValidationSeverity;
104
138
  ruleId: string;
105
139
  };
140
+ type ValidationOptions = {
141
+ recommended?: boolean;
142
+ };
106
143
  type ValidationResult = {
107
144
  ok: boolean;
108
145
  score: number;
109
146
  issues: ValidationIssue[];
110
147
  };
111
- declare const validateSchema: (nodes: SchemaNode[]) => ValidationResult;
148
+ declare const validateSchema: (nodes: SchemaNode[], options?: ValidationOptions) => ValidationResult;
112
149
  declare const stableStringify: (value: JsonLdValue) => string;
113
150
 
114
- export { Article, type ArticleInput, BlogPosting, type BlogPostingInput, type BreadcrumbItem, BreadcrumbList, type BreadcrumbListInput, type FAQItem, FAQPage, type FAQPageInput, HowTo, type HowToInput, type HowToStep, type JsonLdObject, type JsonLdValue, Location, type LocationInput, type Manifest, Organization, type OrganizationInput, Person, type PersonInput, Product, type ProductInput, SCHEMA_CONTEXT, type SchemaNode, type SchemaTypeName, type ValidationIssue, type ValidationResult, type ValidationSeverity, WebPage, type WebPageInput, WebSite, type WebSiteInput, stableStringify, validateSchema };
151
+ export { Article, type ArticleInput, BlogPosting, type BlogPostingInput, type BreadcrumbItem, BreadcrumbList, type BreadcrumbListInput, Event, type EventInput, type FAQItem, FAQPage, type FAQPageInput, HowTo, type HowToInput, type HowToStep, type JsonLdObject, type JsonLdValue, LocalBusiness, type LocalBusinessInput, Location, type LocationInput, type Manifest, Organization, type OrganizationInput, Person, type PersonInput, Product, type ProductInput, Review, type ReviewInput, SCHEMA_CONTEXT, type SchemaNode, type SchemaTypeName, type ValidationIssue, type ValidationOptions, type ValidationResult, type ValidationSeverity, WebPage, type WebPageInput, WebSite, type WebSiteInput, stableStringify, validateSchema };
package/dist/index.d.ts CHANGED
@@ -3,7 +3,7 @@ type JsonLdValue = string | number | boolean | null | JsonLdObject | JsonLdValue
3
3
  type JsonLdObject = {
4
4
  [key: string]: JsonLdValue;
5
5
  };
6
- type SchemaTypeName = "Organization" | "Person" | "Place" | "WebSite" | "WebPage" | "Article" | "BlogPosting" | "Product" | "FAQPage" | "HowTo" | "BreadcrumbList";
6
+ type SchemaTypeName = "Organization" | "Person" | "Place" | "LocalBusiness" | "WebSite" | "WebPage" | "Article" | "BlogPosting" | "Product" | "Event" | "Review" | "FAQPage" | "HowTo" | "BreadcrumbList";
7
7
  type SchemaNode = JsonLdObject & {
8
8
  "@context": typeof SCHEMA_CONTEXT;
9
9
  "@type": SchemaTypeName;
@@ -33,6 +33,17 @@ type LocationInput = BaseInput & {
33
33
  url?: string;
34
34
  };
35
35
  declare const Location: (input: LocationInput) => SchemaNode;
36
+ type LocalBusinessInput = BaseInput & {
37
+ name: string;
38
+ address?: string;
39
+ url?: string;
40
+ telephone?: string;
41
+ image?: string;
42
+ priceRange?: string;
43
+ sameAs?: string[];
44
+ description?: string;
45
+ };
46
+ declare const LocalBusiness: (input: LocalBusinessInput) => SchemaNode;
36
47
  type WebSiteInput = BaseInput & {
37
48
  name: string;
38
49
  url: string;
@@ -67,6 +78,29 @@ type ProductInput = BaseInput & {
67
78
  sku?: string;
68
79
  };
69
80
  declare const Product: (input: ProductInput) => SchemaNode;
81
+ type EventInput = BaseInput & {
82
+ name: string;
83
+ startDate: string;
84
+ endDate?: string;
85
+ url?: string;
86
+ description?: string;
87
+ locationName?: string;
88
+ locationAddress?: string;
89
+ locationUrl?: string;
90
+ organizerName?: string;
91
+ };
92
+ declare const Event: (input: EventInput) => SchemaNode;
93
+ type ReviewInput = BaseInput & {
94
+ itemName: string;
95
+ authorName: string;
96
+ ratingValue: number;
97
+ bestRating?: number;
98
+ worstRating?: number;
99
+ reviewBody?: string;
100
+ datePublished?: string;
101
+ url?: string;
102
+ };
103
+ declare const Review: (input: ReviewInput) => SchemaNode;
70
104
  type FAQItem = {
71
105
  question: string;
72
106
  answer: string;
@@ -103,12 +137,15 @@ type ValidationIssue = {
103
137
  severity: ValidationSeverity;
104
138
  ruleId: string;
105
139
  };
140
+ type ValidationOptions = {
141
+ recommended?: boolean;
142
+ };
106
143
  type ValidationResult = {
107
144
  ok: boolean;
108
145
  score: number;
109
146
  issues: ValidationIssue[];
110
147
  };
111
- declare const validateSchema: (nodes: SchemaNode[]) => ValidationResult;
148
+ declare const validateSchema: (nodes: SchemaNode[], options?: ValidationOptions) => ValidationResult;
112
149
  declare const stableStringify: (value: JsonLdValue) => string;
113
150
 
114
- export { Article, type ArticleInput, BlogPosting, type BlogPostingInput, type BreadcrumbItem, BreadcrumbList, type BreadcrumbListInput, type FAQItem, FAQPage, type FAQPageInput, HowTo, type HowToInput, type HowToStep, type JsonLdObject, type JsonLdValue, Location, type LocationInput, type Manifest, Organization, type OrganizationInput, Person, type PersonInput, Product, type ProductInput, SCHEMA_CONTEXT, type SchemaNode, type SchemaTypeName, type ValidationIssue, type ValidationResult, type ValidationSeverity, WebPage, type WebPageInput, WebSite, type WebSiteInput, stableStringify, validateSchema };
151
+ export { Article, type ArticleInput, BlogPosting, type BlogPostingInput, type BreadcrumbItem, BreadcrumbList, type BreadcrumbListInput, Event, type EventInput, type FAQItem, FAQPage, type FAQPageInput, HowTo, type HowToInput, type HowToStep, type JsonLdObject, type JsonLdValue, LocalBusiness, type LocalBusinessInput, Location, type LocationInput, type Manifest, Organization, type OrganizationInput, Person, type PersonInput, Product, type ProductInput, Review, type ReviewInput, SCHEMA_CONTEXT, type SchemaNode, type SchemaTypeName, type ValidationIssue, type ValidationOptions, type ValidationResult, type ValidationSeverity, WebPage, type WebPageInput, WebSite, type WebSiteInput, stableStringify, validateSchema };
package/dist/index.js CHANGED
@@ -16,6 +16,7 @@ var withBase = (type, input) => {
16
16
  var Organization = (input) => withBase("Organization", input);
17
17
  var Person = (input) => withBase("Person", input);
18
18
  var Location = (input) => withBase("Place", input);
19
+ var LocalBusiness = (input) => withBase("LocalBusiness", input);
19
20
  var WebSite = (input) => withBase("WebSite", input);
20
21
  var WebPage = (input) => withBase("WebPage", input);
21
22
  var Article = (input) => {
@@ -50,6 +51,59 @@ var Product = (input) => {
50
51
  } : {}
51
52
  });
52
53
  };
54
+ var Event = (input) => {
55
+ const {
56
+ locationName,
57
+ locationAddress,
58
+ locationUrl,
59
+ organizerName,
60
+ ...rest
61
+ } = input;
62
+ return withBase("Event", {
63
+ ...rest,
64
+ ...locationName || locationAddress || locationUrl ? {
65
+ location: {
66
+ "@type": "Place",
67
+ ...locationName ? { name: locationName } : {},
68
+ ...locationAddress ? { address: locationAddress } : {},
69
+ ...locationUrl ? { url: locationUrl } : {}
70
+ }
71
+ } : {},
72
+ ...organizerName ? {
73
+ organizer: {
74
+ "@type": "Organization",
75
+ name: organizerName
76
+ }
77
+ } : {}
78
+ });
79
+ };
80
+ var Review = (input) => {
81
+ const {
82
+ itemName,
83
+ authorName,
84
+ ratingValue,
85
+ bestRating,
86
+ worstRating,
87
+ ...rest
88
+ } = input;
89
+ return withBase("Review", {
90
+ ...rest,
91
+ itemReviewed: {
92
+ "@type": "Thing",
93
+ name: itemName
94
+ },
95
+ author: {
96
+ "@type": "Person",
97
+ name: authorName
98
+ },
99
+ reviewRating: {
100
+ "@type": "Rating",
101
+ ratingValue,
102
+ ...bestRating !== void 0 ? { bestRating } : {},
103
+ ...worstRating !== void 0 ? { worstRating } : {}
104
+ }
105
+ });
106
+ };
53
107
  var FAQPage = (input) => withBase("FAQPage", {
54
108
  mainEntity: input.questions.map((item) => ({
55
109
  "@type": "Question",
@@ -76,8 +130,9 @@ var BreadcrumbList = (input) => withBase("BreadcrumbList", {
76
130
  item: item.useItemObject ? { "@id": item.url, name: item.name } : item.url
77
131
  }))
78
132
  });
79
- var validateSchema = (nodes) => {
133
+ var validateSchema = (nodes, options = {}) => {
80
134
  const issues = [];
135
+ const { recommended = true } = options;
81
136
  if (!nodes.length) {
82
137
  issues.push({
83
138
  path: "root",
@@ -168,6 +223,20 @@ var validateSchema = (nodes) => {
168
223
  });
169
224
  }
170
225
  }
226
+ if (recommended) {
227
+ const recommendedFields = RECOMMENDED_FIELDS[type] ?? [];
228
+ for (const field of recommendedFields) {
229
+ const value = node[field];
230
+ if (isEmpty(value)) {
231
+ issues.push({
232
+ path: `${pathPrefix}.${field}`,
233
+ message: `Recommended field '${field}' is missing`,
234
+ severity: "warn",
235
+ ruleId: `schema.recommended.${field}`
236
+ });
237
+ }
238
+ }
239
+ }
171
240
  if (type === "BreadcrumbList") {
172
241
  validateBreadcrumbList(node, pathPrefix, issues);
173
242
  }
@@ -185,15 +254,34 @@ var REQUIRED_FIELDS = {
185
254
  Organization: ["name"],
186
255
  Person: ["name"],
187
256
  Place: ["name"],
257
+ LocalBusiness: ["name"],
188
258
  WebSite: ["name", "url"],
189
259
  WebPage: ["name", "url"],
190
260
  Article: ["headline", "author", "datePublished", "url"],
191
261
  BlogPosting: ["headline", "author", "datePublished", "url"],
192
262
  Product: ["name", "description", "url"],
263
+ Event: ["name", "startDate"],
264
+ Review: ["itemReviewed", "reviewRating", "author"],
193
265
  FAQPage: ["mainEntity"],
194
266
  HowTo: ["name", "step"],
195
267
  BreadcrumbList: ["itemListElement"]
196
268
  };
269
+ var RECOMMENDED_FIELDS = {
270
+ Organization: ["url", "logo", "sameAs"],
271
+ Person: ["url", "sameAs", "jobTitle"],
272
+ Place: ["address", "url"],
273
+ LocalBusiness: ["address", "telephone", "url"],
274
+ WebSite: ["description"],
275
+ WebPage: ["description", "isPartOf"],
276
+ Article: ["description", "image", "dateModified"],
277
+ BlogPosting: ["description", "image", "dateModified"],
278
+ Product: ["image", "brand", "sku"],
279
+ Event: ["description", "location", "organizer", "url"],
280
+ Review: ["reviewBody", "datePublished", "url"],
281
+ FAQPage: [],
282
+ HowTo: [],
283
+ BreadcrumbList: []
284
+ };
197
285
  var isSchemaType = (value) => typeof value === "string" && value in REQUIRED_FIELDS;
198
286
  var isEmpty = (value) => {
199
287
  if (value === null || value === void 0) {
@@ -306,12 +394,15 @@ export {
306
394
  Article,
307
395
  BlogPosting,
308
396
  BreadcrumbList,
397
+ Event,
309
398
  FAQPage,
310
399
  HowTo,
400
+ LocalBusiness,
311
401
  Location,
312
402
  Organization,
313
403
  Person,
314
404
  Product,
405
+ Review,
315
406
  SCHEMA_CONTEXT,
316
407
  WebPage,
317
408
  WebSite,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@schemasentry/core",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Typed schema builders and validation primitives for Schema Sentry.",
5
5
  "license": "MIT",
6
6
  "type": "module",