"use strict";
// SPDX-License-Identifier: AGPL-3.0-or-later
// Copyright (C) 2024-2026 Gracker (Chris)
// This file is part of SmartPerfetto. See LICENSE for details.
Object.defineProperty(exports, "__esModule", { value: true });
exports.DEFAULT_METADATA_FIELDS = exports.SSE_EVENT_TYPES = exports.VALID_DISPLAY_FORMATS = exports.VALID_DISPLAY_LEVELS = exports.VALID_DISPLAY_LAYERS = exports.DEFAULT_COLUMN_PATTERNS = exports.VALID_CLICK_ACTIONS = exports.VALID_COLUMN_FORMATS = exports.VALID_COLUMN_TYPES = void 0;
exports.inferColumnDefinition = inferColumnDefinition;
exports.createDataEnvelope = createDataEnvelope;
exports.buildColumnDefinitions = buildColumnDefinitions;
exports.isValidDisplayLayer = isValidDisplayLayer;
exports.isDataEvent = isDataEvent;
exports.isLegacySkillEvent = isLegacySkillEvent;
exports.validateDisplayResult = validateDisplayResult;
exports.validateLayeredSkillResult = validateLayeredSkillResult;
exports.extractMetadata = extractMetadata;
exports.filterMetadataColumns = filterMetadataColumns;
exports.organizeIntoLayers = organizeIntoLayers;
exports.displayResultToEnvelope = displayResultToEnvelope;
exports.layeredResultToEnvelopes = layeredResultToEnvelopes;
exports.envelopeToDisplayResult = envelopeToDisplayResult;
exports.envelopesToLayeredResult = envelopesToLayeredResult;
exports.validateColumnDefinition = validateColumnDefinition;
exports.validateDataEnvelope = validateDataEnvelope;
exports.generateEventId = generateEventId;
/**
 * SmartPerfetto Data Contract
 *
 * This file defines the SINGLE SOURCE OF TRUTH for data structures
 * shared between:
 * - Backend (SkillExecutor, AgentRuntime, SSE streaming)
 * - Frontend (AIPanel, SqlResultTable)
 * - HTML Report Generator
 *
 * IMPORTANT: Any changes here must be synchronized with frontend types.
 * Run `npm run generate:frontend-types` to sync types to frontend.
 *
 * @module dataContract
 * @version 2.0.0 - DataEnvelope refactoring
 */
// =============================================================================
// Column Definition System (Phase 0 - DataEnvelope Refactoring)
// =============================================================================
/**
 * Column Data Types - Semantic type of column data
 *
 * The type determines:
 * - How values are parsed and validated
 * - Default formatting
 * - Available click actions
 */
exports.VALID_COLUMN_TYPES = [
    'string', // Text value
    'number', // Numeric value (int or float)
    'timestamp', // Time value (in ns by default)
    'duration', // Duration value (in ns by default)
    'percentage', // Percentage (0-100 or 0-1)
    'bytes', // Byte size
    'boolean', // True/false
    'enum', // Categorical value
    'json', // JSON object/array
    'link', // URL or internal link
];
/**
 * Column Format - How to display the value
 */
exports.VALID_COLUMN_FORMATS = [
    'default', // Use type's default formatting
    'compact', // Abbreviated format (e.g., "1.2M" for 1200000)
    'full', // Full precision
    'relative', // Relative to some base (e.g., "+10ms")
    'percentage', // Show as X%
    'duration_ms', // Show duration in ms
    'duration_us', // Legacy alias: interpret input unit, display in ms
    'timestamp_relative', // Show as time offset from trace start
    'timestamp_absolute', // Show as absolute timestamp
    'bytes_human', // Show as KB/MB/GB
    'code', // Monospace/code formatting
    'truncate', // Truncate long values with ellipsis
];
/**
 * Click Action - What happens when user clicks a column value
 */
exports.VALID_CLICK_ACTIONS = [
    'none', // No action
    'navigate_timeline', // Jump to timestamp on Perfetto timeline
    'navigate_range', // Highlight time range on timeline
    'copy', // Copy value to clipboard
    'expand', // Expand to show more details
    'filter', // Add filter for this value
    'link', // Open external/internal link
];
/**
 * Default column definitions based on common column name patterns
 * Used when skills don't explicitly define columns
 *
 * IMPORTANT: Order matters! More specific patterns should come before generic ones.
 * The first matching pattern wins.
 */
