/**
 * @fileoverview Duplicate Detection Kanban Board Resource Component
 *
 * Dashboard resource for reviewing duplicate detection results in a Kanban-style
 * board with three columns: Pending Review, Approved, and Rejected.
 * Supports triggering new detection runs with real-time progress via
 * GraphQL subscriptions.
 */
import { OnDestroy, AfterViewInit } from '@angular/core';
import { Subject } from 'rxjs';
import { CompositeKey, RecordDependency } from '@memberjunction/core';
import { ResourceData, MJDuplicateRunEntity, MJDuplicateRunDetailEntity, MJDuplicateRunDetailMatchEntity } from '@memberjunction/core-entities';
import { BaseResourceComponent, NavigationService } from '@memberjunction/ng-shared';
import * as i0 from "@angular/core";
/**
 * Represents a group of duplicate matches for a single source record,
 * aggregated from detail and match entities for display on the Kanban board.
 */
interface RecordMetadataInfo {
    Name?: string;
    Entity?: string;
    EntityIcon?: string;
    Description?: string;
    Status?: string;
    Type?: string;
    [key: string]: string | undefined;
}
interface DuplicateGroup {
    DetailId: string;
    RunId: string;
    RecordId: string;
    EntityName: string;
    EntityIcon: string;
    RecordName: string;
    ApprovalStatus: 'Pending' | 'Approved' | 'Rejected';
    MatchCount: number;
    HighestScore: number;
    Matches: MJDuplicateRunDetailMatchEntity[];
    MatchedAt: Date;
    /** Parsed source record metadata from vector DB */
    Metadata: RecordMetadataInfo;
    /** Top matches with parsed metadata for display */
    TopMatchSummaries: Array<{
        Name: string;
        Score: number;
    }>;
}
interface DuplicateFilter {
    EntityName: string;
    MinScore: number;
    MaxScore: number;
    DateFrom: string;
    DateTo: string;
}
/** A row in the comparison grid representing one entity field across all records */
interface ComparisonFieldRow {
    FieldName: string;
    DisplayName: string;
    Category: string | null;
    SourceValue: string | undefined;
    MatchValues: (string | undefined)[];
    HasDifference: boolean;
    /** Index of the column whose value is selected for merge (0 = source, 1+ = match index) */
    SelectedColumnIndex: number;
}
/** LLM reasoning recommendation surfaced alongside a match (null when reasoning never ran) */
type LLMRecommendation = 'Merge' | 'NotDuplicate' | 'Uncertain';
/** Parsed match info for the comparison panel columns */
interface ComparisonMatchInfo {
    Match: MJDuplicateRunDetailMatchEntity;
    Name: string;
    Score: number;
    Metadata: RecordMetadataInfo;
    DiffCount: number;
    /** LLM recommendation for this match (null if reasoning did not run) */
    LLMRecommendation: LLMRecommendation | null;
    /** LLM confidence 0-1 (distinct from the vector Score / MatchProbability) */
    LLMConfidence: number | null;
    /** LLM free-text rationale (may be long; shown in an expandable region) */
    LLMReasoning: string | null;
    /** True when the LLM verdict contradicts the vector score — the prime human-review trigger */
    HasDisagreement: boolean;
}
/** Lightweight entity document info for the picker dropdown */
interface EntityDocumentOption {
    ID: string;
    Name: string;
    EntityName: string;
    PotentialMatchThreshold: number;
    AbsoluteMatchThreshold: number;
    /** Master switch for the LLM reasoning layer (off = vector-only). */
    EnableLLMReasoning: boolean;
    /** Vector-score gate (0–1): the LLM only runs on matched sets at or above this. Null = any non-empty set. */
    ReasoningThreshold: number | null;
}
export declare class DuplicateDetectionResourceComponent extends BaseResourceComponent implements AfterViewInit, OnDestroy {
    /** Close the reasoning popover first (if open), otherwise the comparison panel, on Escape */
    OnEscapeKey(): void;
    private cdr;
    private activityService;
    /** Activity-tracker id for the currently running detection (P3). */
    private detectionActivityID;
    protected navigationService: NavigationService;
    protected destroy$: Subject<void>;
    private filterSubject;
    IsLoading: boolean;
    /** Whether the results area (runs/details/matches) is still loading */
    IsLoadingResults: boolean;
    IsSaving: boolean;
    /** The group being compared (null = panel closed) */
    ComparisonGroup: DuplicateGroup | null;
    /** Whether the comparison panel is loading entity records */
    ComparisonLoading: boolean;
    /** Whether to show all fields or only differences */
    ComparisonShowAllFields: boolean;
    /** Precomputed field rows for the comparison grid */
    ComparisonFields: ComparisonFieldRow[];
    /** Parsed match info for comparison columns */
    ComparisonMatches: ComparisonMatchInfo[];
    /** Index of the surviving record column (0 = source, 1+ = match index) */
    SurvivorColumnIndex: number;
    /** Whether the panel is animating closed */
    ComparisonClosing: boolean;
    /** Loaded entity records keyed by record ID (populated on panel open via RunView) */
    private comparisonRecords;
    /**
     * Floating LLM-reasoning popover state. Rendered as a fixed-position card over the
     * comparison grid so the full reasoning prose no longer lives inline in the column
     * header (which made headers tall, uneven, and visually cluttered). Null when closed.
     */
    ReasoningPopover: {
        columnIndex: number;
        name: string;
        recommendation: LLMRecommendation | null;
        confidence: number | null;
        hasDisagreement: boolean;
        reasoning: string;
        top: number;
        left: number;
    } | null;
    /** True once the LLM proposed survivor/field-map has been applied to the selection state */
    LLMProposalsApplied: boolean;
    /** Dependencies per record, keyed by composite key string */
    ComparisonDependencies: Map<string, RecordDependency[]>;
    /** Which column indices have expanded dependency details */
    DepsExpandedColumns: Set<number>;
    /** Tracks which entity groups within deps are expanded (key: "columnIndex::entityName") */
    private depsEntityGroupExpanded;
    /** Whether the merge confirmation panel is visible */
    ShowMergeConfirm: boolean;
    /** Whether the merge is currently executing */
    IsMerging: boolean;
    /** Whether the current entity allows record merging (controls merge button availability) */
    MergeEnabled: boolean;
    /** Whether merge-not-available inline banner should be shown in the results area */
    ShowMergeWarningBanner: boolean;
    Runs: MJDuplicateRunEntity[];
    Details: MJDuplicateRunDetailEntity[];
    Matches: MJDuplicateRunDetailMatchEntity[];
    AllGroups: DuplicateGroup[];
    PendingGroups: DuplicateGroup[];
    ApprovedGroups: DuplicateGroup[];
    RejectedGroups: DuplicateGroup[];
    Filters: DuplicateFilter;
    EntityNames: string[];
    IsDetecting: boolean;
    DetectionProgress: number;
    DetectionStage: string;
    /** Raw stage key from the last progress event — used to detect phase transitions */
    private detectionRawStage;
    /** Runtime threshold overrides — initialized from entity doc, adjustable via sliders */
    RunPotentialThreshold: number;
    RunAbsoluteThreshold: number;
    DetectionCurrentItem: string;
    /**
     * LLM reasoning gate — working copies synced from the selected entity document. Unlike the
     * run-threshold sliders (per-run overrides), these are per-entity config the engine reads from
     * the Entity Document, so editing them persists back to that record (see persistReasoningConfig).
     */
    EnableLLMReasoning: boolean;
    ReasoningThreshold: number;
    EntityDocuments: EntityDocumentOption[];
    private _selectedEntityDocumentID;
    get SelectedEntityDocumentID(): string;
    set SelectedEntityDocumentID(value: string);
    /**
     * When true, renders only the body content (no chrome). Set by parent shells
     * that embed this resource. See plans/explorer-chrome-conventions.md Section 5.
     */
    HideToolbar: boolean;
    /** View mode: 'kanban' (card board) or 'table' (paged grid) */
    DisplayMode: 'kanban' | 'table';
    /** Page size for table view */
    PageSize: number;
    /** Current page (0-based) */
    CurrentPage: number;
    /** Selected entity filter */
    SelectedEntityFilter: string;
    /** Currently selected group for merge panel */
    SelectedMergeGroup: DuplicateGroup | null;
    /** Whether the merge panel is visible */
    ShowMergePanel: boolean;
    /** Auto-switch to table mode when groups exceed threshold */
    private readonly kanbanThreshold;
    /** Toggle display mode */
    ToggleDisplayMode(): void;
    /** Filter by entity name */
    FilterByEntity(entityName: string): void;
    /** Get paged groups for table view */
    GetPagedGroups(): DuplicateGroup[];
    /** Get total page count */
    get TotalPages(): number;
    /** Navigate to next page */
    NextPage(): void;
    /** Navigate to previous page */
    PrevPage(): void;
    /** Open the merge panel for a duplicate group */
    OpenMergePanel(group: DuplicateGroup): void;
    /** Close the merge panel */
    CloseMergePanel(): void;
    /** Auto-select display mode based on group count */
    private autoSelectDisplayMode;
    get TotalGroupCount(): number;
    get PendingCount(): number;
    get ApprovedCount(): number;
    get RejectedCount(): number;
    /** Get selected entity document threshold info */
    get SelectedDocumentThresholds(): EntityDocumentOption | null;
    ngAfterViewInit(): Promise<void>;
    /**
     * Publish the duplicate-detection surface state to the AI agent. Re-emitted
     * whenever detection status changes or a group moves between Pending /
     * Approved / Rejected, so the streamed context tracks the live board. No-op
     * when embedded (the host owns reporting).
     */
    private emitAgentContext;
    /** Build the resolver candidate list from the loaded entity-document picker options. */
    private getEntityDocCandidates;
    /**
     * 🚨 SAFETY BOUNDARY 🚨
     * Register the safe, agent-actionable operations. Exposed:
     *  - SelectEntityDocument (id/name resolution) → drives which doc detection runs against
     *  - RunDuplicateDetection (idempotent — creates a run; the server hook re-detects)
     *  - FilterDuplicatesByEntity / SetDuplicateDisplayMode (read-only board navigation)
     *  - RefreshDuplicateDetection (read-only reload)
     *
     * INTENTIONALLY NOT EXPOSED (gated):
     *  - Group-level Approve/Reject (ApproveMatch / RejectMatch / updateGroupApprovalStatus) and
     *    drag-to-column — they mutate match ApprovalStatus and there is NO current-selection model,
     *    so the agent would have to fabricate a group id.
     *  - Merge (ExecuteMerge / MergeRecords) and per-match approve/reject — destructive, irreversible
     *    record consolidation that requires the human comparison-panel review flow.
     */
    private registerAgentTools;
    ngOnDestroy(): void;
    GetResourceDisplayName(_data: ResourceData): Promise<string>;
    GetResourceIconClass(_data: ResourceData): Promise<string>;
    /**
     * Loads all duplicate run data and builds the Kanban groups.
     * Split into two phases so that controls become interactive immediately:
     *   Phase 1 - entity docs from KH engine cache (instant)
     *   Phase 2 - runs/details/matches via RunViews (heavy)
     */
    LoadData(): Promise<void>;
    /** Phase 1: Load entity documents from KH engine cache (instant). */
    private loadEntityDocuments;
    /** Phase 2: Load runs, details, and matches via RunViews batch. */
    private loadRunData;
    /**
     * Check if there's an in-progress detection run and reconnect to its
     * progress subscription. This handles the case where the user navigated
     * away and came back while a run was active.
     */
    private reconnectToActiveRun;
    /**
     * Trigger a new duplicate detection run by creating a DuplicateRun entity.
     * The server hook auto-triggers detection when a run is saved with EndedAt === null.
     */
    RunDetection(): Promise<void>;
    /**
     * Approves all matches within a duplicate group by updating each match's ApprovalStatus.
     */
    ApproveMatch(group: DuplicateGroup): Promise<void>;
    /**
     * Rejects all matches within a duplicate group by updating each match's ApprovalStatus.
     */
    RejectMatch(group: DuplicateGroup): Promise<void>;
    /** The group currently being dragged */
    DraggedGroup: DuplicateGroup | null;
    /** Which column is being dragged over (for highlight) */
    DragOverColumn: 'Pending' | 'Approved' | 'Rejected' | null;
    OnDragStart(event: DragEvent, group: DuplicateGroup): void;
    OnDragEnd(): void;
    OnDragOver(event: DragEvent, column: 'Pending' | 'Approved' | 'Rejected'): void;
    OnDragLeave(event: DragEvent, column: 'Pending' | 'Approved' | 'Rejected'): void;
    OnDrop(event: DragEvent, targetStatus: 'Pending' | 'Approved' | 'Rejected'): Promise<void>;
    /** Whether a card is currently being dragged */
    get IsDragging(): boolean;
    /** Handle filter changes with debounce */
    OnFilterChange(): void;
    /** Computed range bounds from actual data — used as min/max/placeholder for filter inputs */
    DataMinScore: number;
    DataMaxScore: number;
    DataMinDate: string;
    DataMaxDate: string;
    /** Compute the actual data ranges from AllGroups */
    private computeDataRanges;
    /** Format a Date to YYYY-MM-DD for input[type=date] using local time */
    private toInputDate;
    /** Clear all filters */
    ClearFilters(): void;
    /** Returns the CSS class for a match score indicator */
    GetScoreClass(score: number): string;
    /** Returns a human-readable label for a match score */
    GetScoreLabel(score: number): string;
    /** Format a date for display */
    /**
     * Format a composite key string (e.g., "ID|5A07433E-F36B-1410-8AA5-00F1597429B5")
     * into a readable format. For single-key entities, shows just the value truncated.
     * For composite keys, shows key: value pairs.
     */
    /** Whether there are any non-skipped matches available for merging */
    get HasMergeableMatches(): boolean;
    FormatRecordID(recordID: string): string;
    FormatDate(date: Date | null): string;
    /** Whether any filters are active */
    get HasActiveFilters(): boolean;
    private setupFilterDebounce;
    /** Build entity document options from KnowledgeHubMetadataEngine cached entities */
    private buildEntityDocumentOptionsFromEngine;
    /** Subscribe to PipelineProgress for a specific detection run */
    /**
     * Normalizes a duplicate threshold value — treats null, undefined, 0, and 1.0
     * as "not configured" and falls back to a sensible default.
     * Thresholds of exactly 1.0 mean "100% match only" which is effectively useless
     * for real-world duplicate detection.
     */
    private normalizeDupeThreshold;
    /** Handle potential threshold slider change */
    OnPotentialThresholdChanged(value: number): void;
    /** Handle absolute threshold slider change */
    OnAbsoluteThresholdChanged(value: number): void;
    /** Toggle the LLM reasoning layer for the selected entity document and persist immediately. */
    OnEnableReasoningChanged(enabled: boolean): Promise<void>;
    /** Live-update the reasoning-threshold display as the slider moves (persisted on release). */
    OnReasoningThresholdChanged(value: number): void;
    /** Persist the reasoning threshold when the slider is released. */
    OnReasoningThresholdCommitted(value: number): Promise<void>;
    /**
     * Persist the LLM reasoning gate (EnableLLMReasoning + ReasoningThreshold) onto the selected
     * Entity Document. The detection engine reads the gate from the Entity Document, so — unlike the
     * run-threshold sliders — these are per-entity configuration, not per-run overrides.
     */
    private persistReasoningConfig;
    private subscribeToPipelineProgress;
    /** Format detection stage names for display */
    private formatDetectionStage;
    /** Extract unique entity names from loaded runs */
    private extractEntityNames;
    /**
     * Build DuplicateGroup objects by joining details to their matches,
     * and resolving the entity name from the parent run.
     */
    private buildGroups;
    /** Find the highest match probability across a set of matches */
    private computeHighestScore;
    /** Open the comparison slide-in panel for a group — loads real entity records */
    OpenComparison(group: DuplicateGroup): Promise<void>;
    /** Close the comparison panel with slide-out animation */
    CloseComparison(): void;
    /** Get visible fields based on the toggle state */
    GetVisibleFields(): ComparisonFieldRow[];
    /** Case-insensitive, trimmed comparison of two field values */
    AreValuesEqual(a: string | undefined, b: string | undefined): boolean;
    /** Set the surviving record column index */
    SetSurvivor(columnIndex: number): void;
    /** Select all field values from a specific column */
    UseAllFieldsFrom(columnIndex: number): void;
    /** Select a specific field value from a column */
    SelectFieldValue(field: ComparisonFieldRow, columnIndex: number): void;
    /** Check if all fields are selected from a given column */
    AllFieldsSelectedFrom(columnIndex: number): boolean;
    /** Count how many fields are cherry-picked from non-survivor columns */
    CherryPickedCount(): number;
    /** Get the name of the surviving record */
    SurvivorName(): string;
    /** Get the primary key display string for the surviving record */
    SurvivorKeyDisplay(): string;
    /** Get dependencies for a column (0 = source, 1+ = match index) */
    GetDepsForColumn(columnIndex: number): RecordDependency[];
    /** Get deps grouped by related entity for a column */
    GetGroupedDeps(columnIndex: number): Array<{
        Entity: string;
        Count: number;
    }>;
    /** Cached dependent records loaded on demand, keyed by "columnIndex::entityName" */
    private depRecordsCache;
    /** Tracks which entity groups are currently loading */
    private depRecordsLoading;
    /** Get cached dependent records for an entity group (empty until expanded and loaded) */
    GetDepRecords(columnIndex: number, entityName: string): Array<{
        Name: string;
        PrimaryKey: CompositeKey;
        EntityName: string;
    }>;
    /** Check if dep records are loading for an entity group */
    IsDepRecordsLoading(columnIndex: number, entityName: string): boolean;
    /** Navigate to a dependent record */
    OpenDepRecord(record: {
        EntityName: string;
        PrimaryKey: CompositeKey;
    }): void;
    /** Toggle expanded state for a specific entity group — lazy-loads records on first expand */
    ToggleDepEntityGroup(columnIndex: number, entityName: string): Promise<void>;
    /** Load dependent records for an entity group via RunView */
    private loadDepRecordsForGroup;
    /** Check if a specific entity group is expanded */
    IsDepEntityGroupExpanded(columnIndex: number, entityName: string): boolean;
    /** Get total dependency count for a column */
    GetTotalDeps(columnIndex: number): number;
    /** Toggle expanded state for dependency details in a column */
    ToggleDepsExpanded(columnIndex: number): void;
    /** Check if dependency details are expanded for a column */
    IsDepsExpanded(columnIndex: number): boolean;
    /** Get the column index with the most dependencies */
    GetMaxDepsColumnIndex(): number;
    /** Get the composite key string for a column index */
    private getCompositeKeyStringForColumn;
    /** Get the name for a column index */
    GetColumnName(columnIndex: number): string;
    /**
     * Decide whether the LLM verdict contradicts the vector score for a match.
     * The classic disagreement is a strong vector match the LLM flags as NotDuplicate,
     * or a weak vector pair the LLM nonetheless wants to Merge. Returns false when
     * reasoning never ran (recommendation null) so existing groups never light up.
     *
     * The "strong vs. weak" boundary is the live potential-match threshold the user
     * tuned for this run ({@link RunPotentialThreshold}) — not a hardcoded constant — so
     * the disagreement marker tracks the same threshold driving candidate selection.
     */
    private computeDisagreement;
    /** True when ANY match in the current comparison carries LLM reasoning data */
    get HasAnyLLMData(): boolean;
    /** CSS class for an LLM recommendation badge */
    GetLLMRecommendationClass(recommendation: LLMRecommendation | null): string;
    /** Font Awesome icon class for an LLM recommendation */
    GetLLMRecommendationIcon(recommendation: LLMRecommendation | null): string;
    /** Human-readable label for an LLM recommendation */
    GetLLMRecommendationLabel(recommendation: LLMRecommendation | null): string;
    /**
     * Open (or toggle) the floating reasoning popover for a match column, anchored under
     * the "Why?" trigger that was clicked. Rendering the prose in a fixed-position card
     * keeps the column headers short and uniform instead of letting reasoning text balloon
     * the header inline.
     */
    OpenReasoningPopover(match: ComparisonMatchInfo, columnIndex: number, event: MouseEvent): void;
    /** Close the floating reasoning popover */
    CloseReasoningPopover(): void;
    /** Whether the reasoning popover is currently open for a given match column */
    IsReasoningPopoverOpen(columnIndex: number): boolean;
    /**
     * Preload the LLM-proposed survivor record and per-field choices into the
     * existing selection state. Fully additive: no-op when no match carries a
     * proposal, so the user's manual selection is the default in vector-only runs.
     */
    private applyLLMProposals;
    /** Parse the proposed field map JSON and set each field's SelectedColumnIndex to the proposed source column */
    private applyProposedFieldMap;
    /** Parse LLMProposedFieldMap into typed {FieldName, SourceRecordID} entries (lenient about null/garbage) */
    private parseProposedFieldMap;
    /**
     * Map a record ID (URL-segment composite key, may equal the source) to a column index.
     * Column 0 is the source; columns 1..N are matches. Uses case-insensitive comparison
     * because UUIDs differ in case across SQL Server (upper) and PostgreSQL (lower).
     */
    private resolveColumnForRecordId;
    /** Open the merge confirmation panel */
    OpenMergeConfirm(): void;
    /** Close the merge confirmation panel */
    CloseMergeConfirm(): void;
    /**
     * Get the list of cherry-picked field overrides (fields picked from non-survivor columns).
     * `Value` is the DISPLAY string (with an `(empty)` sentinel for null) for the confirm panel;
     * `RawValue` is the actual value to write at merge time — never the sentinel, so the merge
     * never corrupts a field with the literal text "(empty)".
     */
    GetCherryPickedFields(): Array<{
        FieldName: string;
        DisplayName: string;
        Value: string;
        RawValue: string | null;
        SourceName: string;
    }>;
    /** Get non-surviving columns with their dependency counts */
    /** Get non-surviving columns excluding skipped (Rejected) matches for merge confirmation display */
    GetNonSurvivorColumns(): Array<{
        ColumnIndex: number;
        Name: string;
        DepCount: number;
    }>;
    /** Execute the merge operation */
    ExecuteMerge(): Promise<void>;
    /** Build CompositeKey array for non-surviving records */
    private buildNonSurvivorKeys;
    /** Approve an individual match */
    ApproveIndividualMatch(matchInfo: ComparisonMatchInfo): Promise<void>;
    /** Reject an individual match (skip it from merge) */
    RejectIndividualMatch(matchInfo: ComparisonMatchInfo): Promise<void>;
    /** Undo a rejected individual match (restore to Pending) */
    UndoRejectIndividualMatch(matchInfo: ComparisonMatchInfo): Promise<void>;
    /**
     * Load the actual entity records for the source + all matches in one RunView call.
     * Record IDs are stored as composite key strings (e.g., "ID|uuid") — we parse each
     * into a CompositeKey, generate WHERE clauses, and OR them together for one query.
     * Results stored in comparisonRecords map keyed by the composite key string.
     */
    private loadComparisonRecords;
    /**
     * Load dependencies for all records (source + matches) in parallel.
     * Each record's deps are stored in ComparisonDependencies keyed by composite key string.
     */
    private loadComparisonDependencies;
    /** Get the field value for a record from the loaded entity records map */
    private getRecordFieldValue;
    /** Build comparison data from loaded entity records */
    private buildComparisonData;
    /** Parse RecordMetadata JSON from a detail or match entity */
    private parseRecordMetadata;
    /** Build top N match summaries with parsed names and scores */
    private buildTopMatchSummaries;
    /**
     * Resolve match record name from loaded entity records using IsNameField fields.
     * Falls back to metadata, then truncated record ID.
     */
    private resolveMatchName;
    /**
     * Resolve record display name from metadata using entity IsNameField fields.
     * Combines multiple name fields (e.g., FirstName + LastName → "Sarah Chen").
     * Falls back to metadata.Name, then recordID.
     */
    private resolveRecordName;
    /**
     * Determine the dominant approval status for a group of matches.
     * If any match is Pending, the group is Pending.
     * Otherwise, if all are Approved, the group is Approved.
     * If all are Rejected, the group is Rejected.
     * Mixed Approved/Rejected defaults to Pending.
     */
    private computeDominantApprovalStatus;
    /** Find the latest MatchedAt date across a set of matches */
    private computeLatestMatchDate;
    /** Apply filters and distribute groups into Kanban columns */
    private applyFilters;
    /** Update the ApprovalStatus of all matches within a group and re-sort. */
    private updateGroupApprovalStatus;
    static ɵfac: i0.ɵɵFactoryDeclaration<DuplicateDetectionResourceComponent, never>;
    static ɵcmp: i0.ɵɵComponentDeclaration<DuplicateDetectionResourceComponent, "app-duplicate-detection-resource", never, { "HideToolbar": { "alias": "HideToolbar"; "required": false; }; }, {}, never, never, false, never>;
}
export declare function LoadDuplicateDetectionResource(): void;
export {};
//# sourceMappingURL=duplicate-detection-resource.component.d.ts.map