@jdevalk/seo-graph-core 0.4.1 → 0.5.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.
Files changed (55) hide show
  1. package/AGENTS.md +501 -442
  2. package/README.md +5 -5
  3. package/dist/assemble.d.ts +16 -1
  4. package/dist/assemble.d.ts.map +1 -1
  5. package/dist/assemble.js +59 -2
  6. package/dist/assemble.js.map +1 -1
  7. package/dist/index.d.ts +3 -6
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +2 -4
  10. package/dist/index.js.map +1 -1
  11. package/dist/pieces/article.d.ts +12 -17
  12. package/dist/pieces/article.d.ts.map +1 -1
  13. package/dist/pieces/article.js +16 -6
  14. package/dist/pieces/article.js.map +1 -1
  15. package/dist/pieces/breadcrumb.d.ts +5 -11
  16. package/dist/pieces/breadcrumb.d.ts.map +1 -1
  17. package/dist/pieces/breadcrumb.js +5 -7
  18. package/dist/pieces/breadcrumb.js.map +1 -1
  19. package/dist/pieces/custom.d.ts +28 -7
  20. package/dist/pieces/custom.d.ts.map +1 -1
  21. package/dist/pieces/custom.js +1 -11
  22. package/dist/pieces/custom.js.map +1 -1
  23. package/dist/pieces/image.d.ts +4 -8
  24. package/dist/pieces/image.d.ts.map +1 -1
  25. package/dist/pieces/image.js +15 -4
  26. package/dist/pieces/image.js.map +1 -1
  27. package/dist/pieces/navigation.d.ts +4 -4
  28. package/dist/pieces/navigation.d.ts.map +1 -1
  29. package/dist/pieces/navigation.js +5 -2
  30. package/dist/pieces/navigation.js.map +1 -1
  31. package/dist/pieces/video.d.ts +4 -17
  32. package/dist/pieces/video.d.ts.map +1 -1
  33. package/dist/pieces/video.js +15 -1
  34. package/dist/pieces/video.js.map +1 -1
  35. package/dist/pieces/webpage.d.ts +5 -13
  36. package/dist/pieces/webpage.d.ts.map +1 -1
  37. package/dist/pieces/webpage.js +14 -11
  38. package/dist/pieces/webpage.js.map +1 -1
  39. package/dist/pieces/website.d.ts +4 -3
  40. package/dist/pieces/website.d.ts.map +1 -1
  41. package/dist/pieces/website.js +11 -3
  42. package/dist/pieces/website.js.map +1 -1
  43. package/dist/types.d.ts +7 -0
  44. package/dist/types.d.ts.map +1 -1
  45. package/dist/types.js +24 -0
  46. package/dist/types.js.map +1 -1
  47. package/package.json +1 -1
  48. package/dist/pieces/organization.d.ts +0 -46
  49. package/dist/pieces/organization.d.ts.map +0 -1
  50. package/dist/pieces/organization.js +0 -39
  51. package/dist/pieces/organization.js.map +0 -1
  52. package/dist/pieces/person.d.ts +0 -36
  53. package/dist/pieces/person.d.ts.map +0 -1
  54. package/dist/pieces/person.js +0 -13
  55. package/dist/pieces/person.js.map +0 -1