exports.DEFAULT_COLUMN_PATTERNS = [
    // Timestamp columns (special-case start/end + *_ts_str variants)
    // - end timestamps should jump to a point (navigate_timeline)
    // - start timestamps should prefer range selection when dur_str exists
    { pattern: /^end_ts$|^end_ts_str$|^ts_end$|^end_time$/i,
        definition: { type: 'timestamp', format: 'timestamp_relative', clickAction: 'navigate_timeline', unit: 'ns' } },
    { pattern: /^ts$|^ts_str$|^start_ts$|^start_ts_str$|^start_time$/i,
        definition: { type: 'timestamp', format: 'timestamp_relative', clickAction: 'navigate_range', unit: 'ns', durationColumn: 'dur_str' } },
    { pattern: /_ts$|timestamp$|_timestamp$|start_time|end_time/i,
        definition: { type: 'timestamp', format: 'timestamp_relative', clickAction: 'navigate_timeline', unit: 'ns' } },
    // Duration columns stored as digit strings (e.g., ts_str + dur_str for precise navigation)
    { pattern: /^dur_str$|_dur_str$|^duration_str$|_duration_str$/i,
        definition: { type: 'duration', format: 'duration_ms', unit: 'ns' } },
    // Duration columns with explicit unit suffixes (MUST be before generic duration pattern)
    // These patterns indicate the value is ALREADY in the specified unit, not nanoseconds
    // _ms suffix: value is already in milliseconds (e.g., vsync_period_ms = 8.33)
    { pattern: /_ms$/i,
        definition: { type: 'duration', format: 'duration_ms', unit: 'ms' } },
    // _us suffix: value is in microseconds, normalize display to ms
    { pattern: /_us$/i,
        definition: { type: 'duration', format: 'duration_ms', unit: 'us' } },
    // _ns suffix: value is already in nanoseconds
    { pattern: /_ns$/i,
        definition: { type: 'duration', format: 'duration_ms', unit: 'ns' } },
    // Generic duration columns (no unit suffix - assume nanoseconds from Perfetto trace)
    { pattern: /^dur$|_dur$|duration$|_duration$|elapsed|latency/i,
        definition: { type: 'duration', format: 'duration_ms', unit: 'ns' } },
    // Percentage columns - match rate/ratio/percent/pct but EXCLUDE refresh_rate, frame_rate, sample_rate
    // These Hz-based rates are numbers, not percentages
    { pattern: /(?<!refresh_|frame_|sample_)rate$|ratio$|percent|pct$/i,
        definition: { type: 'percentage', format: 'percentage' } },
    // Byte size columns
    { pattern: /size$|bytes$|memory$|_kb$|_mb$|_gb$/i,
        definition: { type: 'bytes', format: 'bytes_human' } },
    // Token ID columns - large integers that should be preserved as strings (no formatting)
    // frame_id is a display_frame_token which can exceed JavaScript's safe integer range
    { pattern: /^frame_id$|^display_frame_token$|^surface_frame_token$/i,
        definition: { type: 'string' } },
    // Count/ID columns (numeric IDs that can be safely formatted)
    { pattern: /^id$|_id$|^count$|_count$|^num_|_num$|^pid$|^tid$|^upid$|^utid$|^session_id$|^track_id$|^slice_id$|^arg_set_id$|_index$|^frame_index$/i,
        definition: { type: 'number', format: 'compact' } },
    // Boolean columns
    { pattern: /^is_|^has_|^can_|_flag$/i,
        definition: { type: 'boolean' } },
];
/**
 * Infer column definition from column name using patterns
 */
