UNPKG

@microsoft/windows-admin-center-sdk

Version:

Microsoft - Windows Admin Center Shell

1,169 lines (1,167 loc) 56.1 kB
import { LogLevel } from '../diagnostics/log-level'; import { Logging } from '../diagnostics/logging'; /** * The type of entry point. */ export var EnvironmentModuleEntryPointType; (function (EnvironmentModuleEntryPointType) { EnvironmentModuleEntryPointType["ConnectionProvider"] = "connectionProvider"; EnvironmentModuleEntryPointType["Solution"] = "solution"; EnvironmentModuleEntryPointType["Tool"] = "tool"; EnvironmentModuleEntryPointType["Service"] = "service"; EnvironmentModuleEntryPointType["SettingsForm"] = "settingsForm"; EnvironmentModuleEntryPointType["GroupProvider"] = "groupProvider"; EnvironmentModuleEntryPointType["Worker"] = "worker"; EnvironmentModuleEntryPointType["Dialog"] = "dialog"; EnvironmentModuleEntryPointType["SnapIn"] = "snapIn"; EnvironmentModuleEntryPointType["WacCopilot"] = "wacCopilot"; EnvironmentModuleEntryPointType["WacCopilotAngular"] = "wacCopilotAngular"; })(EnvironmentModuleEntryPointType || (EnvironmentModuleEntryPointType = {})); /** * The type of lifetime to implement when creating a worker */ export var ExtensionTargetType; (function (ExtensionTargetType) { ExtensionTargetType["WorkerTarget"] = "workerTarget"; })(ExtensionTargetType || (ExtensionTargetType = {})); /** * The state definition of Tool condition. */ export var EnvironmentModuleToolState; (function (EnvironmentModuleToolState) { /** * Tool is available on the connection. */ EnvironmentModuleToolState[EnvironmentModuleToolState["Available"] = 0] = "Available"; /** * Tool is not available because it's not configured properly. */ EnvironmentModuleToolState[EnvironmentModuleToolState["NotConfigured"] = 1] = "NotConfigured"; /** * Tool it not supported on the connection. */ EnvironmentModuleToolState[EnvironmentModuleToolState["NotSupported"] = 2] = "NotSupported"; })(EnvironmentModuleToolState || (EnvironmentModuleToolState = {})); export const defaultEnvironmentConfiguration = { gateway: { disabled: false }, appBar: { title: { hide: false }, solution: { hide: false, hideDropdown: false }, navigation: { allowHistory: false, allowRefresh: false, allowUrlInput: false, allowHome: false }, urlField: { hideSolutionUrl: true }, rightMenu: { showScripts: true, showSettings: true, showAbout: true, showWindowControls: false, showShareLink: false }, aboutDialog: { showFeedback: true, feedbackLink: 'https://go.microsoft.com/fwlink/?linkid=2033442', showDocumentation: true, documentationLink: 'https://go.microsoft.com/fwlink/?linkid=2150525', releaseNotesLink: 'https://aka.ms/honolulucurrent', seeMoreReleaseDetailsLink: 'https://go.microsoft.com/fwlink/?linkid=2152971', showLicense: true, showScriptsUseRights: true, showLicenseExtensions: true, showPrivacyStatement: true, privacyLink: 'https://aka.ms/winserverprivacy', showSupportPolicy: true, supportLink: 'https://aka.ms/wac-support-policy', showThirdPartyNotice: true, showShortcuts: true, openLinkWithoutHref: false, showAccessibilityCompliance: false, accessibilityComplianceLink: 'https://go.microsoft.com/fwlink/?linkid=2121429', extensionLicenseLink: 'https://aka.ms/wac-ga-extension-eula', eulaLicenseLink: 'https://aka.ms/wac-product-ga-eula' }, overview: { gatewayStatusUpdateLink: 'settings/updates', updateLearnMoreLink: 'https://aka.ms/honolulucurrent' } }, extensionsAutoUpdate: { enable: true, showAlert: true }, gatewayAutoUpdate: { enable: true }, whatsNew: { showDialog: true }, tools: { hide: false }, connections: { type: 'standard', header: { hide: false } }, telemetry: { sourceLibraryCdnLink: 'https://js.monitor.azure.com/scripts/c/ms.analytics-web-3.0.2.min.js', // Integrity hash generated via srihash.org sourceLibraryCdnIntegrityHash: 'sha384-ZCsysyTvHb4xqC4BgJSay4nwlNIGlT1w5t5YQS5htghoD2XEL2b4SrkhRPhtr9GD' }, settings: { account: { show: true }, personalization: { show: true }, diagnostic: { show: true }, language: { show: true }, notification: { show: true }, extension: { show: true }, azure: { show: true }, access: { show: true }, sharedConnections: { show: true }, general: { show: true }, connectivity: { show: true }, updates: { show: true }, extensionUpdates: { show: true }, performance: { show: true }, advanced: { show: true }, proxy: { show: true } } }; /** * Environment module class. */ export class EnvironmentModule { static logSourceName = 'ManifestLoader'; /** * Static mapping for connection information. */ static connectionMap = null; /** * Static mapping for extensionTargetTypes to a map of extensionTargetIds to entry point ids that fulfill that target */ static extensionTargetFulfillmentMap; /** * Static mapping for friendly url strings to entrypoint ids */ static friendlyUrlMap = null; /** * The name of shell. */ static nameOfShell = 'msft.sme.shell'; /** * The default signature if missing manifest. */ static defaultSignature = 'version 0.0.0'; /** * The prefix for identifying an sme-icon reference */ static smeIconPrefix = 'sme-icon:'; /** * All entry points from all modules */ static allEntryPoints = null; /** * All entry points by type */ static entryPointsByType = null; /** * All entry points by type */ static entryPointsById = null; /** * All extension targets from all modules */ static allExtensionTargets = null; /** * All extension targets by type */ static extensionTargetsByType = null; /** * All extension targets by type */ static extensionTargetsById = null; /** * All extension targets by type */ static extensionOrigins = null; /** * schema file information. */ $schema; /** * name of module. */ name; /** * display name of module. */ displayName; /** * description of set of tools. */ description; /** * keywords of the module. */ keywords; /** * origin absolute url. */ origin; /** * target url to open the iframe. */ target; /** * signature. */ signature; /** * The version of this module */ version; /** * icon url, absolute or relative to Shell url. */ icon; /** * The support JEA endpoint with PowerShell module. */ jea; /** * extension targets for this extension * Defines things that can be extended in this module. */ extensionTargets; /** * entry points to launch the tool. */ entryPoints; /** * Assets shared by the module */ assets; /** * localized resources of strings. */ resources; /** * Schemas provided for use in this manifest */ schemas; /** * list of all other available modules for shell. */ modules; /** * Indicates that this module is being sideLoaded */ isSideLoaded; /** * The Environment configuration */ configuration; /** * returns true if the provided origin is a known extension origin */ static isExtensionOrigin(origin) { return this.extensionOrigins.has(origin); } /** * Indicates if a given icon reference is an sme-icon or some other form * @param iconRef the reference to check */ static isSmeIconRef(iconRef) { return (!MsftSme.isNullOrUndefined(iconRef) && iconRef.startsWith(EnvironmentModule.smeIconPrefix)); } /** * Gets the icon class from an iconRef or returns null if the iconRef is not an sme-icon reference * @param iconRef the reference to check */ static getSmeIconClassFromRef(iconRef) { return (!MsftSme.isNullOrUndefined(iconRef) && EnvironmentModule.isSmeIconRef(iconRef)) ? iconRef.substring(EnvironmentModule.smeIconPrefix.length) : null; } /** * Gets the icon url from an iconRef or returns null if the iconRef is an sme-icon reference * @param iconRef the reference to check */ static getFormattedIconRefUrl(iconRef) { return (MsftSme.isNullOrUndefined(iconRef) || EnvironmentModule.isSmeIconRef(iconRef)) ? null : `url(${iconRef})`; } /** * Processes an icon reference to resolve it to either a sme-icon or absolute url * @param iconRef The icon reference * @param module the module that the icon belongs to */ static processIconReference(iconRef, module) { // leave icon ref alone if it is an sme-icon, absolute uri, or null/whitespace if (MsftSme.isNullOrWhiteSpace(iconRef) || EnvironmentModule.isSmeIconRef(iconRef) || MsftSme.isUriAbsolute(iconRef)) { return iconRef; } return this.toAbsolutePath(module, iconRef); } /** * Converts relative module paths into absolute uris */ static toAbsolutePath(module, relativePath) { const pathComponents = [relativePath]; if (module.target && !module.isSideLoaded) { pathComponents.unshift(module.target); } pathComponents.unshift(module.origin); return MsftSme.pathJoin('/', pathComponents); } /** * Find resource string for the key. * * @param resources The resource. * @param locale The locale. * @param key The key string. */ static findResource(resources, locale, key) { const prefix = 'resources:strings:'; if (!key.startsWith(prefix)) { return key; } key = key.substring(prefix.length); const en = resources.first((value) => value.locale === 'default'); const current = resources.first((value) => value.locale === locale); if (current && current.strings[key]) { return current.strings[key]; } else if (en && en.strings[key]) { return en.strings[key]; } return key; } /** * Recursively processes manifest resources * * @param module the manifest object. * @param locale the locale string such as 'en'. * @param obj current object, defaults to the module itself */ static processModuleResources(module, locale, obj = module) { if (!Array.isArray(module.resources)) { module.resources = []; } const keys = Object.keys(obj); keys.forEach(key => { if (typeof obj[key] === 'string') { // first check the string for localized values obj[key] = EnvironmentModule.findResource(module.resources, locale, obj[key]); // if the key is 'icon' treat this field as an icon if (key === 'icon') { obj[key] = EnvironmentModule.processIconReference(obj[key], module); } } else if (typeof obj[key] === 'object') { EnvironmentModule.processModuleResources(module, locale, obj[key]); } }); } static getModuleAssetUri(moduleName, resourceName, version) { const assets = MsftSme.self().Resources.moduleAssets; if (assets[moduleName] && assets[moduleName][resourceName] && assets[moduleName][resourceName][version]) { return assets[moduleName][resourceName][version]; } return null; } /** * Create environment object from the manifest. * * @param manifest the manifest object. * @param locale the locale string such as 'en'. */ static createEnvironment(manifest, locale) { // use local origin automatically. manifest.origin = MsftSme.getSafeOrigin(); // initialize tracking of known trusted origins this.extensionOrigins = new Set(); this.extensionOrigins.add(manifest.origin); // use current time if version is missing so this enforce browser caching if v= option is used. manifest.version = manifest.version ? manifest.version : Date.now().toString(); // merge manifest configuration with default configuration manifest.configuration = MsftSme.deepAssign({}, defaultEnvironmentConfiguration, manifest.configuration); const modules = manifest.modules || []; // process manifest entry points separately from children modules if (manifest.name === 'msft.sme.shell') { const shellExtensionManifest = { ...manifest }; delete shellExtensionManifest.modules; shellExtensionManifest.name = 'msft.sme.shell-extensions'; shellExtensionManifest.target = '/extensions'; modules.push(shellExtensionManifest); } // Initialize Module Assets MsftSme.self().Resources.moduleAssets = {}; // process Modules if (modules && modules.length > 0) { // first process resources, signature, and origin for (const module of modules) { // TODO: signature is going to be removed from modules, for now always use default. module.signature = EnvironmentModule.defaultSignature; // if the module has no origin, use the global one. module.origin = module.origin || manifest.origin; this.extensionOrigins.add(module.origin); // process module parts EnvironmentModule.processModuleResources(module, locale); delete module['resources']; // add assets to module assets using absolute paths if (module.assets) { const moduleAssets = {}; const assetKeys = Object.keys(module.assets); assetKeys.forEach(key => { moduleAssets[key] = {}; const versionKeys = Object.keys(module.assets[key]); versionKeys.forEach(version => { moduleAssets[key][version] = this.toAbsolutePath(module, module.assets[key][version]); }); }); MsftSme.self().Resources.moduleAssets[module.name] = moduleAssets; } } // now process extension targets for (const module of modules) { EnvironmentModule.processExtensionTargets(module); } // finally process entry points for (const module of modules) { EnvironmentModule.processEntryPoints(module); } // default entry point collections to be sorted alphabetically const alphaSort = (a, b) => a.displayName ? a.displayName.localeCompareIgnoreCase(b.displayName) : -1; if (this.allEntryPoints) { this.allEntryPoints.sort(alphaSort); } else { this.allEntryPoints = []; } if (this.entryPointsByType) { this.entryPointsByType[EnvironmentModuleEntryPointType.Solution].sort(alphaSort); this.entryPointsByType[EnvironmentModuleEntryPointType.Tool].sort(alphaSort); } // TODO: do we want settings alphabetized? } return manifest; } /** * Gets the environment module. * * @param name the name of module. * @return EnvironmentModule the environment module. */ static checkEnvironmentInitialized() { if (!MsftSme || !MsftSme.self().Environment) { const message = MsftSme.getStrings().MsftSmeShell.Core.Error.EnvironmentNotInitialized.message; throw Error(message); } } /** * Gets the environment module. * * @param name the name of module. * @return EnvironmentModule the environment module. */ static getEnvironmentModule(name) { const shellEnvironment = MsftSme.self().Environment; const modules = shellEnvironment && shellEnvironment.modules; if (modules) { for (const module of modules) { if (module.name === name) { return module; } } } if (shellEnvironment.name === name) { return shellEnvironment; } return null; } /** * Process extension targets for quick and easy access later * @param module the modules to process */ static processExtensionTargets(module) { // initialize tracking structures if they have not been if (!EnvironmentModule.allExtensionTargets) { EnvironmentModule.allExtensionTargets = []; EnvironmentModule.extensionTargetsByType = {}; EnvironmentModule.extensionTargetsById = {}; EnvironmentModule.extensionTargetFulfillmentMap = {}; Object.keys(ExtensionTargetType).forEach(key => this.extensionTargetsByType[ExtensionTargetType[key]] = []); } // stop if there are no extension targets in this module if (!module.extensionTargets) { return; } module.extensionTargets.forEach(extensionTarget => { // ignore invalid types in the manifest if (!EnvironmentModule.extensionTargetsByType[extensionTarget.extensionTargetType]) { Logging.log({ source: EnvironmentModule.logSourceName, level: LogLevel.Warning, consoleGroupHeader: `Unable to determine extensionTargetType.`, message: `Entry point "${extensionTarget.name}" in module "${module.displayName} has a extensionTargetType of: ${extensionTarget.extensionTargetType}` }); return; } // save the module information into the extension target extensionTarget.parentModule = module; const extensionTargetId = EnvironmentModule.createFormattedExtensionTarget(extensionTarget); // save the module information into the extension target extensionTarget.parentModule = module; // push to data structures EnvironmentModule.allExtensionTargets.push(extensionTarget); EnvironmentModule.extensionTargetsByType[extensionTarget.extensionTargetType].push(extensionTarget); EnvironmentModule.extensionTargetsById[extensionTargetId] = extensionTarget; // initialize fulfillment map EnvironmentModule.extensionTargetFulfillmentMap[extensionTargetId] = []; }); } /** * Process Entry points for quick and easy access later * @param module the modules to process */ static processEntryPoints(module) { // initialize tracking structures if they have not been if (!EnvironmentModule.allEntryPoints) { EnvironmentModule.allEntryPoints = []; EnvironmentModule.entryPointsByType = {}; EnvironmentModule.entryPointsById = {}; Object.keys(EnvironmentModuleEntryPointType).forEach(key => this.entryPointsByType[EnvironmentModuleEntryPointType[key]] = []); this.friendlyUrlMap = { connectionTypes: { to: {}, from: {} }, solutions: { to: {}, from: {} }, tools: { to: {}, from: {} }, settingsForms: { to: {}, from: {} } }; } if (!module.entryPoints) { return; } module.entryPoints.forEach(entryPoint => { // ignore invalid types in the manifest if (!EnvironmentModule.entryPointsByType[entryPoint.entryPointType]) { Logging.log({ source: EnvironmentModule.logSourceName, level: LogLevel.Warning, consoleGroupHeader: `Unable to determine entryPointType.`, message: `Entry point "${entryPoint.displayName}" in module "${module.displayName} has a entryPointType of: ${entryPoint.entryPointType}` }); return; } // if we have experiment keys, dont process the entry point if any key is not enabled if (entryPoint.experimentKeys && entryPoint.experimentKeys.some(key => !MsftSme.isExperimentEnabledRaw(key))) { return; } // save the module information into the entry point entryPoint.parentModule = module; const entryPointId = EnvironmentModule.createFormattedEntrypoint(entryPoint); // push to data structures EnvironmentModule.allEntryPoints.push(entryPoint); EnvironmentModule.entryPointsByType[entryPoint.entryPointType].push(entryPoint); EnvironmentModule.entryPointsById[entryPointId] = entryPoint; // process url mappings if (entryPoint.urlName) { switch (entryPoint.entryPointType) { case EnvironmentModuleEntryPointType.Solution: this.friendlyUrlMap.solutions.to[entryPointId] = entryPoint.urlName; this.friendlyUrlMap.solutions.from[entryPoint.urlName] = entryPointId; break; case EnvironmentModuleEntryPointType.Tool: this.friendlyUrlMap.tools.to[entryPointId] = entryPoint.urlName; this.friendlyUrlMap.tools.from[entryPoint.urlName] = entryPointId; break; case EnvironmentModuleEntryPointType.SettingsForm: this.friendlyUrlMap.settingsForms.to[entryPointId] = entryPoint.urlName; this.friendlyUrlMap.settingsForms.from[entryPoint.urlName] = entryPointId; break; case EnvironmentModuleEntryPointType.ConnectionProvider: if (entryPoint.connectionTypeUrlName) { this.friendlyUrlMap.connectionTypes.to[entryPoint.connectionType] = entryPoint.connectionTypeUrlName; this.friendlyUrlMap.connectionTypes.from[entryPoint.connectionTypeUrlName] = entryPoint.connectionType; } } } if (entryPoint.entryPointType === EnvironmentModuleEntryPointType.ConnectionProvider && entryPoint.connectionTypeUrlName) { this.friendlyUrlMap.connectionTypes.to[entryPoint.connectionType] = entryPoint.connectionTypeUrlName; this.friendlyUrlMap.connectionTypes.from[entryPoint.connectionTypeUrlName] = entryPoint.connectionType; } // process extension mappings if (entryPoint.extends) { entryPoint.extends.forEach(extender => { const extensionTarget = EnvironmentModule.getExtensionTargets(target => { return target.parentModule.name === extender.module && target.name === extender.target; }).first(); if (MsftSme.isNullOrUndefined(extensionTarget)) { // if we get here, either that target is no longer supported or the target extension is not installed. return; } const extensionTargetId = EnvironmentModule.createFormattedExtensionTarget(extensionTarget); EnvironmentModule.extensionTargetFulfillmentMap[extensionTargetId].push(entryPointId); }); } }); } /** * Evaluates all of the modules in the environment and returns a flat list of all of their entry points. * optionally filtered by the 'filter' function * * @param filter the filter to apply to the entry points. * @return a flat list of all module entry points */ static getExtensionTargets(filter) { return filter ? EnvironmentModule.allExtensionTargets.filter(filter) : EnvironmentModule.allExtensionTargets; } /** * Evaluates all of the modules in the environment and returns a flat list of all of their entry points. * optionally filtered by the 'filter' function * * @param filter the filter to apply to the entry points. * @return a flat list of all module entry points */ static getEntryPoints(filter) { EnvironmentModule.checkEnvironmentInitialized(); return filter ? EnvironmentModule.allEntryPoints.filter(filter) : EnvironmentModule.allEntryPoints; } /** * Gets the available entry points from all of the modules in the environment, filtered by type. * * @param name the name of module. * @return EnvironmentModule the environment module. */ static getEntryPointsByType(entryPointTypes) { EnvironmentModule.checkEnvironmentInitialized(); return entryPointTypes.mapMany(type => EnvironmentModule.entryPointsByType[type]); } /** * Gets the connection type mapping data. * * @return the mapping object. */ static fulfillExtensionTarget(extensionTargetId) { EnvironmentModule.checkEnvironmentInitialized(); return EnvironmentModule.extensionTargetFulfillmentMap[extensionTargetId]; } /** * Gets the connection type mapping data. * * @return the mapping object. */ static getConnectionMap() { if (!EnvironmentModule.connectionMap) { EnvironmentModule.connectionMap = EnvironmentModule.createConnectionMap(); } return EnvironmentModule.connectionMap; } /** * Gets a friendly url segment from a connection type * @param connectionType the connection type to map. * @return string friendly url for the connection type or the connection type if no friendly name exists. */ static getFriendlyUrlSegmentForConnectionType(connectionType, fallbackToUnfriendlySegment = true) { return EnvironmentModule.friendlyUrlMap.connectionTypes.to[connectionType] || (fallbackToUnfriendlySegment ? connectionType : null); } /** * Gets a connection type from a friendly url segment * @param urlSegment the url segment to map * @return string connection type found using the friendly url segment */ static getConnectionTypeFromFriendlyUrlSegment(urlSegment) { return EnvironmentModule.friendlyUrlMap.connectionTypes.from[urlSegment]; } /** * Gets a friendly url segment from an entry point id * @param urlSegment the url segment. * @param entryPointType the type of entry point to look for. * @return string friendly url for the entry point id */ static getFriendlyUrlSegmentForEntryPoint(entryPoint, entryPointType, fallbackToUnfriendlySegment = true) { switch (entryPointType) { case EnvironmentModuleEntryPointType.Solution: return EnvironmentModule.friendlyUrlMap.solutions.to[entryPoint] || (fallbackToUnfriendlySegment ? entryPoint : null); case EnvironmentModuleEntryPointType.Tool: return EnvironmentModule.friendlyUrlMap.tools.to[entryPoint] || (fallbackToUnfriendlySegment ? entryPoint : null); case EnvironmentModuleEntryPointType.SettingsForm: return EnvironmentModule.friendlyUrlMap.settingsForms.to[entryPoint] || (fallbackToUnfriendlySegment ? entryPoint : null); } return null; } /** * Gets a friendly url segment for an entry point * @param urlSegment the url segment. * @param entryPointType the type of entry point to look for. * @return string friendly url for the entry point */ static getEntryPointFromFriendlyUrlSegment(urlSegment, entryPointType) { switch (entryPointType) { case EnvironmentModuleEntryPointType.Solution: return EnvironmentModule.friendlyUrlMap.solutions.from[urlSegment]; case EnvironmentModuleEntryPointType.Tool: return EnvironmentModule.friendlyUrlMap.tools.from[urlSegment]; case EnvironmentModuleEntryPointType.SettingsForm: return EnvironmentModule.friendlyUrlMap.settingsForms.from[urlSegment]; } return null; } /** * Gets the connection type information. * * @param typeName the type name. * @return EnvironmentConnectionTypeInfo the connection type information. */ static getConnectionTypeInfo(typeName) { return EnvironmentModule.getConnectionMap()[typeName]; } static createConnectionMap() { const map = {}; const providers = this.getEntryPointsByType([EnvironmentModuleEntryPointType.ConnectionProvider]); providers.forEach(provider => { if (!provider.connectionTypeDefaultSolution) { const message = MsftSme.getStrings().MsftSmeShell.Core.Error.EnvironmentMissingDefault.message; throw new Error(message); } const solutionEntrypoint = EnvironmentModule.splitFormattedEntrypoint(provider.connectionTypeDefaultSolution); const solution = this.getEntryPointsByType([EnvironmentModuleEntryPointType.Solution]) .first(ep => ep.parentModule.name === solutionEntrypoint.moduleName && ep.name === solutionEntrypoint.entrypointName); map[provider.connectionType] = { provider, solution }; if (solution && solution.parentModule && solution.parentModule.name) { map[solution.parentModule.name] = map[provider.connectionType]; } }); return map; } /** * splits an entrypoint identifier string into its respective module and entrypoint names * @param format the formatted entrypoint identifier string */ static splitFormattedEntrypoint(format) { const parts = format.split('!'); return { moduleName: parts[0], entrypointName: parts[1] }; } /** * creates a formatted entrypoint identifier string from an entrypoint * @param entryPoint the entrypoint to create the string from */ static createFormattedEntrypoint(entryPoint) { return EnvironmentModule.createEntrypointId(entryPoint.parentModule.name, entryPoint.name); } static createEntrypointId(moduleName, entryPointName) { return `${moduleName}!${entryPointName}`; } /** * Create a formatted iframe ID used to link iframes and their html element * Replace . with - because . is not permitted in css selectors required for testing * @param parentModuleName the name of the parent module of the iframe content * @param entryPointName the name of the entry point loaded by the iframe */ static createFormattedIFrameId(parentModuleName, entryPointName) { const parentModuleId = MsftSme.replaceAll(parentModuleName, '.', '-'); return '{0}-{1}'.format(parentModuleId, entryPointName); } /** * splits an extension target identifier string into its respective module and extension target names * @param format the formatted extension target identifier string */ static splitFormattedExtensionTarget(format) { const parts = format.split('!'); return { moduleName: parts[0], extensionTargetName: parts[1] }; } /** * creates a formatted extension target identifier string from an extension target * @param entryPoint the extension target to create the string from */ static createFormattedExtensionTarget(extensionTarget) { return `${extensionTarget.parentModule.name}!${extensionTarget.name}`; } /** * resolves an entrypoint from a formatted entrypoint identifier string * @param formattedEntrypointIdentifier the formatted entrypoint identifier string */ static resolveEntrypoint(formattedEntrypointIdentifier) { if (!formattedEntrypointIdentifier) { return null; } const parts = EnvironmentModule.splitFormattedEntrypoint(formattedEntrypointIdentifier); if (!parts.moduleName || !parts.entrypointName) { return null; } const entryPoints = this.getEntryPoints(ep => ep.name === parts.entrypointName && ep.parentModule.name === parts.moduleName); return entryPoints[0] || null; } /** * Gets the name of current shell or module. */ static getModuleName() { const self = MsftSme.self(); return self.Init.moduleName; } /** * Gets the version of current shell or module. */ static getModuleVersion() { const self = MsftSme.self(); if (self.Environment.version) { return self.Environment.version; } for (const module of self.Environment.modules) { if (module.name === self.Init.moduleName && module.version) { self.Environment.version = module.version; return module.version; } } return '0.0.0'; } /** * Compare the shell version with specified version. * - this function must be called after NavigationService Init was called, otherwise it * cannot get current shell version and return false. * - Tag format is expected be like "<Major>.<Minor>.<Patch>-<Tag>.<Tag version>". * ex) 1.2.3-myTagName.123 * * @param inputVersion the version to compare. * @return true if shell has newer version running. */ static shellGreaterOrEqual(inputVersion) { if (MsftSme.isNullOrWhiteSpace(inputVersion)) { // shell doesn't give version. Logging.logError('EnvironmentModule.shellGreaterOrEqual', 'version is empty.'); return false; } const shellVersion = MsftSme.self().Init.shellVersion; if (MsftSme.isNullOrWhiteSpace(shellVersion)) { // shell doesn't give version. Logging.logError('EnvironmentModule.shellGreaterOrEqual', 'shell version is not available.'); return false; } const tagSegments = inputVersion.split("-"); let inputCoreVersion = inputVersion; let inputTag = ""; let inputTagVersion = 0; if (tagSegments.length === 2) { inputCoreVersion = tagSegments[0]; const tag = tagSegments[1].split("."); inputTag = tag[0]; if (tag.length === 2) { const tagNumber = Number(tag[1]); inputTagVersion = isNaN(tagNumber) ? 0 : tagNumber; } } const inputSegments = inputCoreVersion.split('.'); if (inputSegments.length !== 3) { // shell doesn't give right version format. Logging.logError('EnvironmentModule.shellGreaterOrEqual', 'version format is not right.'); return false; } const shellTagSegments = shellVersion.split("-"); let shellCoreVersion = shellVersion; let shellTag = ""; let shellTagVersion = 0; if (shellTagSegments.length === 2) { shellCoreVersion = shellTagSegments[0]; const tag = shellTagSegments[1].split("."); shellTag = tag[0]; if (tag.length === 2) { const tagNumber = Number(tag[1]); shellTagVersion = isNaN(tagNumber) ? 0 : tagNumber; } } const shellSegments = shellCoreVersion.split('.'); if (shellSegments.length !== 3) { // shell doesn't give right version format. Logging.logError('EnvironmentModule.shellGreaterOrEqual', 'shell version is corrupted.'); return false; } const inputDigits = inputSegments.map(digit => { const num = Number(digit); return isNaN(num) ? 0 : num; }); const shellDigits = shellSegments.map(digit => { const num = Number(digit); return isNaN(num) ? 0 : num; }); if (shellDigits[0] < inputDigits[0] || (shellDigits[0] === inputDigits[0] && shellDigits[1] < inputDigits[1]) || (shellDigits[0] === inputDigits[0] && shellDigits[1] === inputDigits[1] && shellDigits[2] < inputDigits[2]) || (shellDigits[0] === inputDigits[0] && shellDigits[1] === inputDigits[1] && shellDigits[2] === inputDigits[2] && shellTag !== "" && shellTag.localeCompare(inputTag) < 0) || (shellDigits[0] === inputDigits[0] && shellDigits[1] === inputDigits[1] && shellDigits[2] === inputDigits[2] && shellTag !== "" && shellTag.localeCompare(inputTag) === 0) && shellTagVersion < inputTagVersion) { return false; } return true; } /** * Check if the gateway uses API set of version 2.0.0. * @returns true if version 2.0.0 */ static get isGatewayV200() { return MsftSme.self().Init.gatewayApiVersion === '2.0.0'; } /** * check if the gateway runs on Windows platform. * @returns true if Windows platform. */ static get isGatewayWindows() { return MsftSme.self().Init.gatewayPlatform === 'Windows'; } /** * Check if the gateway runs on Linux platform. * @returns true if Linux platform. */ static get isGatewayLinux() { return MsftSme.self().Init.gatewayPlatform === 'Linux'; } } //# sourceMappingURL=environment-modules.js.map // SIG // Begin signature block // SIG // MIIoJgYJKoZIhvcNAQcCoIIoFzCCKBMCAQExDzANBglg // SIG // hkgBZQMEAgEFADB3BgorBgEEAYI3AgEEoGkwZzAyBgor // SIG // BgEEAYI3AgEeMCQCAQEEEBDgyQbOONQRoqMAEEvTUJAC // SIG // AQACAQACAQACAQACAQAwMTANBglghkgBZQMEAgEFAAQg // SIG // ZUtOdw/8RtLyUeQ70hw+s0fxr6Kgl5t3RQ4MvnPIHQWg // SIG // gg12MIIF9DCCA9ygAwIBAgITMwAABARsdAb/VysncgAA // SIG // AAAEBDANBgkqhkiG9w0BAQsFADB+MQswCQYDVQQGEwJV // SIG // UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH // SIG // UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBv // SIG // cmF0aW9uMSgwJgYDVQQDEx9NaWNyb3NvZnQgQ29kZSBT // SIG // aWduaW5nIFBDQSAyMDExMB4XDTI0MDkxMjIwMTExNFoX // SIG // DTI1MDkxMTIwMTExNFowdDELMAkGA1UEBhMCVVMxEzAR // SIG // BgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1v // SIG // bmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv // SIG // bjEeMBwGA1UEAxMVTWljcm9zb2Z0IENvcnBvcmF0aW9u // SIG // MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA // SIG // tCg32mOdDA6rBBnZSMwxwXegqiDEUFlvQH9Sxww07hY3 // SIG // w7L52tJxLg0mCZjcszQddI6W4NJYb5E9QM319kyyE0l8 // SIG // EvA/pgcxgljDP8E6XIlgVf6W40ms286Cr0azaA1f7vaJ // SIG // jjNhGsMqOSSSXTZDNnfKs5ENG0bkXeB2q5hrp0qLsm/T // SIG // WO3oFjeROZVHN2tgETswHR3WKTm6QjnXgGNj+V6rSZJO // SIG // /WkTqc8NesAo3Up/KjMwgc0e67x9llZLxRyyMWUBE9co // SIG // T2+pUZqYAUDZ84nR1djnMY3PMDYiA84Gw5JpceeED38O // SIG // 0cEIvKdX8uG8oQa047+evMfDRr94MG9EWwIDAQABo4IB // SIG // czCCAW8wHwYDVR0lBBgwFgYKKwYBBAGCN0wIAQYIKwYB // SIG // BQUHAwMwHQYDVR0OBBYEFPIboTWxEw1PmVpZS+AzTDwo // SIG // oxFOMEUGA1UdEQQ+MDykOjA4MR4wHAYDVQQLExVNaWNy // SIG // b3NvZnQgQ29ycG9yYXRpb24xFjAUBgNVBAUTDTIzMDAx // SIG // Mis1MDI5MjMwHwYDVR0jBBgwFoAUSG5k5VAF04KqFzc3 // SIG // IrVtqMp1ApUwVAYDVR0fBE0wSzBJoEegRYZDaHR0cDov // SIG // L3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jcmwvTWlj // SIG // Q29kU2lnUENBMjAxMV8yMDExLTA3LTA4LmNybDBhBggr // SIG // BgEFBQcBAQRVMFMwUQYIKwYBBQUHMAKGRWh0dHA6Ly93 // SIG // d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY2VydHMvTWlj // SIG // Q29kU2lnUENBMjAxMV8yMDExLTA3LTA4LmNydDAMBgNV // SIG // HRMBAf8EAjAAMA0GCSqGSIb3DQEBCwUAA4ICAQCI5g/S // SIG // KUFb3wdUHob6Qhnu0Hk0JCkO4925gzI8EqhS+K4umnvS // SIG // BU3acsJ+bJprUiMimA59/5x7WhJ9F9TQYy+aD9AYwMtb // SIG // KsQ/rst+QflfML+Rq8YTAyT/JdkIy7R/1IJUkyIS6srf // SIG // G1AKlX8n6YeAjjEb8MI07wobQp1F1wArgl2B1mpTqHND // SIG // lNqBjfpjySCScWjUHNbIwbDGxiFr93JoEh5AhJqzL+8m // SIG // onaXj7elfsjzIpPnl8NyH2eXjTojYC9a2c4EiX0571Ko // SIG // mhENF3RtR25A7/X7+gk6upuE8tyMy4sBkl2MUSF08U+E // SIG // 2LOVcR8trhYxV1lUi9CdgEU2CxODspdcFwxdT1+G8YNc // SIG // gzHyjx3BNSI4nOZcdSnStUpGhCXbaOIXfvtOSfQX/UwJ // SIG // oruhCugvTnub0Wna6CQiturglCOMyIy/6hu5rMFvqk9A // SIG // ltIJ0fSR5FwljW6PHHDJNbCWrZkaEgIn24M2mG1M/Ppb // SIG // /iF8uRhbgJi5zWxo2nAdyDBqWvpWxYIoee/3yIWpquVY // SIG // cYGhJp/1I1sq/nD4gBVrk1SKX7Do2xAMMO+cFETTNSJq // SIG // fTSSsntTtuBLKRB5mw5qglHKuzapDiiBuD1Zt4QwxA/1 // SIG // kKcyQ5L7uBayG78kxlVNNbyrIOFH3HYmdH0Pv1dIX/Mq // SIG // 7avQpAfIiLpOWwcbjzCCB3owggVioAMCAQICCmEOkNIA // SIG // AAAAAAMwDQYJKoZIhvcNAQELBQAwgYgxCzAJBgNVBAYT // SIG // AlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQH // SIG // EwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29y // SIG // cG9yYXRpb24xMjAwBgNVBAMTKU1pY3Jvc29mdCBSb290 // SIG // IENlcnRpZmljYXRlIEF1dGhvcml0eSAyMDExMB4XDTEx // SIG // MDcwODIwNTkwOVoXDTI2MDcwODIxMDkwOVowfjELMAkG // SIG // A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAO // SIG // BgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29m // SIG // dCBDb3Jwb3JhdGlvbjEoMCYGA1UEAxMfTWljcm9zb2Z0 // SIG // IENvZGUgU2lnbmluZyBQQ0EgMjAxMTCCAiIwDQYJKoZI // SIG // hvcNAQEBBQADggIPADCCAgoCggIBAKvw+nIQHC6t2G6q // SIG // ghBNNLrytlghn0IbKmvpWlCquAY4GgRJun/DDB7dN2vG // SIG // EtgL8DjCmQawyDnVARQxQtOJDXlkh36UYCRsr55JnOlo // SIG // XtLfm1OyCizDr9mpK656Ca/XllnKYBoF6WZ26DJSJhIv // SIG // 56sIUM+zRLdd2MQuA3WraPPLbfM6XKEW9Ea64DhkrG5k // SIG // NXimoGMPLdNAk/jj3gcN1Vx5pUkp5w2+oBN3vpQ97/vj // SIG // K1oQH01WKKJ6cuASOrdJXtjt7UORg9l7snuGG9k+sYxd // SIG // 6IlPhBryoS9Z5JA7La4zWMW3Pv4y07MDPbGyr5I4ftKd // SIG // gCz1TlaRITUlwzluZH9TupwPrRkjhMv0ugOGjfdf8NBS // SIG // v4yUh7zAIXQlXxgotswnKDglmDlKNs98sZKuHCOnqWbs // SIG // YR9q4ShJnV+I4iVd0yFLPlLEtVc/JAPw0XpbL9Uj43Bd // SIG // D1FGd7P4AOG8rAKCX9vAFbO9G9RVS+c5oQ/pI0m8GLhE // SIG // fEXkwcNyeuBy5yTfv0aZxe/CHFfbg43sTUkwp6uO3+xb // SIG // n6/83bBm4sGXgXvt1u1L50kppxMopqd9Z4DmimJ4X7Iv // SIG // hNdXnFy/dygo8e1twyiPLI9AN0/B4YVEicQJTMXUpUMv // SIG // dJX3bvh4IFgsE11glZo+TzOE2rCIF96eTvSWsLxGoGyY // SIG // 0uDWiIwLAgMBAAGjggHtMIIB6TAQBgkrBgEEAYI3FQEE // SIG // AwIBADAdBgNVHQ4EFgQUSG5k5VAF04KqFzc3IrVtqMp1 // SIG // ApUwGQYJKwYBBAGCNxQCBAweCgBTAHUAYgBDAEEwCwYD // SIG // VR0PBAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0j // SIG // BBgwFoAUci06AjGQQ7kUBU7h6qfHMdEjiTQwWgYDVR0f // SIG // BFMwUTBPoE2gS4ZJaHR0cDovL2NybC5taWNyb3NvZnQu // SIG // Y29tL3BraS9jcmwvcHJvZHVjdHMvTWljUm9vQ2VyQXV0 // SIG // MjAxMV8yMDExXzAzXzIyLmNybDBeBggrBgEFBQcBAQRS // SIG // MFAwTgYIKwYBBQUHMAKGQmh0dHA6Ly93d3cubWljcm9z // SIG // b2Z0LmNvbS9wa2kvY2VydHMvTWljUm9vQ2VyQXV0MjAx // SIG // MV8yMDExXzAzXzIyLmNydDCBnwYDVR0gBIGXMIGUMIGR // SIG // BgkrBgEEAYI3LgMwgYMwPwYIKwYBBQUHAgEWM2h0dHA6 // SIG // Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvZG9jcy9w // SIG // cmltYXJ5Y3BzLmh0bTBABggrBgEFBQcCAjA0HjIgHQBM // SIG // AGUAZwBhAGwAXwBwAG8AbABpAGMAeQBfAHMAdABhAHQA // SIG // ZQBtAGUAbgB0AC4gHTANBgkqhkiG9w0BAQsFAAOCAgEA // SIG // Z/KGpZjgVHkaLtPYdGcimwuWEeFjkplCln3SeQyQwWVf // SIG // Liw++MNy0W2D/r4/6ArKO79HqaPzadtjvyI1pZddZYSQ // SIG // fYtGUFXYDJJ80hpLHPM8QotS0LD9a+M+By4pm+Y9G6XU // SIG // tR13lDni6WTJRD14eiPzE32mkHSDjfTLJgJGKsKKELuk // SIG // qQUMm+1o+mgulaAqPyprWEljHwlpblqYluSD9MCP80Yr // SIG // 3vw70L01724lruWvJ+3Q3fMOr5kol5hNDj0L8giJ1h/D // SIG // Mhji8MUtzluetEk5CsYKwsatruWy2dsViFFFWDgycSca // SIG // f7H0J/jeLDogaZiyWYlobm+nt3TDQAUGpgEqKD6CPxNN // SIG // ZgvAs0314Y9/HG8VfUWnduVAKmWjw11SYobDHWM2l4bf // SIG // 2vP48hahmifhzaWX0O5dY0HjWwechz4GdwbRBrF1HxS+ // SIG // YWG18NzGGwS+30HHDiju3mUv7Jf2oVyW2ADWoUa9WfOX // SIG // pQlLSBCZgB/QACnFsZulP0V3HjXG0qKin3p6IvpIlR+r // SIG // +0cjgPWe+L9rt0uX4ut1eBrs6jeZeRhL/9azI2h15q/6 // SIG // /IvrC4DqaTuv/DDtBEyO3991bWORPdGdVk5Pv4BXIqF4 // SIG // ETIheu9BCrE/+6jMpF3BoYibV3FWTkhFwELJm3ZbCoBI // SIG // a/15n8G9bW1qyVJzEw16UM0xghoIMIIaBAIBATCBlTB+ // SIG // MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3Rv // SIG // bjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWlj // SIG // cm9zb2Z0IENvcnBvcmF0aW9uMSgwJgYDVQQDEx9NaWNy // SIG // b3NvZnQgQ29kZSBTaWduaW5nIFBDQSAyMDExAhMzAAAE // SIG // BGx0Bv9XKydyAAAAAAQEMA0GCWCGSAFlAwQCAQUAoIGu // SIG // MBkGCSqGSIb3DQEJAzEMBgorBgEEAYI3AgEEMBwGCisG // SIG // AQQBgjcCAQsxDjAMBgorBgEEAYI3AgEVMC8GCSqGSIb3 // SIG // DQEJBDEiBCB1jdOJ3WPeLlP4qSxU3nFKMHU9bCqhWqgs // SIG // 1mL89khmQDBCBgorBgEEAYI3AgEMMTQwMqAUgBIATQBp // SIG // AGMAcgBvAHMAbwBmAHShGoAYaHR0cDovL3d3dy5taWNy // SIG // b3NvZnQuY29tMA0GCSqGSIb3DQEBAQUABIIBAA05Ceki // SIG // CGVXWnT/LMUnXjiFkJXFjQkgsAY0AxPh3Pl4NyKBXdrE // SIG // Wbnq1of3ZsjkRDCyKE1TBehTEnQzNNfUwrsZ5cqOFdmg // SIG // dtlhdPBNzh7yao1czjyCJvhCWiiC8rII+Ir50F1dU1/l // SIG // RM0rJlNvC/Jh5nG7RS+0CrMsA8IhVzGLxY6TokqXPiCb // SIG // KQAGtOu0QJG1AaJ5h++ZhQs7uu8mBnK/GzhIO237wsom // SIG // NOchJAbBDjdpnOq4LWC7MdiJM9PcqhHb0PK/fCiiau5q // SIG // H5axR8vYvWjMRIV5QfpGgmuKPsyksupk7TGPG/7WhYZc // SIG // XnXCVFTE/hoiUU0S/pp9x4xOoVqhgheSMIIXjgYKKwYB // SIG // BAGCNwMDATGCF34wghd6BgkqhkiG9w0BBwKgghdrMIIX // SIG // ZwIBAzEPMA0GCWCGSAFlAwQCAQUAMIIBUAYLKoZIhvcN // SIG // AQkQAQSgggE/BIIBOzCCATcCAQEGCisGAQQBhFkKAwEw // SIG // MTANBglghkgBZQMEAgEFAAQgMa5p8kaYGj5HZkgPG4x1 // SIG // zc6E5oZod+jK2JK2BwJT+90CBmet86n9nBgRMjAyNTAy // SIG // MjAxNTI4MzguNlowBIACAfSggdGkgc4wgcsxCzAJBgNV // SIG // BAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYD // SIG // VQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQg // SIG // Q29ycG9yYXRpb24xJTAjBgNVBAsTHE1pY3Jvc29mdCBB // SIG // bWVyaWNhIE9wZXJhdGlvbnMxJzAlBgNVBAsTHm5TaGll // SIG // bGQgVFNTIEVTTjo4RDAwLTA1RTAtRDk0NzElMCMGA1UE // SIG // AxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2VydmljZaCC // SIG // EeowggcgMIIFCKADAgECAhMzAAAB88UKQ64DzB0xAAEA // SIG // AAHzMA0GCSqGSIb3DQEBCwUAMHwxCzAJBgNVBAYTAlVT // SIG // MRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdS // SIG // ZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9y // SIG // YXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0 // SIG // YW1wIFBDQSAyMDEwMB4XDTIzMTIwNjE4NDYwMloXDTI1 // SIG // MDMwNTE4NDYwMlowgcsxCzAJBgNVBAYTAlVTMRMwEQYD // SIG // VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25k // SIG // MR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24x // SIG // JTAjBgNVBAsTHE1pY3Jvc29mdCBBbWVyaWNhIE9wZXJh // SIG // dGlvbnMxJzAlBgNVBAsTHm5TaGllbGQgVFNTIEVTTjo4 // SIG // RDAwLTA1RTAtRDk0NzElMCMGA1UEAxMcTWljcm9zb2Z0 // SIG // IFRpbWUtU3RhbXAgU2VydmljZTCCAiIwDQYJKoZIhvcN // SIG // AQEBBQADggIPADCCAgoCggIBAP6fptrhK4H2JI7lYyFu // SIG // eCpgBv7Pch/M2lkhZL+yB9eGUtiYaexS2sZfc5VyD7yS // SIG // sl2LG41Qw7tkA6oJmxdSM7PzNyfVpQPkPavY+HNUqMe2 // SIG // K9YaAaPjHnCpZ7VCi/e8zPxYewqx9p0iVaN8EydUpWiY // SIG // 7JtDv7aNzhp/OPZclBBKYT2NBGgGiAPCaplqR5icjHQS // SIG // Y665w+vrvhPr9hpM+IhiUZ/5dXa7qhAcCQwbnrFg9CKS // SIG // K1COM1YcAN8GpsERqqmlqy3GlE1ziJ3ZLXFVDFxAZeOc // SIG // CB55Vts9sCgQuFvD7PdV61HC4QUlHNPqFtYSC/P0sxg9 // SIG // JuKgcvzD5mJajfG7DdHt8myp7umqyePC+eI/ux8TW61+ // SIG // LuTQ1Bkym+I6z//bf0fp4Dog5W0XzDrqKkTvURitxI2s // SIG // 4aVObm6qr6zI7W51k54ozTFjvbw1wYMWqeO4U9sQSbr5 // SIG // 61kp+1T2PEsJLOpc5U7N2oDw7ldrcTjWPezsyVMXhDsF // SIG // itCZunGqFO9+4iVjAjYDN47c6K9x7MnAGPYVCBOJUdpy // SIG // 8xAOBIDsTm/K1qTT4wsGbQBxbgg96vwDiA4YP2hKmubI // SIG // C7UnrAWQGt/ZKOf6J42roXHS1aPwimDe5C9y6DfuNJp0 // SIG // XqrWtQRqg8hqNkIZWT6jnCfqu35zB0nf1ERTjdpYLCfQ // SIG // L5fHAgMBAAGjggFJMIIBRTAdBgNVHQ4EFgQUw2QV9qUR // SIG // UQyMDcCmhTH2oOsNCiQwHwYDVR0jBBgwFoAUn6cVXQBe // SIG // Yl2D9OXSZacbUzUZ6XIwXwYDVR0fBFgwVjBUoFKgUIZO // SIG // aHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9j // SIG // cmwvTWljcm9zb2Z0JTIwVGltZS1TdGFtcCUyMFBDQSUy // SIG // MDIwMTAoMSkuY3JsMGwGCCsGAQUFBwEBBGAwXjBcBggr // SIG // BgEFBQcwAoZQaHR0cDovL3d3dy5taWNyb3NvZnQuY29t // SIG // L3BraW9wcy9jZXJ0cy9NaWNyb3NvZnQlMjBUaW1lLVN0 // SIG // YW1wJTIwUENBJTIwMjAxMCgxKS5jcnQwDAYDVR0TAQH/ // SIG // BAIwADAWBgNVHSUBAf8EDDAKBggrBgEFBQcDCDAOBgNV // SIG // HQ8BAf8EBAMCB4AwDQYJKoZIhvcNAQELBQADggIBAN/E // SIG // HI/80f7v29zeWI7hzudcz9QoVwCbnDrUXFHE/EJdFeWI // SIG // 2NnuwOo0/QPNRMFT21LkOqSpFKIhXXmPurx7p6WDz9wP // SIG // du/Sxbgaj0AwviWEDkwGDfDMp2KF8nQT8cipwdfXWbC1 // SIG // ulOILayABSHv45mdv1PAkTulsQE8lBTHG4KJLn+vSzZB // SIG // WKkGaL/wwRbZ4iLiYn68cjkMJoAaihPgDXn/ug2P3PLN // SIG // EAFNQgI02tLX0p+vIQ3l2HmSo4bhCBxr3DovsIv5K65N // SIG // mLRJnxmrrmIraFDwgwA5XF7AKkPiVkvo0OxU1LAE1c5S // SIG // WzE4A7cbTA1P5wG6D8cPjcHsTah1V+zofYRgJnFRLWuB // SIG // F4Z3a6pDGBDbCsy5NvnKQ76p37ieFp//1I3eB62ia1Cf // SIG // kjOF8KStpPUqdkXxMjfJ7Vnemd6vQKf+nXkfvA3AOQEC // SIG // Jn7aLP01QR5gt8wab28SsNUENEyMawT8eqpjtBNJO0O9 // SIG // Tv7NnBE8aOJhhQVdP5WCR90eIWkrDjZeybQx8vlo5rfU // SIG // XIIzXv+k9MgpNGIqwMXfvRLAjBkCNXOIP/1CEQUG72mi // SIG // MVQs5m/O4vmJIQkhyqilUDB1s12uhmLYc3yd8OPMlrwI // SIG // xORB5J9CxCkqvzc6EGYTcwXazPyCp7eWhzTkNbwk29nf // SIG // bwmmzcskIAu3StA8lic7MIIHcTCCBVmgAwIBAgITMwAA // SIG // ABXF52ueAptJmQAAAAAAFTANBgkqhkiG9w0BAQsFADCB // SIG // iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0 // SIG // b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1p // SIG // Y3Jvc29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWlj // SIG // cm9zb2Z0IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5 // SIG // IDIwMTAwHhcNMjEwOTMwM