package/README.md CHANGED
@@ -46,16 +46,16 @@ object with `@type` and `@id`. Builders for CreativeWork subtypes (`WebSite`,
46
46
  | `buildWebSite` | `WebSite` | — |
47
47
  | `buildWebPage` | `WebPage` | `'WebPage'` \| `'ProfilePage'` \| `'CollectionPage'` |
48
48
  | `buildArticle` | `Article` | `'Article'` \| `'BlogPosting'` \| `'NewsArticle'` \| `'TechArticle'` \| `'ScholarlyArticle'` \| `'Report'` |
49
- | `buildPerson` | `Person` | — |
50
- | `buildOrganization` | `Organization` | Any subtype string (e.g. `'Restaurant'`, `'LocalBusiness'`) |
51
49
  | `buildBreadcrumbList` | `BreadcrumbList` | — |
52
50
  | `buildImageObject` | `ImageObject` | — |
53
51
  | `buildVideoObject` | `VideoObject` | — |
54
52
  | `buildSiteNavigationElement` | `SiteNavigationElement` | — |
55
- | `buildCustomPiece` | Any | Pass `@type` directly in the input object |
53
+ | `buildPiece` | Any | `schema-dts` generic for autocomplete (e.g. `buildPiece<Product>`, `buildPiece<Person>`) |
56
54
 
57
- Every builder accepts an `extra` escape hatch for schema.org properties not
58
- covered by the typed interface.
55
+ All schema.org properties are accepted at the top level with full autocomplete
56
+ from `schema-dts`. Dedicated builders handle ID generation, date conversion,
57
+ and non-trivial transforms. Use `buildPiece<Type>` for everything else
58
+ (Person, Organization, Blog, Product, Recipe, Event, etc.).
59
59
 
60
60
  ## Usage
61
61
 
@@ -1,9 +1,24 @@
1
1
  import type { GraphEntity } from './types.js';
2
+ export interface AssembleGraphOptions {
3
+ /**
4
+ * When `true`, logs a warning for every `{ '@id': '...' }` reference
5
+ * in the graph that doesn't resolve to an entity with a matching
6
+ * `@id`. Helps catch broken links in the graph (e.g. a WebSite
7
+ * referencing a Person that was never included).
8
+ *
9
+ * Defaults to `false`.
10
+ */
11
+ warnOnDanglingReferences?: boolean;
12
+ }
2
13
  /**
3
14
  * Wrap a list of pieces in a `@context + @graph` envelope. Pieces are
4
15
  * deduplicated by `@id` before assembly; first occurrence wins.
16
+ *
17
+ * When `warnOnDanglingReferences` is enabled, logs warnings for any
18
+ * `{ '@id': '...' }` reference that doesn't resolve to an entity in
19
+ * the graph.
5
20
  */
6
- export declare function assembleGraph<T extends GraphEntity>(pieces: readonly T[]): {
21
+ export declare function assembleGraph<T extends GraphEntity>(pieces: readonly T[], options?: AssembleGraphOptions): {
7
22
  '@context': 'https://schema.org';
8
23
  '@graph': T[];
9
24
  };
@@ -1 +1 @@
1
- {"version":3,"file":"assemble.d.ts","sourceRoot":"","sources":["../src/assemble.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAG9C;;;GAGG;AACH,wBAAgB,aAAa,CAAC,CAAC,SAAS,WAAW,EAC/C,MAAM,EAAE,SAAS,CAAC,EAAE,GACrB;IAAE,UAAU,EAAE,oBAAoB,CAAC;IAAC,QAAQ,EAAE,CAAC,EAAE,CAAA;CAAE,CAKrD"}
1
+ {"version":3,"file":"assemble.d.ts","sourceRoot":"","sources":["../src/assemble.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAG9C,MAAM,WAAW,oBAAoB;IACjC;;;;;;;OAOG;IACH,wBAAwB,CAAC,EAAE,OAAO,CAAC;CACtC;AA8BD;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,CAAC,SAAS,WAAW,EAC/C,MAAM,EAAE,SAAS,CAAC,EAAE,EACpB,OAAO,CAAC,EAAE,oBAAoB,GAC/B;IAAE,UAAU,EAAE,oBAAoB,CAAC;IAAC,QAAQ,EAAE,CAAC,EAAE,CAAA;CAAE,CAsCrD"}
package/dist/assemble.js CHANGED
@@ -1,12 +1,69 @@
1
1
  import { deduplicateByGraphId } from './dedupe.js';
2
+ /**
3
+ * Walk a value recursively and collect every `{ '@id': string }` reference
4
+ * that is NOT a top-level entity (i.e. doesn't have `@type`).
5
+ */
6
+ function collectReferences(value, refs) {
7
+ if (value === null || value === undefined || typeof value !== 'object')
8
+ return;
9
+ if (Array.isArray(value)) {
10
+ for (const item of value) {
11
+ collectReferences(item, refs);
12
+ }
13
+ return;
14
+ }
15
+ const obj = value;
16
+ // An object with @id but no @type is a reference, not an entity.
17
+ if (typeof obj['@id'] === 'string' && obj['@type'] === undefined) {
18
+ refs.add(obj['@id']);
19
+ return;
20
+ }
21
+ // Recurse into values of entities/nested objects.
22
+ for (const val of Object.values(obj)) {
23
+ collectReferences(val, refs);
24
+ }
25
+ }
2
26
  /**
3
27
  * Wrap a list of pieces in a `@context + @graph` envelope. Pieces are
4
28
  * deduplicated by `@id` before assembly; first occurrence wins.
29
+ *
30
+ * When `warnOnDanglingReferences` is enabled, logs warnings for any
31
+ * `{ '@id': '...' }` reference that doesn't resolve to an entity in
32
+ * the graph.
5
33
  */
6
- export function assembleGraph(pieces) {
34
+ export function assembleGraph(pieces, options) {
35
+ const graph = deduplicateByGraphId(pieces);
36
+ if (options?.warnOnDanglingReferences) {
37
+ const entityIds = new Set();
38
+ for (const entity of graph) {
39
+ if (typeof entity['@id'] === 'string') {
40
+ entityIds.add(entity['@id']);
41
+ }
42
+ }
43
+ // Map from referenced @id → @type of the entity that references it.
44
+ const refs = new Map();
45
+ for (const entity of graph) {
46
+ const sourceType = String(entity['@type']);
47
+ const collected = new Set();
48
+ for (const [key, val] of Object.entries(entity)) {
49
+ if (key === '@id')
50
+ continue;
51
+ collectReferences(val, collected);
52
+ }
53
+ for (const ref of collected) {
54
+ if (!refs.has(ref))
55
+ refs.set(ref, sourceType);
56
+ }
57
+ }
58
+ for (const [ref, sourceType] of refs) {
59
+ if (!entityIds.has(ref)) {
60
+ console.warn(`[seo-graph] Dangling reference in ${sourceType}: { "@id": "${ref}" } does not match any entity in the graph.`);
61
+ }
62
+ }
63
+ }
7
64
  return {
8
65
  '@context': 'https://schema.org',
9
- '@graph': deduplicateByGraphId(pieces),
66
+ '@graph': graph,
10
67
  };
11
68
  }
12
69
  //# sourceMappingURL=assemble.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"assemble.js","sourceRoot":"","sources":["../src/assemble.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAEnD;;;GAGG;AACH,MAAM,UAAU,aAAa,CACzB,MAAoB;IAEpB,OAAO;QACH,UAAU,EAAE,oBAAoB;QAChC,QAAQ,EAAE,oBAAoB,CAAC,MAAM,CAAC;KACzC,CAAC;AACN,CAAC"}
1
+ {"version":3,"file":"assemble.js","sourceRoot":"","sources":["../src/assemble.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAcnD;;;GAGG;AACH,SAAS,iBAAiB,CAAC,KAAc,EAAE,IAAiB;IACxD,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO;IAE/E,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACvB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACvB,iBAAiB,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QAClC,CAAC;QACD,OAAO;IACX,CAAC;IAED,MAAM,GAAG,GAAG,KAAgC,CAAC;IAE7C,iEAAiE;IACjE,IAAI,OAAO,GAAG,CAAC,KAAK,CAAC,KAAK,QAAQ,IAAI,GAAG,CAAC,OAAO,CAAC,KAAK,SAAS,EAAE,CAAC;QAC/D,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC;QACrB,OAAO;IACX,CAAC;IAED,kDAAkD;IAClD,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;QACnC,iBAAiB,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IACjC,CAAC;AACL,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,aAAa,CACzB,MAAoB,EACpB,OAA8B;IAE9B,MAAM,KAAK,GAAG,oBAAoB,CAAC,MAAM,CAAC,CAAC;IAE3C,IAAI,OAAO,EAAE,wBAAwB,EAAE,CAAC;QACpC,MAAM,SAAS,GAAG,IAAI,GAAG,EAAU,CAAC;QACpC,KAAK,MAAM,MAAM,IAAI,KAAK,EAAE,CAAC;YACzB,IAAI,OAAO,MAAM,CAAC,KAAK,CAAC,KAAK,QAAQ,EAAE,CAAC;gBACpC,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;YACjC,CAAC;QACL,CAAC;QAED,oEAAoE;QACpE,MAAM,IAAI,GAAG,IAAI,GAAG,EAAkB,CAAC;QACvC,KAAK,MAAM,MAAM,IAAI,KAAK,EAAE,CAAC;YACzB,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;YAC3C,MAAM,SAAS,GAAG,IAAI,GAAG,EAAU,CAAC;YACpC,KAAK,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;gBAC9C,IAAI,GAAG,KAAK,KAAK;oBAAE,SAAS;gBAC5B,iBAAiB,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;YACtC,CAAC;YACD,KAAK,MAAM,GAAG,IAAI,SAAS,EAAE,CAAC;gBAC1B,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;oBAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;YAClD,CAAC;QACL,CAAC;QAED,KAAK,MAAM,CAAC,GAAG,EAAE,UAAU,CAAC,IAAI,IAAI,EAAE,CAAC;YACnC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;gBACtB,OAAO,CAAC,IAAI,CACR,qCAAqC,UAAU,eAAe,GAAG,6CAA6C,CACjH,CAAC;YACN,CAAC;QACL,CAAC;IACL,CAAC;IAED,OAAO;QACH,UAAU,EAAE,oBAAoB;QAChC,QAAQ,EAAE,KAAK;KAClB,CAAC;AACN,CAAC"}
package/dist/index.d.ts CHANGED
@@ -1,15 +1,12 @@
1
1
  export type { GraphEntity, Reference, SchemaGraph, CreativeWorkFields } from './types.js';
2
- export { applyCreativeWorkFields } from './types.js';
2
+ export { applyCreativeWorkFields, spreadRemainingProperties, CREATIVE_WORK_KEYS } from './types.js';
3
3
  export type { IdFactory, MakeIdsOptions } from './ids.js';
4
4
  export { makeIds } from './ids.js';
5
5
  export { deduplicateByGraphId } from './dedupe.js';
6
6
  export { assembleGraph } from './assemble.js';
7
+ export type { AssembleGraphOptions } from './assemble.js';
7
8
  export { buildWebSite } from './pieces/website.js';
8
9
  export type { WebSiteInput } from './pieces/website.js';
9
- export { buildPerson } from './pieces/person.js';
10
- export type { PersonInput } from './pieces/person.js';
11
- export { buildOrganization } from './pieces/organization.js';
12
- export type { OrganizationInput } from './pieces/organization.js';
13
10
  export { buildSiteNavigationElement } from './pieces/navigation.js';
14
11
  export type { NavigationItem, SiteNavigationInput } from './pieces/navigation.js';
15
12
  export { buildWebPage } from './pieces/webpage.js';
@@ -22,5 +19,5 @@ export { buildImageObject } from './pieces/image.js';
22
19
  export type { ImageObjectInput } from './pieces/image.js';
23
20
  export { buildVideoObject } from './pieces/video.js';
24
21
  export type { VideoObjectInput } from './pieces/video.js';
25
- export { buildCustomPiece } from './pieces/custom.js';
22
+ export { buildPiece } from './pieces/custom.js';
26
23
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,YAAY,EAAE,WAAW,EAAE,SAAS,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAC1F,OAAO,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AACrD,YAAY,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC1D,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AACnC,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAG9C,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,YAAY,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAExD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,YAAY,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEtD,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,YAAY,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAElE,OAAO,EAAE,0BAA0B,EAAE,MAAM,wBAAwB,CAAC;AACpE,YAAY,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAElF,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAErE,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAErE,OAAO,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAC7D,YAAY,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAElF,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,YAAY,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAE1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,YAAY,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAE1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,YAAY,EAAE,WAAW,EAAE,SAAS,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAC1F,OAAO,EAAE,uBAAuB,EAAE,yBAAyB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AACpG,YAAY,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC1D,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AACnC,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,YAAY,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAG1D,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,YAAY,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAExD,OAAO,EAAE,0BAA0B,EAAE,MAAM,wBAAwB,CAAC;AACpE,YAAY,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAElF,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAErE,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAErE,OAAO,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAC7D,YAAY,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAElF,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,YAAY,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAE1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,YAAY,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAE1D,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC"}
package/dist/index.js CHANGED
@@ -1,17 +1,15 @@
1
1
  // @jdevalk/seo-graph-core — agent-ready schema.org JSON-LD graph builders.
2
- export { applyCreativeWorkFields } from './types.js';
2
+ export { applyCreativeWorkFields, spreadRemainingProperties, CREATIVE_WORK_KEYS } from './types.js';
3
3
  export { makeIds } from './ids.js';
4
4
  export { deduplicateByGraphId } from './dedupe.js';
5
5
  export { assembleGraph } from './assemble.js';
6
6
  // Piece builders
7
7
  export { buildWebSite } from './pieces/website.js';
8
- export { buildPerson } from './pieces/person.js';
9
- export { buildOrganization } from './pieces/organization.js';
10
8
  export { buildSiteNavigationElement } from './pieces/navigation.js';
11
9
  export { buildWebPage } from './pieces/webpage.js';
12
10
  export { buildArticle } from './pieces/article.js';
13
11
  export { buildBreadcrumbList } from './pieces/breadcrumb.js';
14
12
  export { buildImageObject } from './pieces/image.js';
15
13
  export { buildVideoObject } from './pieces/video.js';
16
- export { buildCustomPiece } from './pieces/custom.js';
14
+ export { buildPiece } from './pieces/custom.js';
17
15
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAG3E,OAAO,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAErD,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AACnC,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAE9C,iBAAiB;AACjB,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAGnD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAGjD,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAG7D,OAAO,EAAE,0BAA0B,EAAE,MAAM,wBAAwB,CAAC;AAGpE,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAGnD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAGnD,OAAO,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAG7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAGrD,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAGrD,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAG3E,OAAO,EAAE,uBAAuB,EAAE,yBAAyB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAEpG,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AACnC,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAG9C,iBAAiB;AACjB,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAGnD,OAAO,EAAE,0BAA0B,EAAE,MAAM,wBAAwB,CAAC;AAGpE,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAGnD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAGnD,OAAO,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAG7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAGrD,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAGrD,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC"}
@@ -1,6 +1,14 @@
1
+ import type { ArticleLeaf } from 'schema-dts';
1
2
  import type { IdFactory } from '../ids.js';
2
3
  import type { Reference, CreativeWorkFields } from '../types.js';
3
- export interface ArticleInput extends CreativeWorkFields {
4
+ /**
5
+ * Concrete Article subtype. `Article` is the default; use `BlogPosting`
6
+ * for blog posts, `NewsArticle` for journalism, `TechArticle` for
7
+ * technical docs, `ScholarlyArticle` for academic papers, or `Report`
8
+ * for data/research reports.
9
+ */
10
+ export type ArticleType = 'Article' | 'BlogPosting' | 'NewsArticle' | 'TechArticle' | 'ScholarlyArticle' | 'Report';
11
+ interface ArticleCoreFields extends CreativeWorkFields {
4
12
  /** Canonical URL of the article's page. The @id is `${url}#article`. */
5
13
  url: string;
6
14
  /** Reference to the enclosing WebPage (usually ids.webPage(url)). */
@@ -19,25 +27,12 @@ export interface ArticleInput extends CreativeWorkFields {
19
27
  /** Top-level category, emitted as `articleSection`. */
20
28
  articleSection?: string;
21
29
  wordCount?: number;
22
- /**
23
- * Full or truncated article body, emitted as `articleBody`. Pass from
24
- * the aggregator / build step; core doesn't derive it from anything.
25
- */
26
30
  articleBody?: string;
27
- extra?: Record<string, unknown>;
28
31
  }
32
+ export type ArticleInput = ArticleCoreFields & Omit<Partial<ArticleLeaf>, keyof ArticleCoreFields | '@type'>;
29
33
  /**
30
- * Concrete Article subtype. `Article` is the default; use `BlogPosting`
31
- * for blog posts, `NewsArticle` for journalism, `TechArticle` for
32
- * technical docs, `ScholarlyArticle` for academic papers, or `Report`
33
- * for data/research reports.
34
- */
35
- export type ArticleType = 'Article' | 'BlogPosting' | 'NewsArticle' | 'TechArticle' | 'ScholarlyArticle' | 'Report';
36
- /**
37
- * Build a schema.org Article piece. The caller is responsible for pre-
38
- * computing the references (author, publisher, isPartOf, image) using the
39
- * IdFactory, so core doesn't have to know anything about how the site
40
- * models its Person/Organization/WebPage entities.
34
+ * Build a schema.org Article piece.
41
35
  */
42
36
  export declare function buildArticle(input: ArticleInput, ids: IdFactory, type?: ArticleType): Record<string, unknown>;
37
+ export {};
43
38
  //# sourceMappingURL=article.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"article.d.ts","sourceRoot":"","sources":["../../src/pieces/article.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,KAAK,EAAE,SAAS,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAGjE,MAAM,WAAW,YAAa,SAAQ,kBAAkB;IACpD,wEAAwE;IACxE,GAAG,EAAE,MAAM,CAAC;IACZ,qEAAqE;IACrE,QAAQ,EAAE,SAAS,CAAC;IACpB,kEAAkE;IAClE,MAAM,EAAE,SAAS,CAAC;IAClB,8EAA8E;IAC9E,SAAS,EAAE,SAAS,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,sEAAsE;IACtE,WAAW,EAAE,MAAM,CAAC;IACpB,6FAA6F;IAC7F,aAAa,EAAE,IAAI,CAAC;IACpB,oDAAoD;IACpD,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,uDAAuD;IACvD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAED;;;;;GAKG;AACH,MAAM,MAAM,WAAW,GACjB,SAAS,GACT,aAAa,GACb,aAAa,GACb,aAAa,GACb,kBAAkB,GAClB,QAAQ,CAAC;AAEf;;;;;GAKG;AACH,wBAAgB,YAAY,CACxB,KAAK,EAAE,YAAY,EACnB,GAAG,EAAE,SAAS,EACd,IAAI,GAAE,WAAuB,GAC9B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAkBzB"}
1
+ {"version":3,"file":"article.d.ts","sourceRoot":"","sources":["../../src/pieces/article.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,KAAK,EAAE,SAAS,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAOjE;;;;;GAKG;AACH,MAAM,MAAM,WAAW,GACjB,SAAS,GACT,aAAa,GACb,aAAa,GACb,aAAa,GACb,kBAAkB,GAClB,QAAQ,CAAC;AAEf,UAAU,iBAAkB,SAAQ,kBAAkB;IAClD,wEAAwE;IACxE,GAAG,EAAE,MAAM,CAAC;IACZ,qEAAqE;IACrE,QAAQ,EAAE,SAAS,CAAC;IACpB,kEAAkE;IAClE,MAAM,EAAE,SAAS,CAAC;IAClB,8EAA8E;IAC9E,SAAS,EAAE,SAAS,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,sEAAsE;IACtE,WAAW,EAAE,MAAM,CAAC;IACpB,6FAA6F;IAC7F,aAAa,EAAE,IAAI,CAAC;IACpB,oDAAoD;IACpD,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,uDAAuD;IACvD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,MAAM,YAAY,GAAG,iBAAiB,GACxC,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,MAAM,iBAAiB,GAAG,OAAO,CAAC,CAAC;AAelE;;GAEG;AACH,wBAAgB,YAAY,CACxB,KAAK,EAAE,YAAY,EACnB,GAAG,EAAE,SAAS,EACd,IAAI,GAAE,WAAuB,GAC9B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAmBzB"}
@@ -1,9 +1,18 @@
1
- import { applyCreativeWorkFields } from '../types.js';
1
+ import { applyCreativeWorkFields, spreadRemainingProperties, CREATIVE_WORK_KEYS, } from '../types.js';
2
+ const HANDLED_KEYS = new Set([
3
+ ...CREATIVE_WORK_KEYS,
4
+ 'url',
5
+ 'isPartOf',
6
+ 'author',
7
+ 'publisher',
8
+ 'headline',
9
+ 'image',
10
+ 'articleSection',
11
+ 'wordCount',
12
+ 'articleBody',
13
+ ]);
2
14
  /**
3
- * Build a schema.org Article piece. The caller is responsible for pre-
4
- * computing the references (author, publisher, isPartOf, image) using the
5
- * IdFactory, so core doesn't have to know anything about how the site
6
- * models its Person/Organization/WebPage entities.
15
+ * Build a schema.org Article piece.
7
16
  */
8
17
  export function buildArticle(input, ids, type = 'Article') {
9
18
  const piece = {
@@ -24,6 +33,7 @@ export function buildArticle(input, ids, type = 'Article') {
24
33
  piece.wordCount = input.wordCount;
25
34
  if (input.articleBody !== undefined)
26
35
  piece.articleBody = input.articleBody;
27
- return { ...piece, ...input.extra };
36
+ spreadRemainingProperties(piece, input, HANDLED_KEYS);
37
+ return piece;
28
38
  }
29
39
  //# sourceMappingURL=article.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"article.js","sourceRoot":"","sources":["../../src/pieces/article.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AA2CtD;;;;;GAKG;AACH,MAAM,UAAU,YAAY,CACxB,KAAmB,EACnB,GAAc,EACd,OAAoB,SAAS;IAE7B,MAAM,KAAK,GAA4B;QACnC,OAAO,EAAE,IAAI;QACb,KAAK,EAAE,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC;QAC7B,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,gBAAgB,EAAE,EAAE,KAAK,EAAE,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE;QACnD,SAAS,EAAE,KAAK,CAAC,SAAS;KAC7B,CAAC;IAEF,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS;QAAE,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC;IACzD,uBAAuB,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IACtC,IAAI,KAAK,CAAC,cAAc,KAAK,SAAS;QAAE,KAAK,CAAC,cAAc,GAAG,KAAK,CAAC,cAAc,CAAC;IACpF,IAAI,KAAK,CAAC,SAAS,KAAK,SAAS;QAAE,KAAK,CAAC,SAAS,GAAG,KAAK,CAAC,SAAS,CAAC;IACrE,IAAI,KAAK,CAAC,WAAW,KAAK,SAAS;QAAE,KAAK,CAAC,WAAW,GAAG,KAAK,CAAC,WAAW,CAAC;IAE3E,OAAO,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,CAAC,KAAK,EAAE,CAAC;AACxC,CAAC"}
1
+ {"version":3,"file":"article.js","sourceRoot":"","sources":["../../src/pieces/article.ts"],"names":[],"mappings":"AAIA,OAAO,EACH,uBAAuB,EACvB,yBAAyB,EACzB,kBAAkB,GACrB,MAAM,aAAa,CAAC;AAyCrB,MAAM,YAAY,GAAG,IAAI,GAAG,CAAS;IACjC,GAAG,kBAAkB;IACrB,KAAK;IACL,UAAU;IACV,QAAQ;IACR,WAAW;IACX,UAAU;IACV,OAAO;IACP,gBAAgB;IAChB,WAAW;IACX,aAAa;CAChB,CAAC,CAAC;AAEH;;GAEG;AACH,MAAM,UAAU,YAAY,CACxB,KAAmB,EACnB,GAAc,EACd,OAAoB,SAAS;IAE7B,MAAM,KAAK,GAA4B;QACnC,OAAO,EAAE,IAAI;QACb,KAAK,EAAE,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC;QAC7B,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,gBAAgB,EAAE,EAAE,KAAK,EAAE,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE;QACnD,SAAS,EAAE,KAAK,CAAC,SAAS;KAC7B,CAAC;IAEF,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS;QAAE,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC;IACzD,uBAAuB,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IACtC,IAAI,KAAK,CAAC,cAAc,KAAK,SAAS;QAAE,KAAK,CAAC,cAAc,GAAG,KAAK,CAAC,cAAc,CAAC;IACpF,IAAI,KAAK,CAAC,SAAS,KAAK,SAAS;QAAE,KAAK,CAAC,SAAS,GAAG,KAAK,CAAC,SAAS,CAAC;IACrE,IAAI,KAAK,CAAC,WAAW,KAAK,SAAS;QAAE,KAAK,CAAC,WAAW,GAAG,KAAK,CAAC,WAAW,CAAC;IAC3E,yBAAyB,CAAC,KAAK,EAAE,KAAK,EAAE,YAAY,CAAC,CAAC;IAEtD,OAAO,KAAK,CAAC;AACjB,CAAC"}
@@ -1,3 +1,4 @@
1
+ import type { BreadcrumbListLeaf } from 'schema-dts';
1
2
  import type { IdFactory } from '../ids.js';
2
3
  export interface BreadcrumbItem {
3
4
  /** Display name for this crumb, e.g. 'Home', 'Blog', 'Open Source'. */
@@ -5,23 +6,16 @@ export interface BreadcrumbItem {
5
6
  /** URL for this crumb. */
6
7
  url: string;
7
8
  }
8
- export interface BreadcrumbListInput {
9
- /**
10
- * The URL of the page this breadcrumb belongs to. The @id is
11
- * `${url}#breadcrumb`.
12
- */
9
+ interface BreadcrumbListCoreFields {
10
+ /** The URL of the page this breadcrumb belongs to. */
13
11
  url: string;
14
12
  /** Pre-computed ordered list of crumbs, root first. */
15
13
  items: readonly BreadcrumbItem[];
16
- extra?: Record<string, unknown>;
17
14
  }
15
+ export type BreadcrumbListInput = BreadcrumbListCoreFields & Omit<Partial<BreadcrumbListLeaf>, keyof BreadcrumbListCoreFields | '@type'>;
18
16
  /**
19
17
  * Build a schema.org BreadcrumbList piece.
20
- *
21
- * Breadcrumbs are an input to core, not a derivation. The caller knows
22
- * its routing conventions; core just wraps the pre-computed items in the
23
- * right schema.org shape. If you need to derive breadcrumbs from an
24
- * Astro URL, use `@jdevalk/astro-seo-graph`'s breadcrumb helper instead.
25
18
  */
26
19
  export declare function buildBreadcrumbList(input: BreadcrumbListInput, ids: IdFactory): Record<string, unknown>;
20
+ export {};
27
21
  //# sourceMappingURL=breadcrumb.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"breadcrumb.d.ts","sourceRoot":"","sources":["../../src/pieces/breadcrumb.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAE3C,MAAM,WAAW,cAAc;IAC3B,uEAAuE;IACvE,IAAI,EAAE,MAAM,CAAC;IACb,0BAA0B;IAC1B,GAAG,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,mBAAmB;IAChC;;;OAGG;IACH,GAAG,EAAE,MAAM,CAAC;IACZ,uDAAuD;IACvD,KAAK,EAAE,SAAS,cAAc,EAAE,CAAC;IACjC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAC/B,KAAK,EAAE,mBAAmB,EAC1B,GAAG,EAAE,SAAS,GACf,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAczB"}
1
+ {"version":3,"file":"breadcrumb.d.ts","sourceRoot":"","sources":["../../src/pieces/breadcrumb.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAErD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAG3C,MAAM,WAAW,cAAc;IAC3B,uEAAuE;IACvE,IAAI,EAAE,MAAM,CAAC;IACb,0BAA0B;IAC1B,GAAG,EAAE,MAAM,CAAC;CACf;AAED,UAAU,wBAAwB;IAC9B,sDAAsD;IACtD,GAAG,EAAE,MAAM,CAAC;IACZ,uDAAuD;IACvD,KAAK,EAAE,SAAS,cAAc,EAAE,CAAC;CACpC;AAED,MAAM,MAAM,mBAAmB,GAAG,wBAAwB,GACtD,IAAI,CAAC,OAAO,CAAC,kBAAkB,CAAC,EAAE,MAAM,wBAAwB,GAAG,OAAO,CAAC,CAAC;AAIhF;;GAEG;AACH,wBAAgB,mBAAmB,CAC/B,KAAK,EAAE,mBAAmB,EAC1B,GAAG,EAAE,SAAS,GACf,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAkBzB"}
@@ -1,10 +1,7 @@
1
+ import { spreadRemainingProperties } from '../types.js';
2
+ const HANDLED_KEYS = new Set(['url', 'items']);
1
3
  /**
2
4
  * Build a schema.org BreadcrumbList piece.
3
- *
4
- * Breadcrumbs are an input to core, not a derivation. The caller knows
5
- * its routing conventions; core just wraps the pre-computed items in the
6
- * right schema.org shape. If you need to derive breadcrumbs from an
7
- * Astro URL, use `@jdevalk/astro-seo-graph`'s breadcrumb helper instead.
8
5
  */
9
6
  export function buildBreadcrumbList(input, ids) {
10
7
  const lastIndex = input.items.length - 1;
@@ -14,11 +11,12 @@ export function buildBreadcrumbList(input, ids) {
14
11
  name: item.name,
15
12
  item: index === lastIndex ? { '@id': ids.webPage(item.url) } : item.url,
16
13
  }));
17
- return {
14
+ const piece = {
18
15
  '@type': 'BreadcrumbList',
19
16
  '@id': ids.breadcrumb(input.url),
20
17
  itemListElement,
21
- ...input.extra,
22
18
  };
19
+ spreadRemainingProperties(piece, input, HANDLED_KEYS);
20
+ return piece;
23
21
  }
24
22
  //# sourceMappingURL=breadcrumb.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"breadcrumb.js","sourceRoot":"","sources":["../../src/pieces/breadcrumb.ts"],"names":[],"mappings":"AAoBA;;;;;;;GAOG;AACH,MAAM,UAAU,mBAAmB,CAC/B,KAA0B,EAC1B,GAAc;IAEd,MAAM,SAAS,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;IACzC,MAAM,eAAe,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;QACtD,OAAO,EAAE,UAAU;QACnB,QAAQ,EAAE,KAAK,GAAG,CAAC;QACnB,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,IAAI,EAAE,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG;KAC1E,CAAC,CAAC,CAAC;IACJ,OAAO;QACH,OAAO,EAAE,gBAAgB;QACzB,KAAK,EAAE,GAAG,CAAC,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC;QAChC,eAAe;QACf,GAAG,KAAK,CAAC,KAAK;KACjB,CAAC;AACN,CAAC"}
1
+ {"version":3,"file":"breadcrumb.js","sourceRoot":"","sources":["../../src/pieces/breadcrumb.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,yBAAyB,EAAE,MAAM,aAAa,CAAC;AAmBxD,MAAM,YAAY,GAAG,IAAI,GAAG,CAAS,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC;AAEvD;;GAEG;AACH,MAAM,UAAU,mBAAmB,CAC/B,KAA0B,EAC1B,GAAc;IAEd,MAAM,SAAS,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;IACzC,MAAM,eAAe,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;QACtD,OAAO,EAAE,UAAU;QACnB,QAAQ,EAAE,KAAK,GAAG,CAAC;QACnB,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,IAAI,EAAE,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG;KAC1E,CAAC,CAAC,CAAC;IAEJ,MAAM,KAAK,GAA4B;QACnC,OAAO,EAAE,gBAAgB;QACzB,KAAK,EAAE,GAAG,CAAC,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC;QAChC,eAAe;KAClB,CAAC;IAEF,yBAAyB,CAAC,KAAK,EAAE,KAAK,EAAE,YAAY,CAAC,CAAC;IAEtD,OAAO,KAAK,CAAC;AACjB,CAAC"}
@@ -1,12 +1,33 @@
1
+ import type { Thing } from 'schema-dts';
1
2
  /**
2
- * Escape hatch: build an arbitrary schema.org piece from a raw object.
3
+ * Build an arbitrary schema.org piece from a raw object.
3
4
  *
4
- * Use this when the built-in piece builders don't cover what you need
5
- * e.g. a Recipe, Event, Product, or a LocalBusiness subtype that needs
6
- * a specific shape not captured by `buildOrganization`.
5
+ * Pass a `schema-dts` type as the generic parameter to get autocomplete.
6
+ * The `@type` value in your input narrows the union to the matching leaf
7
+ * type, so `buildPiece<Product>` with `'@type': 'Product'` gives you
8
+ * full ProductLeaf autocomplete — no need to import Leaf types.
7
9
  *
8
- * Note: the built piece is returned as-is. It's the caller's responsibility
9
- * to supply a valid `@type` and (if cross-referenced) a stable `@id`.
10
+ * ```ts
11
+ * import type { Product } from 'schema-dts';
12
+ * buildPiece<Product>({
13
+ * '@type': 'Product',
14
+ * '@id': `${url}#product`,
15
+ * name: 'Running Shoe',
16
+ * color: 'Black', // ← autocomplete from schema-dts
17
+ * sku: 'ABC123', // ← autocomplete from schema-dts
18
+ * });
19
+ * ```
20
+ *
21
+ * Without a generic, the input is untyped — any properties are accepted.
10
22
  */
11
- export declare function buildCustomPiece<T extends Record<string, unknown>>(raw: T): T;
23
+ export declare function buildPiece<T extends Thing, TType extends string = string>(raw: Partial<Extract<T, {
24
+ '@type': TType;
25
+ }>> & {
26
+ '@type': TType;
27
+ '@id'?: string;
28
+ }): Record<string, unknown>;
29
+ export declare function buildPiece(raw: Record<string, unknown> & {
30
+ '@type': string | readonly string[];
31
+ '@id'?: string;
32
+ }): Record<string, unknown>;
12
33
  //# sourceMappingURL=custom.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"custom.d.ts","sourceRoot":"","sources":["../../src/pieces/custom.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,CAE7E"}
1
+ {"version":3,"file":"custom.d.ts","sourceRoot":"","sources":["../../src/pieces/custom.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAExC;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,UAAU,CAAC,CAAC,SAAS,KAAK,EAAE,KAAK,SAAS,MAAM,GAAG,MAAM,EACrE,GAAG,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC,EAAE;IAAE,OAAO,EAAE,KAAK,CAAA;CAAE,CAAC,CAAC,GAAG;IAC3C,OAAO,EAAE,KAAK,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAClB,GACF,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAC3B,wBAAgB,UAAU,CACtB,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG;IAC3B,OAAO,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAAC;IACpC,KAAK,CAAC,EAAE,MAAM,CAAC;CAClB,GACF,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC"}
@@ -1,14 +1,4 @@
1
- /**
2
- * Escape hatch: build an arbitrary schema.org piece from a raw object.
3
- *
4
- * Use this when the built-in piece builders don't cover what you need —
5
- * e.g. a Recipe, Event, Product, or a LocalBusiness subtype that needs
6
- * a specific shape not captured by `buildOrganization`.
7
- *
8
- * Note: the built piece is returned as-is. It's the caller's responsibility
9
- * to supply a valid `@type` and (if cross-referenced) a stable `@id`.
10
- */
11
- export function buildCustomPiece(raw) {
1
+ export function buildPiece(raw) {
12
2
  return raw;
13
3
  }
14
4
  //# sourceMappingURL=custom.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"custom.js","sourceRoot":"","sources":["../../src/pieces/custom.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,MAAM,UAAU,gBAAgB,CAAoC,GAAM;IACtE,OAAO,GAAG,CAAC;AACf,CAAC"}
1
+ {"version":3,"file":"custom.js","sourceRoot":"","sources":["../../src/pieces/custom.ts"],"names":[],"mappings":"AAmCA,MAAM,UAAU,UAAU,CAAC,GAA4B;IACnD,OAAO,GAAG,CAAC;AACf,CAAC"}
@@ -1,12 +1,7 @@
1
+ import type { ImageObjectLeaf } from 'schema-dts';
1
2
  import type { IdFactory } from '../ids.js';
2
- export interface ImageObjectInput {
3
- /**
4
- * Canonical URL of the page this image belongs to. The @id is
5
- * `${pageUrl}#primaryimage` — to build a site-wide image with a
6
- * friendly fragment (e.g. the Person logo), use the `id` override.
7
- */
3
+ interface ImageObjectCoreFields {
8
4
  pageUrl?: string;
9
- /** Explicit @id override; takes precedence over `pageUrl`. */
10
5
  id?: string;
11
6
  /** Public URL of the image file. Used for both `url` and `contentUrl`. */
12
7
  url: string;
@@ -14,12 +9,13 @@ export interface ImageObjectInput {
14
9
  height: number;
15
10
  inLanguage?: string;
16
11
  caption?: string;
17
- extra?: Record<string, unknown>;
18
12
  }
13
+ export type ImageObjectInput = ImageObjectCoreFields & Omit<Partial<ImageObjectLeaf>, keyof ImageObjectCoreFields | '@type'>;
19
14
  /**
20
15
  * Build a schema.org ImageObject piece. Pass `pageUrl` for a page's
21
16
  * primary image (id = `${pageUrl}#primaryimage`), or `id` for a site-
22
17
  * wide image like a personal logo.
23
18
  */
24
19
  export declare function buildImageObject(input: ImageObjectInput, ids: IdFactory): Record<string, unknown>;
20
+ export {};
25
21
  //# sourceMappingURL=image.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"image.d.ts","sourceRoot":"","sources":["../../src/pieces/image.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAE3C,MAAM,WAAW,gBAAgB;IAC7B;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,8DAA8D;IAC9D,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,0EAA0E;IAC1E,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,gBAAgB,EAAE,GAAG,EAAE,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAmBjG"}
1
+ {"version":3,"file":"image.d.ts","sourceRoot":"","sources":["../../src/pieces/image.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAG3C,UAAU,qBAAqB;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,0EAA0E;IAC1E,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,gBAAgB,GAAG,qBAAqB,GAChD,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,EAAE,MAAM,qBAAqB,GAAG,OAAO,CAAC,CAAC;AAY1E;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,gBAAgB,EAAE,GAAG,EAAE,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAoBjG"}
@@ -1,16 +1,26 @@
1
+ import { spreadRemainingProperties } from '../types.js';
2
+ const HANDLED_KEYS = new Set([
3
+ 'pageUrl',
4
+ 'id',
5
+ 'url',
6
+ 'width',
7
+ 'height',
8
+ 'inLanguage',
9
+ 'caption',
10
+ ]);
1
11
  /**
2
12
  * Build a schema.org ImageObject piece. Pass `pageUrl` for a page's
3
13
  * primary image (id = `${pageUrl}#primaryimage`), or `id` for a site-
4
14
  * wide image like a personal logo.
5
15
  */
6
16
  export function buildImageObject(input, ids) {
7
- const id = input.id ?? (input.pageUrl !== undefined ? ids.primaryImage(input.pageUrl) : undefined);
8
- if (id === undefined) {
17
+ const resolvedId = input.id ?? (input.pageUrl !== undefined ? ids.primaryImage(input.pageUrl) : undefined);
18
+ if (resolvedId === undefined) {
9
19
  throw new Error('buildImageObject: either `id` or `pageUrl` is required');
10
20
  }
11
21
  const piece = {
12
22
  '@type': 'ImageObject',
13
- '@id': id,
23
+ '@id': resolvedId,
14
24
  url: input.url,
15
25
  contentUrl: input.url,
16
26
  width: input.width,
@@ -20,6 +30,7 @@ export function buildImageObject(input, ids) {
20
30
  piece.caption = input.caption;
21
31
  if (input.inLanguage !== undefined)
22
32
  piece.inLanguage = input.inLanguage;
23
- return { ...piece, ...input.extra };
33
+ spreadRemainingProperties(piece, input, HANDLED_KEYS);
34
+ return piece;
24
35
  }
25
36
  //# sourceMappingURL=image.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"image.js","sourceRoot":"","sources":["../../src/pieces/image.ts"],"names":[],"mappings":"AAoBA;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAuB,EAAE,GAAc;IACpE,MAAM,EAAE,GACJ,KAAK,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,YAAY,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IAC5F,IAAI,EAAE,KAAK,SAAS,EAAE,CAAC;QACnB,MAAM,IAAI,KAAK,CAAC,wDAAwD,CAAC,CAAC;IAC9E,CAAC;IAED,MAAM,KAAK,GAA4B;QACnC,OAAO,EAAE,aAAa;QACtB,KAAK,EAAE,EAAE;QACT,GAAG,EAAE,KAAK,CAAC,GAAG;QACd,UAAU,EAAE,KAAK,CAAC,GAAG;QACrB,KAAK,EAAE,KAAK,CAAC,KAAK;QAClB,MAAM,EAAE,KAAK,CAAC,MAAM;KACvB,CAAC;IACF,IAAI,KAAK,CAAC,OAAO,KAAK,SAAS;QAAE,KAAK,CAAC,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC;IAC/D,IAAI,KAAK,CAAC,UAAU,KAAK,SAAS;QAAE,KAAK,CAAC,UAAU,GAAG,KAAK,CAAC,UAAU,CAAC;IAExE,OAAO,EAAE,GAAG,KAAK,EAAE,GAAG,KAAK,CAAC,KAAK,EAAE,CAAC;AACxC,CAAC"}
1
+ {"version":3,"file":"image.js","sourceRoot":"","sources":["../../src/pieces/image.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,yBAAyB,EAAE,MAAM,aAAa,CAAC;AAgBxD,MAAM,YAAY,GAAG,IAAI,GAAG,CAAS;IACjC,SAAS;IACT,IAAI;IACJ,KAAK;IACL,OAAO;IACP,QAAQ;IACR,YAAY;IACZ,SAAS;CACZ,CAAC,CAAC;AAEH;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAuB,EAAE,GAAc;IACpE,MAAM,UAAU,GACZ,KAAK,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,YAAY,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IAC5F,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;QAC3B,MAAM,IAAI,KAAK,CAAC,wDAAwD,CAAC,CAAC;IAC9E,CAAC;IAED,MAAM,KAAK,GAA4B;QACnC,OAAO,EAAE,aAAa;QACtB,KAAK,EAAE,UAAU;QACjB,GAAG,EAAE,KAAK,CAAC,GAAG;QACd,UAAU,EAAE,KAAK,CAAC,GAAG;QACrB,KAAK,EAAE,KAAK,CAAC,KAAK;QAClB,MAAM,EAAE,KAAK,CAAC,MAAM;KACvB,CAAC;IACF,IAAI,KAAK,CAAC,OAAO,KAAK,SAAS;QAAE,KAAK,CAAC,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC;IAC/D,IAAI,KAAK,CAAC,UAAU,KAAK,SAAS;QAAE,KAAK,CAAC,UAAU,GAAG,KAAK,CAAC,UAAU,CAAC;IACxE,yBAAyB,CAAC,KAAK,EAAE,KAAK,EAAE,YAAY,CAAC,CAAC;IAEtD,OAAO,KAAK,CAAC;AACjB,CAAC"}
@@ -1,20 +1,20 @@
1
+ import type { SiteNavigationElementLeaf } from 'schema-dts';
1
2
  import type { IdFactory } from '../ids.js';
2
3
  import type { Reference } from '../types.js';
3
4
  export interface NavigationItem {
4
5
  name: string;
5
6
  url: string;
6
7
  }
7
- export interface SiteNavigationInput {
8
- /** Human-readable nav name, e.g. 'Main navigation'. */
8
+ interface SiteNavigationCoreFields {
9
9
  name: string;
10
- /** Parent WebSite reference (usually ids.website). */
11
10
  isPartOf: Reference;
12
11
  items: readonly NavigationItem[];
13
- extra?: Record<string, unknown>;
14
12
  }
13
+ export type SiteNavigationInput = SiteNavigationCoreFields & Omit<Partial<SiteNavigationElementLeaf>, keyof SiteNavigationCoreFields | '@type'>;
15
14
  /**
16
15
  * Build a schema.org SiteNavigationElement whose `hasPart` is a list of
17
16
  * sub-SiteNavigationElement entries (one per nav link).
18
17
  */
19
18
  export declare function buildSiteNavigationElement(input: SiteNavigationInput, ids: IdFactory): Record<string, unknown>;
19
+ export {};
20
20
  //# sourceMappingURL=navigation.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"navigation.d.ts","sourceRoot":"","sources":["../../src/pieces/navigation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C,MAAM,WAAW,cAAc;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,mBAAmB;IAChC,uDAAuD;IACvD,IAAI,EAAE,MAAM,CAAC;IACb,sDAAsD;IACtD,QAAQ,EAAE,SAAS,CAAC;IACpB,KAAK,EAAE,SAAS,cAAc,EAAE,CAAC;IACjC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAED;;;GAGG;AACH,wBAAgB,0BAA0B,CACtC,KAAK,EAAE,mBAAmB,EAC1B,GAAG,EAAE,SAAS,GACf,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAczB"}
1
+ {"version":3,"file":"navigation.d.ts","sourceRoot":"","sources":["../../src/pieces/navigation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,YAAY,CAAC;AAE5D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAG7C,MAAM,WAAW,cAAc;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;CACf;AAED,UAAU,wBAAwB;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,SAAS,CAAC;IACpB,KAAK,EAAE,SAAS,cAAc,EAAE,CAAC;CACpC;AAED,MAAM,MAAM,mBAAmB,GAAG,wBAAwB,GACtD,IAAI,CAAC,OAAO,CAAC,yBAAyB,CAAC,EAAE,MAAM,wBAAwB,GAAG,OAAO,CAAC,CAAC;AAIvF;;;GAGG;AACH,wBAAgB,0BAA0B,CACtC,KAAK,EAAE,mBAAmB,EAC1B,GAAG,EAAE,SAAS,GACf,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAkBzB"}
@@ -1,3 +1,5 @@
1
+ import { spreadRemainingProperties } from '../types.js';
2
+ const HANDLED_KEYS = new Set(['name', 'isPartOf', 'items']);
1
3
  /**
2
4
  * Build a schema.org SiteNavigationElement whose `hasPart` is a list of
3
5
  * sub-SiteNavigationElement entries (one per nav link).
@@ -8,13 +10,14 @@ export function buildSiteNavigationElement(input, ids) {
8
10
  name: item.name,
9
11
  url: item.url,
10
12
  }));
11
- return {
13
+ const piece = {
12
14
  '@type': 'SiteNavigationElement',
13
15
  '@id': ids.navigation,
14
16
  name: input.name,
15
17
  isPartOf: input.isPartOf,
16
18
  hasPart,
17
- ...input.extra,
18
19
  };
20
+ spreadRemainingProperties(piece, input, HANDLED_KEYS);
21
+ return piece;
19
22
  }
20
23
  //# sourceMappingURL=navigation.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"navigation.js","sourceRoot":"","sources":["../../src/pieces/navigation.ts"],"names":[],"mappings":"AAiBA;;;GAGG;AACH,MAAM,UAAU,0BAA0B,CACtC,KAA0B,EAC1B,GAAc;IAEd,MAAM,OAAO,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACvC,OAAO,EAAE,uBAAuB;QAChC,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,GAAG,EAAE,IAAI,CAAC,GAAG;KAChB,CAAC,CAAC,CAAC;IACJ,OAAO;QACH,OAAO,EAAE,uBAAuB;QAChC,KAAK,EAAE,GAAG,CAAC,UAAU;QACrB,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,OAAO;QACP,GAAG,KAAK,CAAC,KAAK;KACjB,CAAC;AACN,CAAC"}
1
+ {"version":3,"file":"navigation.js","sourceRoot":"","sources":["../../src/pieces/navigation.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,yBAAyB,EAAE,MAAM,aAAa,CAAC;AAgBxD,MAAM,YAAY,GAAG,IAAI,GAAG,CAAS,CAAC,MAAM,EAAE,UAAU,EAAE,OAAO,CAAC,CAAC,CAAC;AAEpE;;;GAGG;AACH,MAAM,UAAU,0BAA0B,CACtC,KAA0B,EAC1B,GAAc;IAEd,MAAM,OAAO,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACvC,OAAO,EAAE,uBAAuB;QAChC,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,GAAG,EAAE,IAAI,CAAC,GAAG;KAChB,CAAC,CAAC,CAAC;IAEJ,MAAM,KAAK,GAA4B;QACnC,OAAO,EAAE,uBAAuB;QAChC,KAAK,EAAE,GAAG,CAAC,UAAU;QACrB,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,OAAO;KACV,CAAC;IAEF,yBAAyB,CAAC,KAAK,EAAE,KAAK,EAAE,YAAY,CAAC,CAAC;IAEtD,OAAO,KAAK,CAAC;AACjB,CAAC"}