function inferColumnDefinition(columnName) {
    for (const { pattern, definition } of exports.DEFAULT_COLUMN_PATTERNS) {
        if (pattern.test(columnName)) {
            return { name: columnName, type: 'string', ...definition };
        }
    }
    // Default: string type
    return { name: columnName, type: 'string' };
}
const VALID_COLUMN_UNITS = ['ns', 'us', 'ms', 's'];
const VALID_COLUMN_WIDTHS = ['narrow', 'medium', 'wide', 'auto'];
function normalizeDisplayLayer(layer) {
    return typeof layer === 'string' && isValidDisplayLayer(layer) ? layer : 'list';
}
function normalizeDisplayFormat(format) {
    return typeof format === 'string' && exports.VALID_DISPLAY_FORMATS.includes(format)
        ? format
        : 'table';
}
function normalizeDisplayLevel(level) {
    return typeof level === 'string' && exports.VALID_DISPLAY_LEVELS.includes(level)
        ? level
        : 'detail';
}
function sanitizeColumnDefinition(name, explicit) {
    const inferred = inferColumnDefinition(name);
    if (!explicit || typeof explicit !== 'object')
        return inferred;
    const column = { ...inferred, name };
    if (typeof explicit.label === 'string')
        column.label = explicit.label;
    if (exports.VALID_COLUMN_TYPES.includes(explicit.type))
        column.type = explicit.type;
    if (exports.VALID_COLUMN_FORMATS.includes(explicit.format))
        column.format = explicit.format;
    if (exports.VALID_CLICK_ACTIONS.includes(explicit.clickAction))
        column.clickAction = explicit.clickAction;
    if (typeof explicit.durationColumn === 'string')
        column.durationColumn = explicit.durationColumn;
    if (VALID_COLUMN_UNITS.includes(explicit.unit))
        column.unit = explicit.unit;
    if (typeof explicit.hidden === 'boolean')
        column.hidden = explicit.hidden;
    if (typeof explicit.sortable === 'boolean')
        column.sortable = explicit.sortable;
    if (explicit.defaultSort === 'asc' || explicit.defaultSort === 'desc')
        column.defaultSort = explicit.defaultSort;
    if ((typeof explicit.width === 'number' && Number.isFinite(explicit.width)) ||
        VALID_COLUMN_WIDTHS.includes(explicit.width)) {
        column.width = explicit.width;
    }
    if (typeof explicit.tooltip === 'string')
        column.tooltip = explicit.tooltip;
    if (Array.isArray(explicit.enumValues) && explicit.enumValues.every((value) => typeof value === 'string')) {
        column.enumValues = explicit.enumValues;
    }
    if (typeof explicit.cssClass === 'string')
        column.cssClass = explicit.cssClass;
    return column;
}
function sanitizeColumnDefinitions(columns) {
    if (!Array.isArray(columns))
        return undefined;
    const sanitized = columns
        .filter((column) => !!column && typeof column === 'object' && !Array.isArray(column) &&
        typeof column.name === 'string' && column.name.length > 0)
        .map((column) => sanitizeColumnDefinition(column.name, column));
    return sanitized.length > 0 ? sanitized : undefined;
}
function sanitizeMetadataFields(fields) {
    if (!Array.isArray(fields))
        return undefined;
    const sanitized = fields.filter((field) => typeof field === 'string' && field.length > 0);
    return sanitized.length > 0 ? sanitized : undefined;
}
/**
 * Create a DataEnvelope from raw data
 */
function createDataEnvelope(data, options) {
    const columns = sanitizeColumnDefinitions(options.columns);
    const metadataFields = sanitizeMetadataFields(options.metadataFields);
    return {
        meta: {
            type: options.type,
            version: '2.0.0',
            source: options.source,
            timestamp: Date.now(),
            skillId: options.skillId,
            stepId: options.stepId,
            evidenceRefId: options.evidenceRefId,
            traceSide: options.traceSide,
            traceId: options.traceId,
            queryHash: options.queryHash,
            sourceToolCallId: options.sourceToolCallId,
            paramsHash: options.paramsHash,
            artifactId: options.artifactId,
            sourceArtifactId: options.sourceArtifactId,
            identityRefId: options.identityRefId,
            identityStatus: options.identityStatus,
            identityWarnings: options.identityWarnings,
            identityResolution: options.identityResolution,
            processIdentityWarning: options.processIdentityWarning,
            planPhaseId: options.planPhaseId,
            planPhaseTitle: options.planPhaseTitle,
            planPhaseGoal: options.planPhaseGoal,
            planPhaseAttribution: options.planPhaseAttribution,
            planPhaseWarning: options.planPhaseWarning,
            toolNarration: options.toolNarration,
            producerReason: options.producerReason,
            intent: options.intent,
        },
        data,
        display: {
            layer: normalizeDisplayLayer(options.layer),
            format: normalizeDisplayFormat(options.format),
            title: options.title,
            columns,
            metadataFields,
            highlights: options.highlights,
            level: normalizeDisplayLevel(options.level),
            ...(options.collapsible !== undefined && { collapsible: options.collapsible }),
            ...(options.defaultCollapsed !== undefined && { defaultCollapsed: options.defaultCollapsed }),
        },
    };
}
/**
 * Build column definitions from raw column names
 * Uses explicit definitions if provided, falls back to inference
 */
