export type TemporalValidityMode = "current" | "at" | "all"; export type TemporalDataSelection = { validityMode: TemporalValidityMode; validAt: string | null; recordedAt: string | null; }; export type TemporalContextChangedEventDetail = { selection: TemporalDataSelection; scopeKey: string; }; export const PLATFORM_TEMPORAL_CONTEXT_CHANGED_EVENT = "govoplan:temporal-context-changed"; export const DEFAULT_TEMPORAL_DATA_SELECTION: TemporalDataSelection = { validityMode: "current", validAt: null, recordedAt: null }; let activeSelection = DEFAULT_TEMPORAL_DATA_SELECTION; export function normalizeTemporalDataSelection( value: Partial | null | undefined ): TemporalDataSelection { const validityMode = ["current", "at", "all"].includes( String(value?.validityMode) ) ? value?.validityMode as TemporalValidityMode : "current"; const validAt = validityMode === "at" ? normalizeTimestamp(value?.validAt) : null; if (validityMode === "at" && !validAt) { throw new Error("i18n:govoplan-core.select_valid_date_time"); } return { validityMode, validAt, recordedAt: normalizeTimestamp(value?.recordedAt) }; } export function temporalDataSelectionIsDefault( value: TemporalDataSelection ): boolean { return value.validityMode === "current" && !value.recordedAt; } export function temporalDataSelectionKey(value: TemporalDataSelection): string { return [value.validityMode, value.validAt ?? "", value.recordedAt ?? ""].join(":"); } export function setActiveTemporalDataSelection( value: TemporalDataSelection ): void { activeSelection = normalizeTemporalDataSelection(value); } export function activeTemporalDataSelection(): TemporalDataSelection { return activeSelection; } export function temporalRequestHeaders(): Record { const selection = activeTemporalDataSelection(); if (temporalDataSelectionIsDefault(selection)) return {}; const headers: Record = { "X-Govoplan-Validity-Mode": selection.validityMode }; if (selection.validAt) headers["X-Govoplan-Valid-At"] = selection.validAt; if (selection.recordedAt) { headers["X-Govoplan-Recorded-At"] = selection.recordedAt; } return headers; } export function dispatchTemporalContextChanged( selection: TemporalDataSelection, scopeKey: string ): void { if (typeof window === "undefined") return; window.dispatchEvent( new CustomEvent( PLATFORM_TEMPORAL_CONTEXT_CHANGED_EVENT, { detail: { selection, scopeKey } } ) ); } function normalizeTimestamp(value: string | null | undefined): string | null { const clean = String(value || "").trim(); if (!clean) return null; const instant = new Date(clean); if (!Number.isFinite(instant.getTime())) return null; return instant.toISOString(); }