import { StreamBundle } from './streambundle';
import { ContextMatcher, SignalKServer } from './types';
import { NormalizedDelta, SourceRef } from '@signalk/server-api';
interface StringKeyed {
    [key: string]: any;
}
/**
 * Build a `<label>.<src>` → `<label>.<canName>` map from the Signal K
 * sources summary tree.
 *
 * Providers with `useCanName: true` emit deltas tagged with the canName
 * form once the device's PGN 60928 has been observed. Frames that
 * arrived before that point — common with UDP gateways that miss the
 * early ISO Address Claim — leak out tagged by numeric src and create
 * stale leaves that survive in the cache. Returning a translation map
 * here lets callers collapse those duplicates back onto the canonical
 * canName form.
 *
 * Providers with `useCanName` off never populate `n2k.canName`; the
 * map stays empty and callers see refs unchanged.
 */
export declare function buildSrcToCanonicalMap(sources: unknown): Map<string, string>;
/**
 * Collect the IDs of providers in `settings.pipedProviders` that are
 * currently enabled. Skips entries with `enabled: false` (matching the
 * convention in `pipedproviders.ts`: undefined is treated as enabled).
 *
 * Returns an empty Set when the settings tree is missing or empty —
 * callers must skip the prune in that case so a fresh install (or a
 * settings file we can't read) doesn't wipe out the entire cache.
 */
export declare function collectProviderIds(pipedProviders: unknown): Set<string>;
/**
 * Remove cache entries whose `<providerId>.<rest>` key prefix is not in
 * `knownProviderIds`. Mutates the input object and returns the number
 * of entries removed.
 *
 * If `knownProviderIds` is empty, leaves the cache untouched so a
 * misread settings file can't drop the user's whole history.
 */
