UNPKG

@azure/msal-browser

Version:
200 lines (184 loc) 6.89 kB
/* * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. */ import { AccountInfo, Constants, InProgressPerformanceEvent, IPerformanceClient, Logger, PerformanceClient, PerformanceEvent, SubMeasurement, } from "@azure/msal-common/browser"; import { Configuration } from "../config/Configuration.js"; import { name, version } from "../packageMetadata.js"; import { BrowserCacheLocation } from "../utils/BrowserConstants.js"; import * as BrowserCrypto from "../crypto/BrowserCrypto.js"; import { BROWSER_PERF_ENABLED_KEY } from "../cache/CacheKeys.js"; import { getNetworkInfo } from "../utils/MsalFrameStatsUtils.js"; /** * Returns browser performance measurement module if session flag is enabled. Returns undefined otherwise. */ function getPerfMeasurementModule() { let sessionStorage: Storage | undefined; try { sessionStorage = window[BrowserCacheLocation.SessionStorage]; const perfEnabled = sessionStorage?.getItem(BROWSER_PERF_ENABLED_KEY); if (Number(perfEnabled) === 1) { return import("./BrowserPerformanceMeasurement.js"); } // Mute errors if it's a non-browser environment or cookies are blocked. } catch (e) {} return undefined; } /** * Returns boolean, indicating whether browser supports window.performance.now() function. */ function supportsBrowserPerformanceNow(): boolean { return ( typeof window !== "undefined" && typeof window.performance !== "undefined" && typeof window.performance.now === "function" ); } /** * Returns event duration in milliseconds using window performance API if available. Returns undefined otherwise. * @param startTime {DOMHighResTimeStamp | undefined} * @returns {number | undefined} */ function getPerfDurationMs( startTime: DOMHighResTimeStamp | undefined ): number | undefined { if (!startTime || !supportsBrowserPerformanceNow()) { return undefined; } return Math.round(window.performance.now() - startTime); } export class BrowserPerformanceClient extends PerformanceClient implements IPerformanceClient { constructor(configuration: Configuration, intFields?: Set<string>) { super( configuration.auth.clientId, configuration.auth.authority || `${Constants.DEFAULT_AUTHORITY}`, new Logger( configuration.system?.loggerOptions || {}, name, version ), name, version, configuration.telemetry?.application || { appName: "", appVersion: "", }, intFields ); } generateId(): string { return BrowserCrypto.createNewGuid(); } private getPageVisibility(): string | null { return document.visibilityState?.toString() || null; } private getOnlineStatus(): boolean | null { return typeof navigator !== "undefined" ? navigator.onLine : null; } private deleteIncompleteSubMeasurements( inProgressEvent: InProgressPerformanceEvent ): void { void getPerfMeasurementModule()?.then((module) => { const rootEvent = this.eventsByCorrelationId.get( inProgressEvent.event.correlationId ); const isRootEvent = rootEvent && rootEvent.eventId === inProgressEvent.event.eventId; const incompleteMeasurements: SubMeasurement[] = []; if (isRootEvent && rootEvent?.incompleteSubMeasurements) { rootEvent.incompleteSubMeasurements.forEach( (subMeasurement: SubMeasurement) => { incompleteMeasurements.push({ ...subMeasurement }); } ); } // Clean up remaining marks for incomplete sub-measurements module.BrowserPerformanceMeasurement.flushMeasurements( inProgressEvent.event.correlationId, incompleteMeasurements ); }); } /** * Starts measuring performance for a given operation. Returns a function that should be used to end the measurement. * Also captures browser page visibilityState. * * @param {PerformanceEvents} measureName * @param {?string} [correlationId] * @returns {((event?: Partial<PerformanceEvent>) => PerformanceEvent| null)} */ startMeasurement( measureName: string, correlationId?: string ): InProgressPerformanceEvent { // Capture page visibilityState and then invoke start/end measurement const startPageVisibility = this.getPageVisibility(); const startOnlineStatus = this.getOnlineStatus(); const inProgressEvent = super.startMeasurement( measureName, correlationId ); const startTime: number | undefined = supportsBrowserPerformanceNow() ? window.performance.now() : undefined; const browserMeasurement = getPerfMeasurementModule()?.then( (module) => { return new module.BrowserPerformanceMeasurement( measureName, inProgressEvent.event.correlationId ); } ); void browserMeasurement?.then((measurement) => measurement.startMeasurement() ); return { ...inProgressEvent, end: ( event?: Partial<PerformanceEvent>, error?: unknown, account?: AccountInfo ): PerformanceEvent | null => { const networkInfo = getNetworkInfo(); const res = inProgressEvent.end( { ...event, startPageVisibility, startOnlineStatus, endPageVisibility: this.getPageVisibility(), durationMs: getPerfDurationMs(startTime), networkEffectiveType: networkInfo.effectiveType, networkRtt: networkInfo.rtt, }, error, account ); void browserMeasurement?.then((measurement) => measurement.endMeasurement() ); this.deleteIncompleteSubMeasurements(inProgressEvent); return res; }, discard: () => { inProgressEvent.discard(); void browserMeasurement?.then((measurement) => measurement.flushMeasurement() ); this.deleteIncompleteSubMeasurements(inProgressEvent); }, }; } }