import HashTree from './hashTree'; import { Enum } from '../constants'; import { HtMeta, HashTreeObject } from './types'; import { LocusDTO } from '../locus-info/types'; export interface DataSet { url: string; root: string; version: number; leafCount: number; name: string; idleMs: number; backoff: { maxMs: number; exponent: number; }; } export interface RootHashMessage { dataSets: Array; } export interface HashTreeMessage { dataSets: Array; visibleDataSetsUrl: string; locusStateElements?: Array; locusSessionId?: string; locusUrl: string; heartbeatIntervalMs?: number; } export interface VisibleDataSetInfo { name: string; url: string; dataChannelUrl?: string; } export interface Metadata { htMeta: HtMeta; visibleDataSets: VisibleDataSetInfo[]; } interface InternalDataSet extends DataSet { hashTree?: HashTree; timer?: ReturnType; heartbeatWatchdogTimer?: ReturnType; } type WebexRequestMethod = (options: Record) => Promise; export declare const LocusInfoUpdateType: { readonly OBJECTS_UPDATED: "OBJECTS_UPDATED"; readonly MEETING_ENDED: "MEETING_ENDED"; }; export type LocusInfoUpdateType = Enum; export type LocusInfoUpdateCallback = (updateType: LocusInfoUpdateType, data?: { updatedObjects: HashTreeObject[]; }) => void; /** * This error is thrown if we receive information that the meeting has ended while we're processing some hash messages. * It's handled internally by HashTreeParser and results in MEETING_ENDED being sent up. */ export declare class MeetingEndedError extends Error { } /** * Parses hash tree eventing locus data */ declare class HashTreeParser { dataSets: Record; visibleDataSetsUrl: string; webexRequest: WebexRequestMethod; locusInfoUpdateCallback: LocusInfoUpdateCallback; visibleDataSets: VisibleDataSetInfo[]; debugId: string; heartbeatIntervalMs?: number; private excludedDataSets; /** * Constructor for HashTreeParser * @param {Object} options * @param {Object} options.initialLocus The initial locus data containing the hash tree information */ constructor(options: { initialLocus: { dataSets: Array; locus: any; }; metadata: Metadata | null; webexRequest: WebexRequestMethod; locusInfoUpdateCallback: LocusInfoUpdateCallback; debugId: string; excludedDataSets?: string[]; }); /** * Checks if the given data set name is in the list of visible data sets * @param {string} dataSetName data set name to check * @returns {Boolean} True if the data set is visible, false otherwise */ private isVisibleDataSet; /** * Checks if the given data set name is in the excluded list * @param {string} dataSetName data set name to check * @returns {boolean} True if the data set is excluded, false otherwise */ private isExcludedDataSet; /** * Adds a data set to the visible data sets list, unless it is in the excluded list. * @param {VisibleDataSetInfo} dataSetInfo data set info to add * @returns {boolean} True if the data set was added, false if it was excluded */ private addToVisibleDataSetsList; /** * Initializes a new visible data set by creating a hash tree for it, adding it to all the internal structures, * and sending an initial sync request to Locus with empty leaf data - that will trigger Locus to gives us all the data * from that dataset (in the response or via messages). * * @param {VisibleDataSetInfo} visibleDataSetInfo Information about the new visible data set * @param {DataSet} dataSetInfo The new data set to be added * @returns {Promise} */ private initializeNewVisibleDataSet; /** * Sends a special sync request to Locus with all leaves empty - this is a way to get all the data for a given dataset. * * @param {string} datasetName - name of the dataset for which to send the request * @param {string} debugText - text to include in logs * @returns {Promise} */ private sendInitializationSyncRequestToLocus; /** * Queries Locus for all up-to-date information about all visible data sets * * @returns {Promise} */ private getAllVisibleDataSetsFromLocus; /** * Initializes the hash tree parser from a message received from Locus. * * @param {HashTreeMessage} message - initial hash tree message received from Locus * @returns {Promise} */ initializeFromMessage(message: HashTreeMessage): Promise; /** * Initializes the hash tree parser from GET /loci API response by fetching all data sets metadata * first and then doing an initialization sync on each data set * * This function requires that this.visibleDataSets have been already populated correctly by the constructor. * * @param {LocusDTO} locus - locus object received from GET /loci * @returns {Promise} */ initializeFromGetLociResponse(locus: LocusDTO): Promise; /** * Initializes data sets by doing an initialization sync on each visible data set that doesn't have a hash tree yet. * * @param {DataSet[]} visibleDataSets Array of visible DataSet objects to initialize * @param {string} debugText Text to include in logs for debugging purposes * @returns {Promise} */ private initializeDataSets; /** * Each dataset exists at a different place in the dto * iterate recursively over the locus and if it has a htMeta key, * create an object with the type, id and version and add it to the appropriate leafData array * * @param {any} locus - The current part of the locus being processed * @param {Object} [options] * @param {boolean} [options.copyData=false] - Whether to copy the data for each leaf into returned result * @returns {any} - An object mapping dataset names to arrays of leaf data */ private analyzeLocusHtMeta; /** * Analyzes the Metadata object that is sent outside of Locus object, and appends its data to passed in leafInfo * structure. * * @param {Record} leafInfo the structure to which the Metadata info will be appended * @param {Metadata} metadata Metadata object * @returns {void} */ private analyzeMetadata; /** * Checks if the provided hash tree message indicates the end of the meeting and that there won't be any more updates. * * @param {HashTreeMessage} message - The hash tree message to check * @returns {boolean} - Returns true if the message indicates the end of the meeting, false otherwise */ private isEndMessage; /** * Handles the root hash heartbeat message * * @param {RootHashMessage} message - The root hash heartbeat message * @returns {void} */ private handleRootHashHeartBeatMessage; /** * Asynchronously initializes new visible data sets * * @param {VisibleDataSetInfo[]} dataSetsRequiringInitialization list of datasets to initialize * @returns {void} */ private queueInitForNewVisibleDataSets; /** * Handles updates to Metadata object that we receive from Locus via other means than messages. Right now * that means only in the API response alongside locus object. * * @param {Metadata} metadata received in Locus update other than a message (for example in an API response) * @param {HashTreeObject[]} updatedObjects a list of updated hash tree objects to which any updates resulting from new Metadata will be added * @returns {void} */ handleMetadataUpdate(metadata: Metadata, updatedObjects: HashTreeObject[]): void; /** * This method should be called when we receive a partial locus DTO that contains dataSets and htMeta information * It updates the hash trees with the new leaf data based on the received Locus * * @param {Object} update - The locus update containing data sets and locus information * @returns {void} */ handleLocusUpdate(update: { dataSets?: Array; locus: any; metadata?: Metadata; }): void; /** * Updates the internal data set information based on the received data set from Locus. * * @param {DataSet} receivedDataSet - The latest data set information received from Locus to update the internal state. * @returns {void} */ private updateDataSetInfo; /** * Checks for changes in the visible data sets based on the updated objects. * @param {HashTreeObject[]} updatedObjects - The list of updated hash tree objects. * @returns {Object} An object containing the removed and added visible data sets. */ private checkForVisibleDataSetChanges; /** * Deletes the hash tree for the specified data set. * * @param {string} dataSetName name of the data set to delete * @returns {void} */ private deleteHashTree; /** * Adds entries to the passed in updateObjects array * for the changes that result from removing visible data sets and creates hash * trees for the new visible data sets, but without populating the hash trees. * * This function is synchronous. If we are missing information about some new * visible data sets and they require async initialization, the names of these data sets * are returned in an array. * * @param {VisibleDataSetInfo[]} removedDataSets - The list of removed data sets. * @param {VisibleDataSetInfo[]} addedDataSets - The list of added data sets. * @param {HashTreeObject[]} updatedObjects - The list of updated hash tree objects to which changes will be added. * @returns {VisibleDataSetInfo[]} list of data sets that couldn't be initialized synchronously */ private processVisibleDataSetChanges; /** * Adds entries to the passed in updateObjects array * for the changes that result from adding and removing visible data sets. * * @param {VisibleDataSetInfo[]} addedDataSets - The list of added data sets. * @returns {Promise} */ private initializeNewVisibleDataSets; /** * Parses incoming hash tree messages, updates the hash trees and returns information about the changes * * @param {HashTreeMessage} message - The hash tree message containing data sets and objects to be processed * @param {string} [debugText] - Optional debug text to include in logs * @returns {HashTreeObject[]} list of hash tree objects that were updated as a result of processing the message */ private parseMessage; /** * Handles incoming hash tree messages, updates the hash trees and calls locusInfoUpdateCallback * * @param {HashTreeMessage} message - The hash tree message containing data sets and objects to be processed * @param {string} [debugText] - Optional debug text to include in logs * @returns {void} */ handleMessage(message: HashTreeMessage, debugText?: string): void; /** * Calls the updateInfo callback if there are any updates to report * * @param {Object} updates parsed from a Locus message * @returns {void} */ private callLocusInfoUpdateCallback; /** * Calculates a weighted backoff time that should be used for syncs * * @param {Object} backoff - The backoff configuration containing maxMs and exponent * @returns {number} - A weighted backoff time based on the provided configuration, using algorithm supplied by Locus team */ private getWeightedBackoffTime; /** * Performs a sync for the given data set. * * @param {InternalDataSet} dataSet - The data set to sync * @param {string} rootHash - Our current root hash for this data set * @param {string} reason - The reason for the sync (used for logging) * @returns {Promise} */ private performSync; /** * Runs the sync algorithm for the given data set. * * @param {DataSet} receivedDataSet - The data set to run the sync algorithm for. * @returns {void} */ private runSyncAlgorithm; /** * Resets the heartbeat watchdog timers for the specified data sets. Each data set has its own * watchdog timer that monitors whether heartbeats are being received within the expected interval. * If a heartbeat is not received for a specific data set within heartbeatIntervalMs plus * a backoff-calculated time, the sync algorithm is initiated for that data set * * @param {Array} receivedDataSets - The data sets from the received message for which watchdog timers should be reset * @returns {void} */ private resetHeartbeatWatchdogs; /** * Stops all timers for the data sets to prevent any further sync attempts. * @returns {void} */ private stopAllTimers; private checkForSentinelHttpResponse; /** * Gets the current hashes from the locus for a specific data set. * @param {string} dataSetName * @param {string} currentRootHash * @returns {string[]} */ private getHashesFromLocus; /** * Sends a sync request to Locus for the specified data set. * * @param {InternalDataSet} dataSet The data set to sync. * @param {Record} mismatchedLeavesData The mismatched leaves data to include in the sync request. * @returns {Promise} */ private sendSyncRequestToLocus; } export default HashTreeParser;