function buildColumnDefinitions(columnNames, explicitDefinitions) {
    const explicitMap = new Map();
    if (explicitDefinitions) {
        for (const def of explicitDefinitions) {
            if (def.name) {
                explicitMap.set(def.name, def);
            }
        }
    }
    return columnNames.map(name => {
        const explicit = explicitMap.get(name);
        return sanitizeColumnDefinition(name, explicit);
    });
}
// =============================================================================
// Display Layer System
// =============================================================================
/**
 * Display Layers - Controls WHERE data appears in the UI
 *
 * - overview: L1 - Top-level aggregated metrics (summary cards)
 * - list: L2 - Main data tables with rows
 * - session: Session-level grouping (e.g., scroll sessions)
 * - deep: L4 - Detailed analysis (expandable rows in L2)
 * - diagnosis: Diagnostic findings and root-cause evidence
 *
 * This is an EXHAUSTIVE list. To add a new layer:
 * 1. Add to this array
 * 2. Update frontend layer handling
 * 3. Update HTML report generation
 */
exports.VALID_DISPLAY_LAYERS = ['overview', 'list', 'session', 'deep', 'diagnosis'];
/**
 * Validate if a string is a valid DisplayLayer
 */
function isValidDisplayLayer(layer) {
    if (!layer)
        return false;
    return exports.VALID_DISPLAY_LAYERS.includes(layer);
}
/**
 * Display Levels - Controls HOW MUCH detail to show
 *
 * - none: Do not display
 * - debug: Only show in debug mode
 * - detail: Show full details
 * - summary: Show summarized version
 * - key: Key metric, always prominent
 * - hidden: Keep in envelope but hide by default
 */
exports.VALID_DISPLAY_LEVELS = ['none', 'debug', 'detail', 'summary', 'key', 'hidden'];
/**
 * Display Formats - HOW to render the data
 */
exports.VALID_DISPLAY_FORMATS = ['table', 'chart', 'text', 'timeline', 'summary', 'metric'];
// =============================================================================
// SSE Event Types
// =============================================================================
/**
 * SSE Event Types for streaming updates
 *
 * NEW (v2.0):
 * - data: Unified event carrying DataEnvelope or DataEnvelope[]
 *
 * LEGACY (still supported for backward compatibility):
 * - skill_data: Contains LayeredSkillResult from skill execution
 * - skill_layered_result: Alias for skill_data
 *
 * COMMON:
 * - finding: Individual finding/diagnostic
 * - progress: Phase/step progress update
 * - error: Error message
 * - snapshot_created: Analysis result snapshot persisted for comparison
 * - analysis_completed: Final result with report URL
 */
exports.SSE_EVENT_TYPES = [
    // v2.0 unified event
    'data',
    // Legacy events (backward compatibility)
    'skill_data',
    'skill_layered_result',
    // Common events
    'finding',
    'progress',
    'conversation_step',
    'error',
    'snapshot_created',
    'analysis_completed',
    'thought',
    'tool_call',
    'conclusion',
    'scene_detected',
    'track_data',
    'worker_thought',
    'architecture_detected',
    // Scene Story Pipeline events — 'scene_story_' prefix keeps these strictly
    // distinct from the legacy 'scene_detected' event (singular vs plural trap).
    'scene_story_detected',
    'scene_story_selection_ready',
    'scene_story_queued',
    'scene_story_started',
    'scene_story_retrying',
    'scene_story_completed',
    'scene_story_failed',
    'scene_story_cancelled',
    'scene_story_dropped',
    'scene_story_report_ready',
    'scene_story_smart_eta_refined',
];
/**
 * Check if SSE event type is the new unified data event
 */
function isDataEvent(eventType) {
    return eventType === 'data';
}
/**
 * Check if SSE event type is a legacy skill data event
 */
function isLegacySkillEvent(eventType) {
    return eventType === 'skill_data' || eventType === 'skill_layered_result';
}
/**
 * Default metadata fields - extracted from data if present
 * These are commonly used fields that make sense as metadata
 */
