/** * Structured Data Projector * * Takes the raw `StructuredDataResult` extracted from a page (via * `structured-data-extractor.ts`) and projects it into a flat, * source-agnostic "common fields" view that downstream consumers * can read uniformly without caring which markup format the source * site happened to use. * * ## Why this exists * * `extractStructuredData()` returns a rich tree with separate * branches for JSON-LD, microdata, OpenGraph, Twitter Card, Dublin * Core, and RDFa. That's the right shape for callers who care * which source provided the data, but it's the wrong shape for * "I just want the page's title and author." * * Real pages tend to publish overlapping subsets of the same * fields across multiple formats: a Wikisource page has both * JSON-LD `Article.headline` and OpenGraph `og:title`; a news * article has Twitter Card title, OpenGraph title, microdata * NewsArticle headline, AND a regular `` tag — all saying * the same thing. The projector picks the most-preferred source * per field and returns a single value, with provenance attached. * * ## Provenance * * Every projected field carries a `source` string (e.g. * "jsonld:Article.headline", "opengraph:title", "microdata:headline") * so callers can audit where each value came from. Useful for * debugging unexpected projections and for AI-citation * verification ("the page's title came from JSON-LD, which is * publisher-controlled metadata"). * * ## Field priority * * For each common field, the projector walks a priority list of * sources and returns the first one that resolves. The lists are * tuned to favor publisher-curated structured metadata (JSON-LD, * microdata) over social-media tags (Twitter Card, OpenGraph), * which in turn beat the bare HTML `<title>`. JSON-LD is * preferred because it's the most widely-adopted machine-readable * format on modern sites and has the richest semantics. * * ## Limitations * * - Bag-of-fields, not full schema introspection. The projection * targets a fixed set of common fields. Custom schemas (e.g. * "give me back ProductData with these specific properties") * are out of scope for this module — the raw `StructuredDataResult` * is still surfaced separately so callers needing custom shapes * can read it directly. * - First-match wins. The projector doesn't try to merge or * reconcile conflicting values across sources. For most fields * on most sites this is fine; for the rare case where multiple * sources disagree, the priority order is deterministic and * the source is reported. */ import type { StructuredDataResult } from './structured-data-extractor.js'; /** * The projected common fields. Every field is optional — pages * vary widely in which structured data they publish. */ export interface ProjectedFields { /** Page or document title. */ title?: string; /** * Document authors. ALWAYS an array, even for single-author * documents (which produce a one-element array). This sidesteps * the polymorphic-string-or-array footgun: callers can always * write `result.author?.join(', ')` without runtime type checks. */ author?: string[]; /** ISO 8601 publication date string. */ datePublished?: string; /** ISO 8601 last-modified date string. */ dateModified?: string; /** * Schema.org type chosen by the projector — the highest-priority * type signal it found (typically from JSON-LD Article-like). * Use `typeAlternates` for the full set of type signals across * all sources, which is useful when sources disagree (e.g. * JSON-LD says Person but microdata says Article). */ type?: string; /** * All distinct type signals found across sources. Empty array * if no type was detected. The first entry matches `type`. * Useful for citation verification where the Book-vs-Article * distinction matters and disagreement between sources is itself * signal worth surfacing. */ typeAlternates?: Array<{ type: string; source: string; }>; /** Document description / summary. */ description?: string; /** Primary image URL. */ image?: string; /** * Page language as a BCP47 code (e.g. "en", "en-US", "es", * "ja-JP"). The projector normalizes Facebook's `og:locale` * underscore convention (`en_US`) to BCP47 (`en-US`). */ language?: string; /** Publisher name. */ publisher?: string; /** * Human-readable site name (e.g. "The New York Times"). * Sourced only from `og:site_name` — Twitter Card's `twitter:site` * is a Twitter handle (@nytimes), not a display name, and is * not used here. */ siteName?: string; } /** * Provenance for each projected field — where the value came from. * Keys mirror `ProjectedFields`. Only present when the corresponding * field is set. */ export type ProjectedFieldSources = Partial<Record<keyof ProjectedFields, string>>; /** * Result of projecting structured data into common fields. */ export interface ProjectedFieldsWithSources { /** The projected values. */ values: ProjectedFields; /** Provenance string per populated field. */ sources: ProjectedFieldSources; } /** * Project a structured data result into common fields. * * Each field is resolved by walking a priority list of sources * and returning the first one that has a value. The priority * order is: * * 1. Schema.org Article-like JSON-LD (Article, NewsArticle, * BlogPosting, ScholarlyArticle) — richest, most curated * 2. Normalized `result.article` (already merged from JSON-LD * and microdata by structured-data-extractor.ts) * 3. OpenGraph * 4. Twitter Card * 5. Dublin Core * 6. Raw meta tags (last resort) * * Title-specific override: if no Article-like JSON-LD exists, the * raw page `<title>` (passed in as `pageTitle`) is also considered * AFTER OpenGraph but before Twitter Card. The page title is * usually the worst source because it's optimized for browser * tabs, but it's the only thing some sites have. */ export declare function projectFields(structured: StructuredDataResult, pageTitle?: string): ProjectedFieldsWithSources; //# sourceMappingURL=structured-projector.d.ts.map