/*
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 ?? []),
]),
};
}