exports.DEFAULT_METADATA_FIELDS = [
    'layer_name',
    'process_name',
    'package_name',
    'architecture',
    'device_model',
    'android_version',
];
/**
 * Validate a DisplayResult structure
 */
function validateDisplayResult(result) {
    const errors = [];
    if (!result) {
        errors.push({ path: '', message: 'DisplayResult is null or undefined' });
        return errors;
    }
    if (!result.stepId) {
        errors.push({ path: 'stepId', message: 'stepId is required' });
    }
    if (!result.title) {
        errors.push({ path: 'title', message: 'title is required' });
    }
    if (result.layer && !isValidDisplayLayer(result.layer)) {
        errors.push({
            path: 'layer',
            message: `Invalid layer: ${result.layer}. Valid values: ${exports.VALID_DISPLAY_LAYERS.join(', ')}`,
            value: result.layer
        });
    }
    if (result.level && !exports.VALID_DISPLAY_LEVELS.includes(result.level)) {
        errors.push({
            path: 'level',
            message: `Invalid level: ${result.level}. Valid values: ${exports.VALID_DISPLAY_LEVELS.join(', ')}`,
            value: result.level
        });
    }
    if (result.format && !exports.VALID_DISPLAY_FORMATS.includes(result.format)) {
        errors.push({
            path: 'format',
            message: `Invalid format: ${result.format}. Valid values: ${exports.VALID_DISPLAY_FORMATS.join(', ')}`,
            value: result.format
        });
    }
    return errors;
}
/**
 * Validate a LayeredSkillResult structure
 */
function validateLayeredSkillResult(result) {
    const errors = [];
    if (!result) {
        errors.push({ path: '', message: 'LayeredSkillResult is null or undefined' });
        return errors;
    }
    if (!result.skillId) {
        errors.push({ path: 'skillId', message: 'skillId is required' });
    }
    if (!result.layers) {
        errors.push({ path: 'layers', message: 'layers object is required' });
        return errors;
    }
    // Validate each layer's results
    for (const layerName of exports.VALID_DISPLAY_LAYERS) {
        const layer = result.layers[layerName];
        if (layer) {
            for (const [stepId, displayResult] of Object.entries(layer)) {
                const displayErrors = validateDisplayResult(displayResult);
                for (const err of displayErrors) {
                    errors.push({
                        path: `layers.${layerName}.${stepId}.${err.path}`,
                        message: err.message,
                        value: err.value
                    });
                }
            }
        }
    }
    return errors;
}
// =============================================================================
// Helper Functions
// =============================================================================
/**
 * Extract metadata from a data row based on configured fields
 */
function extractMetadata(row, columns, metadataFields = [...exports.DEFAULT_METADATA_FIELDS]) {
    const metadata = {};
    if (Array.isArray(row)) {
        // Row is an array, use columns to map
        for (const field of metadataFields) {
            const idx = columns.indexOf(field);
            if (idx >= 0 && row[idx] !== undefined && row[idx] !== null) {
                metadata[field] = row[idx];
            }
        }
    }
    else if (typeof row === 'object') {
        // Row is an object
        for (const field of metadataFields) {
            if (row[field] !== undefined && row[field] !== null) {
                metadata[field] = row[field];
            }
        }
    }
    return metadata;
}
/**
 * Remove metadata columns from column list
 */
function filterMetadataColumns(columns, metadataFields = [...exports.DEFAULT_METADATA_FIELDS]) {
    return columns.filter(col => !metadataFields.includes(col));
}
/**
 * Organize DisplayResults into LayeredSkillResult
 */
function organizeIntoLayers(skillId, skillName, displayResults, diagnostics = [], executionTimeMs = 0) {
    const layers = {
        overview: {},
        list: {},
        session: {},
        deep: {},
        diagnosis: {},
    };
    for (const result of displayResults) {
        const layer = result.layer || 'list'; // Default to list
        if (isValidDisplayLayer(layer)) {
            layers[layer][result.stepId] = result;
        }
    }
    return {
        skillId,
        skillName,
        layers,
        diagnostics,
        metadata: {
            executedAt: new Date().toISOString(),
            executionTimeMs,
        },
    };
}
// =============================================================================
// DataEnvelope Conversion Utilities
// =============================================================================
/**
 * Convert a DisplayResult to a DataEnvelope
 *
 * This bridges the legacy DisplayResult format to the new DataEnvelope format,
 * enabling gradual migration.
 */
