/* Assembles a complete JSON-LD document from a SemanticPageModel by composing all entity-specific nodes. Do not handle raw content parsing. Do not manage external API interactions. RFC-0912: added VideoObject node composition for opted-in content video blocks. RFC-1097: step 6 — compass.migrate codemod run Mechanical v1 to v2 header migration across the workspace: 942 files rewritten — CHANGE_SUMMARY windows collapsed into history, forbidden v1 blocks stripped, KEY_DECISIONS seeded from @ai-invariant comments (5 files) or TODO placeholders (103 files), blocks reordered to canonical order. RFC-1097: sweep — werkstatt-engine clean Sweep batch 4: 73 Compass headers on headerless engine files (certification, component-runtime, isolation, evolution, testing), real KEY_DECISIONS on 75 files (kernel, cache, dht, swim, gitmesh, runtime), ~80 purpose expansions (CONTRACT-02/PURPOSE-02), non-goals on 13 CONTRACT-03 files, CS-07 history literal fix repo-wide (253 files). Policy: .template.ts/.template.astro excludedPaths. werkstatt-engine now 0 diagnostics. */ import { buildArticleNode } from "./jsonld/article.ts"; import { buildBreadcrumbNode } from "./jsonld/breadcrumb.ts"; import { buildCollectionListNode } from "./jsonld/collection-list.ts"; import { createJsonLdContext } from "./jsonld/context.ts"; import { buildFaqNodes } from "./jsonld/faq.ts"; import { buildInitiativesListNode, buildInitiativeNodes } from "./jsonld/initiative.ts"; import { buildOrganizationNode } from "./jsonld/organization.ts"; import { buildPersonNodes } from "./jsonld/person.ts"; import { buildServiceNodes } from "./jsonld/service.ts"; import { dedupeGraph } from "./jsonld/shared.ts"; import type { JsonLdDocument } from "./jsonld/types.ts"; import { buildVideoObjectNodes } from "./jsonld/video.ts"; import { buildWebPageNode } from "./jsonld/webpage.ts"; import { buildWebSiteNode } from "./jsonld/website.ts"; import type { SemanticPageModel } from "./models.ts"; export type { JsonLdDocument } from "./jsonld/types.ts"; export type { JsonLdContext } from "./jsonld/context.ts"; export { buildVideoObjectNodes } from "./jsonld/video.ts"; export function buildJsonLd(page: SemanticPageModel): JsonLdDocument { const context = createJsonLdContext(page); const articleNode = buildArticleNode(context); const breadcrumbNode = buildBreadcrumbNode(context); const collectionListNode = buildCollectionListNode(context); const initiativesListNode = buildInitiativesListNode(context); // RFC-0498: suppress the org-level services ItemList for all surface pages. // Surface pages emit industry-specific Service nodes (depth-1, website-service, depth-5) // or no Service nodes at all (depth-0, 2, 3, 4) — never org-level services. const suppressOrgServices = page.surfaceId !== undefined; const servicesListNode = !suppressOrgServices && context.servicesListId ? { "@type": "ItemList" as const, "@id": context.servicesListId, itemListElement: (context.page.organization.services ?? []).map((service, index) => ({ "@type": "ListItem", position: index + 1, item: { "@id": context.ids.service(service.name) }, name: service.name, })), } : null; return { "@context": "https://schema.org", "@graph": dedupeGraph([ buildOrganizationNode(context), buildWebSiteNode(context), buildWebPageNode(context), ...buildPersonNodes(context), ...buildServiceNodes(context), ...buildFaqNodes(context), ...buildInitiativeNodes(context), ...(initiativesListNode ? [initiativesListNode] : []), ...(servicesListNode ? [servicesListNode] : []), ...(collectionListNode ? [collectionListNode] : []), ...(articleNode ? [articleNode] : []), ...(breadcrumbNode ? [breadcrumbNode] : []), // RFC-0912: VideoObject nodes for opted-in content video blocks. ...buildVideoObjectNodes(context), // RFC-0512: extra nodes from team profile pages (SoftwareApplication, CollectionPage). ...(page.extraGraphNodes ?? []), ]), }; }