/* Implements RFC-1185 explicit per-block shared-context inheritance. A block opts into cross-page prop inheritance by declaring `inherit: `. The resolver performs a single-hop shallow merge — the source block's authored top-level props first, the consuming block's own top-level keys second (an authored key replaces the source key wholesale; nested objects are never recursively merged across pages). Blocks without `inherit` — or with `inherit: false` — keep only their authored props. Do not read files or Astro collections directly. Do not validate propsSchema correctness — that is page.block.validate's job. Do not detect collisions or validate inherit targets — that is shared.context.validate's job. Do not follow the source block's own `inherit` edge — resolution is a single hop. RFC-1185: explicit per-block inherit edge replaces the RFC-0099 implicit priority-order fallback. `collectSharedContextCandidatesByLevel` and `buildSharedContextPageOrder` are removed from the public runtime API — the migration codemod (`shared.context.migrate` in werkstatt-site) hosts the legacy ordering it needs to name effective sources. */ export interface SharedContextBlockLike { id?: string; type?: string; use?: string; props?: Record; /** * Explicit inheritance edge (RFC-1185): a pageId to inherit the matching * block's authored props from, or `false` to opt out of a collision group. */ inherit?: string | false; } export interface SharedContextPageLike { pageId: string; blocks: SharedContextBlockLike[]; } function isRecord(value: unknown): value is Record { return typeof value === "object" && value !== null && !Array.isArray(value); } function getBlockSelector(block: SharedContextBlockLike): string | null { return block.type ?? block.use ?? null; } /** * Canonical shared-context block identity: same `id` and, when both sides * declare a selector, the same `type`/`use`. Exported so validators and the * migration codemod share one matching rule instead of duplicating it. */ export function matchesSharedBlock( source: SharedContextBlockLike, target: SharedContextBlockLike, ): boolean { if (!source.id || !target.id || source.id !== target.id) { return false; } const sourceSelector = getBlockSelector(source); const targetSelector = getBlockSelector(target); if (sourceSelector && targetSelector) { return sourceSelector === targetSelector; } return true; } /** * Resolve the effective props for `block` within one language bucket of pages. * * RFC-1185: inheritance happens only through an explicit `inherit: ` * edge. The source is the matching (`id` + selector) block on the named page — * its `inherit` edge is intentionally not followed. The merge is shallow over * top-level keys: `{ ...sourceAuthoredProps, ...ownProps }`. * * Missing source pages, missing matching blocks, and self-edges resolve to the * authored props unchanged; those conditions are flagged by * `shared.context.validate` (SHARED-CONTEXT-INHERIT-*), not by the resolver. */ export function resolveSharedContextProps(options: { block: SharedContextBlockLike; pages: Map; }): Record { const { block, pages } = options; const ownProps = isRecord(block.props) ? block.props : {}; if (typeof block.inherit !== "string" || block.inherit.length === 0 || !block.id) { return ownProps; } const sourcePage = pages.get(block.inherit); if (!sourcePage) { return ownProps; } const sourceBlock = sourcePage.blocks.find((candidate) => matchesSharedBlock(candidate, block)); if (!sourceBlock || !isRecord(sourceBlock.props)) { return ownProps; } return { ...sourceBlock.props, ...ownProps }; }