function displayResultToEnvelope(result, skillId, explicitColumns) {
    const explicitColumnNames = Array.isArray(explicitColumns)
        ? explicitColumns
            .map((def) => def?.name)
            .filter((name) => typeof name === 'string' && name.length > 0)
        : [];
    const envelopeColumnNames = result.data.columns
        ? (explicitColumnNames.length > 0 ? explicitColumnNames : result.data.columns)
        : undefined;
    // Build column definitions from data columns
    const columns = envelopeColumnNames
        ? buildColumnDefinitions(envelopeColumnNames, explicitColumns)
        : undefined;
    return createDataEnvelope(result.data, {
        type: 'skill_result',
        source: `${skillId}:${result.stepId}`,
        skillId,
        stepId: result.stepId,
        title: result.title,
        layer: result.layer,
        format: result.format,
        columns,
        metadataFields: result.metadataConfig?.fields,
        highlights: result.highlight,
        level: result.level,
        collapsible: result.collapsible,
        defaultCollapsed: result.defaultCollapsed,
    });
}
/**
 * Convert a LayeredSkillResult to an array of DataEnvelopes
 *
 * Each DisplayResult in each layer becomes a separate DataEnvelope.
 */
function layeredResultToEnvelopes(result, columnDefinitions) {
    const envelopes = [];
    for (const layerName of exports.VALID_DISPLAY_LAYERS) {
        const layer = result.layers[layerName];
        if (!layer)
            continue;
        for (const [stepId, displayResult] of Object.entries(layer)) {
            // Prefer external columnDefinitions, fallback to embedded columnDefinitions in DisplayResult
            const explicitColumns = columnDefinitions?.[stepId] ?? displayResult.columnDefinitions;
            const envelope = displayResultToEnvelope(displayResult, result.skillId, explicitColumns);
            envelopes.push(envelope);
        }
    }
    return envelopes;
}
/**
 * Convert a DataEnvelope back to DisplayResult (for backward compatibility)
 */
function envelopeToDisplayResult(envelope) {
    return {
        stepId: envelope.meta.stepId || envelope.meta.source,
        title: envelope.display.title,
        level: envelope.display.level || 'detail',
        layer: envelope.display.layer,
        format: envelope.display.format,
        data: envelope.data,
        highlight: envelope.display.highlights,
        metadataConfig: envelope.display.metadataFields
            ? { fields: envelope.display.metadataFields }
            : undefined,
    };
}
/**
 * Convert an array of DataEnvelopes to LayeredSkillResult
 */
function envelopesToLayeredResult(envelopes, skillId, skillName, diagnostics = [], executionTimeMs = 0) {
    const displayResults = envelopes.map(envelopeToDisplayResult);
    return organizeIntoLayers(skillId, skillName, displayResults, diagnostics, executionTimeMs);
}
// =============================================================================
// Enhanced Validation
// =============================================================================
/**
 * Validate a ColumnDefinition
 */
function validateColumnDefinition(column) {
    const errors = [];
    if (!column) {
        errors.push({ path: '', message: 'ColumnDefinition is null or undefined' });
        return errors;
    }
    if (!column.name || typeof column.name !== 'string') {
        errors.push({ path: 'name', message: 'name is required and must be a string' });
    }
    if (!column.type || !exports.VALID_COLUMN_TYPES.includes(column.type)) {
        errors.push({
            path: 'type',
            message: `Invalid column type: ${column.type}. Valid values: ${exports.VALID_COLUMN_TYPES.join(', ')}`,
            value: column.type,
        });
    }
    if (column.format && !exports.VALID_COLUMN_FORMATS.includes(column.format)) {
        errors.push({
            path: 'format',
            message: `Invalid column format: ${column.format}. Valid values: ${exports.VALID_COLUMN_FORMATS.join(', ')}`,
            value: column.format,
        });
    }
    if (column.clickAction && !exports.VALID_CLICK_ACTIONS.includes(column.clickAction)) {
        errors.push({
            path: 'clickAction',
            message: `Invalid click action: ${column.clickAction}. Valid values: ${exports.VALID_CLICK_ACTIONS.join(', ')}`,
            value: column.clickAction,
        });
    }
    if (column.unit && !VALID_COLUMN_UNITS.includes(column.unit)) {
        errors.push({
            path: 'unit',
            message: `Invalid unit: ${column.unit}. Valid values: ${VALID_COLUMN_UNITS.join(', ')}`,
            value: column.unit,
        });
    }
    if (column.width !== undefined &&
        !((typeof column.width === 'number' && Number.isFinite(column.width)) ||
            VALID_COLUMN_WIDTHS.includes(column.width))) {
        errors.push({
            path: 'width',
            message: `Invalid width: ${column.width}. Must be a finite number or one of: ${VALID_COLUMN_WIDTHS.join(', ')}`,
            value: column.width,
        });
    }
    return errors;
}
/**
 * Validate a DataEnvelope
 */
