@crawl-me-maybe/schema 0.1.1 → 0.2.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/README.md +299 -1
- package/dist/build.d.ts +2 -0
- package/dist/builders/aggregate-offer.d.ts +3 -0
- package/dist/builders/brand.d.ts +3 -0
- package/dist/builders/course-instance.d.ts +3 -0
- package/dist/builders/geo-coordinates.d.ts +3 -0
- package/dist/builders/how-to-step.d.ts +3 -0
- package/dist/builders/index.d.ts +16 -0
- package/dist/builders/merchant-return-policy.d.ts +3 -0
- package/dist/builders/monetary-amount.d.ts +3 -0
- package/dist/builders/nutrition-information.d.ts +3 -0
- package/dist/builders/offer-shipping-details.d.ts +3 -0
- package/dist/builders/offer.d.ts +3 -0
- package/dist/builders/organization.d.ts +3 -0
- package/dist/builders/person.d.ts +3 -0
- package/dist/builders/place.d.ts +3 -0
- package/dist/builders/postal-address.d.ts +3 -0
- package/dist/builders/quantitative-value.d.ts +3 -0
- package/dist/builders/schedule.d.ts +3 -0
- package/dist/identity-ref.d.ts +4 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +233 -43
- package/dist/index.js.map +32 -13
- package/dist/utils/datetime.d.ts +11 -0
- package/dist/utils/image.d.ts +8 -1
- package/dist/utils/url.d.ts +1 -0
- package/package.json +7 -4
package/README.md
CHANGED
|
@@ -15,6 +15,11 @@ Schema markup should be generated from your content model, not rebuilt beside it
|
|
|
15
15
|
- [`mainEntity`](#mainentity)
|
|
16
16
|
- [`@id` and de-duplication](#id-and-de-duplication)
|
|
17
17
|
- [Rendering the graph](#rendering-the-graph)
|
|
18
|
+
- [Localized pages](#localized-pages)
|
|
19
|
+
- [Nesting entities](#nesting-entities)
|
|
20
|
+
- [Dates and durations](#dates-and-durations)
|
|
21
|
+
- [Helpers](#helpers)
|
|
22
|
+
- [Empty values](#empty-values)
|
|
18
23
|
- [Supported schemas](#supported-schemas)
|
|
19
24
|
- [With Sanity](#with-sanity)
|
|
20
25
|
- [Core exports](#core-exports)
|
|
@@ -36,8 +41,11 @@ yarn add @crawl-me-maybe/schema
|
|
|
36
41
|
- `buildSchemaMarkup` emits a JSON-LD graph for identity, `WebSite`, and `WebPage`
|
|
37
42
|
- Typed builders for common Schema.org nodes, backed by `schema-dts`
|
|
38
43
|
- Entity de-duping by `@id` when nested nodes reference the same entity
|
|
39
|
-
- Identity roots for `Person`, `Organization`, and `LocalBusiness`
|
|
44
|
+
- Identity roots for `Person`, `Organization`, and `LocalBusiness`, linkable from any page via `identityRef`
|
|
40
45
|
- Utility builders for breadcrumbs, FAQ pages, products, articles, events, and more
|
|
46
|
+
- Nested builders for offers, course instances, schedules, places, people, and more
|
|
47
|
+
- Cleans the graph for you: nested `@context` and empty values (`null`, `""`, `[]`) are dropped
|
|
48
|
+
- Small helpers for ISO 8601 durations and dates, and absolute URLs
|
|
41
49
|
|
|
42
50
|
## Why this exists
|
|
43
51
|
|
|
@@ -136,6 +144,10 @@ If multiple nodes reference the same entity, give them the same `@id`. The libra
|
|
|
136
144
|
|
|
137
145
|
In most cases, `buildSchemaMarkup` handles page-level identity, `WebSite`, and `WebPage` IDs for you. You'll mainly care about `@id` when composing custom or nested entities manually.
|
|
138
146
|
|
|
147
|
+
A nested node with both `@type` and `@id` is hoisted to its own top-level node in the graph, and replaced where it was nested with an `{ "@id": ... }` reference. To link to the site identity, use [`identityRef`](#identityref).
|
|
148
|
+
|
|
149
|
+
Generated IDs ignore trailing slashes on `siteUrl`, so `https://example.com` and `https://example.com/` produce the same IDs.
|
|
150
|
+
|
|
139
151
|
---
|
|
140
152
|
|
|
141
153
|
## Rendering the graph
|
|
@@ -152,6 +164,229 @@ In most cases, `buildSchemaMarkup` handles page-level identity, `WebSite`, and `
|
|
|
152
164
|
|
|
153
165
|
---
|
|
154
166
|
|
|
167
|
+
## Localized pages
|
|
168
|
+
|
|
169
|
+
Pass `inLanguage` to set the language of the current page. It is emitted on the `WebPage` node only.
|
|
170
|
+
|
|
171
|
+
```ts
|
|
172
|
+
buildSchemaMarkup({
|
|
173
|
+
// ...
|
|
174
|
+
siteUrl: "https://example.com",
|
|
175
|
+
pageUrl: "https://example.com/fr/a-propos",
|
|
176
|
+
pageTitle: "À propos",
|
|
177
|
+
inLanguage: "fr",
|
|
178
|
+
});
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
- Pass the language of the content that is actually rendered. If a `/fr/...` route falls back to English content, pass `"en"`.
|
|
182
|
+
- Use a BCP 47 tag with hyphens (`fr-CA`, not `fr_CA`). Never pass `x-default`; it is an hreflang value, not a language.
|
|
183
|
+
- Keep `siteUrl` as the root origin for every locale (`https://example.com`, not `https://example.com/fr`) so all locales share one `WebSite` `@id`.
|
|
184
|
+
- Nested entities don't inherit it and don't need it. Set `inLanguage` on an entity only when its language differs from the page.
|
|
185
|
+
- `inLanguage` does not replace hreflang. Localized alternates still come from your sitemap or `<link rel="alternate" hreflang>` tags.
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
## Nesting entities
|
|
190
|
+
|
|
191
|
+
Nested values like a course's offers or instances need their own `@type`. Use a nested builder where one exists, or a plain object with `"@type"` for anything else:
|
|
192
|
+
|
|
193
|
+
```ts
|
|
194
|
+
import {
|
|
195
|
+
buildCourse,
|
|
196
|
+
buildCourseInstance,
|
|
197
|
+
buildOffer,
|
|
198
|
+
buildSchedule,
|
|
199
|
+
identityRef,
|
|
200
|
+
toIsoDate,
|
|
201
|
+
toIsoDuration,
|
|
202
|
+
} from "@crawl-me-maybe/schema";
|
|
203
|
+
|
|
204
|
+
const mainEntity = buildCourse({
|
|
205
|
+
name: course.title,
|
|
206
|
+
description: course.summary,
|
|
207
|
+
provider: identityRef,
|
|
208
|
+
offers: [
|
|
209
|
+
buildOffer({
|
|
210
|
+
category: "Paid",
|
|
211
|
+
price: course.price,
|
|
212
|
+
priceCurrency: "USD",
|
|
213
|
+
}),
|
|
214
|
+
],
|
|
215
|
+
hasCourseInstance: [
|
|
216
|
+
buildCourseInstance({
|
|
217
|
+
courseMode: "Online",
|
|
218
|
+
courseWorkload: toIsoDuration({ hours: course.totalHours }),
|
|
219
|
+
courseSchedule: buildSchedule({
|
|
220
|
+
duration: toIsoDuration({ minutes: course.sessionMinutes }),
|
|
221
|
+
repeatFrequency: "P1W",
|
|
222
|
+
startDate: toIsoDate(course.startsAt),
|
|
223
|
+
}),
|
|
224
|
+
}),
|
|
225
|
+
],
|
|
226
|
+
// No builder for this type — a plain object with "@type" works too.
|
|
227
|
+
audience: { "@type": "EducationalAudience", educationalRole: "student" },
|
|
228
|
+
});
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
- Builders add their own `@context`. When nested, `buildSchemaMarkup` strips it, so only top-level nodes carry `@context`.
|
|
232
|
+
- There is no automatic `@type` inference. Many properties accept more than one type (`author` can be a `Person` or `Organization`), and `@type` is what gives you accurate autocomplete from `schema-dts`.
|
|
233
|
+
- The graph cleanup (nested `@context`, [empty values](#empty-values), and `identityRef`) only happens inside `buildSchemaMarkup`. If you serialize a builder's output yourself, it's emitted as-is.
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
|
|
237
|
+
## Dates and durations
|
|
238
|
+
|
|
239
|
+
Schema.org expects ISO 8601 for dates and durations, but `schema-dts` types them as plain `string`, so nothing catches `"3 hours"` at compile time.
|
|
240
|
+
|
|
241
|
+
| Format | Common fields |
|
|
242
|
+
|---|---|
|
|
243
|
+
| Duration (`PT1H30M`) | `Recipe.prepTime` / `cookTime` / `totalTime`, `VideoObject.duration`, `Movie.duration`, `Schedule.duration` / `repeatFrequency`, `CourseInstance.courseWorkload` |
|
|
244
|
+
| Date / date-time (`2026-09-28`, `2026-09-28T13:00:00-06:00`) | `Article.datePublished` / `dateModified`, `Event.startDate` / `endDate`, `JobPosting.datePosted` / `validThrough`, `VideoObject.uploadDate`, `Offer.validFrom` / `validThrough` / `priceValidUntil`, `Schedule.startDate` / `endDate` |
|
|
245
|
+
|
|
246
|
+
### `toIsoDuration(parts)`
|
|
247
|
+
|
|
248
|
+
Takes an object of named parts, so the unit is always explicit:
|
|
249
|
+
|
|
250
|
+
```ts
|
|
251
|
+
toIsoDuration({ hours: 1, minutes: 30 }); // "PT1H30M"
|
|
252
|
+
toIsoDuration({ seconds: video.duration }); // "PT183S"
|
|
253
|
+
toIsoDuration({ minutes: recipe.cookMinutes }); // "PT45M"
|
|
254
|
+
toIsoDuration({ weeks: 1 }); // "P1W"
|
|
255
|
+
toIsoDuration({ months: 1 }); // "P1M"
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
- Accepts `years`, `months`, `weeks`, `days`, `hours`, `minutes`, and `seconds`.
|
|
259
|
+
- Parts are emitted as given, without carrying over — `{ minutes: 90 }` is `"PT90M"`, which is valid ISO 8601.
|
|
260
|
+
- Zero parts are omitted; all zeros gives `"PT0S"`.
|
|
261
|
+
- Throws on negative or non-finite values, fractional values other than `seconds`, and `weeks` combined with other parts.
|
|
262
|
+
|
|
263
|
+
### `toIsoDate(value)`
|
|
264
|
+
|
|
265
|
+
Accepts a `Date` or an ISO 8601 string:
|
|
266
|
+
|
|
267
|
+
```ts
|
|
268
|
+
toIsoDate(new Date()); // "2026-09-28T19:17:00.000Z"
|
|
269
|
+
toIsoDate("2026-09-28T19:17:00.000Z"); // unchanged (Sanity datetime)
|
|
270
|
+
toIsoDate("2026-09-28T13:00:00-06:00"); // unchanged, offset kept
|
|
271
|
+
toIsoDate("2026-09-28T13:00+0600"); // "2026-09-28T13:00+06:00"
|
|
272
|
+
toIsoDate("2026-09-28"); // unchanged
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
- `Date` objects are converted to UTC with `toISOString()`.
|
|
276
|
+
- Valid ISO strings are returned as written, so an editor's local time and offset survive. `+hhmm` offsets are rewritten to `+hh:mm`.
|
|
277
|
+
- Date-times without an offset are passed through unchanged — the helper can't safely guess a timezone.
|
|
278
|
+
- Anything else throws.
|
|
279
|
+
|
|
280
|
+
---
|
|
281
|
+
|
|
282
|
+
## Helpers
|
|
283
|
+
|
|
284
|
+
### `identityRef`
|
|
285
|
+
|
|
286
|
+
Page- and component-level code usually doesn't have the global identity config handy. Use `identityRef` anywhere an entity is expected, and `buildSchemaMarkup` resolves it to the `@id` of the identity node it emits:
|
|
287
|
+
|
|
288
|
+
```ts
|
|
289
|
+
// Page or component level
|
|
290
|
+
const mainEntity = buildCourse({
|
|
291
|
+
name: course.title,
|
|
292
|
+
provider: identityRef,
|
|
293
|
+
publisher: identityRef,
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
// App level
|
|
297
|
+
buildSchemaMarkup({
|
|
298
|
+
identity: { type: "organization", name: siteName },
|
|
299
|
+
siteUrl,
|
|
300
|
+
siteName,
|
|
301
|
+
pageUrl,
|
|
302
|
+
pageTitle,
|
|
303
|
+
mainEntity,
|
|
304
|
+
});
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
Prefer this over inlining `{ "@type": "Organization", name: siteName, url: siteUrl }`. Without an `@id`, the inline version is a second organization that search engines can't link to your site identity.
|
|
308
|
+
|
|
309
|
+
`identityRef` is a plain object (`{ "@id": "@identity" }`), so it survives being serialized across a server/client or loader boundary. It only resolves inside `buildSchemaMarkup`.
|
|
310
|
+
|
|
311
|
+
### Reusing other entities
|
|
312
|
+
|
|
313
|
+
For entities that appear in several places, like an instructor across courses, give them a stable `@id`. They're hoisted to top-level nodes and de-duplicated:
|
|
314
|
+
|
|
315
|
+
```ts
|
|
316
|
+
const instructor = buildPerson({
|
|
317
|
+
"@id": `${siteUrl}#instructor-jane`,
|
|
318
|
+
name: "Jane Doe",
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
buildCourse({
|
|
322
|
+
name: "Intro",
|
|
323
|
+
hasCourseInstance: [
|
|
324
|
+
buildCourseInstance({ courseMode: "Online", instructor: [instructor] }),
|
|
325
|
+
],
|
|
326
|
+
});
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
### `toAbsoluteUrl(value, siteUrl)`
|
|
330
|
+
|
|
331
|
+
Search engines expect absolute URLs for `url`, `image`, and breadcrumb `item` values. CMS content usually gives you slugs or relative paths:
|
|
332
|
+
|
|
333
|
+
```ts
|
|
334
|
+
toAbsoluteUrl("/blog/post", "https://example.com"); // "https://example.com/blog/post"
|
|
335
|
+
toAbsoluteUrl("blog/post", "https://example.com/"); // "https://example.com/blog/post"
|
|
336
|
+
toAbsoluteUrl("/a", "https://example.com/docs"); // "https://example.com/docs/a"
|
|
337
|
+
toAbsoluteUrl("https://cdn.sanity.io/x.jpg", siteUrl); // unchanged
|
|
338
|
+
toAbsoluteUrl("//cdn.example.com/x.jpg", "https://example.com"); // "https://cdn.example.com/x.jpg"
|
|
339
|
+
toAbsoluteUrl(null, siteUrl); // undefined
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
Relative values are joined to `siteUrl`, keeping any base path. Empty input returns `undefined`, so the field is dropped from the graph.
|
|
343
|
+
|
|
344
|
+
### `buildImageObject(input)`
|
|
345
|
+
|
|
346
|
+
Turns a URL string or an object into an `ImageObject`, typed so it can be passed straight to any `image` or `logo` field. Returns `undefined` for empty input.
|
|
347
|
+
|
|
348
|
+
```ts
|
|
349
|
+
buildImageObject("https://example.com/logo.png");
|
|
350
|
+
buildImageObject({ url: image.url, width: 1200, height: 630 });
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
The output always includes `contentUrl` alongside `url`, with the same value. Google's [image metadata](https://developers.google.com/search/docs/appearance/structured-data/image-license-metadata) feature requires `contentUrl`, and other uses such as logos read `url`.
|
|
354
|
+
|
|
355
|
+
To show license and credit details in Google Images, add any of the image metadata fields:
|
|
356
|
+
|
|
357
|
+
```ts
|
|
358
|
+
buildImageObject({
|
|
359
|
+
url: image.url,
|
|
360
|
+
caption: image.alt,
|
|
361
|
+
creditText: "Acme Photography",
|
|
362
|
+
copyrightNotice: "© 2026 Acme",
|
|
363
|
+
license: "https://example.com/image-license",
|
|
364
|
+
acquireLicensePage: "https://example.com/licensing",
|
|
365
|
+
creator: identityRef,
|
|
366
|
+
});
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
`creator` accepts `identityRef`, `buildPerson(...)`, `buildOrganization(...)`, or an `{ "@id" }` reference.
|
|
370
|
+
|
|
371
|
+
### Portable Text
|
|
372
|
+
|
|
373
|
+
For descriptions stored as Portable Text, convert to a plain string first — for example with `toPlainText` from [`@portabletext/toolkit`](https://github.com/portabletext/toolkit). This package has no Sanity dependency.
|
|
374
|
+
|
|
375
|
+
---
|
|
376
|
+
|
|
377
|
+
## Empty values
|
|
378
|
+
|
|
379
|
+
`buildSchemaMarkup` drops values that carry no information before serializing:
|
|
380
|
+
|
|
381
|
+
- `null` and `undefined`
|
|
382
|
+
- empty or whitespace-only strings
|
|
383
|
+
- empty arrays (after removing empty items)
|
|
384
|
+
- objects with nothing besides `@type`
|
|
385
|
+
|
|
386
|
+
`0` and `false` are kept. GROQ returns `null` for unset fields, so projections don't need `coalesce()` just to keep `null` out of the markup.
|
|
387
|
+
|
|
388
|
+
---
|
|
389
|
+
|
|
155
390
|
## Supported schemas
|
|
156
391
|
|
|
157
392
|
Builders are exported from the package root.
|
|
@@ -200,8 +435,67 @@ Site identity (`Organization`, `Person`, or `LocalBusiness`) is emitted by `buil
|
|
|
200
435
|
| `buildDiscussionForumPosting` | `DiscussionForumPosting` |
|
|
201
436
|
| `buildItemList` | `ItemList` |
|
|
202
437
|
|
|
438
|
+
### Nested values
|
|
439
|
+
|
|
440
|
+
Building blocks for properties like `offers`, `hasCourseInstance`, `location`, or `author`. See [Nesting entities](#nesting-entities).
|
|
441
|
+
|
|
442
|
+
| Builder | Schema.org type | Typical parent properties |
|
|
443
|
+
|---|---|---|
|
|
444
|
+
| `buildOffer` | `Offer` | `offers` on Product, Course, Event, SoftwareApplication |
|
|
445
|
+
| `buildAggregateOffer` | `AggregateOffer` | `offers` with a price range (`lowPrice`, `highPrice`, `offerCount`) |
|
|
446
|
+
| `buildMonetaryAmount` | `MonetaryAmount` | `baseSalary` on JobPosting |
|
|
447
|
+
| `buildQuantitativeValue` | `QuantitativeValue` | `value` in MonetaryAmount, product dimensions |
|
|
448
|
+
| `buildBrand` | `Brand` | `brand` on Product |
|
|
449
|
+
| `buildCourseInstance` | `CourseInstance` | `hasCourseInstance` on Course |
|
|
450
|
+
| `buildSchedule` | `Schedule` | `courseSchedule`, `eventSchedule` |
|
|
451
|
+
| `buildPlace` | `Place` | `location` on Event, `jobLocation` on JobPosting |
|
|
452
|
+
| `buildPostalAddress` | `PostalAddress` | `address` on Place, Organization, LocalBusiness |
|
|
453
|
+
| `buildGeoCoordinates` | `GeoCoordinates` | `geo` on Place, LocalBusiness |
|
|
454
|
+
| `buildPerson` | `Person` | `author`, `actor`, `director`, `instructor` on CourseInstance |
|
|
455
|
+
| `buildOrganization` | `Organization` | `hiringOrganization`, `organizer`, `productionCompany` |
|
|
456
|
+
| `buildHowToStep` | `HowToStep` | `recipeInstructions` on Recipe |
|
|
457
|
+
| `buildNutritionInformation` | `NutritionInformation` | `nutrition` on Recipe |
|
|
458
|
+
| `buildOfferShippingDetails` | `OfferShippingDetails` | `shippingDetails` on Offer |
|
|
459
|
+
| `buildMerchantReturnPolicy` | `MerchantReturnPolicy` | `hasMerchantReturnPolicy` on Offer or Organization |
|
|
460
|
+
|
|
461
|
+
`buildPerson` and `buildOrganization` are for other people and organizations on the page. For the site owner, use `identity` on `buildSchemaMarkup` and link to it with [`identityRef`](#identityref).
|
|
462
|
+
|
|
463
|
+
**Product merchant listing example:**
|
|
464
|
+
|
|
465
|
+
```ts
|
|
466
|
+
buildProduct({
|
|
467
|
+
name: product.title,
|
|
468
|
+
image: buildImageObject(product.imageUrl),
|
|
469
|
+
offers: [
|
|
470
|
+
buildOffer({
|
|
471
|
+
price: product.price,
|
|
472
|
+
priceCurrency: "USD",
|
|
473
|
+
availability: "InStock",
|
|
474
|
+
shippingDetails: buildOfferShippingDetails({
|
|
475
|
+
shippingRate: buildMonetaryAmount({ value: 0, currency: "USD" }),
|
|
476
|
+
shippingDestination: { "@type": "DefinedRegion", addressCountry: "US" },
|
|
477
|
+
deliveryTime: {
|
|
478
|
+
"@type": "ShippingDeliveryTime",
|
|
479
|
+
handlingTime: { "@type": "QuantitativeValue", minValue: 0, maxValue: 1, unitCode: "DAY" },
|
|
480
|
+
transitTime: { "@type": "QuantitativeValue", minValue: 1, maxValue: 5, unitCode: "DAY" },
|
|
481
|
+
},
|
|
482
|
+
}),
|
|
483
|
+
hasMerchantReturnPolicy: buildMerchantReturnPolicy({
|
|
484
|
+
applicableCountry: "US",
|
|
485
|
+
returnPolicyCategory: "MerchantReturnFiniteReturnWindow",
|
|
486
|
+
merchantReturnDays: 30,
|
|
487
|
+
returnMethod: "ReturnByMail",
|
|
488
|
+
returnFees: "FreeReturn",
|
|
489
|
+
}),
|
|
490
|
+
}),
|
|
491
|
+
],
|
|
492
|
+
});
|
|
493
|
+
```
|
|
494
|
+
|
|
203
495
|
See Google's [structured data gallery](https://developers.google.com/search/docs/appearance/structured-data/search-gallery) for which schema types are eligible for rich results.
|
|
204
496
|
|
|
497
|
+
> Google no longer lists FAQ or Course info rich results. `buildFAQPage`, and `offers` / `hasCourseInstance` on a Course, still produce valid Schema.org markup but won't earn a rich result. Google's Course list only needs `name`, `description`, and `provider` on each `Course`, plus an `ItemList` for the carousel.
|
|
498
|
+
|
|
205
499
|
---
|
|
206
500
|
|
|
207
501
|
## With Sanity
|
|
@@ -262,6 +556,10 @@ Pick the `build*` helper that matches your document type (`buildProduct`, `build
|
|
|
262
556
|
|
|
263
557
|
- `buildSchemaMarkup` — builds the page-level JSON-LD graph
|
|
264
558
|
- `build*` schema builders — typed builders for individual Schema.org nodes
|
|
559
|
+
- `identityRef` — reference to the site identity, resolved by `buildSchemaMarkup`
|
|
560
|
+
- `toIsoDuration`, `toIsoDate`, `DurationParts` — ISO 8601 duration and date helpers
|
|
561
|
+
- `toAbsoluteUrl` — resolves slugs and relative paths against `siteUrl`
|
|
562
|
+
- `buildImageObject`, `ImageInput` — `ImageObject` helper
|
|
265
563
|
- `BuilderInput` — input helper type for builder functions
|
|
266
564
|
- `BuildSchemaMarkupInput` — input type for `buildSchemaMarkup`
|
|
267
565
|
- `Identity`, `PersonIdentity`, `OrganizationIdentity`, `LocalBusinessIdentity` — supported site identity roots
|
package/dist/build.d.ts
CHANGED
|
@@ -9,6 +9,8 @@ export type BuildSchemaMarkupInput = {
|
|
|
9
9
|
pageUrl: string;
|
|
10
10
|
pageTitle: string;
|
|
11
11
|
pageDescription?: string;
|
|
12
|
+
/** BCP 47 language tag for the page content, e.g. `"fr"` or `"fr-CA"`. */
|
|
13
|
+
inLanguage?: string;
|
|
12
14
|
breadcrumb?: WithContext<BreadcrumbList>;
|
|
13
15
|
mainEntity?: Thing | SchemaNode;
|
|
14
16
|
};
|
package/dist/builders/index.d.ts
CHANGED
|
@@ -1,25 +1,41 @@
|
|
|
1
1
|
export * from "./about-page";
|
|
2
|
+
export * from "./aggregate-offer";
|
|
2
3
|
export * from "./aggregate-rating";
|
|
3
4
|
export * from "./answer";
|
|
4
5
|
export * from "./article";
|
|
6
|
+
export * from "./brand";
|
|
5
7
|
export * from "./breadcrumb-list";
|
|
6
8
|
export * from "./comment";
|
|
7
9
|
export * from "./contact-page";
|
|
8
10
|
export * from "./course";
|
|
11
|
+
export * from "./course-instance";
|
|
9
12
|
export * from "./dataset";
|
|
10
13
|
export * from "./discussion-forum-posting";
|
|
11
14
|
export * from "./event";
|
|
12
15
|
export * from "./faq";
|
|
16
|
+
export * from "./geo-coordinates";
|
|
17
|
+
export * from "./how-to-step";
|
|
13
18
|
export * from "./item-list";
|
|
14
19
|
export * from "./job-posting";
|
|
15
20
|
export * from "./local-business";
|
|
21
|
+
export * from "./merchant-return-policy";
|
|
22
|
+
export * from "./monetary-amount";
|
|
16
23
|
export * from "./movie";
|
|
24
|
+
export * from "./nutrition-information";
|
|
25
|
+
export * from "./offer";
|
|
26
|
+
export * from "./offer-shipping-details";
|
|
27
|
+
export * from "./organization";
|
|
28
|
+
export * from "./person";
|
|
29
|
+
export * from "./place";
|
|
30
|
+
export * from "./postal-address";
|
|
17
31
|
export * from "./product";
|
|
18
32
|
export * from "./profile-page";
|
|
19
33
|
export * from "./qa-page";
|
|
34
|
+
export * from "./quantitative-value";
|
|
20
35
|
export * from "./question";
|
|
21
36
|
export * from "./recipe";
|
|
22
37
|
export * from "./review";
|
|
38
|
+
export * from "./schedule";
|
|
23
39
|
export * from "./software-application";
|
|
24
40
|
export * from "./vacation-rental";
|
|
25
41
|
export * from "./video-object";
|
package/dist/index.d.ts
CHANGED
|
@@ -2,3 +2,7 @@ export type { BuildSchemaMarkupInput, Identity, LocalBusinessIdentity, Organizat
|
|
|
2
2
|
export { buildSchemaMarkup } from "./build";
|
|
3
3
|
export * from "./builders";
|
|
4
4
|
export type { BuilderInput } from "./define-builder";
|
|
5
|
+
export { identityRef } from "./identity-ref";
|
|
6
|
+
export { type DurationParts, toIsoDate, toIsoDuration } from "./utils/datetime";
|
|
7
|
+
export { buildImageObject, type ImageInput } from "./utils/image";
|
|
8
|
+
export { toAbsoluteUrl } from "./utils/url";
|
package/dist/index.js
CHANGED
|
@@ -14,6 +14,10 @@ function createSchemaId({
|
|
|
14
14
|
return `${baseUrl}#${kind}-${normalizeId(name)}`;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
+
// src/identity-ref.ts
|
|
18
|
+
var IDENTITY_REF_ID = "@identity";
|
|
19
|
+
var identityRef = { "@id": IDENTITY_REF_ID };
|
|
20
|
+
|
|
17
21
|
// src/utils/image.ts
|
|
18
22
|
function buildImageObject(input) {
|
|
19
23
|
if (!input) {
|
|
@@ -22,14 +26,16 @@ function buildImageObject(input) {
|
|
|
22
26
|
if (typeof input === "string") {
|
|
23
27
|
return {
|
|
24
28
|
"@type": "ImageObject",
|
|
25
|
-
url: input
|
|
29
|
+
url: input,
|
|
30
|
+
contentUrl: input
|
|
26
31
|
};
|
|
27
32
|
}
|
|
33
|
+
const { url, ...rest } = input;
|
|
28
34
|
return {
|
|
29
35
|
"@type": "ImageObject",
|
|
30
|
-
url
|
|
31
|
-
|
|
32
|
-
|
|
36
|
+
url,
|
|
37
|
+
contentUrl: url,
|
|
38
|
+
...rest
|
|
33
39
|
};
|
|
34
40
|
}
|
|
35
41
|
|
|
@@ -72,6 +78,18 @@ var hasType = (value) => {
|
|
|
72
78
|
var hasId = (value) => {
|
|
73
79
|
return typeof value["@id"] === "string" && value["@id"].length > 0;
|
|
74
80
|
};
|
|
81
|
+
var isEmptyValue = (value) => {
|
|
82
|
+
if (value === null || value === undefined)
|
|
83
|
+
return true;
|
|
84
|
+
if (typeof value === "string")
|
|
85
|
+
return value.trim().length === 0;
|
|
86
|
+
if (Array.isArray(value))
|
|
87
|
+
return value.length === 0;
|
|
88
|
+
if (hasObjectShape(value)) {
|
|
89
|
+
return Object.keys(value).every((key) => key === "@type" || key === "@context");
|
|
90
|
+
}
|
|
91
|
+
return false;
|
|
92
|
+
};
|
|
75
93
|
var ensureContext = (node) => {
|
|
76
94
|
if (hasType(node) && node["@context"] === undefined) {
|
|
77
95
|
return {
|
|
@@ -81,7 +99,7 @@ var ensureContext = (node) => {
|
|
|
81
99
|
}
|
|
82
100
|
return node;
|
|
83
101
|
};
|
|
84
|
-
var assembleNodes = (nodes) => {
|
|
102
|
+
var assembleNodes = (nodes, identityId) => {
|
|
85
103
|
const collected = [];
|
|
86
104
|
const seenIds = new Set;
|
|
87
105
|
const processingIds = new Set;
|
|
@@ -100,17 +118,25 @@ var assembleNodes = (nodes) => {
|
|
|
100
118
|
};
|
|
101
119
|
const processValue = (value, isRoot = false) => {
|
|
102
120
|
if (Array.isArray(value)) {
|
|
103
|
-
return value.map((item) => processValue(item, false));
|
|
121
|
+
return value.map((item) => processValue(item, false)).filter((item) => !isEmptyValue(item));
|
|
104
122
|
}
|
|
105
123
|
if (!hasObjectShape(value))
|
|
106
124
|
return value;
|
|
125
|
+
if (value["@id"] === IDENTITY_REF_ID) {
|
|
126
|
+
return { "@id": identityId };
|
|
127
|
+
}
|
|
107
128
|
if (!isRoot && hasType(value) && hasId(value)) {
|
|
108
129
|
registerEntity(value);
|
|
109
130
|
return { "@id": value["@id"] };
|
|
110
131
|
}
|
|
111
132
|
const output = {};
|
|
112
133
|
for (const [key, child] of Object.entries(value)) {
|
|
113
|
-
|
|
134
|
+
if (!isRoot && key === "@context")
|
|
135
|
+
continue;
|
|
136
|
+
const processed = processValue(child, false);
|
|
137
|
+
if (isEmptyValue(processed))
|
|
138
|
+
continue;
|
|
139
|
+
output[key] = processed;
|
|
114
140
|
}
|
|
115
141
|
return output;
|
|
116
142
|
};
|
|
@@ -144,13 +170,13 @@ var assembleNodes = (nodes) => {
|
|
|
144
170
|
});
|
|
145
171
|
return ranked.map(({ node }) => JSON.stringify(node));
|
|
146
172
|
};
|
|
147
|
-
var buildIdentityNodes = (identity, siteUrl) => {
|
|
173
|
+
var buildIdentityNodes = (identity, siteUrl, idBase) => {
|
|
148
174
|
switch (identity.type) {
|
|
149
175
|
case "person": {
|
|
150
176
|
const id = createSchemaId({
|
|
151
177
|
kind: "person",
|
|
152
178
|
name: identity.name,
|
|
153
|
-
baseUrl:
|
|
179
|
+
baseUrl: idBase
|
|
154
180
|
});
|
|
155
181
|
return {
|
|
156
182
|
node: {
|
|
@@ -169,7 +195,7 @@ var buildIdentityNodes = (identity, siteUrl) => {
|
|
|
169
195
|
const id = createSchemaId({
|
|
170
196
|
kind: "organization",
|
|
171
197
|
name: identity.name,
|
|
172
|
-
baseUrl:
|
|
198
|
+
baseUrl: idBase
|
|
173
199
|
});
|
|
174
200
|
return {
|
|
175
201
|
node: {
|
|
@@ -188,7 +214,7 @@ var buildIdentityNodes = (identity, siteUrl) => {
|
|
|
188
214
|
const id = createSchemaId({
|
|
189
215
|
kind: "local-business",
|
|
190
216
|
name: identity.name,
|
|
191
|
-
baseUrl:
|
|
217
|
+
baseUrl: idBase
|
|
192
218
|
});
|
|
193
219
|
return {
|
|
194
220
|
node: {
|
|
@@ -210,15 +236,16 @@ var buildIdentityNodes = (identity, siteUrl) => {
|
|
|
210
236
|
}
|
|
211
237
|
};
|
|
212
238
|
var buildSchemaMarkup = (input) => {
|
|
213
|
-
const
|
|
214
|
-
const
|
|
239
|
+
const idBase = input.siteUrl.replace(/\/+$/, "");
|
|
240
|
+
const { node: identityNode, ref: identityNodeRef } = buildIdentityNodes(input.identity, input.siteUrl, idBase);
|
|
241
|
+
const websiteId = `${idBase}#website`;
|
|
215
242
|
const websiteNode = {
|
|
216
243
|
"@type": "WebSite",
|
|
217
244
|
"@id": websiteId,
|
|
218
245
|
name: input.siteName,
|
|
219
246
|
url: input.siteUrl,
|
|
220
247
|
description: input.siteDescription,
|
|
221
|
-
publisher:
|
|
248
|
+
publisher: identityNodeRef
|
|
222
249
|
};
|
|
223
250
|
const webpageNode = {
|
|
224
251
|
"@type": "WebPage",
|
|
@@ -226,11 +253,12 @@ var buildSchemaMarkup = (input) => {
|
|
|
226
253
|
url: input.pageUrl,
|
|
227
254
|
name: input.pageTitle,
|
|
228
255
|
description: input.pageDescription,
|
|
256
|
+
inLanguage: input.inLanguage,
|
|
229
257
|
isPartOf: { "@id": websiteId },
|
|
230
258
|
breadcrumb: input.breadcrumb,
|
|
231
259
|
mainEntity: input.mainEntity
|
|
232
260
|
};
|
|
233
|
-
return assembleNodes([identityNode, websiteNode, webpageNode]);
|
|
261
|
+
return assembleNodes([identityNode, websiteNode, webpageNode], identityNodeRef["@id"]);
|
|
234
262
|
};
|
|
235
263
|
// src/define-builder.ts
|
|
236
264
|
function defineBuilder(type) {
|
|
@@ -243,12 +271,22 @@ function defineBuilder(type) {
|
|
|
243
271
|
|
|
244
272
|
// src/builders/about-page.ts
|
|
245
273
|
var buildAboutPage = defineBuilder("AboutPage");
|
|
274
|
+
|
|
275
|
+
// src/builders/aggregate-offer.ts
|
|
276
|
+
var buildAggregateOffer = defineBuilder("AggregateOffer");
|
|
277
|
+
|
|
246
278
|
// src/builders/aggregate-rating.ts
|
|
247
279
|
var buildAggregateRating = defineBuilder("AggregateRating");
|
|
280
|
+
|
|
248
281
|
// src/builders/answer.ts
|
|
249
282
|
var buildAnswer = defineBuilder("Answer");
|
|
283
|
+
|
|
250
284
|
// src/builders/article.ts
|
|
251
285
|
var buildArticle = defineBuilder("Article");
|
|
286
|
+
|
|
287
|
+
// src/builders/brand.ts
|
|
288
|
+
var buildBrand = defineBuilder("Brand");
|
|
289
|
+
|
|
252
290
|
// src/builders/internal/slug-to-title.ts
|
|
253
291
|
function slugToTitle(segment) {
|
|
254
292
|
const decoded = decodeURIComponent(segment || "");
|
|
@@ -304,80 +342,232 @@ function buildBreadcrumbListSchema({
|
|
|
304
342
|
itemListElement: toListItems(finalItems)
|
|
305
343
|
};
|
|
306
344
|
}
|
|
345
|
+
|
|
307
346
|
// src/builders/comment.ts
|
|
308
347
|
var buildComment = defineBuilder("Comment");
|
|
348
|
+
|
|
309
349
|
// src/builders/contact-page.ts
|
|
310
350
|
var buildContactPage = defineBuilder("ContactPage");
|
|
351
|
+
|
|
311
352
|
// src/builders/course.ts
|
|
312
353
|
var buildCourse = defineBuilder("Course");
|
|
354
|
+
|
|
355
|
+
// src/builders/course-instance.ts
|
|
356
|
+
var buildCourseInstance = defineBuilder("CourseInstance");
|
|
357
|
+
|
|
313
358
|
// src/builders/dataset.ts
|
|
314
359
|
var buildDataset = defineBuilder("Dataset");
|
|
360
|
+
|
|
315
361
|
// src/builders/discussion-forum-posting.ts
|
|
316
362
|
var buildDiscussionForumPosting = defineBuilder("DiscussionForumPosting");
|
|
363
|
+
|
|
317
364
|
// src/builders/event.ts
|
|
318
365
|
var buildEvent = defineBuilder("Event");
|
|
366
|
+
|
|
319
367
|
// src/builders/faq.ts
|
|
320
368
|
var buildFAQPage = defineBuilder("FAQPage");
|
|
369
|
+
|
|
370
|
+
// src/builders/geo-coordinates.ts
|
|
371
|
+
var buildGeoCoordinates = defineBuilder("GeoCoordinates");
|
|
372
|
+
|
|
373
|
+
// src/builders/how-to-step.ts
|
|
374
|
+
var buildHowToStep = defineBuilder("HowToStep");
|
|
375
|
+
|
|
321
376
|
// src/builders/item-list.ts
|
|
322
377
|
var buildItemList = defineBuilder("ItemList");
|
|
378
|
+
|
|
323
379
|
// src/builders/job-posting.ts
|
|
324
380
|
var buildJobPosting = defineBuilder("JobPosting");
|
|
381
|
+
|
|
325
382
|
// src/builders/local-business.ts
|
|
326
383
|
var buildLocalBusiness = defineBuilder("LocalBusiness");
|
|
384
|
+
|
|
385
|
+
// src/builders/merchant-return-policy.ts
|
|
386
|
+
var buildMerchantReturnPolicy = defineBuilder("MerchantReturnPolicy");
|
|
387
|
+
|
|
388
|
+
// src/builders/monetary-amount.ts
|
|
389
|
+
var buildMonetaryAmount = defineBuilder("MonetaryAmount");
|
|
390
|
+
|
|
327
391
|
// src/builders/movie.ts
|
|
328
392
|
var buildMovie = defineBuilder("Movie");
|
|
393
|
+
|
|
394
|
+
// src/builders/nutrition-information.ts
|
|
395
|
+
var buildNutritionInformation = defineBuilder("NutritionInformation");
|
|
396
|
+
|
|
397
|
+
// src/builders/offer.ts
|
|
398
|
+
var buildOffer = defineBuilder("Offer");
|
|
399
|
+
|
|
400
|
+
// src/builders/offer-shipping-details.ts
|
|
401
|
+
var buildOfferShippingDetails = defineBuilder("OfferShippingDetails");
|
|
402
|
+
|
|
403
|
+
// src/builders/organization.ts
|
|
404
|
+
var buildOrganization = defineBuilder("Organization");
|
|
405
|
+
|
|
406
|
+
// src/builders/person.ts
|
|
407
|
+
var buildPerson = defineBuilder("Person");
|
|
408
|
+
|
|
409
|
+
// src/builders/place.ts
|
|
410
|
+
var buildPlace = defineBuilder("Place");
|
|
411
|
+
|
|
412
|
+
// src/builders/postal-address.ts
|
|
413
|
+
var buildPostalAddress = defineBuilder("PostalAddress");
|
|
414
|
+
|
|
329
415
|
// src/builders/product.ts
|
|
330
416
|
var buildProduct = defineBuilder("Product");
|
|
417
|
+
|
|
331
418
|
// src/builders/profile-page.ts
|
|
332
419
|
var buildProfilePage = defineBuilder("ProfilePage");
|
|
420
|
+
|
|
333
421
|
// src/builders/qa-page.ts
|
|
334
422
|
var buildQAPage = defineBuilder("QAPage");
|
|
423
|
+
|
|
424
|
+
// src/builders/quantitative-value.ts
|
|
425
|
+
var buildQuantitativeValue = defineBuilder("QuantitativeValue");
|
|
426
|
+
|
|
335
427
|
// src/builders/question.ts
|
|
336
428
|
var buildQuestion = defineBuilder("Question");
|
|
429
|
+
|
|
337
430
|
// src/builders/recipe.ts
|
|
338
431
|
var buildRecipe = defineBuilder("Recipe");
|
|
432
|
+
|
|
339
433
|
// src/builders/review.ts
|
|
340
434
|
var buildReview = defineBuilder("Review");
|
|
435
|
+
|
|
436
|
+
// src/builders/schedule.ts
|
|
437
|
+
var buildSchedule = defineBuilder("Schedule");
|
|
438
|
+
|
|
341
439
|
// src/builders/software-application.ts
|
|
342
440
|
var buildSoftwareApplication = defineBuilder("SoftwareApplication");
|
|
441
|
+
|
|
343
442
|
// src/builders/vacation-rental.ts
|
|
344
443
|
var buildVacationRental = defineBuilder("VacationRental");
|
|
444
|
+
|
|
345
445
|
// src/builders/video-object.ts
|
|
346
446
|
var buildVideoObject = defineBuilder("VideoObject");
|
|
447
|
+
|
|
347
448
|
// src/builders/webpage.ts
|
|
348
449
|
var buildWebPage = defineBuilder("WebPage");
|
|
450
|
+
|
|
349
451
|
// src/builders/website.ts
|
|
350
452
|
var buildWebSite = defineBuilder("WebSite");
|
|
453
|
+
// src/utils/datetime.ts
|
|
454
|
+
var DATE_DESIGNATORS = [
|
|
455
|
+
["years", "Y"],
|
|
456
|
+
["months", "M"],
|
|
457
|
+
["weeks", "W"],
|
|
458
|
+
["days", "D"]
|
|
459
|
+
];
|
|
460
|
+
var TIME_DESIGNATORS = [
|
|
461
|
+
["hours", "H"],
|
|
462
|
+
["minutes", "M"],
|
|
463
|
+
["seconds", "S"]
|
|
464
|
+
];
|
|
465
|
+
var formatParts = (parts, designators) => designators.map(([key, designator]) => {
|
|
466
|
+
const value = parts[key];
|
|
467
|
+
return value ? `${value}${designator}` : "";
|
|
468
|
+
}).join("");
|
|
469
|
+
function toIsoDuration(parts) {
|
|
470
|
+
for (const [key, value] of Object.entries(parts)) {
|
|
471
|
+
if (value === undefined)
|
|
472
|
+
continue;
|
|
473
|
+
if (!Number.isFinite(value) || value < 0) {
|
|
474
|
+
throw new Error(`toIsoDuration: "${key}" must be a non-negative finite number, got ${value}`);
|
|
475
|
+
}
|
|
476
|
+
if (key !== "seconds" && !Number.isInteger(value)) {
|
|
477
|
+
throw new Error(`toIsoDuration: only "seconds" may be fractional, got ${key}: ${value}`);
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
if (parts.weeks && Object.entries(parts).some(([key, value]) => key !== "weeks" && value)) {
|
|
481
|
+
throw new Error('toIsoDuration: "weeks" cannot be combined with other parts');
|
|
482
|
+
}
|
|
483
|
+
const datePart = formatParts(parts, DATE_DESIGNATORS);
|
|
484
|
+
const timePart = formatParts(parts, TIME_DESIGNATORS);
|
|
485
|
+
if (!datePart && !timePart)
|
|
486
|
+
return "PT0S";
|
|
487
|
+
return `P${datePart}${timePart ? `T${timePart}` : ""}`;
|
|
488
|
+
}
|
|
489
|
+
var ISO_DATE_PATTERN = /^(\d{4}-\d{2}-\d{2})(?:(T\d{2}:\d{2}(?::\d{2}(?:\.\d+)?)?)(Z|[+-]\d{2}:?\d{2})?)?$/;
|
|
490
|
+
function toIsoDate(value) {
|
|
491
|
+
if (value instanceof Date) {
|
|
492
|
+
if (Number.isNaN(value.getTime())) {
|
|
493
|
+
throw new Error("toIsoDate: received an invalid Date");
|
|
494
|
+
}
|
|
495
|
+
return value.toISOString();
|
|
496
|
+
}
|
|
497
|
+
const trimmed = value.trim();
|
|
498
|
+
const match = ISO_DATE_PATTERN.exec(trimmed);
|
|
499
|
+
if (!match || Number.isNaN(Date.parse(`${match[1]}T00:00:00Z`))) {
|
|
500
|
+
throw new Error(`toIsoDate: "${value}" is not an ISO 8601 date`);
|
|
501
|
+
}
|
|
502
|
+
const [, date, time = "", offset = ""] = match;
|
|
503
|
+
const normalizedOffset = offset.length === 5 ? `${offset.slice(0, 3)}:${offset.slice(3)}` : offset;
|
|
504
|
+
return `${date}${time}${normalizedOffset}`;
|
|
505
|
+
}
|
|
506
|
+
// src/utils/url.ts
|
|
507
|
+
var SCHEME_PATTERN = /^[a-z][a-z\d+.-]*:/i;
|
|
508
|
+
function toAbsoluteUrl(value, siteUrl) {
|
|
509
|
+
const trimmed = value?.trim();
|
|
510
|
+
if (!trimmed)
|
|
511
|
+
return;
|
|
512
|
+
if (SCHEME_PATTERN.test(trimmed))
|
|
513
|
+
return trimmed;
|
|
514
|
+
if (trimmed.startsWith("//")) {
|
|
515
|
+
const protocol = SCHEME_PATTERN.exec(siteUrl)?.[0] ?? "https:";
|
|
516
|
+
return `${protocol}${trimmed}`;
|
|
517
|
+
}
|
|
518
|
+
return `${siteUrl.replace(/\/+$/, "")}/${trimmed.replace(/^\/+/, "")}`;
|
|
519
|
+
}
|
|
351
520
|
export {
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
buildVideoObject,
|
|
355
|
-
buildVacationRental,
|
|
356
|
-
buildSoftwareApplication,
|
|
357
|
-
buildSchemaMarkup,
|
|
358
|
-
buildReview,
|
|
359
|
-
buildRecipe,
|
|
360
|
-
buildQuestion,
|
|
361
|
-
buildQAPage,
|
|
362
|
-
buildProfilePage,
|
|
363
|
-
buildProduct,
|
|
364
|
-
buildMovie,
|
|
365
|
-
buildLocalBusiness,
|
|
366
|
-
buildJobPosting,
|
|
367
|
-
buildItemList,
|
|
368
|
-
buildFAQPage,
|
|
369
|
-
buildEvent,
|
|
370
|
-
buildDiscussionForumPosting,
|
|
371
|
-
buildDataset,
|
|
372
|
-
buildCourse,
|
|
373
|
-
buildContactPage,
|
|
374
|
-
buildComment,
|
|
375
|
-
buildBreadcrumbListSchema,
|
|
376
|
-
buildArticle,
|
|
377
|
-
buildAnswer,
|
|
521
|
+
buildAboutPage,
|
|
522
|
+
buildAggregateOffer,
|
|
378
523
|
buildAggregateRating,
|
|
379
|
-
|
|
524
|
+
buildAnswer,
|
|
525
|
+
buildArticle,
|
|
526
|
+
buildBrand,
|
|
527
|
+
buildBreadcrumbListSchema,
|
|
528
|
+
buildComment,
|
|
529
|
+
buildContactPage,
|
|
530
|
+
buildCourse,
|
|
531
|
+
buildCourseInstance,
|
|
532
|
+
buildDataset,
|
|
533
|
+
buildDiscussionForumPosting,
|
|
534
|
+
buildEvent,
|
|
535
|
+
buildFAQPage,
|
|
536
|
+
buildGeoCoordinates,
|
|
537
|
+
buildHowToStep,
|
|
538
|
+
buildImageObject,
|
|
539
|
+
buildItemList,
|
|
540
|
+
buildJobPosting,
|
|
541
|
+
buildLocalBusiness,
|
|
542
|
+
buildMerchantReturnPolicy,
|
|
543
|
+
buildMonetaryAmount,
|
|
544
|
+
buildMovie,
|
|
545
|
+
buildNutritionInformation,
|
|
546
|
+
buildOffer,
|
|
547
|
+
buildOfferShippingDetails,
|
|
548
|
+
buildOrganization,
|
|
549
|
+
buildPerson,
|
|
550
|
+
buildPlace,
|
|
551
|
+
buildPostalAddress,
|
|
552
|
+
buildProduct,
|
|
553
|
+
buildProfilePage,
|
|
554
|
+
buildQAPage,
|
|
555
|
+
buildQuantitativeValue,
|
|
556
|
+
buildQuestion,
|
|
557
|
+
buildRecipe,
|
|
558
|
+
buildReview,
|
|
559
|
+
buildSchedule,
|
|
560
|
+
buildSchemaMarkup,
|
|
561
|
+
buildSoftwareApplication,
|
|
562
|
+
buildVacationRental,
|
|
563
|
+
buildVideoObject,
|
|
564
|
+
buildWebPage,
|
|
565
|
+
buildWebSite,
|
|
566
|
+
identityRef,
|
|
567
|
+
toAbsoluteUrl,
|
|
568
|
+
toIsoDate,
|
|
569
|
+
toIsoDuration
|
|
380
570
|
};
|
|
381
571
|
|
|
382
|
-
//# debugId=
|
|
572
|
+
//# debugId=91D355419575781164756E2164756E21
|
|
383
573
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1,41 +1,60 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../src/builders/fragments.ts", "../src/utils/image.ts", "../src/build.ts", "../src/define-builder.ts", "../src/builders/about-page.ts", "../src/builders/aggregate-rating.ts", "../src/builders/answer.ts", "../src/builders/article.ts", "../src/builders/internal/slug-to-title.ts", "../src/builders/breadcrumb-list.ts", "../src/builders/comment.ts", "../src/builders/contact-page.ts", "../src/builders/course.ts", "../src/builders/dataset.ts", "../src/builders/discussion-forum-posting.ts", "../src/builders/event.ts", "../src/builders/faq.ts", "../src/builders/item-list.ts", "../src/builders/job-posting.ts", "../src/builders/local-business.ts", "../src/builders/movie.ts", "../src/builders/product.ts", "../src/builders/profile-page.ts", "../src/builders/qa-page.ts", "../src/builders/question.ts", "../src/builders/recipe.ts", "../src/builders/review.ts", "../src/builders/software-application.ts", "../src/builders/vacation-rental.ts", "../src/builders/video-object.ts", "../src/builders/webpage.ts", "../src/builders/website.ts"],
|
|
3
|
+
"sources": ["../src/builders/fragments.ts", "../src/identity-ref.ts", "../src/utils/image.ts", "../src/build.ts", "../src/define-builder.ts", "../src/builders/about-page.ts", "../src/builders/aggregate-offer.ts", "../src/builders/aggregate-rating.ts", "../src/builders/answer.ts", "../src/builders/article.ts", "../src/builders/brand.ts", "../src/builders/internal/slug-to-title.ts", "../src/builders/breadcrumb-list.ts", "../src/builders/comment.ts", "../src/builders/contact-page.ts", "../src/builders/course.ts", "../src/builders/course-instance.ts", "../src/builders/dataset.ts", "../src/builders/discussion-forum-posting.ts", "../src/builders/event.ts", "../src/builders/faq.ts", "../src/builders/geo-coordinates.ts", "../src/builders/how-to-step.ts", "../src/builders/item-list.ts", "../src/builders/job-posting.ts", "../src/builders/local-business.ts", "../src/builders/merchant-return-policy.ts", "../src/builders/monetary-amount.ts", "../src/builders/movie.ts", "../src/builders/nutrition-information.ts", "../src/builders/offer.ts", "../src/builders/offer-shipping-details.ts", "../src/builders/organization.ts", "../src/builders/person.ts", "../src/builders/place.ts", "../src/builders/postal-address.ts", "../src/builders/product.ts", "../src/builders/profile-page.ts", "../src/builders/qa-page.ts", "../src/builders/quantitative-value.ts", "../src/builders/question.ts", "../src/builders/recipe.ts", "../src/builders/review.ts", "../src/builders/schedule.ts", "../src/builders/software-application.ts", "../src/builders/vacation-rental.ts", "../src/builders/video-object.ts", "../src/builders/webpage.ts", "../src/builders/website.ts", "../src/utils/datetime.ts", "../src/utils/url.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
5
|
"type IdReference = { \"@id\": string };\n\ntype SchemaIdOptions = {\n\tkind: string;\n\tname: string;\n\tbaseUrl?: string;\n\texplicitId?: string;\n};\n\nexport function normalizeId(name: string): string {\n\treturn name\n\t\t.toLowerCase()\n\t\t.replace(/[^a-z0-9]+/g, \"-\")\n\t\t.replace(/^-|-$/g, \"\");\n}\n\nexport function createSchemaId({\n\tkind,\n\tname,\n\tbaseUrl = \"\",\n\texplicitId,\n}: SchemaIdOptions): string {\n\tif (explicitId) {\n\t\treturn explicitId.startsWith(\"http\")\n\t\t\t? explicitId\n\t\t\t: `${baseUrl}${explicitId}`;\n\t}\n\n\treturn `${baseUrl}#${kind}-${normalizeId(name)}`;\n}\n\nexport function asIdReference(id: string): IdReference {\n\treturn { \"@id\": id };\n}\n",
|
|
6
|
-
"
|
|
7
|
-
"import type {\n\
|
|
6
|
+
"export const IDENTITY_REF_ID = \"@identity\";\n\nexport const identityRef = { \"@id\": IDENTITY_REF_ID } as const;\n",
|
|
7
|
+
"import type {\n\tIdReference,\n\tImageObjectLeaf,\n\tOrganization,\n\tPerson,\n} from \"schema-dts\";\n\ntype ImageObjectInput = {\n\turl: string;\n\twidth?: number;\n\theight?: number;\n\tcaption?: string;\n\tcreditText?: string;\n\tcopyrightNotice?: string;\n\tlicense?: string;\n\tacquireLicensePage?: string;\n\tcreator?: Person | Organization | IdReference;\n};\n\nexport type ImageInput = string | ImageObjectInput;\n\nexport function buildImageObject(\n\tinput?: ImageInput,\n): ImageObjectLeaf | undefined {\n\tif (!input) {\n\t\treturn undefined;\n\t}\n\n\tif (typeof input === \"string\") {\n\t\treturn {\n\t\t\t\"@type\": \"ImageObject\",\n\t\t\turl: input,\n\t\t\tcontentUrl: input,\n\t\t};\n\t}\n\n\tconst { url, ...rest } = input;\n\n\t// schema-dts types width/height as Distance strings, but numeric pixel values are valid JSON-LD.\n\treturn {\n\t\t\"@type\": \"ImageObject\",\n\t\turl,\n\t\tcontentUrl: url,\n\t\t...rest,\n\t} as ImageObjectLeaf;\n}\n",
|
|
8
|
+
"import type {\n\tBreadcrumbList,\n\tOpeningHoursSpecification,\n\tPostalAddress,\n\tThing,\n\tWithContext,\n} from \"schema-dts\";\nimport { createSchemaId } from \"./builders/fragments\";\nimport { IDENTITY_REF_ID } from \"./identity-ref\";\nimport { buildImageObject, type ImageInput } from \"./utils/image\";\n\ntype SchemaNode = Record<string, unknown>;\n\nexport type BuildSchemaMarkupInput = {\n\tidentity: Identity;\n\tsiteUrl: string;\n\tsiteName: string;\n\tsiteDescription?: string;\n\tpageUrl: string;\n\tpageTitle: string;\n\tpageDescription?: string;\n\t/** BCP 47 language tag for the page content, e.g. `\"fr\"` or `\"fr-CA\"`. */\n\tinLanguage?: string;\n\tbreadcrumb?: WithContext<BreadcrumbList>;\n\tmainEntity?: Thing | SchemaNode;\n};\n\nexport type PersonIdentity = {\n\ttype: \"person\";\n\tname: string;\n\tdescription?: string;\n\timage?: ImageInput;\n\tsameAs?: string[];\n};\n\nexport type OrganizationIdentity = {\n\ttype: \"organization\";\n\tname: string;\n\tdescription?: string;\n\tlogo?: ImageInput;\n\tsameAs?: string[];\n};\n\nexport type LocalBusinessIdentity = {\n\ttype: \"localBusiness\";\n\tname: string;\n\tdescription?: string;\n\tlogo?: ImageInput;\n\tphone?: string;\n\temail?: string;\n\taddress?: PostalAddress;\n\topeningHours?: OpeningHoursSpecification[];\n\tsameAs?: string[];\n};\n\nexport type Identity =\n\t| PersonIdentity\n\t| OrganizationIdentity\n\t| LocalBusinessIdentity;\n\nconst TYPE_PRIORITY = [\n\t\"Organization\",\n\t\"Person\",\n\t\"LocalBusiness\",\n\t\"WebSite\",\n\t\"WebPage\",\n\t\"ProfilePage\",\n\t\"QAPage\",\n\t\"BreadcrumbList\",\n\t\"Article\",\n\t\"Course\",\n\t\"Dataset\",\n\t\"DiscussionForumPosting\",\n\t\"JobPosting\",\n\t\"Movie\",\n\t\"Product\",\n\t\"Recipe\",\n\t\"SoftwareApplication\",\n\t\"VacationRental\",\n\t\"VideoObject\",\n\t\"Event\",\n\t\"ItemList\",\n\t\"Question\",\n\t\"Answer\",\n\t\"Review\",\n\t\"Comment\",\n\t\"AggregateRating\",\n] as const;\n\nconst typeWeight = new Map<string, number>(\n\tTYPE_PRIORITY.map((type, index) => [type, index]),\n);\n\nconst hasObjectShape = (value: unknown): value is SchemaNode => {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n};\n\nconst hasType = (\n\tvalue: SchemaNode,\n): value is SchemaNode & { \"@type\": string } => {\n\treturn typeof value[\"@type\"] === \"string\";\n};\n\nconst hasId = (value: SchemaNode): value is SchemaNode & { \"@id\": string } => {\n\treturn typeof value[\"@id\"] === \"string\" && value[\"@id\"].length > 0;\n};\n\nconst isEmptyValue = (value: unknown): boolean => {\n\tif (value === null || value === undefined) return true;\n\tif (typeof value === \"string\") return value.trim().length === 0;\n\tif (Array.isArray(value)) return value.length === 0;\n\tif (hasObjectShape(value)) {\n\t\treturn Object.keys(value).every(\n\t\t\t(key) => key === \"@type\" || key === \"@context\",\n\t\t);\n\t}\n\treturn false;\n};\n\nconst ensureContext = (node: SchemaNode): SchemaNode => {\n\tif (hasType(node) && node[\"@context\"] === undefined) {\n\t\treturn {\n\t\t\t\"@context\": \"https://schema.org\",\n\t\t\t...node,\n\t\t};\n\t}\n\n\treturn node;\n};\n\ntype RankedNode = {\n\tnode: SchemaNode;\n\torder: number;\n};\n\nconst assembleNodes = (\n\tnodes: Array<Thing | SchemaNode | undefined>,\n\tidentityId: string,\n): string[] => {\n\tconst collected: RankedNode[] = [];\n\tconst seenIds = new Set<string>();\n\tconst processingIds = new Set<string>();\n\tlet orderCounter = 0;\n\n\tconst addNode = (node: SchemaNode) => {\n\t\tconst normalized = ensureContext(node);\n\t\tif (hasId(normalized)) {\n\t\t\tif (seenIds.has(normalized[\"@id\"])) return;\n\t\t\tseenIds.add(normalized[\"@id\"]);\n\t\t}\n\n\t\tcollected.push({\n\t\t\tnode: normalized,\n\t\t\torder: orderCounter++,\n\t\t});\n\t};\n\n\tconst processValue = (value: unknown, isRoot = false): unknown => {\n\t\tif (Array.isArray(value)) {\n\t\t\treturn value\n\t\t\t\t.map((item) => processValue(item, false))\n\t\t\t\t.filter((item) => !isEmptyValue(item));\n\t\t}\n\n\t\tif (!hasObjectShape(value)) return value;\n\n\t\tif (value[\"@id\"] === IDENTITY_REF_ID) {\n\t\t\treturn { \"@id\": identityId };\n\t\t}\n\n\t\tif (!isRoot && hasType(value) && hasId(value)) {\n\t\t\tregisterEntity(value);\n\t\t\treturn { \"@id\": value[\"@id\"] };\n\t\t}\n\n\t\tconst output: SchemaNode = {};\n\t\tfor (const [key, child] of Object.entries(value)) {\n\t\t\tif (!isRoot && key === \"@context\") continue;\n\t\t\tconst processed = processValue(child, false);\n\t\t\tif (isEmptyValue(processed)) continue;\n\t\t\toutput[key] = processed;\n\t\t}\n\t\treturn output;\n\t};\n\n\tconst registerEntity = (entity: SchemaNode & { \"@id\": string }) => {\n\t\tconst id = entity[\"@id\"];\n\t\tif (seenIds.has(id) || processingIds.has(id)) return;\n\n\t\tprocessingIds.add(id);\n\t\tconst processedEntity = processValue(entity, true);\n\t\tif (hasObjectShape(processedEntity)) {\n\t\t\taddNode(processedEntity);\n\t\t}\n\t\tprocessingIds.delete(id);\n\t};\n\n\tfor (const node of nodes) {\n\t\tif (!node) continue;\n\t\tconst processed = processValue(node, true);\n\t\tif (!hasObjectShape(processed)) continue;\n\t\taddNode(processed);\n\t}\n\n\tconst ranked = collected.sort((a, b) => {\n\t\tconst aType = hasType(a.node) ? a.node[\"@type\"] : \"\";\n\t\tconst bType = hasType(b.node) ? b.node[\"@type\"] : \"\";\n\t\tconst aWeight = typeWeight.get(aType) ?? Number.MAX_SAFE_INTEGER;\n\t\tconst bWeight = typeWeight.get(bType) ?? Number.MAX_SAFE_INTEGER;\n\n\t\tif (aWeight !== bWeight) return aWeight - bWeight;\n\t\treturn a.order - b.order;\n\t});\n\n\treturn ranked.map(({ node }) => JSON.stringify(node));\n};\n\nconst buildIdentityNodes = (\n\tidentity: Identity,\n\tsiteUrl: string,\n\tidBase: string,\n): { node: SchemaNode; ref: { \"@id\": string } } => {\n\tswitch (identity.type) {\n\t\tcase \"person\": {\n\t\t\tconst id = createSchemaId({\n\t\t\t\tkind: \"person\",\n\t\t\t\tname: identity.name,\n\t\t\t\tbaseUrl: idBase,\n\t\t\t});\n\t\t\treturn {\n\t\t\t\tnode: {\n\t\t\t\t\t\"@type\": \"Person\",\n\t\t\t\t\t\"@id\": id,\n\t\t\t\t\tname: identity.name,\n\t\t\t\t\tdescription: identity.description,\n\t\t\t\t\timage: buildImageObject(identity.image),\n\t\t\t\t\tsameAs: identity.sameAs,\n\t\t\t\t\turl: siteUrl,\n\t\t\t\t},\n\t\t\t\tref: { \"@id\": id },\n\t\t\t};\n\t\t}\n\t\tcase \"organization\": {\n\t\t\tconst id = createSchemaId({\n\t\t\t\tkind: \"organization\",\n\t\t\t\tname: identity.name,\n\t\t\t\tbaseUrl: idBase,\n\t\t\t});\n\t\t\treturn {\n\t\t\t\tnode: {\n\t\t\t\t\t\"@type\": \"Organization\",\n\t\t\t\t\t\"@id\": id,\n\t\t\t\t\tname: identity.name,\n\t\t\t\t\tdescription: identity.description,\n\t\t\t\t\tlogo: buildImageObject(identity.logo),\n\t\t\t\t\tsameAs: identity.sameAs,\n\t\t\t\t\turl: siteUrl,\n\t\t\t\t},\n\t\t\t\tref: { \"@id\": id },\n\t\t\t};\n\t\t}\n\t\tcase \"localBusiness\": {\n\t\t\tconst id = createSchemaId({\n\t\t\t\tkind: \"local-business\",\n\t\t\t\tname: identity.name,\n\t\t\t\tbaseUrl: idBase,\n\t\t\t});\n\t\t\treturn {\n\t\t\t\tnode: {\n\t\t\t\t\t\"@type\": \"LocalBusiness\",\n\t\t\t\t\t\"@id\": id,\n\t\t\t\t\tname: identity.name,\n\t\t\t\t\tdescription: identity.description,\n\t\t\t\t\tlogo: buildImageObject(identity.logo),\n\t\t\t\t\ttelephone: identity.phone,\n\t\t\t\t\temail: identity.email,\n\t\t\t\t\taddress: identity.address,\n\t\t\t\t\topeningHoursSpecification: identity.openingHours,\n\t\t\t\t\tsameAs: identity.sameAs,\n\t\t\t\t\turl: siteUrl,\n\t\t\t\t},\n\t\t\t\tref: { \"@id\": id },\n\t\t\t};\n\t\t}\n\t}\n};\n\nexport const buildSchemaMarkup = (input: BuildSchemaMarkupInput): string[] => {\n\tconst idBase = input.siteUrl.replace(/\\/+$/, \"\");\n\tconst { node: identityNode, ref: identityNodeRef } = buildIdentityNodes(\n\t\tinput.identity,\n\t\tinput.siteUrl,\n\t\tidBase,\n\t);\n\tconst websiteId = `${idBase}#website`;\n\n\tconst websiteNode: SchemaNode = {\n\t\t\"@type\": \"WebSite\",\n\t\t\"@id\": websiteId,\n\t\tname: input.siteName,\n\t\turl: input.siteUrl,\n\t\tdescription: input.siteDescription,\n\t\tpublisher: identityNodeRef,\n\t};\n\n\tconst webpageNode: SchemaNode = {\n\t\t\"@type\": \"WebPage\",\n\t\t\"@id\": `${input.pageUrl}#webpage`,\n\t\turl: input.pageUrl,\n\t\tname: input.pageTitle,\n\t\tdescription: input.pageDescription,\n\t\tinLanguage: input.inLanguage,\n\t\tisPartOf: { \"@id\": websiteId },\n\t\tbreadcrumb: input.breadcrumb,\n\t\tmainEntity: input.mainEntity,\n\t};\n\n\treturn assembleNodes(\n\t\t[identityNode, websiteNode, webpageNode],\n\t\tidentityNodeRef[\"@id\"],\n\t);\n};\n",
|
|
8
9
|
"import type { Thing, WithContext } from \"schema-dts\";\n\ntype Simplify<T> = { [K in keyof T]: T[K] } & {};\n\nexport type BuilderInput<T extends Thing> = Simplify<\n\tOmit<T, \"@type\" | \"@context\">\n>;\n\nexport type SchemaBuilder<T extends Thing> = (\n\tinput: BuilderInput<T>,\n) => WithContext<T>;\n\nexport function defineBuilder<T extends Thing>(type: string): SchemaBuilder<T> {\n\treturn (input: BuilderInput<T>) =>\n\t\t({\n\t\t\t\"@context\": \"https://schema.org\",\n\t\t\t\"@type\": type,\n\t\t\t...input,\n\t\t}) as unknown as WithContext<T>;\n}\n",
|
|
9
10
|
"import type { AboutPage } from \"schema-dts\";\nimport { defineBuilder, type SchemaBuilder } from \"../define-builder\";\n\nexport const buildAboutPage: SchemaBuilder<AboutPage> =\n\tdefineBuilder<AboutPage>(\"AboutPage\");\n",
|
|
11
|
+
"import type { AggregateOffer } from \"schema-dts\";\nimport { defineBuilder, type SchemaBuilder } from \"../define-builder\";\n\nexport const buildAggregateOffer: SchemaBuilder<AggregateOffer> =\n\tdefineBuilder<AggregateOffer>(\"AggregateOffer\");\n",
|
|
10
12
|
"import type { AggregateRating } from \"schema-dts\";\nimport { defineBuilder, type SchemaBuilder } from \"../define-builder\";\n\nexport const buildAggregateRating: SchemaBuilder<AggregateRating> =\n\tdefineBuilder<AggregateRating>(\"AggregateRating\");\n",
|
|
11
|
-
"import type { Answer } from \"schema-dts\";\nimport { defineBuilder, type SchemaBuilder } from \"../define-builder\";\n\nexport const buildAnswer: SchemaBuilder<Answer>
|
|
13
|
+
"import type { Answer } from \"schema-dts\";\nimport { defineBuilder, type SchemaBuilder } from \"../define-builder\";\n\nexport const buildAnswer: SchemaBuilder<Answer> =\n\tdefineBuilder<Answer>(\"Answer\");\n",
|
|
12
14
|
"import type { Article } from \"schema-dts\";\nimport { defineBuilder, type SchemaBuilder } from \"../define-builder\";\n\nexport const buildArticle: SchemaBuilder<Article> =\n\tdefineBuilder<Article>(\"Article\");\n",
|
|
13
|
-
"
|
|
14
|
-
"
|
|
15
|
+
"import type { Brand } from \"schema-dts\";\nimport { defineBuilder, type SchemaBuilder } from \"../define-builder\";\n\nexport const buildBrand: SchemaBuilder<Brand> = defineBuilder<Brand>(\"Brand\");\n",
|
|
16
|
+
"export function slugToTitle(segment: string): string {\n\tconst decoded = decodeURIComponent(segment || \"\");\n\tconst normalized = decoded.replace(/[-_]+/g, \" \").replace(/\\s+/g, \" \").trim();\n\n\tif (!normalized) return \"\";\n\n\treturn normalized.replace(/\\b\\w/g, (char) => char.toUpperCase());\n}\n",
|
|
17
|
+
"import type { BreadcrumbList, ListItem, WithContext } from \"schema-dts\";\nimport { slugToTitle } from \"./internal/slug-to-title\";\n\nexport type BreadcrumbItemInput = {\n\ttitle: string;\n\turl: string;\n};\n\nexport type BuildBreadcrumbListSchemaInput = {\n\tpagePath: string;\n\tpageTitle: string;\n\titems?: BreadcrumbItemInput[];\n};\n\nfunction normalizePath(path: string): string {\n\tif (!path) return \"/\";\n\tconst withoutHash = path.split(\"#\")[0];\n\tconst withoutQuery = withoutHash.split(\"?\")[0];\n\tconst prefixed = withoutQuery.startsWith(\"/\")\n\t\t? withoutQuery\n\t\t: `/${withoutQuery}`;\n\treturn prefixed.replace(/\\/+$/, \"\") || \"/\";\n}\n\nfunction createDerivedParentItems(pagePath: string): BreadcrumbItemInput[] {\n\tconst normalized = normalizePath(pagePath);\n\tconst segments = normalized.split(\"/\").filter(Boolean);\n\n\tif (segments.length <= 1) return [];\n\n\treturn segments.slice(0, -1).map((segment, index) => ({\n\t\ttitle: slugToTitle(segment),\n\t\turl: `/${segments.slice(0, index + 1).join(\"/\")}`,\n\t}));\n}\n\nfunction toListItems(items: BreadcrumbItemInput[]): ListItem[] {\n\treturn items.map((item, index) => ({\n\t\t\"@type\": \"ListItem\",\n\t\tposition: index + 1,\n\t\tname: item.title,\n\t\titem: item.url,\n\t}));\n}\n\nexport function buildBreadcrumbListSchema({\n\tpagePath,\n\tpageTitle,\n\titems,\n}: BuildBreadcrumbListSchemaInput): WithContext<BreadcrumbList> {\n\tconst parentItems = items?.length\n\t\t? items\n\t\t: createDerivedParentItems(pagePath);\n\tconst finalItems = [\n\t\t...parentItems,\n\t\t{\n\t\t\ttitle: pageTitle,\n\t\t\turl: normalizePath(pagePath),\n\t\t},\n\t];\n\n\treturn {\n\t\t\"@context\": \"https://schema.org\",\n\t\t\"@type\": \"BreadcrumbList\",\n\t\titemListElement: toListItems(finalItems),\n\t};\n}\n",
|
|
15
18
|
"import type { Comment } from \"schema-dts\";\nimport { defineBuilder, type SchemaBuilder } from \"../define-builder\";\n\nexport const buildComment: SchemaBuilder<Comment> =\n\tdefineBuilder<Comment>(\"Comment\");\n",
|
|
16
19
|
"import type { ContactPage } from \"schema-dts\";\nimport { defineBuilder, type SchemaBuilder } from \"../define-builder\";\n\nexport const buildContactPage: SchemaBuilder<ContactPage> =\n\tdefineBuilder<ContactPage>(\"ContactPage\");\n",
|
|
17
|
-
"import type { Course } from \"schema-dts\";\nimport { defineBuilder, type SchemaBuilder } from \"../define-builder\";\n\nexport const buildCourse: SchemaBuilder<Course>
|
|
20
|
+
"import type { Course } from \"schema-dts\";\nimport { defineBuilder, type SchemaBuilder } from \"../define-builder\";\n\nexport const buildCourse: SchemaBuilder<Course> =\n\tdefineBuilder<Course>(\"Course\");\n",
|
|
21
|
+
"import type { CourseInstance } from \"schema-dts\";\nimport { defineBuilder, type SchemaBuilder } from \"../define-builder\";\n\nexport const buildCourseInstance: SchemaBuilder<CourseInstance> =\n\tdefineBuilder<CourseInstance>(\"CourseInstance\");\n",
|
|
18
22
|
"import type { Dataset } from \"schema-dts\";\nimport { defineBuilder, type SchemaBuilder } from \"../define-builder\";\n\nexport const buildDataset: SchemaBuilder<Dataset> =\n\tdefineBuilder<Dataset>(\"Dataset\");\n",
|
|
19
23
|
"import type { DiscussionForumPosting } from \"schema-dts\";\nimport { defineBuilder, type SchemaBuilder } from \"../define-builder\";\n\nexport const buildDiscussionForumPosting: SchemaBuilder<DiscussionForumPosting> =\n\tdefineBuilder<DiscussionForumPosting>(\"DiscussionForumPosting\");\n",
|
|
20
24
|
"import type { Event } from \"schema-dts\";\nimport { defineBuilder, type SchemaBuilder } from \"../define-builder\";\n\nexport const buildEvent: SchemaBuilder<Event> = defineBuilder<Event>(\"Event\");\n",
|
|
21
25
|
"import type { FAQPage } from \"schema-dts\";\nimport { defineBuilder, type SchemaBuilder } from \"../define-builder\";\n\nexport const buildFAQPage: SchemaBuilder<FAQPage> =\n\tdefineBuilder<FAQPage>(\"FAQPage\");\n",
|
|
26
|
+
"import type { GeoCoordinates } from \"schema-dts\";\nimport { defineBuilder, type SchemaBuilder } from \"../define-builder\";\n\nexport const buildGeoCoordinates: SchemaBuilder<GeoCoordinates> =\n\tdefineBuilder<GeoCoordinates>(\"GeoCoordinates\");\n",
|
|
27
|
+
"import type { HowToStep } from \"schema-dts\";\nimport { defineBuilder, type SchemaBuilder } from \"../define-builder\";\n\nexport const buildHowToStep: SchemaBuilder<HowToStep> =\n\tdefineBuilder<HowToStep>(\"HowToStep\");\n",
|
|
22
28
|
"import type { ItemList } from \"schema-dts\";\nimport { defineBuilder, type SchemaBuilder } from \"../define-builder\";\n\nexport const buildItemList: SchemaBuilder<ItemList> =\n\tdefineBuilder<ItemList>(\"ItemList\");\n",
|
|
23
29
|
"import type { JobPosting } from \"schema-dts\";\nimport { defineBuilder, type SchemaBuilder } from \"../define-builder\";\n\nexport const buildJobPosting: SchemaBuilder<JobPosting> =\n\tdefineBuilder<JobPosting>(\"JobPosting\");\n",
|
|
24
30
|
"import type { LocalBusiness } from \"schema-dts\";\nimport { defineBuilder, type SchemaBuilder } from \"../define-builder\";\n\nexport const buildLocalBusiness: SchemaBuilder<LocalBusiness> =\n\tdefineBuilder<LocalBusiness>(\"LocalBusiness\");\n",
|
|
31
|
+
"import type { MerchantReturnPolicy } from \"schema-dts\";\nimport { defineBuilder, type SchemaBuilder } from \"../define-builder\";\n\nexport const buildMerchantReturnPolicy: SchemaBuilder<MerchantReturnPolicy> =\n\tdefineBuilder<MerchantReturnPolicy>(\"MerchantReturnPolicy\");\n",
|
|
32
|
+
"import type { MonetaryAmount } from \"schema-dts\";\nimport { defineBuilder, type SchemaBuilder } from \"../define-builder\";\n\nexport const buildMonetaryAmount: SchemaBuilder<MonetaryAmount> =\n\tdefineBuilder<MonetaryAmount>(\"MonetaryAmount\");\n",
|
|
25
33
|
"import type { Movie } from \"schema-dts\";\nimport { defineBuilder, type SchemaBuilder } from \"../define-builder\";\n\nexport const buildMovie: SchemaBuilder<Movie> = defineBuilder<Movie>(\"Movie\");\n",
|
|
34
|
+
"import type { NutritionInformation } from \"schema-dts\";\nimport { defineBuilder, type SchemaBuilder } from \"../define-builder\";\n\nexport const buildNutritionInformation: SchemaBuilder<NutritionInformation> =\n\tdefineBuilder<NutritionInformation>(\"NutritionInformation\");\n",
|
|
35
|
+
"import type { Offer } from \"schema-dts\";\nimport { defineBuilder, type SchemaBuilder } from \"../define-builder\";\n\nexport const buildOffer: SchemaBuilder<Offer> = defineBuilder<Offer>(\"Offer\");\n",
|
|
36
|
+
"import type { OfferShippingDetails } from \"schema-dts\";\nimport { defineBuilder, type SchemaBuilder } from \"../define-builder\";\n\nexport const buildOfferShippingDetails: SchemaBuilder<OfferShippingDetails> =\n\tdefineBuilder<OfferShippingDetails>(\"OfferShippingDetails\");\n",
|
|
37
|
+
"import type { Organization } from \"schema-dts\";\nimport { defineBuilder, type SchemaBuilder } from \"../define-builder\";\n\nexport const buildOrganization: SchemaBuilder<Organization> =\n\tdefineBuilder<Organization>(\"Organization\");\n",
|
|
38
|
+
"import type { Person } from \"schema-dts\";\nimport { defineBuilder, type SchemaBuilder } from \"../define-builder\";\n\nexport const buildPerson: SchemaBuilder<Person> =\n\tdefineBuilder<Person>(\"Person\");\n",
|
|
39
|
+
"import type { Place } from \"schema-dts\";\nimport { defineBuilder, type SchemaBuilder } from \"../define-builder\";\n\nexport const buildPlace: SchemaBuilder<Place> = defineBuilder<Place>(\"Place\");\n",
|
|
40
|
+
"import type { PostalAddress } from \"schema-dts\";\nimport { defineBuilder, type SchemaBuilder } from \"../define-builder\";\n\nexport const buildPostalAddress: SchemaBuilder<PostalAddress> =\n\tdefineBuilder<PostalAddress>(\"PostalAddress\");\n",
|
|
26
41
|
"import type { Product } from \"schema-dts\";\nimport { defineBuilder, type SchemaBuilder } from \"../define-builder\";\n\nexport const buildProduct: SchemaBuilder<Product> =\n\tdefineBuilder<Product>(\"Product\");\n",
|
|
27
42
|
"import type { ProfilePage } from \"schema-dts\";\nimport { defineBuilder, type SchemaBuilder } from \"../define-builder\";\n\nexport const buildProfilePage: SchemaBuilder<ProfilePage> =\n\tdefineBuilder<ProfilePage>(\"ProfilePage\");\n",
|
|
28
|
-
"import type { QAPage } from \"schema-dts\";\nimport { defineBuilder, type SchemaBuilder } from \"../define-builder\";\n\nexport const buildQAPage: SchemaBuilder<QAPage>
|
|
43
|
+
"import type { QAPage } from \"schema-dts\";\nimport { defineBuilder, type SchemaBuilder } from \"../define-builder\";\n\nexport const buildQAPage: SchemaBuilder<QAPage> =\n\tdefineBuilder<QAPage>(\"QAPage\");\n",
|
|
44
|
+
"import type { QuantitativeValue } from \"schema-dts\";\nimport { defineBuilder, type SchemaBuilder } from \"../define-builder\";\n\nexport const buildQuantitativeValue: SchemaBuilder<QuantitativeValue> =\n\tdefineBuilder<QuantitativeValue>(\"QuantitativeValue\");\n",
|
|
29
45
|
"import type { Question } from \"schema-dts\";\nimport { defineBuilder, type SchemaBuilder } from \"../define-builder\";\n\nexport const buildQuestion: SchemaBuilder<Question> =\n\tdefineBuilder<Question>(\"Question\");\n",
|
|
30
|
-
"import type { Recipe } from \"schema-dts\";\nimport { defineBuilder, type SchemaBuilder } from \"../define-builder\";\n\nexport const buildRecipe: SchemaBuilder<Recipe>
|
|
31
|
-
"import type { Review } from \"schema-dts\";\nimport { defineBuilder, type SchemaBuilder } from \"../define-builder\";\n\nexport const buildReview: SchemaBuilder<Review>
|
|
46
|
+
"import type { Recipe } from \"schema-dts\";\nimport { defineBuilder, type SchemaBuilder } from \"../define-builder\";\n\nexport const buildRecipe: SchemaBuilder<Recipe> =\n\tdefineBuilder<Recipe>(\"Recipe\");\n",
|
|
47
|
+
"import type { Review } from \"schema-dts\";\nimport { defineBuilder, type SchemaBuilder } from \"../define-builder\";\n\nexport const buildReview: SchemaBuilder<Review> =\n\tdefineBuilder<Review>(\"Review\");\n",
|
|
48
|
+
"import type { Schedule } from \"schema-dts\";\nimport { defineBuilder, type SchemaBuilder } from \"../define-builder\";\n\nexport const buildSchedule: SchemaBuilder<Schedule> =\n\tdefineBuilder<Schedule>(\"Schedule\");\n",
|
|
32
49
|
"import type { SoftwareApplication } from \"schema-dts\";\nimport { defineBuilder, type SchemaBuilder } from \"../define-builder\";\n\nexport const buildSoftwareApplication: SchemaBuilder<SoftwareApplication> =\n\tdefineBuilder<SoftwareApplication>(\"SoftwareApplication\");\n",
|
|
33
50
|
"import type { VacationRental } from \"schema-dts\";\nimport { defineBuilder, type SchemaBuilder } from \"../define-builder\";\n\nexport const buildVacationRental: SchemaBuilder<VacationRental> =\n\tdefineBuilder<VacationRental>(\"VacationRental\");\n",
|
|
34
51
|
"import type { VideoObject } from \"schema-dts\";\nimport { defineBuilder, type SchemaBuilder } from \"../define-builder\";\n\nexport const buildVideoObject: SchemaBuilder<VideoObject> =\n\tdefineBuilder<VideoObject>(\"VideoObject\");\n",
|
|
35
52
|
"import type { WebPage } from \"schema-dts\";\nimport { defineBuilder, type SchemaBuilder } from \"../define-builder\";\n\nexport const buildWebPage: SchemaBuilder<WebPage> =\n\tdefineBuilder<WebPage>(\"WebPage\");\n",
|
|
36
|
-
"import type { WebSite } from \"schema-dts\";\nimport { defineBuilder, type SchemaBuilder } from \"../define-builder\";\n\nexport const buildWebSite: SchemaBuilder<WebSite> =\n\tdefineBuilder<WebSite>(\"WebSite\");\n"
|
|
53
|
+
"import type { WebSite } from \"schema-dts\";\nimport { defineBuilder, type SchemaBuilder } from \"../define-builder\";\n\nexport const buildWebSite: SchemaBuilder<WebSite> =\n\tdefineBuilder<WebSite>(\"WebSite\");\n",
|
|
54
|
+
"export type DurationParts = {\n\tyears?: number;\n\tmonths?: number;\n\tweeks?: number;\n\tdays?: number;\n\thours?: number;\n\tminutes?: number;\n\tseconds?: number;\n};\n\nconst DATE_DESIGNATORS = [\n\t[\"years\", \"Y\"],\n\t[\"months\", \"M\"],\n\t[\"weeks\", \"W\"],\n\t[\"days\", \"D\"],\n] as const;\n\nconst TIME_DESIGNATORS = [\n\t[\"hours\", \"H\"],\n\t[\"minutes\", \"M\"],\n\t[\"seconds\", \"S\"],\n] as const;\n\nconst formatParts = (\n\tparts: DurationParts,\n\tdesignators: ReadonlyArray<readonly [keyof DurationParts, string]>,\n): string =>\n\tdesignators\n\t\t.map(([key, designator]) => {\n\t\t\tconst value = parts[key];\n\t\t\treturn value ? `${value}${designator}` : \"\";\n\t\t})\n\t\t.join(\"\");\n\nexport function toIsoDuration(parts: DurationParts): string {\n\tfor (const [key, value] of Object.entries(parts)) {\n\t\tif (value === undefined) continue;\n\t\tif (!Number.isFinite(value) || value < 0) {\n\t\t\tthrow new Error(\n\t\t\t\t`toIsoDuration: \"${key}\" must be a non-negative finite number, got ${value}`,\n\t\t\t);\n\t\t}\n\t\tif (key !== \"seconds\" && !Number.isInteger(value)) {\n\t\t\tthrow new Error(\n\t\t\t\t`toIsoDuration: only \"seconds\" may be fractional, got ${key}: ${value}`,\n\t\t\t);\n\t\t}\n\t}\n\n\t// ISO 8601-1 does not allow weeks to be combined with other parts.\n\tif (\n\t\tparts.weeks &&\n\t\tObject.entries(parts).some(([key, value]) => key !== \"weeks\" && value)\n\t) {\n\t\tthrow new Error(\n\t\t\t'toIsoDuration: \"weeks\" cannot be combined with other parts',\n\t\t);\n\t}\n\n\tconst datePart = formatParts(parts, DATE_DESIGNATORS);\n\tconst timePart = formatParts(parts, TIME_DESIGNATORS);\n\n\tif (!datePart && !timePart) return \"PT0S\";\n\treturn `P${datePart}${timePart ? `T${timePart}` : \"\"}`;\n}\n\nconst ISO_DATE_PATTERN =\n\t/^(\\d{4}-\\d{2}-\\d{2})(?:(T\\d{2}:\\d{2}(?::\\d{2}(?:\\.\\d+)?)?)(Z|[+-]\\d{2}:?\\d{2})?)?$/;\n\nexport function toIsoDate(value: Date | string): string {\n\tif (value instanceof Date) {\n\t\tif (Number.isNaN(value.getTime())) {\n\t\t\tthrow new Error(\"toIsoDate: received an invalid Date\");\n\t\t}\n\t\treturn value.toISOString();\n\t}\n\n\tconst trimmed = value.trim();\n\tconst match = ISO_DATE_PATTERN.exec(trimmed);\n\tif (!match || Number.isNaN(Date.parse(`${match[1]}T00:00:00Z`))) {\n\t\tthrow new Error(`toIsoDate: \"${value}\" is not an ISO 8601 date`);\n\t}\n\n\tconst [, date, time = \"\", offset = \"\"] = match;\n\tconst normalizedOffset =\n\t\toffset.length === 5 ? `${offset.slice(0, 3)}:${offset.slice(3)}` : offset;\n\n\treturn `${date}${time}${normalizedOffset}`;\n}\n",
|
|
55
|
+
"const SCHEME_PATTERN = /^[a-z][a-z\\d+.-]*:/i;\n\nexport function toAbsoluteUrl(\n\tvalue: string | null | undefined,\n\tsiteUrl: string,\n): string | undefined {\n\tconst trimmed = value?.trim();\n\tif (!trimmed) return undefined;\n\n\tif (SCHEME_PATTERN.test(trimmed)) return trimmed;\n\n\tif (trimmed.startsWith(\"//\")) {\n\t\tconst protocol = SCHEME_PATTERN.exec(siteUrl)?.[0] ?? \"https:\";\n\t\treturn `${protocol}${trimmed}`;\n\t}\n\n\treturn `${siteUrl.replace(/\\/+$/, \"\")}/${trimmed.replace(/^\\/+/, \"\")}`;\n}\n"
|
|
37
56
|
],
|
|
38
|
-
"mappings": ";AASO,SAAS,WAAW,CAAC,MAAsB;AAAA,EACjD,OAAO,KACL,YAAY,EACZ,QAAQ,eAAe,GAAG,EAC1B,QAAQ,UAAU,EAAE;AAAA;AAGhB,SAAS,cAAc;AAAA,EAC7B;AAAA,EACA;AAAA,EACA,UAAU;AAAA,EACV;AAAA,GAC2B;AAAA,EAC3B,IAAI,YAAY;AAAA,IACf,OAAO,WAAW,WAAW,MAAM,IAChC,aACA,GAAG,UAAU;AAAA,EACjB;AAAA,EAEA,OAAO,GAAG,WAAW,QAAQ,YAAY,IAAI;AAAA;;;
|
|
39
|
-
"debugId": "
|
|
57
|
+
"mappings": "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",
|
|
58
|
+
"debugId": "91D355419575781164756E2164756E21",
|
|
40
59
|
"names": []
|
|
41
60
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type DurationParts = {
|
|
2
|
+
years?: number;
|
|
3
|
+
months?: number;
|
|
4
|
+
weeks?: number;
|
|
5
|
+
days?: number;
|
|
6
|
+
hours?: number;
|
|
7
|
+
minutes?: number;
|
|
8
|
+
seconds?: number;
|
|
9
|
+
};
|
|
10
|
+
export declare function toIsoDuration(parts: DurationParts): string;
|
|
11
|
+
export declare function toIsoDate(value: Date | string): string;
|
package/dist/utils/image.d.ts
CHANGED
|
@@ -1,8 +1,15 @@
|
|
|
1
|
+
import type { IdReference, ImageObjectLeaf, Organization, Person } from "schema-dts";
|
|
1
2
|
type ImageObjectInput = {
|
|
2
3
|
url: string;
|
|
3
4
|
width?: number;
|
|
4
5
|
height?: number;
|
|
6
|
+
caption?: string;
|
|
7
|
+
creditText?: string;
|
|
8
|
+
copyrightNotice?: string;
|
|
9
|
+
license?: string;
|
|
10
|
+
acquireLicensePage?: string;
|
|
11
|
+
creator?: Person | Organization | IdReference;
|
|
5
12
|
};
|
|
6
13
|
export type ImageInput = string | ImageObjectInput;
|
|
7
|
-
export declare function buildImageObject(input?: ImageInput):
|
|
14
|
+
export declare function buildImageObject(input?: ImageInput): ImageObjectLeaf | undefined;
|
|
8
15
|
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function toAbsoluteUrl(value: string | null | undefined, siteUrl: string): string | undefined;
|
package/package.json
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@crawl-me-maybe/schema",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"sideEffects":
|
|
3
|
+
"version": "0.2.1",
|
|
4
|
+
"sideEffects": [
|
|
5
|
+
"./src/index.ts"
|
|
6
|
+
],
|
|
5
7
|
"type": "module",
|
|
6
8
|
"author": "Nathan Nye <nathan@nye.dev> (https://nye.dev)",
|
|
7
9
|
"license": "MIT",
|
|
@@ -22,10 +24,11 @@
|
|
|
22
24
|
"scripts": {
|
|
23
25
|
"lint": "biome check .",
|
|
24
26
|
"fix": "biome check --write .",
|
|
25
|
-
"build": "rm -rf dist && bun build src/index.ts --outdir dist --format esm --sourcemap --external schema-dts && tsc -p tsconfig.json --declaration --emitDeclarationOnly --declarationDir dist --rootDir src"
|
|
27
|
+
"build": "rm -rf dist && bun build src/index.ts --outdir dist --format esm --sourcemap --external schema-dts && node ../../scripts/check-exports.mjs dist/index.js && tsc -p tsconfig.json --declaration --emitDeclarationOnly --declarationDir dist --rootDir src",
|
|
28
|
+
"prepublishOnly": "bun run build"
|
|
26
29
|
},
|
|
27
30
|
"dependencies": {
|
|
28
|
-
"schema-dts": "^
|
|
31
|
+
"schema-dts": "^2.0.0"
|
|
29
32
|
},
|
|
30
33
|
"devDependencies": {}
|
|
31
34
|
}
|