Estructura inicial del proyecto
This commit is contained in:
42
backend/node_modules/@azure/msal-common/src/telemetry/performance/IPerformanceClient.ts
generated
vendored
Normal file
42
backend/node_modules/@azure/msal-common/src/telemetry/performance/IPerformanceClient.ts
generated
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License.
|
||||
*/
|
||||
|
||||
import { PerformanceEvent } from "./PerformanceEvent.js";
|
||||
import { AccountInfo } from "../../account/AccountInfo.js";
|
||||
|
||||
export type PerformanceCallbackFunction = (events: PerformanceEvent[]) => void;
|
||||
|
||||
export type InProgressPerformanceEvent = {
|
||||
end: (
|
||||
event?: Partial<PerformanceEvent>,
|
||||
error?: unknown,
|
||||
account?: AccountInfo
|
||||
) => PerformanceEvent | null;
|
||||
discard: () => void;
|
||||
add: (fields: { [key: string]: {} | undefined }) => void;
|
||||
increment: (fields: { [key: string]: number | undefined }) => void;
|
||||
event: PerformanceEvent;
|
||||
};
|
||||
|
||||
export interface IPerformanceClient {
|
||||
startMeasurement(
|
||||
measureName: string,
|
||||
correlationId?: string
|
||||
): InProgressPerformanceEvent;
|
||||
endMeasurement(event: PerformanceEvent): PerformanceEvent | null;
|
||||
discardMeasurements(correlationId: string): void;
|
||||
addFields(
|
||||
fields: { [key: string]: {} | undefined },
|
||||
correlationId: string
|
||||
): void;
|
||||
incrementFields(
|
||||
fields: { [key: string]: number | undefined },
|
||||
correlationId: string
|
||||
): void;
|
||||
removePerformanceCallback(callbackId: string): boolean;
|
||||
addPerformanceCallback(callback: PerformanceCallbackFunction): string;
|
||||
emitEvents(events: PerformanceEvent[], correlationId: string): void;
|
||||
generateId(): string;
|
||||
}
|
||||
10
backend/node_modules/@azure/msal-common/src/telemetry/performance/IPerformanceMeasurement.ts
generated
vendored
Normal file
10
backend/node_modules/@azure/msal-common/src/telemetry/performance/IPerformanceMeasurement.ts
generated
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
/*
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License.
|
||||
*/
|
||||
|
||||
export interface IPerformanceMeasurement {
|
||||
startMeasurement(): void;
|
||||
endMeasurement(): void;
|
||||
flushMeasurement(): number | null;
|
||||
}
|
||||
747
backend/node_modules/@azure/msal-common/src/telemetry/performance/PerformanceClient.ts
generated
vendored
Normal file
747
backend/node_modules/@azure/msal-common/src/telemetry/performance/PerformanceClient.ts
generated
vendored
Normal file
@ -0,0 +1,747 @@
|
||||
/*
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License.
|
||||
*/
|
||||
|
||||
import { ApplicationTelemetry } from "../../config/ClientConfiguration.js";
|
||||
import { getAndFlushLogsFromCache, Logger } from "../../logger/Logger.js";
|
||||
import {
|
||||
InProgressPerformanceEvent,
|
||||
IPerformanceClient,
|
||||
PerformanceCallbackFunction,
|
||||
} from "./IPerformanceClient.js";
|
||||
import {
|
||||
EXT_FIELD_PREFIX,
|
||||
IntFields,
|
||||
PerformanceEvent,
|
||||
PerformanceEventContext,
|
||||
PerformanceEventStackedContext,
|
||||
PerformanceEventStatus,
|
||||
} from "./PerformanceEvent.js";
|
||||
import { AuthError } from "../../error/AuthError.js";
|
||||
import { CacheError } from "../../error/CacheError.js";
|
||||
import { ServerError } from "../../error/ServerError.js";
|
||||
import { InteractionRequiredAuthError } from "../../error/InteractionRequiredAuthError.js";
|
||||
import { AccountInfo } from "../../account/AccountInfo.js";
|
||||
|
||||
/**
|
||||
* Starts context by adding payload to the stack
|
||||
* @param event {PerformanceEvent}
|
||||
* @param stack {?PerformanceEventStackedContext[]} stack
|
||||
*/
|
||||
export function startContext(
|
||||
event: PerformanceEvent,
|
||||
stack?: PerformanceEventStackedContext[]
|
||||
): void {
|
||||
if (!stack) {
|
||||
return;
|
||||
}
|
||||
|
||||
stack.push({
|
||||
name: event.name,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Ends context by removing payload from the stack and returning parent or self, if stack is empty, payload
|
||||
*
|
||||
* @param event {PerformanceEvent}
|
||||
* @param stack {?PerformanceEventStackedContext[]} stack
|
||||
* @param error {?unknown} error
|
||||
*/
|
||||
export function endContext(
|
||||
event: PerformanceEvent,
|
||||
stack?: PerformanceEventStackedContext[],
|
||||
error?: unknown
|
||||
): PerformanceEventContext | undefined {
|
||||
if (!stack?.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const peek = (stack: PerformanceEventStackedContext[]) => {
|
||||
return stack.length ? stack[stack.length - 1] : undefined;
|
||||
};
|
||||
|
||||
const abbrEventName = event.name;
|
||||
const top = peek(stack);
|
||||
if (top?.name !== abbrEventName) {
|
||||
return;
|
||||
}
|
||||
|
||||
const current = stack?.pop();
|
||||
if (!current) {
|
||||
return;
|
||||
}
|
||||
|
||||
const errorCode =
|
||||
error instanceof AuthError
|
||||
? error.errorCode
|
||||
: error instanceof Error
|
||||
? error.name
|
||||
: undefined;
|
||||
const subErr = error instanceof AuthError ? error.subError : undefined;
|
||||
|
||||
if (errorCode && current.childErr !== errorCode) {
|
||||
current.err = errorCode;
|
||||
if (subErr) {
|
||||
current.subErr = subErr;
|
||||
}
|
||||
}
|
||||
|
||||
delete current.name;
|
||||
delete current.childErr;
|
||||
|
||||
const context: PerformanceEventContext = {
|
||||
...current,
|
||||
dur: event.durationMs,
|
||||
};
|
||||
|
||||
if (!event.success) {
|
||||
context.fail = 1;
|
||||
}
|
||||
|
||||
const parent = peek(stack);
|
||||
if (!parent) {
|
||||
return { [abbrEventName]: context };
|
||||
}
|
||||
|
||||
if (errorCode) {
|
||||
parent.childErr = errorCode;
|
||||
}
|
||||
|
||||
let childName: string;
|
||||
if (!parent[abbrEventName]) {
|
||||
childName = abbrEventName;
|
||||
} else {
|
||||
const siblings = Object.keys(parent).filter((key) =>
|
||||
key.startsWith(abbrEventName)
|
||||
).length;
|
||||
childName = `${abbrEventName}_${siblings + 1}`;
|
||||
}
|
||||
parent[childName] = context;
|
||||
return parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds error name and stack trace to the telemetry event
|
||||
* @param error {Error}
|
||||
* @param logger {Logger}
|
||||
* @param event {PerformanceEvent}
|
||||
* @param stackMaxSize {number} max error stack size to capture
|
||||
*/
|
||||
export function addError(
|
||||
error: unknown,
|
||||
logger: Logger,
|
||||
event: PerformanceEvent,
|
||||
stackMaxSize: number = 5
|
||||
): void {
|
||||
if (!(error instanceof Error)) {
|
||||
logger.trace(
|
||||
"PerformanceClient.addErrorStack: Input error is not instance of Error",
|
||||
event.correlationId
|
||||
);
|
||||
return;
|
||||
} else if (error instanceof AuthError) {
|
||||
event.errorCode = error.errorCode;
|
||||
event.subErrorCode = error.subError;
|
||||
if (
|
||||
!event.serverErrorNo &&
|
||||
(error instanceof ServerError ||
|
||||
error instanceof InteractionRequiredAuthError) &&
|
||||
error.errorNo
|
||||
) {
|
||||
event.serverErrorNo = error.errorNo;
|
||||
}
|
||||
return;
|
||||
} else if (error instanceof CacheError) {
|
||||
event.errorCode = error.errorCode;
|
||||
return;
|
||||
} else if (event.errorStack?.length) {
|
||||
logger.trace(
|
||||
"PerformanceClient.addErrorStack: Stack already exist",
|
||||
event.correlationId
|
||||
);
|
||||
return;
|
||||
} else if (!error.stack?.length) {
|
||||
logger.trace(
|
||||
"PerformanceClient.addErrorStack: Input stack is empty",
|
||||
event.correlationId
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (error.stack) {
|
||||
event.errorStack = compactStack(error.stack, stackMaxSize);
|
||||
}
|
||||
event.errorName = error.name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compacts error stack into array by fetching N first entries
|
||||
* @param stack {string} error stack
|
||||
* @param stackMaxSize {number} max error stack size to capture
|
||||
* @returns {string[]}
|
||||
*/
|
||||
export function compactStack(stack: string, stackMaxSize: number): string[] {
|
||||
if (stackMaxSize < 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const stackArr = stack.split("\n") || [];
|
||||
|
||||
const res = [];
|
||||
|
||||
// Check for a handful of known, common runtime errors and log them (with redaction where applicable).
|
||||
const firstLine = stackArr[0];
|
||||
if (
|
||||
firstLine.startsWith("TypeError: Cannot read property") ||
|
||||
firstLine.startsWith("TypeError: Cannot read properties of") ||
|
||||
firstLine.startsWith("TypeError: Cannot set property") ||
|
||||
firstLine.startsWith("TypeError: Cannot set properties of") ||
|
||||
firstLine.endsWith("is not a function")
|
||||
) {
|
||||
// These types of errors are not at risk of leaking PII. They will indicate unavailable APIs
|
||||
res.push(compactStackLine(firstLine));
|
||||
} else if (
|
||||
firstLine.startsWith("SyntaxError") ||
|
||||
firstLine.startsWith("TypeError")
|
||||
) {
|
||||
// Prevent unintentional leaking of arbitrary info by redacting contents between both single and double quotes
|
||||
res.push(
|
||||
compactStackLine(
|
||||
// Example: SyntaxError: Unexpected token 'e', "test" is not valid JSON -> SyntaxError: Unexpected token <redacted>, <redacted> is not valid JSON
|
||||
firstLine.replace(/['].*[']|["].*["]/g, "<redacted>")
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Get top N stack lines
|
||||
for (let ix = 1; ix < stackArr.length; ix++) {
|
||||
if (res.length >= stackMaxSize) {
|
||||
break;
|
||||
}
|
||||
const line = stackArr[ix];
|
||||
res.push(compactStackLine(line));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compacts error stack line by shortening file path
|
||||
* Example: https://localhost/msal-common/src/authority/Authority.js:100:1 -> Authority.js:100:1
|
||||
* @param line {string} stack line
|
||||
* @returns {string}
|
||||
*/
|
||||
export function compactStackLine(line: string): string {
|
||||
const filePathIx = line.lastIndexOf(" ") + 1;
|
||||
if (filePathIx < 1) {
|
||||
return line;
|
||||
}
|
||||
const filePath = line.substring(filePathIx);
|
||||
|
||||
let fileNameIx = filePath.lastIndexOf("/");
|
||||
fileNameIx = fileNameIx < 0 ? filePath.lastIndexOf("\\") : fileNameIx;
|
||||
|
||||
if (fileNameIx >= 0) {
|
||||
return (
|
||||
line.substring(0, filePathIx) +
|
||||
"(" +
|
||||
filePath.substring(fileNameIx + 1) +
|
||||
(filePath.charAt(filePath.length - 1) === ")" ? "" : ")")
|
||||
).trimStart();
|
||||
}
|
||||
|
||||
return line.trimStart();
|
||||
}
|
||||
|
||||
export function getAccountType(
|
||||
account?: AccountInfo
|
||||
): "AAD" | "MSA" | "B2C" | undefined {
|
||||
const idTokenClaims = account?.idTokenClaims;
|
||||
if (idTokenClaims?.tfp || idTokenClaims?.acr) {
|
||||
return "B2C";
|
||||
}
|
||||
|
||||
if (!idTokenClaims?.tid) {
|
||||
return undefined;
|
||||
} else if (idTokenClaims?.tid === "9188040d-6c67-4c5b-b112-36a304b66dad") {
|
||||
return "MSA";
|
||||
}
|
||||
return "AAD";
|
||||
}
|
||||
|
||||
export abstract class PerformanceClient implements IPerformanceClient {
|
||||
protected authority: string;
|
||||
protected libraryName: string;
|
||||
protected libraryVersion: string;
|
||||
protected applicationTelemetry: ApplicationTelemetry;
|
||||
protected clientId: string;
|
||||
protected logger: Logger;
|
||||
protected callbacks: Map<string, PerformanceCallbackFunction>;
|
||||
|
||||
/**
|
||||
* Multiple events with the same correlation id.
|
||||
* @protected
|
||||
* @type {Map<string, PerformanceEvent>}
|
||||
*/
|
||||
protected eventsByCorrelationId: Map<string, PerformanceEvent>;
|
||||
|
||||
protected intFields: Set<string>;
|
||||
|
||||
/**
|
||||
* Map of stacked events by correlation id.
|
||||
*
|
||||
* @protected
|
||||
*/
|
||||
protected eventStack: Map<string, PerformanceEventStackedContext[]>;
|
||||
|
||||
/**
|
||||
* Creates an instance of PerformanceClient,
|
||||
* an abstract class containing core performance telemetry logic.
|
||||
*
|
||||
* @constructor
|
||||
* @param {string} clientId Client ID of the application
|
||||
* @param {string} authority Authority used by the application
|
||||
* @param {Logger} logger Logger used by the application
|
||||
* @param {string} libraryName Name of the library
|
||||
* @param {string} libraryVersion Version of the library
|
||||
* @param {ApplicationTelemetry} applicationTelemetry application name and version
|
||||
* @param {Set<String>} intFields integer fields to be truncated
|
||||
*/
|
||||
constructor(
|
||||
clientId: string,
|
||||
authority: string,
|
||||
logger: Logger,
|
||||
libraryName: string,
|
||||
libraryVersion: string,
|
||||
applicationTelemetry: ApplicationTelemetry,
|
||||
intFields?: Set<string>
|
||||
) {
|
||||
this.authority = authority;
|
||||
this.libraryName = libraryName;
|
||||
this.libraryVersion = libraryVersion;
|
||||
this.applicationTelemetry = applicationTelemetry;
|
||||
this.clientId = clientId;
|
||||
this.logger = logger;
|
||||
this.callbacks = new Map();
|
||||
this.eventsByCorrelationId = new Map();
|
||||
this.eventStack = new Map();
|
||||
this.intFields = intFields || new Set();
|
||||
for (const item of IntFields) {
|
||||
this.intFields.add(item);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates and returns a unique id, typically a guid.
|
||||
*
|
||||
* @abstract
|
||||
* @returns {string}
|
||||
*/
|
||||
abstract generateId(): string;
|
||||
|
||||
/**
|
||||
* Starts measuring performance for a given operation. Returns a function that should be used to end the measurement.
|
||||
*
|
||||
* @param {PerformanceEvents} measureName
|
||||
* @param {?string} [correlationId]
|
||||
* @returns {InProgressPerformanceEvent}
|
||||
*/
|
||||
startMeasurement(
|
||||
measureName: string,
|
||||
correlationId?: string
|
||||
): InProgressPerformanceEvent {
|
||||
// Generate a placeholder correlation if the request does not provide one
|
||||
const eventCorrelationId = correlationId || this.generateId();
|
||||
|
||||
const inProgressEvent: PerformanceEvent = {
|
||||
eventId: this.generateId(),
|
||||
status: PerformanceEventStatus.InProgress,
|
||||
authority: this.authority,
|
||||
libraryName: this.libraryName,
|
||||
libraryVersion: this.libraryVersion,
|
||||
clientId: this.clientId,
|
||||
name: measureName,
|
||||
startTimeMs: Date.now(),
|
||||
correlationId: eventCorrelationId,
|
||||
appName: this.applicationTelemetry?.appName,
|
||||
appVersion: this.applicationTelemetry?.appVersion,
|
||||
};
|
||||
|
||||
// Store in progress events so they can be discarded if not ended properly
|
||||
this.cacheEventByCorrelationId(inProgressEvent);
|
||||
startContext(inProgressEvent, this.eventStack.get(eventCorrelationId));
|
||||
|
||||
// Return the event and functions the caller can use to properly end/flush the measurement
|
||||
return {
|
||||
end: (
|
||||
event?: Partial<PerformanceEvent>,
|
||||
error?: unknown,
|
||||
account?: AccountInfo
|
||||
): PerformanceEvent | null => {
|
||||
return this.endMeasurement(
|
||||
{
|
||||
// Initial set of event properties
|
||||
...inProgressEvent,
|
||||
// Properties set when event ends
|
||||
...event,
|
||||
},
|
||||
error,
|
||||
account
|
||||
);
|
||||
},
|
||||
discard: () => {
|
||||
return this.discardMeasurements(inProgressEvent.correlationId);
|
||||
},
|
||||
add: (fields: { [key: string]: {} | undefined }) => {
|
||||
return this.addFields(fields, inProgressEvent.correlationId);
|
||||
},
|
||||
increment: (fields: { [key: string]: number | undefined }) => {
|
||||
return this.incrementFields(
|
||||
fields,
|
||||
inProgressEvent.correlationId
|
||||
);
|
||||
},
|
||||
event: inProgressEvent,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops measuring the performance for an operation. Should only be called directly by PerformanceClient classes,
|
||||
* as consumers should instead use the function returned by startMeasurement.
|
||||
* Adds a new field named as "[event name]DurationMs" for sub-measurements, completes and emits an event
|
||||
* otherwise.
|
||||
*
|
||||
* @param {PerformanceEvent} event
|
||||
* @param {unknown} error
|
||||
* @param {AccountInfo?} account
|
||||
* @returns {(PerformanceEvent | null)}
|
||||
*/
|
||||
endMeasurement(
|
||||
event: PerformanceEvent,
|
||||
error?: unknown,
|
||||
account?: AccountInfo
|
||||
): PerformanceEvent | null {
|
||||
const rootEvent: PerformanceEvent | undefined =
|
||||
this.eventsByCorrelationId.get(event.correlationId);
|
||||
if (!rootEvent) {
|
||||
this.logger.trace(
|
||||
`PerformanceClient: Measurement not found for '${event.eventId}'`,
|
||||
event.correlationId
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
const isRoot = event.eventId === rootEvent.eventId;
|
||||
|
||||
event.durationMs = Math.round(
|
||||
event.durationMs || this.getDurationMs(event.startTimeMs)
|
||||
);
|
||||
|
||||
const context = JSON.stringify(
|
||||
endContext(
|
||||
event,
|
||||
this.eventStack.get(rootEvent.correlationId),
|
||||
error
|
||||
)
|
||||
);
|
||||
|
||||
if (isRoot) {
|
||||
this.discardMeasurements(rootEvent.correlationId);
|
||||
} else {
|
||||
rootEvent.incompleteSubMeasurements?.delete(event.eventId);
|
||||
}
|
||||
|
||||
if (error) {
|
||||
addError(error, this.logger, rootEvent);
|
||||
}
|
||||
|
||||
// Add sub-measurement attribute to root event's ext field.
|
||||
if (!isRoot) {
|
||||
rootEvent.ext = {
|
||||
...rootEvent.ext,
|
||||
...event.ext,
|
||||
};
|
||||
rootEvent.ext[event.name + "DurationMs"] = Math.floor(
|
||||
event.durationMs
|
||||
);
|
||||
return { ...rootEvent };
|
||||
}
|
||||
|
||||
if (
|
||||
isRoot &&
|
||||
!error &&
|
||||
(rootEvent.errorCode || rootEvent.subErrorCode)
|
||||
) {
|
||||
this.logger.trace(
|
||||
`PerformanceClient: Remove error and sub-error codes for root event '${event.name}' as intermediate error was successfully handled`,
|
||||
event.correlationId
|
||||
);
|
||||
rootEvent.errorCode = undefined;
|
||||
rootEvent.subErrorCode = undefined;
|
||||
}
|
||||
|
||||
let finalEvent: PerformanceEvent = { ...rootEvent, ...event };
|
||||
let incompleteSubsCount: number = 0;
|
||||
// Incomplete sub-measurements are discarded. They are likely an instrumentation bug that should be fixed.
|
||||
finalEvent.incompleteSubMeasurements?.forEach((subMeasurement) => {
|
||||
this.logger.trace(
|
||||
`PerformanceClient: Incomplete submeasurement '${subMeasurement.name}' found for '${event.name}'`,
|
||||
finalEvent.correlationId
|
||||
);
|
||||
incompleteSubsCount++;
|
||||
});
|
||||
finalEvent.incompleteSubMeasurements = undefined;
|
||||
|
||||
const logs = getAndFlushLogsFromCache(event.correlationId);
|
||||
// Format logs: [millis1,hash1;millis2,hash2;...]
|
||||
const formattedLogs = logs
|
||||
.map(
|
||||
(logMessage) => `${logMessage.milliseconds},${logMessage.hash}`
|
||||
)
|
||||
.join(";");
|
||||
|
||||
finalEvent = {
|
||||
...finalEvent,
|
||||
status: PerformanceEventStatus.Completed,
|
||||
incompleteSubsCount,
|
||||
context,
|
||||
logs: formattedLogs,
|
||||
};
|
||||
if (account) {
|
||||
finalEvent.accountType = getAccountType(account);
|
||||
finalEvent.dataBoundary = account.dataBoundary;
|
||||
}
|
||||
|
||||
this.truncateIntegralFields(finalEvent);
|
||||
this.emitEvents([finalEvent], event.correlationId);
|
||||
|
||||
return finalEvent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves extra information to be emitted when the measurements are flushed
|
||||
* @param fields
|
||||
* @param correlationId
|
||||
*/
|
||||
addFields(
|
||||
fields: { [key: string]: {} | undefined },
|
||||
correlationId: string
|
||||
): void {
|
||||
const event = this.eventsByCorrelationId.get(correlationId);
|
||||
if (event) {
|
||||
const staticFields: { [key: string]: {} | undefined } = {};
|
||||
const dynamicFields: Record<string, string | number> = {};
|
||||
|
||||
for (const key in fields) {
|
||||
if (key.startsWith(EXT_FIELD_PREFIX)) {
|
||||
const dynamicKey = key.slice(EXT_FIELD_PREFIX.length);
|
||||
const value = fields[key];
|
||||
if (
|
||||
typeof value === "string" ||
|
||||
typeof value === "number"
|
||||
) {
|
||||
dynamicFields[dynamicKey] = value;
|
||||
}
|
||||
} else {
|
||||
staticFields[key] = fields[key];
|
||||
}
|
||||
}
|
||||
|
||||
const updatedEvent: PerformanceEvent = {
|
||||
...event,
|
||||
...staticFields,
|
||||
};
|
||||
if (Object.keys(dynamicFields).length) {
|
||||
updatedEvent.ext = {
|
||||
...updatedEvent.ext,
|
||||
...dynamicFields,
|
||||
};
|
||||
}
|
||||
|
||||
this.eventsByCorrelationId.set(correlationId, updatedEvent);
|
||||
} else {
|
||||
this.logger.trace(
|
||||
"PerformanceClient: Event not found for",
|
||||
correlationId
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Increment counters to be emitted when the measurements are flushed
|
||||
* @param fields {string[]}
|
||||
* @param correlationId {string} correlation identifier
|
||||
*/
|
||||
incrementFields(
|
||||
fields: { [key: string]: number | undefined },
|
||||
correlationId: string
|
||||
): void {
|
||||
const event = this.eventsByCorrelationId.get(correlationId);
|
||||
if (event) {
|
||||
for (const counter in fields) {
|
||||
if (counter.startsWith(EXT_FIELD_PREFIX)) {
|
||||
event.ext = event.ext || {};
|
||||
// Route to ext sub-object
|
||||
const dynamicKey = counter.slice(EXT_FIELD_PREFIX.length);
|
||||
const currentValue = event.ext[dynamicKey];
|
||||
if (currentValue === undefined) {
|
||||
event.ext[dynamicKey] = 0;
|
||||
} else if (isNaN(Number(currentValue))) {
|
||||
return;
|
||||
}
|
||||
event.ext[dynamicKey] =
|
||||
(Number(event.ext[dynamicKey]) || 0) +
|
||||
(fields[counter] ?? 0);
|
||||
} else {
|
||||
/* eslint-disable custom-msal/no-dynamic-telemetry-fields -- internal dispatching of static fields by name */
|
||||
if (!event.hasOwnProperty(counter)) {
|
||||
event[counter] = 0;
|
||||
} else if (isNaN(Number(event[counter]))) {
|
||||
return;
|
||||
}
|
||||
event[counter] += fields[counter];
|
||||
/* eslint-enable custom-msal/no-dynamic-telemetry-fields */
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.logger.trace(
|
||||
"PerformanceClient: Event not found for",
|
||||
correlationId
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Upserts event into event cache.
|
||||
* First key is the correlation id, second key is the event id.
|
||||
* Allows for events to be grouped by correlation id,
|
||||
* and to easily allow for properties on them to be updated.
|
||||
*
|
||||
* @private
|
||||
* @param {PerformanceEvent} event
|
||||
*/
|
||||
protected cacheEventByCorrelationId(event: PerformanceEvent): void {
|
||||
const rootEvent = this.eventsByCorrelationId.get(event.correlationId);
|
||||
if (rootEvent) {
|
||||
rootEvent.incompleteSubMeasurements =
|
||||
rootEvent.incompleteSubMeasurements || new Map();
|
||||
rootEvent.incompleteSubMeasurements.set(event.eventId, {
|
||||
name: event.name,
|
||||
startTimeMs: event.startTimeMs,
|
||||
});
|
||||
} else {
|
||||
this.eventsByCorrelationId.set(event.correlationId, { ...event });
|
||||
this.eventStack.set(event.correlationId, []);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes measurements and aux data for a given correlation id.
|
||||
*
|
||||
* @param {string} correlationId
|
||||
*/
|
||||
discardMeasurements(correlationId: string): void {
|
||||
this.eventsByCorrelationId.delete(correlationId);
|
||||
this.eventStack.delete(correlationId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a callback function to receive performance events.
|
||||
*
|
||||
* @param {PerformanceCallbackFunction} callback
|
||||
* @returns {string}
|
||||
*/
|
||||
addPerformanceCallback(callback: PerformanceCallbackFunction): string {
|
||||
for (const [id, cb] of this.callbacks) {
|
||||
if (cb.toString() === callback.toString()) {
|
||||
this.logger.warning(
|
||||
`PerformanceClient: Performance callback is already registered with id: ${id}`,
|
||||
""
|
||||
);
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
||||
const callbackId = this.generateId();
|
||||
this.callbacks.set(callbackId, callback);
|
||||
this.logger.verbose(
|
||||
`PerformanceClient: Performance callback registered with id: '${callbackId}'`,
|
||||
""
|
||||
);
|
||||
|
||||
return callbackId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a callback registered with addPerformanceCallback.
|
||||
*
|
||||
* @param {string} callbackId
|
||||
* @returns {boolean}
|
||||
*/
|
||||
removePerformanceCallback(callbackId: string): boolean {
|
||||
const result = this.callbacks.delete(callbackId);
|
||||
|
||||
if (result) {
|
||||
this.logger.verbose(
|
||||
`PerformanceClient: Performance callback '${callbackId}' removed.`,
|
||||
""
|
||||
);
|
||||
} else {
|
||||
this.logger.verbose(
|
||||
`PerformanceClient: Performance callback '${callbackId}' not removed.`,
|
||||
""
|
||||
);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits events to all registered callbacks.
|
||||
*
|
||||
* @param {PerformanceEvent[]} events
|
||||
* @param {?string} [correlationId]
|
||||
*/
|
||||
emitEvents(events: PerformanceEvent[], correlationId: string): void {
|
||||
this.logger.verbose(
|
||||
"PerformanceClient: Emitting performance events",
|
||||
correlationId
|
||||
);
|
||||
|
||||
this.callbacks.forEach(
|
||||
(callback: PerformanceCallbackFunction, callbackId: string) => {
|
||||
this.logger.trace(
|
||||
`PerformanceClient: Emitting event to callback '${callbackId}'`,
|
||||
correlationId
|
||||
);
|
||||
callback.apply(null, [events]);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enforce truncation of integral fields in performance event.
|
||||
* @param {PerformanceEvent} event performance event to update.
|
||||
*/
|
||||
private truncateIntegralFields(event: PerformanceEvent): void {
|
||||
this.intFields.forEach((key) => {
|
||||
/* eslint-disable custom-msal/no-dynamic-telemetry-fields -- internal truncation of known integer fields */
|
||||
if (key in event && typeof event[key] === "number") {
|
||||
event[key] = Math.floor(event[key]);
|
||||
}
|
||||
/* eslint-enable custom-msal/no-dynamic-telemetry-fields */
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns event duration in milliseconds
|
||||
* @param startTimeMs {number}
|
||||
* @returns {number}
|
||||
*/
|
||||
private getDurationMs(startTimeMs: number): number {
|
||||
const durationMs = Date.now() - startTimeMs;
|
||||
// Handle clock skew
|
||||
return durationMs < 0 ? durationMs : 0;
|
||||
}
|
||||
}
|
||||
551
backend/node_modules/@azure/msal-common/src/telemetry/performance/PerformanceEvent.ts
generated
vendored
Normal file
551
backend/node_modules/@azure/msal-common/src/telemetry/performance/PerformanceEvent.ts
generated
vendored
Normal file
@ -0,0 +1,551 @@
|
||||
/*
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License.
|
||||
*/
|
||||
|
||||
import { DataBoundary } from "../../account/AccountInfo.js";
|
||||
|
||||
/**
|
||||
* State of the performance event.
|
||||
*
|
||||
* @export
|
||||
* @enum {number}
|
||||
*/
|
||||
export const PerformanceEventStatus = {
|
||||
NotStarted: 0,
|
||||
InProgress: 1,
|
||||
Completed: 2,
|
||||
} as const;
|
||||
export type PerformanceEventStatus =
|
||||
(typeof PerformanceEventStatus)[keyof typeof PerformanceEventStatus];
|
||||
|
||||
export type SubMeasurement = {
|
||||
name: string;
|
||||
startTimeMs: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* Performance measurement taken by the library, including metadata about the request and application.
|
||||
*
|
||||
* @export
|
||||
* @typedef {PerformanceEvent}
|
||||
*/
|
||||
export type PerformanceEvent = {
|
||||
/**
|
||||
* Unique id for the event
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
eventId: string;
|
||||
|
||||
/**
|
||||
* State of the perforance measure.
|
||||
*
|
||||
* @type {PerformanceEventStatus}
|
||||
*/
|
||||
status: PerformanceEventStatus;
|
||||
|
||||
/**
|
||||
* Login authority used for the request
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
authority: string;
|
||||
|
||||
/**
|
||||
* Client id for the application
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
clientId: string;
|
||||
|
||||
/**
|
||||
* Correlation ID used for the request
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
correlationId: string;
|
||||
|
||||
/**
|
||||
* End-to-end duration in milliseconds.
|
||||
* @date 3/22/2022 - 3:40:05 PM
|
||||
*
|
||||
* @type {number}
|
||||
*/
|
||||
durationMs?: number;
|
||||
|
||||
/**
|
||||
* Visibility of the page when the event completed.
|
||||
* Read from: https://developer.mozilla.org/docs/Web/API/Page_Visibility_API
|
||||
*
|
||||
* @type {?(string | null)}
|
||||
*/
|
||||
endPageVisibility?: string | null;
|
||||
|
||||
/**
|
||||
* Whether the result was retrieved from the cache.
|
||||
*
|
||||
* @type {(boolean | null)}
|
||||
*/
|
||||
fromCache?: boolean | null;
|
||||
|
||||
/**
|
||||
* Event name (usually in the form of classNameFunctionName)
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
name: string;
|
||||
|
||||
/**
|
||||
* Visibility of the page when the event completed.
|
||||
* Read from: https://developer.mozilla.org/docs/Web/API/Page_Visibility_API
|
||||
*
|
||||
* @type {?(string | null)}
|
||||
*/
|
||||
startPageVisibility?: string | null;
|
||||
|
||||
/**
|
||||
* Online status when the event started.
|
||||
* Read from: https://developer.mozilla.org/docs/Web/API/NavigatorOnLine/onLine
|
||||
*/
|
||||
startOnlineStatus?: boolean | null;
|
||||
|
||||
/**
|
||||
* Unix millisecond timestamp when the event was initiated.
|
||||
*
|
||||
* @type {number}
|
||||
*/
|
||||
startTimeMs: number;
|
||||
|
||||
/**
|
||||
* Whether or the operation completed successfully.
|
||||
*
|
||||
* @type {(boolean | null)}
|
||||
*/
|
||||
success?: boolean | null;
|
||||
|
||||
/**
|
||||
* Add specific error code in case of failure
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
errorCode?: string;
|
||||
|
||||
/**
|
||||
* Add specific sub error code in case of failure
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
subErrorCode?: string;
|
||||
|
||||
/**
|
||||
* Server error number
|
||||
*/
|
||||
serverErrorNo?: string;
|
||||
|
||||
/**
|
||||
* Server sub error number
|
||||
*/
|
||||
serverSubErrorNo?: string;
|
||||
|
||||
/**
|
||||
* Name of the library used for the operation.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
libraryName: string;
|
||||
|
||||
/**
|
||||
* Version of the library used for the operation.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
libraryVersion: string;
|
||||
|
||||
/**
|
||||
* Version of the library used last. Used to track upgrades and downgrades
|
||||
*/
|
||||
previousLibraryVersion?: string;
|
||||
|
||||
/**
|
||||
* Whether the response is from a native component (e.g., WAM)
|
||||
*
|
||||
* @type {?boolean}
|
||||
*/
|
||||
isNativeBroker?: boolean;
|
||||
|
||||
/**
|
||||
* Platform-specific fields, when calling STS and/or broker for token requests
|
||||
*/
|
||||
isPlatformAuthorizeRequest?: boolean;
|
||||
isPlatformBrokerRequest?: boolean;
|
||||
brokerErrorName?: string;
|
||||
brokerErrorCode?: string;
|
||||
|
||||
/**
|
||||
* Request ID returned from the response
|
||||
*
|
||||
* @type {?string}
|
||||
*/
|
||||
requestId?: string;
|
||||
|
||||
/**
|
||||
* Cache lookup policy
|
||||
*
|
||||
* @type {?number}
|
||||
*/
|
||||
cacheLookupPolicy?: number | undefined;
|
||||
|
||||
/**
|
||||
* Cache Outcome
|
||||
* @type {?number}
|
||||
*/
|
||||
cacheOutcome?: number;
|
||||
|
||||
/**
|
||||
* Sub-measurements for internal use. To be deleted before flushing.
|
||||
*/
|
||||
incompleteSubMeasurements?: Map<string, SubMeasurement>;
|
||||
|
||||
visibilityChangeCount?: number;
|
||||
onlineStatusChangeCount?: number;
|
||||
incompleteSubsCount?: number;
|
||||
|
||||
/**
|
||||
* Network connection info from the Network Information API (Chromium only).
|
||||
* Read from: https://developer.mozilla.org/docs/Web/API/NetworkInformation
|
||||
*/
|
||||
networkEffectiveType?: string;
|
||||
networkRtt?: number;
|
||||
|
||||
/**
|
||||
* CorrelationId of the in progress iframe request that was awaited
|
||||
*/
|
||||
awaitIframeCorrelationId?: string;
|
||||
/**
|
||||
* Monitor_window_timeout debugging telemetry
|
||||
*/
|
||||
redirectBridgeTimeoutMs?: number;
|
||||
isRedirectUriCrossOrigin?: boolean;
|
||||
redirectBridgeMessageVersion?: number;
|
||||
lateResponseExperimentEnabled?: boolean;
|
||||
|
||||
/**
|
||||
* Size of the id token
|
||||
*
|
||||
* @type {number}
|
||||
*/
|
||||
idTokenSize?: number;
|
||||
|
||||
/**
|
||||
*
|
||||
* Size of the access token
|
||||
*
|
||||
* @type {number}
|
||||
*/
|
||||
|
||||
accessTokenSize?: number;
|
||||
|
||||
/**
|
||||
*
|
||||
* Size of the refresh token
|
||||
*
|
||||
* @type {number}
|
||||
*/
|
||||
|
||||
refreshTokenSize?: number | undefined;
|
||||
|
||||
/**
|
||||
* Application name as specified by the app.
|
||||
*
|
||||
* @type {?string}
|
||||
*/
|
||||
appName?: string;
|
||||
|
||||
/**
|
||||
* Application version as specified by the app.
|
||||
*
|
||||
* @type {?string}
|
||||
*/
|
||||
appVersion?: string;
|
||||
|
||||
/**
|
||||
* The following are fields that may be emitted in native broker scenarios
|
||||
*/
|
||||
extensionId?: string;
|
||||
extensionVersion?: string;
|
||||
matsBrokerVersion?: string;
|
||||
matsAccountJoinOnStart?: string;
|
||||
matsAccountJoinOnEnd?: string;
|
||||
matsDeviceJoin?: string;
|
||||
matsPromptBehavior?: string;
|
||||
matsApiErrorCode?: number;
|
||||
matsUiVisible?: boolean;
|
||||
matsSilentCode?: number;
|
||||
matsSilentBiSubCode?: number;
|
||||
matsSilentMessage?: string;
|
||||
matsSilentStatus?: number;
|
||||
matsHttpStatus?: number;
|
||||
matsHttpEventCount?: number;
|
||||
|
||||
/**
|
||||
* Http POST metadata
|
||||
*/
|
||||
httpVerToken?: string;
|
||||
httpStatus?: number;
|
||||
contentTypeHeader?: string;
|
||||
contentLengthHeader?: string;
|
||||
|
||||
/**
|
||||
* Platform broker fields
|
||||
*/
|
||||
allowPlatformBroker?: boolean;
|
||||
extensionInstalled?: boolean;
|
||||
extensionHandshakeTimeoutMs?: number;
|
||||
extensionHandshakeTimedOut?: boolean;
|
||||
|
||||
/**
|
||||
* Nested App Auth Fields
|
||||
*/
|
||||
nestedAppAuthRequest?: boolean;
|
||||
|
||||
/**
|
||||
* Multiple matched access/id/refresh tokens in the cache
|
||||
*/
|
||||
multiMatchedAT?: number;
|
||||
multiMatchedID?: number;
|
||||
multiMatchedRT?: number;
|
||||
|
||||
errorName?: string;
|
||||
errorStack?: string[];
|
||||
|
||||
// Event context as JSON string
|
||||
context?: string;
|
||||
|
||||
// Cache Data
|
||||
cacheLocation?: string;
|
||||
cacheRetentionDays?: number;
|
||||
accountCachedBy?: string;
|
||||
acntLoggedOut?: boolean;
|
||||
|
||||
// Number of cached accounts matched by homeAccountId in buildAccountToCache
|
||||
cacheMatchedAccounts?: number;
|
||||
|
||||
// Number of tokens in the cache to be reported when cache quota is exceeded
|
||||
cacheRtCount?: number;
|
||||
cacheIdCount?: number;
|
||||
cacheAtCount?: number;
|
||||
|
||||
// Scenario id to track custom user prompts
|
||||
scenarioId?: string;
|
||||
|
||||
accountType?: "AAD" | "MSA" | "B2C";
|
||||
|
||||
/**
|
||||
* Server error that triggers a request retry
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
retryError?: string;
|
||||
|
||||
embeddedClientId?: string;
|
||||
embeddedRedirectUri?: string;
|
||||
|
||||
isAsyncPopup?: boolean;
|
||||
|
||||
cacheRtExpiresOnSeconds?: number;
|
||||
ntwkRtExpiresOnSeconds?: number;
|
||||
extRtExpiresOnSeconds?: number;
|
||||
rtOffsetSeconds?: number;
|
||||
|
||||
sidFromClaim?: boolean;
|
||||
// Backward-compatible alias for sidFromClaim
|
||||
sidFromClaims?: boolean;
|
||||
sidFromRequest?: boolean;
|
||||
loginHintFromRequest?: boolean;
|
||||
loginHintFromUpn?: boolean;
|
||||
loginHintFromClaim?: boolean;
|
||||
domainHintFromRequest?: boolean;
|
||||
|
||||
prompt?: string;
|
||||
|
||||
usePreGeneratedPkce?: boolean;
|
||||
|
||||
// Number of MSAL JS instances in the frame
|
||||
msalInstanceCount?: number;
|
||||
// Number of MSAL JS instances using the same client id in the frame
|
||||
sameClientIdInstanceCount?: number;
|
||||
|
||||
navigateCallbackResult?: boolean;
|
||||
|
||||
dataBoundary?: DataBoundary;
|
||||
|
||||
// Hashed logs in the format [millis1,hash1;millis2,hash2;...]
|
||||
logs?: string;
|
||||
|
||||
// Whether the application is configured for MCP flows
|
||||
isMcp?: boolean;
|
||||
|
||||
/**
|
||||
* Source of cloud discovery metadata (config, cache, network, hardcoded_values)
|
||||
*/
|
||||
cloudDiscoverySource?: string;
|
||||
|
||||
/**
|
||||
* Source of authority endpoint metadata (config, cache, network, hardcoded_values)
|
||||
*/
|
||||
authorityEndpointSource?: string;
|
||||
|
||||
/**
|
||||
* Number of accounts removed during cache cleanup
|
||||
*/
|
||||
accountsRemoved?: number;
|
||||
|
||||
/**
|
||||
* Number of access tokens removed during cache cleanup
|
||||
*/
|
||||
accessTokensRemoved?: number;
|
||||
|
||||
/**
|
||||
* Number of failures when removing token binding keys
|
||||
*/
|
||||
removeTokenBindingKeyFailure?: number;
|
||||
|
||||
/**
|
||||
* Reason for silent refresh fallback to iframe
|
||||
* Format: errorCode or errorCode|subError
|
||||
*
|
||||
* @type {?string}
|
||||
*/
|
||||
silentRefreshReason?: string;
|
||||
|
||||
/**
|
||||
* Whether this request was deduped with another in-flight request
|
||||
*/
|
||||
deduped?: boolean;
|
||||
|
||||
/**
|
||||
* Whether the user has "Keep Me Signed In" enabled
|
||||
*/
|
||||
kmsi?: boolean;
|
||||
|
||||
/**
|
||||
* Cached SSO capability status from the most recent SSO verification
|
||||
*/
|
||||
ssoCapable?: boolean;
|
||||
|
||||
/**
|
||||
* Whether this event was executed in the background
|
||||
*/
|
||||
isBackground?: boolean;
|
||||
|
||||
/**
|
||||
* Cache migration telemetry — pre-migration counts
|
||||
*/
|
||||
preMigrateAcntCount?: number;
|
||||
preMigrateATCount?: number;
|
||||
preMigrateITCount?: number;
|
||||
preMigrateRTCount?: number;
|
||||
|
||||
/**
|
||||
* Cache migration telemetry — post-migration counts
|
||||
*/
|
||||
postMigrateAcntCount?: number;
|
||||
postMigrateATCount?: number;
|
||||
postMigrateITCount?: number;
|
||||
postMigrateRTCount?: number;
|
||||
|
||||
/**
|
||||
* Cache migration telemetry — old schema counts
|
||||
*/
|
||||
oldAcntCount?: number;
|
||||
oldATCount?: number;
|
||||
oldITCount?: number;
|
||||
oldRTCount?: number;
|
||||
|
||||
/**
|
||||
* Cache migration telemetry — skipped and migrated counts
|
||||
*/
|
||||
skipATMigrateCount?: number;
|
||||
skipITMigrateCount?: number;
|
||||
skipRTMigrateCount?: number;
|
||||
migratedATCount?: number;
|
||||
migratedITCount?: number;
|
||||
migratedRTCount?: number;
|
||||
|
||||
/**
|
||||
* Cache telemetry — expired, invalid, and removed counts
|
||||
*/
|
||||
expiredCacheRemovedCount?: number;
|
||||
expiredAcntRemovedCount?: number;
|
||||
invalidCacheCount?: number;
|
||||
|
||||
/**
|
||||
* Encrypted cache telemetry
|
||||
*/
|
||||
unencryptedCacheCount?: number;
|
||||
encryptedCacheCount?: number;
|
||||
encryptedCacheExpiredCount?: number;
|
||||
encryptedCacheCorruptionCount?: number;
|
||||
|
||||
/**
|
||||
* Container for dynamically-named telemetry fields.
|
||||
* Fields whose names are constructed at runtime (e.g., "[eventName]CallCount")
|
||||
* should be stored here instead of being set as top-level properties.
|
||||
* Use the "ext." prefix when calling addFields/incrementFields to automatically
|
||||
* route fields to this sub-object.
|
||||
*
|
||||
* @remarks
|
||||
* This property is typed as `Record<string, string | number>`.
|
||||
*/
|
||||
ext?: Record<string, string | number>;
|
||||
};
|
||||
|
||||
export type PerformanceEventContext = {
|
||||
dur?: number;
|
||||
err?: string;
|
||||
subErr?: string;
|
||||
fail?: number;
|
||||
};
|
||||
|
||||
export type PerformanceEventStackedContext = PerformanceEventContext & {
|
||||
name?: string;
|
||||
childErr?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Prefix used to mark telemetry field names as dynamic.
|
||||
* Fields with this prefix in addFields/incrementFields calls will be routed
|
||||
* to the PerformanceEvent.ext sub-object.
|
||||
*/
|
||||
export const EXT_FIELD_PREFIX = "ext.";
|
||||
|
||||
export const IntFields: ReadonlySet<string> = new Set([
|
||||
"accessTokenSize",
|
||||
"durationMs",
|
||||
"idTokenSize",
|
||||
"matsSilentStatus",
|
||||
"matsHttpStatus",
|
||||
"refreshTokenSize",
|
||||
"startTimeMs",
|
||||
"status",
|
||||
"multiMatchedAT",
|
||||
"multiMatchedID",
|
||||
"multiMatchedRT",
|
||||
"unencryptedCacheCount",
|
||||
"encryptedCacheExpiredCount",
|
||||
"oldAccountCount",
|
||||
"oldAccessCount",
|
||||
"oldIdCount",
|
||||
"oldRefreshCount",
|
||||
"currAccountCount",
|
||||
"currAccessCount",
|
||||
"currIdCount",
|
||||
"currRefreshCount",
|
||||
"expiredCacheRemovedCount",
|
||||
"upgradedCacheCount",
|
||||
"cacheMatchedAccounts",
|
||||
"networkRtt",
|
||||
"redirectBridgeTimeoutMs",
|
||||
"redirectBridgeMessageVersion",
|
||||
]);
|
||||
98
backend/node_modules/@azure/msal-common/src/telemetry/performance/PerformanceEvents.ts
generated
vendored
Normal file
98
backend/node_modules/@azure/msal-common/src/telemetry/performance/PerformanceEvents.ts
generated
vendored
Normal file
@ -0,0 +1,98 @@
|
||||
/*
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Time spent sending/waiting for the response of a request to the token endpoint
|
||||
*/
|
||||
export const NetworkClientSendPostRequestAsync =
|
||||
"networkClientSendPostRequestAsync";
|
||||
export const RefreshTokenClientExecutePostToTokenEndpoint =
|
||||
"refreshTokenClientExecutePostToTokenEndpoint";
|
||||
export const AuthorizationCodeClientExecutePostToTokenEndpoint =
|
||||
"authorizationCodeClientExecutePostToTokenEndpoint";
|
||||
|
||||
/**
|
||||
* Time spent on the network for refresh token acquisition
|
||||
*/
|
||||
export const RefreshTokenClientExecuteTokenRequest =
|
||||
"refreshTokenClientExecuteTokenRequest";
|
||||
|
||||
/**
|
||||
* Time taken for acquiring refresh token , records RT size
|
||||
*/
|
||||
export const RefreshTokenClientAcquireToken = "refreshTokenClientAcquireToken";
|
||||
|
||||
/**
|
||||
* Time taken for acquiring cached refresh token
|
||||
*/
|
||||
export const RefreshTokenClientAcquireTokenWithCachedRefreshToken =
|
||||
"refreshTokenClientAcquireTokenWithCachedRefreshToken";
|
||||
|
||||
/**
|
||||
* Helper function to create token request body in RefreshTokenClient (msal-common).
|
||||
*/
|
||||
export const RefreshTokenClientCreateTokenRequestBody =
|
||||
"refreshTokenClientCreateTokenRequestBody";
|
||||
|
||||
export const SilentFlowClientGenerateResultFromCacheRecord =
|
||||
"silentFlowClientGenerateResultFromCacheRecord";
|
||||
|
||||
/**
|
||||
* getAuthCodeUrl API (msal-browser and msal-node).
|
||||
*/
|
||||
export const GetAuthCodeUrl = "getAuthCodeUrl";
|
||||
|
||||
/**
|
||||
* Functions from InteractionHandler (msal-browser)
|
||||
*/
|
||||
export const HandleCodeResponseFromServer = "handleCodeResponseFromServer";
|
||||
|
||||
/**
|
||||
* APIs in Authorization Code Client (msal-common)
|
||||
*/
|
||||
export const AuthClientExecuteTokenRequest = "authClientExecuteTokenRequest";
|
||||
export const AuthClientCreateTokenRequestBody =
|
||||
"authClientCreateTokenRequestBody";
|
||||
export const UpdateTokenEndpointAuthority = "updateTokenEndpointAuthority";
|
||||
|
||||
/**
|
||||
* Generate functions in PopTokenGenerator (msal-common)
|
||||
*/
|
||||
export const PopTokenGenerateCnf = "popTokenGenerateCnf";
|
||||
|
||||
/**
|
||||
* handleServerTokenResponse API in ResponseHandler (msal-common)
|
||||
*/
|
||||
export const HandleServerTokenResponse = "handleServerTokenResponse";
|
||||
|
||||
/**
|
||||
* Authority functions
|
||||
*/
|
||||
export const AuthorityResolveEndpointsAsync = "authorityResolveEndpointsAsync";
|
||||
export const AuthorityGetCloudDiscoveryMetadataFromNetwork =
|
||||
"authorityGetCloudDiscoveryMetadataFromNetwork";
|
||||
export const AuthorityUpdateCloudDiscoveryMetadata =
|
||||
"authorityUpdateCloudDiscoveryMetadata";
|
||||
export const AuthorityGetEndpointMetadataFromNetwork =
|
||||
"authorityGetEndpointMetadataFromNetwork";
|
||||
export const AuthorityUpdateEndpointMetadata =
|
||||
"authorityUpdateEndpointMetadata";
|
||||
export const AuthorityUpdateMetadataWithRegionalInformation =
|
||||
"authorityUpdateMetadataWithRegionalInformation";
|
||||
|
||||
/**
|
||||
* Region Discovery functions
|
||||
*/
|
||||
export const RegionDiscoveryDetectRegion = "regionDiscoveryDetectRegion";
|
||||
export const RegionDiscoveryGetRegionFromIMDS =
|
||||
"regionDiscoveryGetRegionFromIMDS";
|
||||
export const RegionDiscoveryGetCurrentVersion =
|
||||
"regionDiscoveryGetCurrentVersion";
|
||||
|
||||
/**
|
||||
* Cache operations
|
||||
*/
|
||||
export const CacheManagerGetRefreshToken = "cacheManagerGetRefreshToken";
|
||||
export const SetUserData = "setUserData";
|
||||
87
backend/node_modules/@azure/msal-common/src/telemetry/performance/StubPerformanceClient.ts
generated
vendored
Normal file
87
backend/node_modules/@azure/msal-common/src/telemetry/performance/StubPerformanceClient.ts
generated
vendored
Normal file
@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License.
|
||||
*/
|
||||
|
||||
import {
|
||||
IPerformanceClient,
|
||||
InProgressPerformanceEvent,
|
||||
} from "./IPerformanceClient.js";
|
||||
import { IPerformanceMeasurement } from "./IPerformanceMeasurement.js";
|
||||
import {
|
||||
PerformanceEvent,
|
||||
PerformanceEventStatus,
|
||||
} from "./PerformanceEvent.js";
|
||||
|
||||
export class StubPerformanceMeasurement implements IPerformanceMeasurement {
|
||||
startMeasurement(): void {
|
||||
return;
|
||||
}
|
||||
endMeasurement(): void {
|
||||
return;
|
||||
}
|
||||
flushMeasurement(): number | null {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export class StubPerformanceClient implements IPerformanceClient {
|
||||
generateId(): string {
|
||||
return "callback-id";
|
||||
}
|
||||
|
||||
startMeasurement(
|
||||
measureName: string,
|
||||
correlationId?: string | undefined
|
||||
): InProgressPerformanceEvent {
|
||||
return {
|
||||
end: () => null,
|
||||
discard: () => {},
|
||||
add: () => {},
|
||||
increment: () => {},
|
||||
event: {
|
||||
eventId: this.generateId(),
|
||||
status: PerformanceEventStatus.InProgress,
|
||||
authority: "",
|
||||
libraryName: "",
|
||||
libraryVersion: "",
|
||||
clientId: "",
|
||||
name: measureName,
|
||||
startTimeMs: Date.now(),
|
||||
correlationId: correlationId || "",
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
endMeasurement(): PerformanceEvent | null {
|
||||
return null;
|
||||
}
|
||||
|
||||
discardMeasurements(): void {
|
||||
return;
|
||||
}
|
||||
|
||||
removePerformanceCallback(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
addPerformanceCallback(): string {
|
||||
return "";
|
||||
}
|
||||
|
||||
emitEvents(): void {
|
||||
return;
|
||||
}
|
||||
|
||||
addFields(): void {
|
||||
return;
|
||||
}
|
||||
|
||||
incrementFields(): void {
|
||||
return;
|
||||
}
|
||||
|
||||
cacheEventByCorrelationId(): void {
|
||||
return;
|
||||
}
|
||||
}
|
||||
371
backend/node_modules/@azure/msal-common/src/telemetry/server/ServerTelemetryManager.ts
generated
vendored
Normal file
371
backend/node_modules/@azure/msal-common/src/telemetry/server/ServerTelemetryManager.ts
generated
vendored
Normal file
@ -0,0 +1,371 @@
|
||||
/*
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License.
|
||||
*/
|
||||
|
||||
import * as Constants from "../../utils/Constants.js";
|
||||
import { CacheManager } from "../../cache/CacheManager.js";
|
||||
import { AuthError } from "../../error/AuthError.js";
|
||||
import { ServerTelemetryRequest } from "./ServerTelemetryRequest.js";
|
||||
import { ServerTelemetryEntity } from "../../cache/entities/ServerTelemetryEntity.js";
|
||||
import { RegionDiscoveryMetadata } from "../../authority/RegionDiscoveryMetadata.js";
|
||||
|
||||
const skuGroupSeparator = ",";
|
||||
const skuValueSeparator = "|";
|
||||
|
||||
type SkuParams = {
|
||||
libraryName?: string;
|
||||
libraryVersion?: string;
|
||||
extensionName?: string;
|
||||
extensionVersion?: string;
|
||||
skus?: string;
|
||||
};
|
||||
|
||||
function makeExtraSkuString(params: SkuParams): string {
|
||||
const {
|
||||
skus,
|
||||
libraryName,
|
||||
libraryVersion,
|
||||
extensionName,
|
||||
extensionVersion,
|
||||
} = params;
|
||||
const skuMap: Map<number, (string | undefined)[]> = new Map([
|
||||
[0, [libraryName, libraryVersion]],
|
||||
[2, [extensionName, extensionVersion]],
|
||||
]);
|
||||
let skuArr: string[] = [];
|
||||
|
||||
if (skus?.length) {
|
||||
skuArr = skus.split(skuGroupSeparator);
|
||||
|
||||
// Ignore invalid input sku param
|
||||
if (skuArr.length < 4) {
|
||||
return skus;
|
||||
}
|
||||
} else {
|
||||
skuArr = Array.from({ length: 4 }, () => skuValueSeparator);
|
||||
}
|
||||
|
||||
skuMap.forEach((value, key) => {
|
||||
if (value.length === 2 && value[0]?.length && value[1]?.length) {
|
||||
setSku({
|
||||
skuArr,
|
||||
index: key,
|
||||
skuName: value[0],
|
||||
skuVersion: value[1],
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return skuArr.join(skuGroupSeparator);
|
||||
}
|
||||
|
||||
function setSku(params: {
|
||||
skuArr: string[];
|
||||
index: number;
|
||||
skuName: string;
|
||||
skuVersion: string;
|
||||
}): void {
|
||||
const { skuArr, index, skuName, skuVersion } = params;
|
||||
if (index >= skuArr.length) {
|
||||
return;
|
||||
}
|
||||
skuArr[index] = [skuName, skuVersion].join(skuValueSeparator);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
export class ServerTelemetryManager {
|
||||
private cacheManager: CacheManager;
|
||||
private apiId: number;
|
||||
private correlationId: string;
|
||||
private telemetryCacheKey: string;
|
||||
private wrapperSKU: String;
|
||||
private wrapperVer: String;
|
||||
private regionUsed: string | undefined;
|
||||
private regionSource: Constants.RegionDiscoverySources | undefined;
|
||||
private regionOutcome: Constants.RegionDiscoveryOutcomes | undefined;
|
||||
private cacheOutcome: Constants.CacheOutcome =
|
||||
Constants.CacheOutcome.NOT_APPLICABLE;
|
||||
|
||||
constructor(
|
||||
telemetryRequest: ServerTelemetryRequest,
|
||||
cacheManager: CacheManager
|
||||
) {
|
||||
this.cacheManager = cacheManager;
|
||||
this.apiId = telemetryRequest.apiId;
|
||||
this.correlationId = telemetryRequest.correlationId;
|
||||
this.wrapperSKU = telemetryRequest.wrapperSKU || "";
|
||||
this.wrapperVer = telemetryRequest.wrapperVer || "";
|
||||
|
||||
this.telemetryCacheKey =
|
||||
Constants.SERVER_TELEM_CACHE_KEY +
|
||||
Constants.CACHE_KEY_SEPARATOR +
|
||||
telemetryRequest.clientId;
|
||||
}
|
||||
|
||||
/**
|
||||
* API to add MSER Telemetry to request
|
||||
*/
|
||||
generateCurrentRequestHeaderValue(): string {
|
||||
const request = `${this.apiId}${Constants.SERVER_TELEM_VALUE_SEPARATOR}${this.cacheOutcome}`;
|
||||
const platformFieldsArr = [this.wrapperSKU, this.wrapperVer];
|
||||
const nativeBrokerErrorCode = this.getNativeBrokerErrorCode();
|
||||
if (nativeBrokerErrorCode?.length) {
|
||||
platformFieldsArr.push(`broker_error=${nativeBrokerErrorCode}`);
|
||||
}
|
||||
const platformFields = platformFieldsArr.join(
|
||||
Constants.SERVER_TELEM_VALUE_SEPARATOR
|
||||
);
|
||||
const regionDiscoveryFields = this.getRegionDiscoveryFields();
|
||||
const requestWithRegionDiscoveryFields = [
|
||||
request,
|
||||
regionDiscoveryFields,
|
||||
].join(Constants.SERVER_TELEM_VALUE_SEPARATOR);
|
||||
|
||||
return [
|
||||
Constants.SERVER_TELEM_SCHEMA_VERSION,
|
||||
requestWithRegionDiscoveryFields,
|
||||
platformFields,
|
||||
].join(Constants.SERVER_TELEM_CATEGORY_SEPARATOR);
|
||||
}
|
||||
|
||||
/**
|
||||
* API to add MSER Telemetry for the last failed request
|
||||
*/
|
||||
generateLastRequestHeaderValue(): string {
|
||||
const lastRequests = this.getLastRequests();
|
||||
|
||||
const maxErrors = ServerTelemetryManager.maxErrorsToSend(lastRequests);
|
||||
const failedRequests = lastRequests.failedRequests
|
||||
.slice(0, 2 * maxErrors)
|
||||
.join(Constants.SERVER_TELEM_VALUE_SEPARATOR);
|
||||
const errors = lastRequests.errors
|
||||
.slice(0, maxErrors)
|
||||
.join(Constants.SERVER_TELEM_VALUE_SEPARATOR);
|
||||
const errorCount = lastRequests.errors.length;
|
||||
|
||||
// Indicate whether this header contains all data or partial data
|
||||
const overflow =
|
||||
maxErrors < errorCount
|
||||
? Constants.SERVER_TELEM_OVERFLOW_TRUE
|
||||
: Constants.SERVER_TELEM_OVERFLOW_FALSE;
|
||||
const platformFields = [errorCount, overflow].join(
|
||||
Constants.SERVER_TELEM_VALUE_SEPARATOR
|
||||
);
|
||||
|
||||
return [
|
||||
Constants.SERVER_TELEM_SCHEMA_VERSION,
|
||||
lastRequests.cacheHits,
|
||||
failedRequests,
|
||||
errors,
|
||||
platformFields,
|
||||
].join(Constants.SERVER_TELEM_CATEGORY_SEPARATOR);
|
||||
}
|
||||
|
||||
/**
|
||||
* API to cache token failures for MSER data capture
|
||||
* @param error
|
||||
*/
|
||||
cacheFailedRequest(error: unknown): void {
|
||||
const lastRequests = this.getLastRequests();
|
||||
if (
|
||||
lastRequests.errors.length >=
|
||||
Constants.SERVER_TELEM_MAX_CACHED_ERRORS
|
||||
) {
|
||||
// Remove a cached error to make room, first in first out
|
||||
lastRequests.failedRequests.shift(); // apiId
|
||||
lastRequests.failedRequests.shift(); // correlationId
|
||||
lastRequests.errors.shift();
|
||||
}
|
||||
|
||||
lastRequests.failedRequests.push(this.apiId, this.correlationId);
|
||||
|
||||
if (error instanceof Error && !!error && error.toString()) {
|
||||
if (error instanceof AuthError) {
|
||||
if (error.subError) {
|
||||
lastRequests.errors.push(error.subError);
|
||||
} else if (error.errorCode) {
|
||||
lastRequests.errors.push(error.errorCode);
|
||||
} else {
|
||||
lastRequests.errors.push(error.toString());
|
||||
}
|
||||
} else {
|
||||
lastRequests.errors.push(error.toString());
|
||||
}
|
||||
} else {
|
||||
lastRequests.errors.push(Constants.SERVER_TELEM_UNKNOWN_ERROR);
|
||||
}
|
||||
|
||||
this.cacheManager.setServerTelemetry(
|
||||
this.telemetryCacheKey,
|
||||
lastRequests,
|
||||
this.correlationId
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update server telemetry cache entry by incrementing cache hit counter
|
||||
*/
|
||||
incrementCacheHits(): number {
|
||||
const lastRequests = this.getLastRequests();
|
||||
lastRequests.cacheHits += 1;
|
||||
|
||||
this.cacheManager.setServerTelemetry(
|
||||
this.telemetryCacheKey,
|
||||
lastRequests,
|
||||
this.correlationId
|
||||
);
|
||||
return lastRequests.cacheHits;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the server telemetry entity from cache or initialize a new one
|
||||
*/
|
||||
getLastRequests(): ServerTelemetryEntity {
|
||||
const initialValue: ServerTelemetryEntity = {
|
||||
failedRequests: [],
|
||||
errors: [],
|
||||
cacheHits: 0,
|
||||
};
|
||||
const lastRequests = this.cacheManager.getServerTelemetry(
|
||||
this.telemetryCacheKey,
|
||||
this.correlationId
|
||||
) as ServerTelemetryEntity;
|
||||
|
||||
return lastRequests || initialValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove server telemetry cache entry
|
||||
*/
|
||||
clearTelemetryCache(): void {
|
||||
const lastRequests = this.getLastRequests();
|
||||
const numErrorsFlushed =
|
||||
ServerTelemetryManager.maxErrorsToSend(lastRequests);
|
||||
const errorCount = lastRequests.errors.length;
|
||||
if (numErrorsFlushed === errorCount) {
|
||||
// All errors were sent on last request, clear Telemetry cache
|
||||
this.cacheManager.removeItem(
|
||||
this.telemetryCacheKey,
|
||||
this.correlationId
|
||||
);
|
||||
} else {
|
||||
// Partial data was flushed to server, construct a new telemetry cache item with errors that were not flushed
|
||||
const serverTelemEntity: ServerTelemetryEntity = {
|
||||
failedRequests: lastRequests.failedRequests.slice(
|
||||
numErrorsFlushed * 2
|
||||
), // failedRequests contains 2 items for each error
|
||||
errors: lastRequests.errors.slice(numErrorsFlushed),
|
||||
cacheHits: 0,
|
||||
};
|
||||
|
||||
this.cacheManager.setServerTelemetry(
|
||||
this.telemetryCacheKey,
|
||||
serverTelemEntity,
|
||||
this.correlationId
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the maximum number of errors that can be flushed to the server in the next network request
|
||||
* @param serverTelemetryEntity
|
||||
*/
|
||||
static maxErrorsToSend(
|
||||
serverTelemetryEntity: ServerTelemetryEntity
|
||||
): number {
|
||||
let i;
|
||||
let maxErrors = 0;
|
||||
let dataSize = 0;
|
||||
const errorCount = serverTelemetryEntity.errors.length;
|
||||
for (i = 0; i < errorCount; i++) {
|
||||
// failedRequests parameter contains pairs of apiId and correlationId, multiply index by 2 to preserve pairs
|
||||
const apiId = serverTelemetryEntity.failedRequests[2 * i] || "";
|
||||
const correlationId =
|
||||
serverTelemetryEntity.failedRequests[2 * i + 1] || "";
|
||||
const errorCode = serverTelemetryEntity.errors[i] || "";
|
||||
|
||||
// Count number of characters that would be added to header, each character is 1 byte. Add 3 at the end to account for separators
|
||||
dataSize +=
|
||||
apiId.toString().length +
|
||||
correlationId.toString().length +
|
||||
errorCode.length +
|
||||
3;
|
||||
|
||||
if (dataSize < Constants.SERVER_TELEM_MAX_LAST_HEADER_BYTES) {
|
||||
// Adding this entry to the header would still keep header size below the limit
|
||||
maxErrors += 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return maxErrors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the region discovery fields
|
||||
*
|
||||
* @returns string
|
||||
*/
|
||||
getRegionDiscoveryFields(): string {
|
||||
const regionDiscoveryFields: string[] = [];
|
||||
|
||||
regionDiscoveryFields.push(this.regionUsed || "");
|
||||
regionDiscoveryFields.push(this.regionSource || "");
|
||||
regionDiscoveryFields.push(this.regionOutcome || "");
|
||||
|
||||
return regionDiscoveryFields.join(",");
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the region discovery metadata
|
||||
*
|
||||
* @param regionDiscoveryMetadata
|
||||
* @returns void
|
||||
*/
|
||||
updateRegionDiscoveryMetadata(
|
||||
regionDiscoveryMetadata: RegionDiscoveryMetadata
|
||||
): void {
|
||||
this.regionUsed = regionDiscoveryMetadata.region_used;
|
||||
this.regionSource = regionDiscoveryMetadata.region_source;
|
||||
this.regionOutcome = regionDiscoveryMetadata.region_outcome;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set cache outcome
|
||||
*/
|
||||
setCacheOutcome(cacheOutcome: Constants.CacheOutcome): void {
|
||||
this.cacheOutcome = cacheOutcome;
|
||||
}
|
||||
|
||||
setNativeBrokerErrorCode(errorCode: string): void {
|
||||
const lastRequests = this.getLastRequests();
|
||||
lastRequests.nativeBrokerErrorCode = errorCode;
|
||||
this.cacheManager.setServerTelemetry(
|
||||
this.telemetryCacheKey,
|
||||
lastRequests,
|
||||
this.correlationId
|
||||
);
|
||||
}
|
||||
|
||||
getNativeBrokerErrorCode(): string | undefined {
|
||||
return this.getLastRequests().nativeBrokerErrorCode;
|
||||
}
|
||||
|
||||
clearNativeBrokerErrorCode(): void {
|
||||
const lastRequests = this.getLastRequests();
|
||||
delete lastRequests.nativeBrokerErrorCode;
|
||||
this.cacheManager.setServerTelemetry(
|
||||
this.telemetryCacheKey,
|
||||
lastRequests,
|
||||
this.correlationId
|
||||
);
|
||||
}
|
||||
|
||||
static makeExtraSkuString(params: SkuParams): string {
|
||||
return makeExtraSkuString(params);
|
||||
}
|
||||
}
|
||||
13
backend/node_modules/@azure/msal-common/src/telemetry/server/ServerTelemetryRequest.ts
generated
vendored
Normal file
13
backend/node_modules/@azure/msal-common/src/telemetry/server/ServerTelemetryRequest.ts
generated
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
/*
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License.
|
||||
*/
|
||||
|
||||
export type ServerTelemetryRequest = {
|
||||
clientId: string;
|
||||
apiId: number;
|
||||
correlationId: string;
|
||||
forceRefresh?: boolean;
|
||||
wrapperSKU?: string;
|
||||
wrapperVer?: string;
|
||||
};
|
||||
Reference in New Issue
Block a user