function validateDataEnvelope(envelope) {
    const errors = [];
    if (!envelope) {
        errors.push({ path: '', message: 'DataEnvelope is null or undefined' });
        return errors;
    }
    // Validate meta
    if (!envelope.meta) {
        errors.push({ path: 'meta', message: 'meta is required' });
    }
    else {
        if (!envelope.meta.type) {
            errors.push({ path: 'meta.type', message: 'meta.type is required' });
        }
        if (!envelope.meta.source) {
            errors.push({ path: 'meta.source', message: 'meta.source is required' });
        }
        if (!envelope.meta.version) {
            errors.push({ path: 'meta.version', message: 'meta.version is required' });
        }
    }
    // Validate display
    if (!envelope.display) {
        errors.push({ path: 'display', message: 'display is required' });
    }
    else {
        if (!envelope.display.layer || !isValidDisplayLayer(envelope.display.layer)) {
            errors.push({
                path: 'display.layer',
                message: `Invalid display layer: ${envelope.display.layer}. Valid values: ${exports.VALID_DISPLAY_LAYERS.join(', ')}`,
                value: envelope.display.layer,
            });
        }
        if (!envelope.display.format || !exports.VALID_DISPLAY_FORMATS.includes(envelope.display.format)) {
            errors.push({
                path: 'display.format',
                message: `Invalid display format: ${envelope.display.format}. Valid values: ${exports.VALID_DISPLAY_FORMATS.join(', ')}`,
                value: envelope.display.format,
            });
        }
        if (envelope.display.level && !exports.VALID_DISPLAY_LEVELS.includes(envelope.display.level)) {
            errors.push({
                path: 'display.level',
                message: `Invalid display level: ${envelope.display.level}. Valid values: ${exports.VALID_DISPLAY_LEVELS.join(', ')}`,
                value: envelope.display.level,
            });
        }
        if (!envelope.display.title) {
            errors.push({ path: 'display.title', message: 'display.title is required' });
        }
        // Validate columns if present
        if (envelope.display.columns !== undefined && !Array.isArray(envelope.display.columns)) {
            errors.push({
                path: 'display.columns',
                message: 'display.columns must be an array',
                value: envelope.display.columns,
            });
        }
        else if (Array.isArray(envelope.display.columns)) {
            for (let i = 0; i < envelope.display.columns.length; i++) {
                const columnErrors = validateColumnDefinition(envelope.display.columns[i]);
                for (const err of columnErrors) {
                    errors.push({
                        path: `display.columns[${i}].${err.path}`,
                        message: err.message,
                        value: err.value,
                    });
                }
            }
        }
        if (envelope.display.metadataFields !== undefined && !Array.isArray(envelope.display.metadataFields)) {
            errors.push({
                path: 'display.metadataFields',
                message: 'display.metadataFields must be an array of strings',
                value: envelope.display.metadataFields,
            });
        }
        else if (Array.isArray(envelope.display.metadataFields)) {
            envelope.display.metadataFields.forEach((field, index) => {
                if (typeof field !== 'string') {
                    errors.push({
                        path: `display.metadataFields[${index}]`,
                        message: 'metadataFields entries must be strings',
                        value: field,
                    });
                }
            });
        }
    }
    // Validate data exists
    if (envelope.data === undefined) {
        errors.push({ path: 'data', message: 'data is required' });
    }
    return errors;
}
/**
 * Generate a unique event ID for deduplication
 */
function generateEventId(skillId, stepId) {
    const timestamp = Date.now();
    const random = Math.random().toString(36).substring(2, 8);
    return stepId ? `${skillId}:${stepId}:${timestamp}:${random}` : `${skillId}:${timestamp}:${random}`;
}
//# sourceMappingURL=dataContract.js.map