packageDocumentation module import * as hash from "object-hash" ; import * as path from "path" ; import { BriefcaseDb , IModelDb , IModelJsNative , IpcHost } from "@itwin/core-backend" ; import { Id64String } from "@itwin/core-bentley" ; import { FormatProps , UnitSystemKey } from "@itwin/core-quantity" ; import { Content , ContentDescriptorRequestOptions , ContentFlags , ContentRequestOptions , ContentSourcesRequestOptions , DefaultContentDisplayTypes , Descriptor , DescriptorOverrides , DiagnosticsOptionsWithHandler , DisplayLabelRequestOptions , DisplayLabelsRequestOptions , DisplayValueGroup , DistinctValuesRequestOptions , ElementProperties , ElementPropertiesRequestOptions , FilterByInstancePathsHierarchyRequestOptions , FilterByTextHierarchyRequestOptions , getLocalesDirectory , HierarchyCompareInfo , HierarchyCompareOptions , HierarchyRequestOptions , InstanceKey , isSingleElementPropertiesRequestOptions , Key , KeySet , LabelDefinition , MultiElementPropertiesRequestOptions , Node , NodeKey , NodePathElement , Paged , PagedResponse , PresentationError , PresentationStatus , Prioritized , Ruleset , SelectClassInfo , SelectionScope , SelectionScopeRequestOptions , SingleElementPropertiesRequestOptions , } from "@itwin/presentation-common" ; import { PRESENTATION_BACKEND_ASSETS_ROOT , PRESENTATION_COMMON_ASSETS_ROOT } from "./Constants" ; import { buildElementsProperties , getElementsCount , iterateElementIds } from "./ElementPropertiesHelper" ; import { createDefaultNativePlatform , NativePlatformDefinition , NativePlatformRequestTypes , NativePresentationDefaultUnitFormats , NativePresentationKeySetJSON , NativePresentationUnitSystem , } from "./NativePlatform" ; import { RulesetManager , RulesetManagerImpl } from "./RulesetManager" ; import { RulesetVariablesManager , RulesetVariablesManagerImpl } from "./RulesetVariablesManager" ; import { SelectionScopesHelper } from "./SelectionScopesHelper" ; import { UpdatesTracker } from "./UpdatesTracker" ; import { getElementKey } from "./Utils" ; public export enum PresentationManagerMode { /**
     * Presentation manager assumes iModels are opened in read-only mode and avoids doing some work
     * related to reacting to changes in iModels.
     */ ReadOnly , /**
     * Presentation manager assumes iModels are opened in read-write mode and it may need to
     * react to changes. This involves some additional work and gives slightly worse performance.
     */ ReadWrite } beta export enum HierarchyCacheMode { /**
     * Hierarchy cache is created in memory.
     */ Memory = "memory" , /**
     * Hierarchy cache is created on disk. In this mode hierarchy cache is persisted between iModel
     * openings.
     */ Disk = "disk" , /**
     * Hierarchy cache is created on disk. In this mode everything is cached in memory while creating hierarchy level
     * and persisted in disk cache when whole hierarchy level is created.
     */ Hybrid = "hybrid" } beta export type HierarchyCacheConfig = MemoryHierarchyCacheConfig | DiskHierarchyCacheConfig | HybridCacheConfig ; beta export interface HierarchyCacheConfigBase { mode : HierarchyCacheMode ; } beta export interface MemoryHierarchyCacheConfig extends HierarchyCacheConfigBase { mode : HierarchyCacheMode . Memory ; } beta export interface DiskHierarchyCacheConfig extends HierarchyCacheConfigBase { mode : HierarchyCacheMode . Disk ; /**
     * A directory for Presentation hierarchy cache. If set, the directory must exist.
     *
     * The default directory depends on the iModel and the way it's opened.
     */ directory ? : string ; } beta export interface HybridCacheConfig extends HierarchyCacheConfigBase { mode : HierarchyCacheMode . Hybrid ; /** Configuration for disk cache used to persist hierarchies. */ disk ? : DiskHierarchyCacheConfig ; } public export interface ContentCacheConfig { alpha size ? : number ; } public export interface UnitSystemFormat { unitSystems : UnitSystemKey [ ] ; format : FormatProps ; } alpha export interface MultiElementPropertiesResponse { total : number ; iterator : ( ) => AsyncGenerator < ElementProperties [ ] > ; } public export interface PresentationManagerProps { /**
     * Path overrides for presentation assets. Need to be overriden by application if it puts these assets someplace else than the default.
     *
     * By default paths to asset directories are determined during the call of [[Presentation.initialize]] using this algorithm:
     *
     * - for `presentation-backend` assets:
     *
     *   - if path of `.js` file that contains [[PresentationManager]] definition contains "presentation-backend", assume the package is in `node_modules` and the directory structure is:
     *     - `assets/*\*\/*`
     *     - `presentation-backend/{presentation-backend source files}`
     *
     *     which means the assets can be found through a relative path `../assets/` from the JS file being executed.
     *
     * - for `presentation-common` assets:
     *
     *   - if path of `.js` files of `presentation-common` package contain "presentation-common", assume the package is in `node_modules` and the directory structure is:
     *     - `assets/*\*\/*`
     *     - `presentation-common/{presentation-common source files}`
     *
     *     which means the assets can be found through a relative path `../assets/` from the package's source files.
     *
     * - in both cases, if we determine that source files are not in `node_modules`, assume the backend is webpacked into a single file with assets next to it:
     *   - `assets/*\*\/*`
     *   - `{source file being executed}.js`
     *
     *   which means the assets can be found through a relative path `./assets/` from the `{source file being executed}`.
     *
     * The overrides can be specified as a single path (when assets of both `presentation-backend` and `presentation-common` packages are merged into a single directory) or as an object with two separate paths for each package.
     */ presentationAssetsRoot ? : string | { /** Path to `presentation-backend` assets */ backend : string ; /** Path to `presentation-common` assets */ common : string ; } ; /**
     * A list of directories containing application's presentation rulesets.
     */ rulesetDirectories ? : string [ ] ; /**
     * A list of directories containing application's supplemental presentation rulesets.
     */ supplementalRulesetDirectories ? : string [ ] ; /**
     * A list of directories containing application's locale-specific localized
     * string files (in simplified i18next v3 format)
     */ localeDirectories ? : string [ ] ; /**
     * Sets the active locale to use when localizing presentation-related
     * strings. It can later be changed through [[PresentationManager.activeLocale]].
     */ defaultLocale ? : string ; /**
     * Sets the active unit system to use for formatting property values with
     * units. Default presentation units are used if this is not specified. The active unit
     * system can later be changed through [[PresentationManager.activeUnitSystem]] or overriden for each request
     * through request options.
     */ defaultUnitSystem ? : UnitSystemKey ; /**
     * A map of default unit formats to use for formatting properties that don't have a presentation format
     * in requested unit system.
     */ defaultFormats ? : { [ phenomenon ] : UnitSystemFormat ; } ; /**
     * Should schemas preloading be enabled. If true, presentation manager listens
     * for `BriefcaseDb.onOpened` event and force pre-loads all ECSchemas.
     */ enableSchemasPreload ? : boolean ; /**
     * A number of worker threads to use for handling presentation requests. Defaults to `2`.
     */ workerThreadsCount ? : number ; /**
     * Presentation manager working mode. Backends that use iModels in read-write mode should
     * use `ReadWrite`, others might want to set to `ReadOnly` for better performance.
     *
     * Defaults to [[PresentationManagerMode.ReadWrite]].
     */ mode ? : PresentationManagerMode ; alpha updatesPollInterval ? : number ; /** Options for caching. */ caching ? : { beta hierarchies ? : HierarchyCacheConfig ; /** Content-related caching options. */ content ? : ContentCacheConfig ; } ; alpha useMmap ? : boolean | number ; internal id ? : string ; internal addon ? : NativePlatformDefinition ; } public export class PresentationManager { private _props : PresentationManagerProps ; private _nativePlatform ? : NativePlatformDefinition ; private _rulesets : RulesetManager ; private _isDisposed : boolean ; private _disposeIModelOpenedListener ? : ( ) => void ; private _updatesTracker ? : UpdatesTracker ; private _onManagerUsed ? : ( ) => void ; /** Get / set active locale used for localizing presentation data */ public activeLocale : string | undefined ; /** Get / set active unit system used to format property values with units */ public activeUnitSystem : UnitSystemKey | undefined ; param props constructor ( props ? ) { this . _props = props ?? { } ; this . _isDisposed = false ; const mode = props ?. mode ?? PresentationManagerMode . ReadWrite ; const isChangeTrackingEnabled = ( mode === PresentationManagerMode . ReadWrite && ! ! props ?. updatesPollInterval ) ; if ( props && props . addon ) { this . _nativePlatform = props . addon ; } else { const nativePlatformImpl = createDefaultNativePlatform ( { id : this . _props . id ?? "" , localeDirectories : createLocaleDirectoryList ( props ) , taskAllocationsMap : createTaskAllocationsMap ( props ) , mode , isChangeTrackingEnabled , cacheConfig : createCacheConfig ( this . _props . caching ?. hierarchies ) , contentCacheSize : this . _props . caching ?. content ?. size , defaultFormats : toNativeUnitFormatsMap ( this . _props . defaultFormats ) , useMmap : this . _props . useMmap } ) ; this . _nativePlatform = new nativePlatformImpl ( ) ; } this . setupRulesetDirectories ( props ) ; if ( props ) { this . activeLocale = props . defaultLocale ; this . activeUnitSystem = props . defaultUnitSystem ; } this . _rulesets = new RulesetManagerImpl ( this . getNativePlatform ) ; if ( this . _props . enableSchemasPreload ) this . _disposeIModelOpenedListener = BriefcaseDb . onOpened . addListener ( this . onIModelOpened ) ; if ( IpcHost . isValid && isChangeTrackingEnabled ) { this . _updatesTracker = UpdatesTracker . create ( { nativePlatformGetter : this . getNativePlatform , pollInterval : props . updatesPollInterval ! } ) ; } } /**
     * Dispose the presentation manager. Must be called to clean up native resources.
     */ public dispose ( ) { if ( this . _nativePlatform ) { this . getNativePlatform ( ) . dispose ( ) ; this . _nativePlatform = undefined ; } if ( this . _disposeIModelOpenedListener ) this . _disposeIModelOpenedListener ( ) ; if ( this . _updatesTracker ) { this . _updatesTracker . dispose ( ) ; this . _updatesTracker = undefined ; } this . _isDisposed = true ; } internal public setOnManagerUsedHandler ( handler ) { this . _onManagerUsed = handler ; } /** Properties used to initialize the manager */ public get props ( ) { return this . _props ; } /**
     * Get rulesets manager
     */ public rulesets ( ) { return this . _rulesets ; } param rulesetId public vars ( rulesetId ) { return new RulesetVariablesManagerImpl ( this . getNativePlatform , rulesetId ) ; } private onIModelOpened = ( imodel ) => { const imodelAddon = this . getNativePlatform ( ) . getImodelAddon ( imodel ) ; this . getNativePlatform ( ) . forceLoadSchemas ( imodelAddon ) ; } ; internal public getNativePlatform = ( ) : NativePlatformDefinition => { if ( this . _isDisposed ) throw new PresentationError ( PresentationStatus . NotInitialized , "Attempting to use Presentation manager after disposal" ) ; return this . _nativePlatform ! ; } ; private setupRulesetDirectories ( props ? ) { const supplementalRulesetDirectories = [ path . join ( getPresentationBackendAssetsRoot ( props ?. presentationAssetsRoot ) , "supplemental-presentation-rules" ) ] ; if ( props && props . supplementalRulesetDirectories ) { props . supplementalRulesetDirectories . forEach ( ( dir ) => { if ( - 1 === supplementalRulesetDirectories . indexOf ( dir ) ) supplementalRulesetDirectories . push ( dir ) ; } ) ; } this . getNativePlatform ( ) . setupSupplementalRulesetDirectories ( supplementalRulesetDirectories ) ; const primaryRulesetDirectories = [ path . join ( getPresentationBackendAssetsRoot ( props ?. presentationAssetsRoot ) , "primary-presentation-rules" ) ] ; if ( props && props . rulesetDirectories ) { props . rulesetDirectories . forEach ( ( dir ) => { if ( - 1 === primaryRulesetDirectories . indexOf ( dir ) ) primaryRulesetDirectories . push ( dir ) ; } ) ; } this . getNativePlatform ( ) . setupRulesetDirectories ( primaryRulesetDirectories ) ; } private getRulesetIdObject ( rulesetOrId ) { if ( typeof rulesetOrId === "object" ) { if ( IpcHost . isValid ) { return { uniqueId : rulesetOrId . id , parts : { id : rulesetOrId . id } } ; } const hashedId = hash . MD5 ( rulesetOrId ) ; return { uniqueId : `${ rulesetOrId . id }-${ hashedId }` , parts : { id : rulesetOrId . id , hash : hashedId } } ; } return { uniqueId : rulesetOrId , parts : { id : rulesetOrId } } ; } internal public getRulesetId ( rulesetOrId ) { return this . getRulesetIdObject ( rulesetOrId ) . uniqueId ; } private ensureRulesetRegistered ( rulesetOrId ) { if ( typeof rulesetOrId === "object" ) { const rulesetWithNativeId = { ... rulesetOrId , id : this . getRulesetId ( rulesetOrId ) } ; return this . rulesets ( ) . add ( rulesetWithNativeId ) . id ; } return rulesetOrId ; } /** Registers given ruleset and replaces the ruleset with its ID in the resulting object */ private registerRuleset < TOptions extends { rulesetOrId : Ruleset | string ; } > ( options ) { const { rulesetOrId , ... strippedOptions } = options ; const registeredRulesetId = this . ensureRulesetRegistered ( rulesetOrId ) ; return { rulesetId : registeredRulesetId , strippedOptions } ; } public public async getNodes ( requestOptions ) { const { rulesetId , strippedOptions : { parentKey , ... strippedOptions } } = this . registerRuleset ( requestOptions ) ; const params = { requestId : parentKey ? NativePlatformRequestTypes . GetChildren : NativePlatformRequestTypes . GetRootNodes , rulesetId , ... strippedOptions , nodeKey : parentKey } ; return this . request ( params , Node . listReviver ) ; } public public async getNodesCount ( requestOptions ) { const { rulesetId , strippedOptions : { parentKey , ... strippedOptions } } = this . registerRuleset ( requestOptions ) ; const params = { requestId : parentKey ? NativePlatformRequestTypes . GetChildrenCount : NativePlatformRequestTypes . GetRootNodesCount , rulesetId , ... strippedOptions , nodeKey : parentKey } ; return this . request ( params ) ; } public public async getNodePaths ( requestOptions ) { const { rulesetId , strippedOptions : { instancePaths , ... strippedOptions } } = this . registerRuleset ( requestOptions ) ; const params = { requestId : NativePlatformRequestTypes . GetNodePaths , rulesetId , ... strippedOptions , paths : instancePaths . map ( ( p ) => p . map ( ( s ) => InstanceKey . toJSON ( s ) ) ) } ; return this . request ( params , NodePathElement . listReviver ) ; } public public async getFilteredNodePaths ( requestOptions ) { const { rulesetId , strippedOptions } = this . registerRuleset ( requestOptions ) ; const params = { requestId : NativePlatformRequestTypes . GetFilteredNodePaths , rulesetId , ... strippedOptions } ; return this . request ( params , NodePathElement . listReviver ) ; } beta public async getContentSources ( requestOptions ) { const params = { requestId : NativePlatformRequestTypes . GetContentSources , rulesetId : "ElementProperties" , ... requestOptions } ; const reviver = ( key , value ) => { return key === "" ? SelectClassInfo . listFromCompressedJSON ( value . sources , value . classesMap ) : value ; } ; return this . request ( params , reviver ) ; } public public async getContentDescriptor ( requestOptions ) { const { rulesetId , strippedOptions } = this . registerRuleset ( requestOptions ) ; const params = { requestId : NativePlatformRequestTypes . GetContentDescriptor , rulesetId , ... strippedOptions , keys : getKeysForContentRequest ( requestOptions . keys , ( map ) => bisElementInstanceKeysProcessor ( requestOptions . imodel , map ) ) } ; const reviver = ( key , value ) => { return key === "" ? Descriptor . fromJSON ( value ) : value ; } ; return this . request ( params , reviver ) ; } public public async getContentSetSize ( requestOptions ) { const { rulesetId , strippedOptions : { descriptor , ... strippedOptions } } = this . registerRuleset ( requestOptions ) ; const params = { requestId : NativePlatformRequestTypes . GetContentSetSize , rulesetId , ... strippedOptions , keys : getKeysForContentRequest ( requestOptions . keys , ( map ) => bisElementInstanceKeysProcessor ( requestOptions . imodel , map ) ) , descriptorOverrides : createContentDescriptorOverrides ( descriptor ) } ; return this . request ( params ) ; } public public async getContent ( requestOptions ) { const { rulesetId , strippedOptions : { descriptor , ... strippedOptions } } = this . registerRuleset ( requestOptions ) ; const params = { requestId : NativePlatformRequestTypes . GetContent , rulesetId , ... strippedOptions , keys : getKeysForContentRequest ( requestOptions . keys , ( map ) => bisElementInstanceKeysProcessor ( requestOptions . imodel , map ) ) , descriptorOverrides : createContentDescriptorOverrides ( descriptor ) } ; return this . request ( params , Content . reviver ) ; } param requestOptions return public public async getPagedDistinctValues ( requestOptions ) { const { rulesetId , strippedOptions } = this . registerRuleset ( requestOptions ) ; const { descriptor , keys , ... strippedOptionsNoDescriptorAndKeys } = strippedOptions ; const params = { requestId : NativePlatformRequestTypes . GetPagedDistinctValues , rulesetId , ... strippedOptionsNoDescriptorAndKeys , keys : getKeysForContentRequest ( keys , ( map ) => bisElementInstanceKeysProcessor ( requestOptions . imodel , map ) ) , descriptorOverrides : createContentDescriptorOverrides ( descriptor ) } ; const reviver = ( key , value ) => { return key === "" ? { total : value . total , items : value . items . map ( DisplayValueGroup . fromJSON ) } : value ; } ; return this . request ( params , reviver ) ; } beta public async getElementProperties ( requestOptions ) ; return alpha public async getElementProperties ( requestOptions ) ; public async getElementProperties ( requestOptions ) { if ( isSingleElementPropertiesRequestOptions ( requestOptions ) ) { const { elementId , ... optionsNoElementId } = requestOptions ; const content = await this . getContent ( { ... optionsNoElementId , descriptor : { displayType : DefaultContentDisplayTypes . PropertyPane , contentFlags : ContentFlags . ShowLabels } , rulesetOrId : "ElementProperties" , keys : new KeySet ( [ { className : "BisCore:Element" , id : elementId } ] ) } ) ; const properties = buildElementsProperties ( content ) ; return properties [ 0 ] ; } return this . getMultipleElementProperties ( requestOptions ) ; } private async getMultipleElementProperties ( requestOptions ) { const { elementClasses , ... optionsNoElementClasses } = requestOptions ; const elementsCount = getElementsCount ( requestOptions . imodel , requestOptions . elementClasses ) ; const propertiesGetter = async ( className , ids ) => buildElementsPropertiesInPages ( className , ids , async ( keys ) => { const content = await this . getContent ( { ... optionsNoElementClasses , descriptor : { displayType : DefaultContentDisplayTypes . PropertyPane , contentFlags : ContentFlags . ShowLabels } , rulesetOrId : "ElementProperties" , keys } ) ; return buildElementsProperties ( content ) ; } ) ; const ELEMENT_IDS_BATCH_SIZE = 1000 ; return { total : elementsCount , async * iterator ( ) { for ( const idsByClass of iterateElementIds ( requestOptions . imodel , elementClasses , ELEMENT_IDS_BATCH_SIZE ) ) { const propertiesPage = [ ] ; for ( const entry of idsByClass ) { propertiesPage . push ( ... ( await propertiesGetter ( entry [ 0 ] , entry [ 1 ] ) ) ) ; } yield propertiesPage ; } } } ; } public public async getDisplayLabelDefinition ( requestOptions ) { const params = { requestId : NativePlatformRequestTypes . GetDisplayLabel , ... requestOptions , key : InstanceKey . toJSON ( requestOptions . key ) } ; return this . request ( params , LabelDefinition . reviver ) ; } public public async getDisplayLabelDefinitions ( requestOptions ) { const concreteKeys = requestOptions . keys . map ( ( k ) => { if ( k . className === "BisCore:Element" ) return getElementKey ( requestOptions . imodel , k . id ) ; return k ; } ) . filter < InstanceKey > ( ( k ) : k is InstanceKey => ! ! k ) ; const contentRequestOptions = { ... requestOptions , rulesetOrId : "RulesDrivenECPresentationManager_RulesetId_DisplayLabel" , descriptor : { displayType : DefaultContentDisplayTypes . List , contentFlags : ContentFlags . ShowLabels | ContentFlags . NoFields } , keys : new KeySet ( concreteKeys ) } ; const content = await this . getContent ( contentRequestOptions ) ; return concreteKeys . map ( ( key ) => { const item = content ? content . contentSet . find ( ( it ) => it . primaryKeys . length > 0 && InstanceKey . compare ( it . primaryKeys [ 0 ] , key ) === 0 ) : undefined ; if ( ! item ) return { displayValue : "" , rawValue : "" , typeName : "" } ; return item . label ; } ) ; } public public async getSelectionScopes ( _requestOptions ) { return SelectionScopesHelper . getSelectionScopes ( ) ; } public public async computeSelection ( requestOptions ) { const { ids , scopeId , ... opts } = requestOptions ; return SelectionScopesHelper . computeSelection ( opts , ids , scopeId ) ; } private async request < TParams extends { diagnostics ? : DiagnosticsOptionsWithHandler ; requestId : string ; imodel : IModelDb ; locale ? : string ; unitSystem ? : UnitSystemKey ; } , TResult > ( params , reviver ? ) { const { requestId , imodel , locale , unitSystem , diagnostics , ... strippedParams } = params ; if ( this . _onManagerUsed ) this . _onManagerUsed ( ) ; const imodelAddon = this . getNativePlatform ( ) . getImodelAddon ( imodel ) ; const nativeRequestParams = { requestId , params : { locale : normalizeLocale ( locale ?? this . activeLocale ) , unitSystem : toOptionalNativeUnitSystem ( unitSystem ?? this . activeUnitSystem ) , ... strippedParams } } ; let diagnosticsListener ; if ( diagnostics ) { const { handler : tempDiagnosticsListener , ... diagnosticsOptions } = diagnostics ; diagnosticsListener = tempDiagnosticsListener ; nativeRequestParams . params . diagnostics = diagnosticsOptions ; } const response = await this . getNativePlatform ( ) . handleRequest ( imodelAddon , JSON . stringify ( nativeRequestParams ) ) ; diagnosticsListener && response . diagnostics && diagnosticsListener ( [ response . diagnostics ] ) ; return JSON . parse ( response . result , reviver ) ; } public public async compareHierarchies ( requestOptions ) { if ( ! requestOptions . prev . rulesetOrId && ! requestOptions . prev . rulesetVariables ) return { changes : [ ] } ; const { strippedOptions : { prev , rulesetVariables , ... options } } = this . registerRuleset ( requestOptions ) ; const currRulesetId = this . getRulesetIdObject ( requestOptions . rulesetOrId ) ; const prevRulesetId = prev . rulesetOrId ? this . getRulesetIdObject ( prev . rulesetOrId ) : currRulesetId ; if ( prevRulesetId . parts . id !== currRulesetId . parts . id ) throw new PresentationError ( PresentationStatus . InvalidArgument , "Can't compare rulesets with different IDs" ) ; const currRulesetVariables = rulesetVariables ?? [ ] ; const prevRulesetVariables = prev . rulesetVariables ?? currRulesetVariables ; const params = { requestId : NativePlatformRequestTypes . CompareHierarchies , ... options , prevRulesetId : prevRulesetId . uniqueId , currRulesetId : currRulesetId . uniqueId , prevRulesetVariables : JSON . stringify ( prevRulesetVariables ) , currRulesetVariables : JSON . stringify ( currRulesetVariables ) , expandedNodeKeys : JSON . stringify ( options . expandedNodeKeys ?? [ ] ) } ; return this . request ( params , ( key , value ) => ( ( key === "" ) ? HierarchyCompareInfo . fromJSON ( value ) : value ) ) ; } } function addInstanceKey ( classInstancesMap , key ) { let set = classInstancesMap . get ( key . className ) ; if ( ! set ) { set = new Set ( ) ; classInstancesMap . set ( key . className , set ) ; } set . add ( key . id ) ; } function bisElementInstanceKeysProcessor ( imodel , classInstancesMap ) { const elementClassName = "BisCore:Element" ; const elementIds = classInstancesMap . get ( elementClassName ) ; if ( elementIds ) { const deleteElementIds = new Array < string > ( ) ; elementIds . forEach ( ( elementId ) => { const concreteKey = getElementKey ( imodel , elementId ) ; if ( concreteKey && concreteKey . className !== elementClassName ) { deleteElementIds . push ( elementId ) ; addInstanceKey ( classInstancesMap , { className : concreteKey . className , id : elementId } ) ; } } ) ; for ( const id of deleteElementIds ) elementIds . delete ( id ) ; } } internal export function getKeysForContentRequest ( keys , classInstanceKeysProcessor ? ) { const result = { instanceKeys : [ ] , nodeKeys : [ ] } ; const classInstancesMap = new Map < string , Set < string > > ( ) ; keys . forEach ( ( key ) => { if ( Key . isNodeKey ( key ) ) result . nodeKeys . push ( key ) ; if ( Key . isInstanceKey ( key ) ) addInstanceKey ( classInstancesMap , key ) ; } ) ; if ( classInstanceKeysProcessor ) classInstanceKeysProcessor ( classInstancesMap ) ; for ( const entry of classInstancesMap ) { if ( entry [ 1 ] . size > 0 ) result . instanceKeys . push ( [ entry [ "0" ] , [ ... entry [ 1 ] ] ] ) ; } return result ; } const createContentDescriptorOverrides = ( descriptorOrOverrides ) : DescriptorOverrides => { if ( descriptorOrOverrides instanceof Descriptor ) return descriptorOrOverrides . createDescriptorOverrides ( ) ; return descriptorOrOverrides ; } ; const createLocaleDirectoryList = ( props ? ) => { const localeDirectories = [ getLocalesDirectory ( getPresentationCommonAssetsRoot ( props ?. presentationAssetsRoot ) ) ] ; if ( props && props . localeDirectories ) { props . localeDirectories . forEach ( ( dir ) => { if ( - 1 === localeDirectories . indexOf ( dir ) ) localeDirectories . push ( dir ) ; } ) ; } return localeDirectories ; } ; const createTaskAllocationsMap = ( props ? ) => { const count = props ?. workerThreadsCount ?? 2 ; return { [ Number . MAX_SAFE_INTEGER ] : count } ; } ; const normalizeLocale = ( locale ? ) => { if ( ! locale ) return undefined ; return locale . toLocaleLowerCase ( ) ; } ; const normalizeDirectory = ( directory ? ) => { return directory ? path . resolve ( directory ) : "" ; } ; const toNativeUnitSystem = ( unitSystem ) => { switch ( unitSystem ) { case "imperial" : return NativePresentationUnitSystem . BritishImperial ; case "metric" : return NativePresentationUnitSystem . Metric ; case "usCustomary" : return NativePresentationUnitSystem . UsCustomary ; case "usSurvey" : return NativePresentationUnitSystem . UsSurvey ; } } ; const toOptionalNativeUnitSystem = ( unitSystem ) => { return unitSystem ? toNativeUnitSystem ( unitSystem ) : undefined ; } ; const toNativeUnitFormatsMap = ( map ) => { if ( ! map ) return undefined ; const nativeFormatsMap = { } ; Object . keys ( map ) . forEach ( ( phenomenon ) => { const unitSystemsFormat = map [ phenomenon ] ; nativeFormatsMap [ phenomenon ] = { unitSystems : unitSystemsFormat . unitSystems . map ( toNativeUnitSystem ) , format : unitSystemsFormat . format } ; } ) ; return nativeFormatsMap ; } ; const createCacheConfig = ( config ? ) : IModelJsNative . ECPresentationHierarchyCacheConfig => { if ( config ?. mode === HierarchyCacheMode . Disk ) return { ... config , directory : normalizeDirectory ( config . directory ) } ; if ( config ?. mode === HierarchyCacheMode . Hybrid ) return { ... config , disk : config . disk ? { ... config . disk , directory : normalizeDirectory ( config . disk . directory ) } : undefined } ; if ( config ?. mode === HierarchyCacheMode . Memory ) return config ; return { mode : HierarchyCacheMode . Disk , directory : "" } ; } ; const getPresentationBackendAssetsRoot = ( ovr ? ) => { if ( typeof ovr === "string" ) return ovr ; if ( typeof ovr === "object" ) return ovr . backend ; return PRESENTATION_BACKEND_ASSETS_ROOT ; } ; const getPresentationCommonAssetsRoot = ( ovr ? ) => { if ( typeof ovr === "string" ) return ovr ; if ( typeof ovr === "object" ) return ovr . common ; return PRESENTATION_COMMON_ASSETS_ROOT ; } ; const ELEMENT_PROPERTIES_CONTENT_BATCH_SIZE = 100 ; async function buildElementsPropertiesInPages ( className , ids , getter ) { const elementProperties = [ ] ; const elementIds = [ ... ids ] ; while ( elementIds . length > 0 ) { const idsPage = elementIds . splice ( 0 , ELEMENT_PROPERTIES_CONTENT_BATCH_SIZE ) ; const keys = new KeySet ( idsPage . map ( ( id ) => ( { id , className } ) ) ) ; elementProperties . push ( ... ( await getter ( keys ) ) ) ; } return elementProperties ; }