export declare function pruneSourcesByProvider(cached: Record<string, unknown>, knownProviderIds: Set<string>): number;
export default class DeltaCache {
    cache: StringKeyed;
    lastModifieds: StringKeyed;
    app: SignalKServer;
    defaults: any;
    sourceDeltas: StringKeyed;
    cachedContextPaths: {
        [context: string]: {
            [path: string]: string[];
        };
    };
    private sourcesCachePath;
    private saveTimer;
    private preferredSources;
    private multiSourceTimer;
    private lastEmittedMultiSourceCount;
    private livePreferredEmitTimer;
    private livePreferredDirtyPaths;
    private canonicalMap;
    private canonicalSnapshot;
    private subscribedPaths;
    private routesPath;
    constructor(app: SignalKServer, streambundle: StreamBundle);
    getContextAndPathParts(msg: NormalizedDelta): string[];
    private canonicaliseSourceRef;
    onValue(msg: NormalizedDelta): void;
    private scheduleMultiSourceEmit;
    private scheduleLivePreferredEmit;
    /**
     * Snapshot of the current "winning" source per path, keyed as
     * `${context}\0${path}`. Updated by onValue on every accepted delta;
     * reflects what the priority engine is actually routing right now.
     */
    getLivePreferredSources(): Record<string, string>;
    private emitMultiSourcePaths;
    /**
     * Remove all cached deltas for a given sourceRef and re-emit
     * MULTISOURCEPATHS so the UI reflects the change. For every path whose
     * preferred source pointed at the removed ref, pick a deterministic
     * replacement from the remaining leaf entries so filterDeltasToPreferred
     * doesn't fall back to whatever Object.keys iterates first.
     */
    /**
     * Inject the engine's "is this (path, sourceRef) routed?" predicate
     * so onValue can avoid recording a preferred winner for paths the
     * engine isn't filtering. Called from activateSourcePriorities each
     * time the engine is rebuilt; pass null to drop the gate.
     */
    setRoutesPathPredicate(fn: ((path: string, sourceRef: string) => boolean) | null): void;
    removeSource(sourceRef: SourceRef): void;
    private pickReplacementSource;
    /**
     * Remove a source delta entry by key, purge all cached data for the
     * corresponding sourceRefs (numeric address and CAN Name forms), and
     * persist the updated sources cache to disk.
     */
    removeSourceDelta(key: string): void;
    ingestDelta(delta: any): void;
    setSourceDelta(key: string, delta: any): void;
    private getSourcesCachePath;
    private loadSourcesCache;
    private scheduleSaveSourcesCache;
    private saveSourcesCache;
    deleteContext(contextKey: string): void;
    pruneContexts(seconds: number): void;
    buildFull(user: string, path: string[]): Record<string, any>;
    /**
     * Return paths on the self vessel that have more than one source.
     * Result: { path: sourceRef[] } for each multi-source path.
     *
     * Persisted source priorities are also unioned in. Without this, a path
     * whose user-ranked sources are temporarily silent (e.g. an MFD that has
     * been powered off) drops to one or zero live publishers and disappears
     * from the multi-source view — the admin-ui then shows the path's
     * persisted entries under "Ungrouped" and surprises the user. Keeping
     * the persisted view in lets the priorities page render the user's
     * intent stably across power cycles.
     */
    /**
     * Return every (path, publishers[]) pair the cache has seen for the
     * self vessel, regardless of publisher count. Used to seed the
     * priority engine's `seenPublishersByPath` so routesPath flips on
     * for already-contended paths immediately at boot, instead of
     * flickering "no priority configured" warnings until live deltas
     * land. Refs are returned in canonical (canName) form to match the
     * engine's identity rule.
     */
    getSelfPathPublishers(): Record<string, string[]>;
    getMultiSourcePaths(): Record<string, string[]>;
    /**
     * Supplementary `<conn>.<src>` → `<conn>.<canName>` translation built
     * from the per-source delta store. `buildSrcToCanonicalMap` goes blind
     * when `app.signalk.sources` has no `n2k.canName` for a numeric src —
     * the cold-boot / UDP-gateway late-join window the comment at the top
     * of this file describes — but a metadata delta carrying the resolved
     * canName may already sit in `sourceDeltas` keyed by the same numeric
     * src. Recovering it here lets a device that left a stale numeric leaf
     * AND a canName leaf in the cache collapse onto one ref, so the two
     * forms of one physical device don't land in two reconciled groups and
     * trip the "a source may belong to at most one active group" save
     * validator.
     *
     * Each `sourceDeltas` entry carries exactly one canName, so the map is
     * single-valued per key and never merges two physically-different
     * devices. Keys are usually numeric-form (the `setSourceDelta` key,
     * n2k-signalk.ts) but `loadSourcesCache` can hydrate canName-form keys
     * from disk; those map onto themselves (a harmless identity), which
     * leaves the ref unchanged.
     */
    private buildSourceDeltaCanonicalMap;
    /**
     * Source-ref canonicaliser shared by the three cache-walk callers
     * (getReconciledGroups, getSelfPathPublishers, getMultiSourcePaths).
     * Prefers the live sources tree, then falls back to the canName
     * recovered from the source-delta store for numeric refs the tree
     * hasn't resolved yet. Routing all three through one closure keeps the
     * reconciled groups, the engine's seenPublishersByPath seed and the
     * multi-source UI from disagreeing about a device's ref during the
     * late-join window.
     */
    private reconcileCanonical;
    /**
     * Compute reconciled priority groups for the admin UI: saved groups
     * are authoritative (composition is fixed by priorityGroups, not by
     * who is currently live), unsaved sources fall through to connected-
     * components discovery on multiSourcePaths.
     *
     * For each saved group:
     * - sources = saved order + newcomers (sources currently publishing a
     *   group path but not in the saved list)
     * - paths = paths historically published by ≥2 of the saved sources,
     *   excluding paths owned by another active group (the group with
     *   more current live publishers wins a shared path)
     *
     * For unsaved residue: standard connected-components grouping with
     * the ≥2-live-publisher edge rule.
     */
    getReconciledGroups(): Array<{
        id: string;
        matchedSavedId: string | null;
        inactive?: boolean;
        sources: string[];
        paths: string[];
        newcomerSources: string[];
    }>;
    getSources(): any;
    buildFullFromDeltas(user: string, deltas: any[] | undefined, includeSources: boolean): Record<string, any>;
    private filterDeltasToPreferred;
    getCachedDeltas(contextFilter: ContextMatcher, user?: string, key?: string, sourcePolicy?: 'preferred' | 'all'): import("@signalk/server-api").Delta[];
}
export {};
//# sourceMappingURL=deltacache.d.ts.map